Skip to content

Commit 0765636

Browse files
committed
[platform] Amiga port (m68k)
Tested on real hardware and under emulation; combinations of accelerated and stock A500, A600, and A1200. All CPU models are supported, although 060 requires being built with 'M68K_CPU = 68060'. Current default target is 68000, for greatest compatibility. Features and hardware support: - Basic 1bpp display driver - Keyboard driver - Serial console support - Timer hardware driver - Custom bootloader - Heap arena size is based on detected chip RAM size Current limitations: - Floppy boot only, no IDE/ATA (tested with Gotek, should work with real disk) - Only chip RAM supported. Boots with Fast Mem present, but doesn't utilise it. - Basic graphical abilities, room for improvements in acceleration and colour depth. - Early kernel boot output only present on serial console, though I believe this is due to the nature of the current gfxconsole design.
1 parent 8a55951 commit 0765636

14 files changed

Lines changed: 1457 additions & 0 deletions

File tree

platform/amiga/display.c

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/*
2+
* Copyright (c) 2026 Josh Cummings
3+
*
4+
* Use of this source code is governed by a MIT-style
5+
* license that can be found in the LICENSE file or at
6+
* https://opensource.org/licenses/MIT
7+
*/
8+
9+
#include "platform_p.h"
10+
#include <dev/display.h>
11+
#include <lib/page_alloc.h>
12+
#include <lk/err.h>
13+
#include <lk/main.h>
14+
#include <platform/display.h>
15+
#include <string.h>
16+
#if WITH_KERNEL_VM
17+
#include <kernel/vm.h>
18+
#else
19+
#include <kernel/novm.h>
20+
#endif
21+
22+
static volatile uint16_t *const paula_base = (void *)0xDFF000;
23+
static struct display_framebuffer display_fb;
24+
25+
// 1bpp bitplane
26+
static uint8_t bitplane[BPL_BYTES];
27+
static uint16_t copper[64];
28+
29+
void make_copper_list(uint8_t *bpl) {
30+
// Bitplane ptr register is two 16bit halves. May need to change this if vm/MMU is ever enabled.
31+
uintptr_t bpl_ptr = (uintptr_t)bpl;
32+
uint16_t bplptr_hi = (bpl_ptr >> 16);
33+
uint16_t bplptr_lo = (bpl_ptr & 0xFFFF);
34+
35+
int i = 0;
36+
37+
// clang-format off
38+
39+
// Copper instructions are two words. First word is the destination register offset, second is the value.
40+
41+
// Set display window
42+
copper[i++] = DIWSTRT; copper[i++] = 0x2C81; // Display window vert + horiz start, PAL low-res mode
43+
copper[i++] = DIWSTOP; copper[i++] = ((((0x2C + H) & 0xFF) << 8) | 0xC1); // vert + horiz end
44+
copper[i++] = DDFSTRT; copper[i++] = 0x0038; // low-res DMA fetch start
45+
copper[i++] = DDFSTOP; copper[i++] = 0x00D0; // DMA fetch stop. Stop and start may need to change if screen width ever does.
46+
47+
// 1 bitplane, enable "colour" output
48+
copper[i++] = BPLCON0; copper[i++] = (0x200 | (1 << 12)); // Set display mode and bitplane count ("BPU"); one bitplane
49+
copper[i++] = BPLCON1; copper[i++] = 0x0000; // No horizontal playfield scroll
50+
copper[i++] = BPLCON2; copper[i++] = 0x0000; // Set playfield priority
51+
52+
// Bitplane modulo = 0 for a single linear bitplane
53+
copper[i++] = BPL1MOD; copper[i++] = 0x0000;
54+
copper[i++] = BPL2MOD; copper[i++] = 0x0000;
55+
56+
// Set bitplane pointer
57+
copper[i++] = BPL1PTH; copper[i++] = bplptr_hi;
58+
copper[i++] = BPL1PTL; copper[i++] = bplptr_lo;
59+
60+
// Background and foreground colours
61+
copper[i++] = COLOR00; copper[i++] = 0x0000;
62+
copper[i++] = COLOR01; copper[i++] = 0x0FFF;
63+
64+
// Send end/terminator words, causes Copper to stop processing the list
65+
copper[i++] = 0xFFFF;
66+
copper[i++] = 0xFFFE;
67+
68+
// clang-format on
69+
}
70+
71+
static void write_reg(unsigned int reg, uint32_t val) {
72+
paula_base[reg >> 1] = val;
73+
}
74+
75+
status_t display_get_framebuffer(struct display_framebuffer *fb) {
76+
fb->image.pixels = bitplane;
77+
78+
fb->image.format = IMAGE_FORMAT_MONO_1;
79+
fb->image.rowbytes = BYTES_PER_ROW;
80+
fb->image.width = W;
81+
fb->image.height = H;
82+
fb->image.stride = BYTES_PER_ROW;
83+
fb->format = DISPLAY_FORMAT_MONO_1;
84+
fb->flush = NULL;
85+
86+
return NO_ERROR;
87+
}
88+
89+
status_t display_get_info(struct display_info *info) {
90+
info->format = DISPLAY_FORMAT_MONO_1;
91+
info->width = W;
92+
info->height = H;
93+
94+
return NO_ERROR;
95+
}
96+
97+
void platform_init_display(void) {
98+
memset(bitplane, 0, BPL_BYTES);
99+
100+
make_copper_list(bitplane);
101+
102+
// Disable DMA
103+
write_reg(DMACON, 0x7FFF);
104+
105+
// Set up copper list address, high and low.
106+
// Physical address, might need revisiting if/when vm/MMU
107+
uintptr_t clist = (uintptr_t)copper;
108+
write_reg(COP1LCH, (clist >> 16));
109+
write_reg(COP1LCL, (clist & 0xFFFF));
110+
write_reg(COPJMP1, 0x0000);
111+
112+
// Enable DMA.
113+
// 0x200 = master DMA enable, 0x100 = bitplane DMA enable, 0x0080 = Copper DMA enable
114+
write_reg(DMACON, (0x8000 | 0x0200 | 0x0080 | 0x0100));
115+
116+
display_get_framebuffer(&display_fb);
117+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/*
2+
* Copyright (c) 2026 Josh Cummings
3+
*
4+
* Use of this source code is governed by a MIT-style
5+
* license that can be found in the LICENSE file or at
6+
* https://opensource.org/licenses/MIT
7+
*/
8+
9+
#pragma once
10+
11+
enum { W = 320,
12+
H = 256,
13+
// Round up width to next multiple. bitplane DMA is word-aligned (16 pixels).
14+
BYTES_PER_ROW = (((W + 15) & ~15) / 8),
15+
BPL_BYTES = (BYTES_PER_ROW * H) };
16+
17+
void make_copper_list(uint8_t *bpl);
18+
void platform_init_display(void);
19+
void init_display(void);

platform/amiga/keyboard.c

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
/*
2+
* Copyright (c) 2026 Josh Cummings
3+
*
4+
* Use of this source code is governed by a MIT-style
5+
* license that can be found in the LICENSE file or at
6+
* https://opensource.org/licenses/MIT
7+
*/
8+
9+
#include "platform_p.h"
10+
#include <lib/cbuf.h>
11+
#include <lib/console.h>
12+
#include <platform.h>
13+
#include <platform/interrupts.h>
14+
15+
#define SCANCODE_LSHIFT 0x60
16+
#define SCANCODE_RSHIFT 0x61
17+
18+
static volatile uint8_t *const cia_base = (volatile uint8_t *)CIA_A_BASE;
19+
20+
// clang-format off
21+
// Amiga raw keycode -> ASCII (US layout). Unassigned entries are 0.
22+
static const int KeyCodeSingleLower[128] = {
23+
[0x00] = '`', [0x01] = '1', [0x02] = '2', [0x03] = '3', [0x04] = '4', [0x05] = '5',
24+
[0x06] = '6', [0x07] = '7', [0x08] = '8', [0x09] = '9', [0x0a] = '0', [0x0b] = '-',
25+
[0x0c] = '=', [0x0d] = '\\', [0x0e] = 0, [0x41] = '\b', [0x42] = '\t',
26+
27+
[0x10] = 'q', [0x11] = 'w', [0x12] = 'e', [0x13] = 'r', [0x14] = 't', [0x15] = 'y',
28+
[0x16] = 'u', [0x17] = 'i', [0x18] = 'o', [0x19] = 'p', [0x1a] = '[', [0x1b] = ']',
29+
[0x44] = '\n', // Enter/Return
30+
31+
[0x62] = 0,
32+
[0x20] = 'a', [0x21] = 's', [0x22] = 'd', [0x23] = 'f', [0x24] = 'g', [0x25] = 'h',
33+
[0x26] = 'j', [0x27] = 'k', [0x28] = 'l', [0x29] = ';', [0x2a] = '\'', [0x31] = 'z',
34+
[0x32] = 'x', [0x33] = 'c', [0x34] = 'v', [0x35] = 'b', [0x36] = 'n', [0x37] = 'm',
35+
[0x38] = ',', [0x39] = '.', [0x3a] = '/',
36+
[0x40] = ' ', [0x45] = 0x1b, // Space & Esc
37+
38+
// Keypad
39+
[0x0f] = '0', [0x1d] = '1', [0x1e] = '2', [0x1f] = '3', [0x2d] = '4', [0x2e] = '5',
40+
[0x2f] = '6', [0x3d] = '7', [0x3e] = '8', [0x3f] = '9', [0x3c] = '.', [0x4a] = '-',
41+
[0x5a] = '(', [0x5b] = ')', [0x5c] = '/', [0x5e] = '+', [0x43] = '\n',
42+
43+
[0x2b] = 0, // (international key on some layouts)
44+
[0x30] = 0, // (international key on some layouts)
45+
};
46+
47+
static const int KeyCodeSingleUpper[128] = {
48+
[0x00] = '~', [0x01] = '!', [0x02] = '@', [0x03] = '#', [0x04] = '$', [0x05] = '%',
49+
[0x06] = '^', [0x07] = '&', [0x08] = '*', [0x09] = '(', [0x0a] = ')', [0x0b] = '_',
50+
[0x0c] = '+', [0x0d] = '|', [0x0e] = 0, [0x0f] = '0', // keypad 0 (optional)
51+
52+
[0x10] = 'Q', [0x11] = 'W', [0x12] = 'E', [0x13] = 'R', [0x14] = 'T', [0x15] = 'Y',
53+
[0x16] = 'U', [0x17] = 'I', [0x18] = 'O', [0x19] = 'P', [0x1a] = '{', [0x1b] = '}',
54+
[0x1c] = '\n', [0x1d] = 0, [0x1e] = 0, [0x1f] = 0,
55+
56+
[0x20] = 'A', [0x21] = 'S', [0x22] = 'D', [0x23] = 'F', [0x24] = 'G', [0x25] = 'H',
57+
[0x26] = 'J', [0x27] = 'K', [0x28] = 'L', [0x29] = ':', [0x2a] = '"', [0x2b] = 0,
58+
[0x2c] = 0, [0x2d] = 0, [0x2e] = 0, [0x2f] = 0, [0x30] = 0,
59+
60+
[0x31] = 'Z', [0x32] = 'X', [0x33] = 'C', [0x34] = 'V', [0x35] = 'B', [0x36] = 'N',
61+
[0x37] = 'M', [0x38] = '<', [0x39] = '>', [0x3a] = '?', [0x3b] = 0, [0x3c] = ' ',
62+
};
63+
// clang-format on
64+
65+
static bool key_lshift;
66+
static bool key_rshift;
67+
68+
static cbuf_t *key_buf;
69+
70+
static inline uint8_t decode_key(uint8_t key) {
71+
return ~((key >> 1) | (key << 7));
72+
}
73+
74+
static bool generate_esc_seq(cbuf_t *buf, char a, char b, char c) {
75+
char esc_seq[3] = {a, b, c};
76+
77+
if (cbuf_space_avail(buf) < sizeof(esc_seq)) {
78+
return false;
79+
}
80+
81+
return cbuf_write(buf, esc_seq, sizeof(esc_seq), false) == sizeof(esc_seq);
82+
}
83+
84+
static bool cia_process_scode(uint8_t scode) {
85+
bool resched = false;
86+
bool keyUpBit;
87+
int keyCode;
88+
89+
keyUpBit = (scode & 0x80) != 0;
90+
scode &= 0x7f;
91+
92+
if (scode == SCANCODE_LSHIFT) {
93+
key_lshift = !keyUpBit;
94+
}
95+
96+
if (scode == SCANCODE_RSHIFT) {
97+
key_rshift = !keyUpBit;
98+
}
99+
100+
if (!keyUpBit) {
101+
switch (scode) {
102+
case 0x4f: // LEFT
103+
resched = generate_esc_seq(key_buf, 0x1b, '[', 'D');
104+
break;
105+
case 0x4e: // RIGHT
106+
resched = generate_esc_seq(key_buf, 0x1b, '[', 'C');
107+
break;
108+
case 0x4c: // UP
109+
resched = generate_esc_seq(key_buf, 0x1b, '[', 'A');
110+
break;
111+
case 0x4d: // DOWN
112+
resched = generate_esc_seq(key_buf, 0x1b, '[', 'B');
113+
break;
114+
}
115+
}
116+
117+
if (key_lshift || key_rshift) {
118+
keyCode = KeyCodeSingleUpper[scode];
119+
} else {
120+
keyCode = KeyCodeSingleLower[scode];
121+
}
122+
123+
if (keyCode != 0 && !keyUpBit) {
124+
resched = cbuf_write_char(key_buf, keyCode, false);
125+
}
126+
127+
cia_base[CIA_A_CRA] |= (1 << 6);
128+
cia_base[CIA_A_CRA] &= ~(1 << 6);
129+
130+
return resched;
131+
}
132+
133+
static enum handler_return cia_kbd_interrupt(void *arg) {
134+
bool resched = false;
135+
uint8_t sdr = cia_base[CIA_A_SDR];
136+
uint8_t scode = decode_key(sdr);
137+
138+
resched = cia_process_scode(scode);
139+
140+
return resched ? INT_RESCHEDULE : INT_NO_RESCHEDULE;
141+
}
142+
143+
int platform_read_key(char *c) {
144+
return cbuf_read_char(key_buf, c, true);
145+
}
146+
147+
void platform_keyboard_init(cbuf_t *buffer) {
148+
key_buf = buffer;
149+
150+
cia_base[CIA_A_CRA] &= ~(1 << 6); // Set SPMODE to input
151+
cia_base[CIA_A_ICR] = (1 << 7 | 1 << 3); // Set SP bit in ICR
152+
153+
register_int_handler(INTERRUPT_SERP_A, cia_kbd_interrupt, NULL);
154+
unmask_interrupt(INTERRUPT_PORTS);
155+
unmask_interrupt(INTERRUPT_SERP_A);
156+
}

0 commit comments

Comments
 (0)