fix(z80-cpu): map RAM over the whole 0x8000-0xFFFF so vanilla SDCC C runs
SDCC's z80 crt0 sets SP=0x0000 and makes its first stack push at 0xFFFF. The chip only mapped RAM at 0x8000-0xBFFF (0xC000+ was MMIO/ignored), so the stack landed on unmapped memory and a plain C program crashed inside crt0 — before main — which is why z80-led-chaser-c compiled but drove nothing. Extend RAM to cover 0x8000-0xFFFF (32 KB) with the MMIO window 0xC000-0xC0FF carved out and checked first, in scripts/make-z80-cpu.py + regenerated z80-cpu.c. Now SDCC's default stack works and "write C from scratch, click Run" just works — no manual `LD SP` needed (dropped from chaser.c). Bumped the chip WASM initial memory to 4 pages to hold the larger RAM buffer. Larson (asm, SP=0xBFFF, LED at 0xC000) is unaffected. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
bf36487642
commit
1d6961d03c
|
|
@ -82,7 +82,13 @@ static uint8_t read_data(void) { return 0; }
|
|||
/* ─── External ROM + internal RAM + MMIO state ───────────────────────── */
|
||||
#define ROM_MAX 0x8000
|
||||
#define RAM_BASE 0x8000
|
||||
#define RAM_SIZE 0x4000
|
||||
/* RAM spans 0x8000-0xFFFF (32 KB) so SDCC's default crt0 — which sets SP to
|
||||
0x0000 and makes its first push at 0xFFFF — lands in real RAM. Without this
|
||||
a plain C program crashes in crt0 (before main) on this chip. The MMIO
|
||||
window below is carved out of the RAM range and checked first. */
|
||||
#define RAM_SIZE 0x8000
|
||||
#define MMIO_BASE 0xC000
|
||||
#define MMIO_END 0xC0FF
|
||||
#define MMIO_LED_OUT 0xC000
|
||||
#define MMIO_UART_DATA 0xC001
|
||||
#define MMIO_UART_STAT 0xC002
|
||||
|
|
@ -130,24 +136,31 @@ static void drive_leds(uint8_t v) {
|
|||
|
||||
static uint8_t bus_mem_read(uint16_t addr) {
|
||||
if (addr < ROMSZ) return ROMBUF[addr];
|
||||
if (addr >= RAM_BASE && addr < RAM_BASE + RAM_SIZE) return RAMBUF[addr - RAM_BASE];
|
||||
switch (addr) {
|
||||
case MMIO_UART_DATA: return rx_has() ? rx_pop() : 0;
|
||||
case MMIO_UART_STAT: { uint8_t s = 0x01; if (rx_has()) s |= 0x02; return s; }
|
||||
case MMIO_BTN_IN: return read_btn_bitmap();
|
||||
case MMIO_EDGE_FLAGS: { uint8_t v = edge_latch; edge_latch = 0; return v; }
|
||||
default: return 0xFF;
|
||||
/* MMIO window has priority over RAM (it is carved out of the RAM range). */
|
||||
if (addr >= MMIO_BASE && addr <= MMIO_END) {
|
||||
switch (addr) {
|
||||
case MMIO_UART_DATA: return rx_has() ? rx_pop() : 0;
|
||||
case MMIO_UART_STAT: { uint8_t s = 0x01; if (rx_has()) s |= 0x02; return s; }
|
||||
case MMIO_BTN_IN: return read_btn_bitmap();
|
||||
case MMIO_EDGE_FLAGS: { uint8_t v = edge_latch; edge_latch = 0; return v; }
|
||||
default: return 0xFF;
|
||||
}
|
||||
}
|
||||
if (addr >= RAM_BASE) return RAMBUF[addr - RAM_BASE]; /* 0x8000-0xFFFF */
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
static void bus_mem_write(uint16_t addr, uint8_t v) {
|
||||
if (addr >= RAM_BASE && addr < RAM_BASE + RAM_SIZE) {
|
||||
RAMBUF[addr - RAM_BASE] = v; return;
|
||||
/* MMIO window has priority over RAM (it is carved out of the RAM range). */
|
||||
if (addr >= MMIO_BASE && addr <= MMIO_END) {
|
||||
switch (addr) {
|
||||
case MMIO_LED_OUT: drive_leds(v); return;
|
||||
case MMIO_UART_DATA: vx_uart_write(g_uart, &v, 1); return;
|
||||
default: return;
|
||||
}
|
||||
}
|
||||
switch (addr) {
|
||||
case MMIO_LED_OUT: drive_leds(v); return;
|
||||
case MMIO_UART_DATA: vx_uart_write(g_uart, &v, 1); return;
|
||||
default: return;
|
||||
if (addr >= RAM_BASE) { /* 0x8000-0xFFFF */
|
||||
RAMBUF[addr - RAM_BASE] = v; return;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -57,15 +57,6 @@ static void delay(unsigned int loops) {
|
|||
}
|
||||
|
||||
void main(void) {
|
||||
/* Park the stack at the top of the chip's 16 KB RAM (0x8000-0xBFFF).
|
||||
SDCC's crt0 defaults SP to 0x0000; on this chip's memory map (RAM
|
||||
0x8000-0xBFFF, MMIO at 0xC000+) that would push the stack onto
|
||||
unmapped high memory and crash on the first CALL. The asm Larson
|
||||
example does the same with "LD SP, 0xBFFF". */
|
||||
__asm
|
||||
ld sp, #0xBFFF
|
||||
__endasm;
|
||||
|
||||
unsigned char bit = 0x01;
|
||||
char dir = 1; /* +1 = walking left, -1 = walking right */
|
||||
while (1) {
|
||||
|
|
|
|||
|
|
@ -208,7 +208,10 @@ export class ChipInstance {
|
|||
}
|
||||
|
||||
private async _instantiate(): Promise<void> {
|
||||
this.memory = new WebAssembly.Memory({ initial: 2, maximum: 16 });
|
||||
// 4 pages (256 KB) initial: CPU-emulator chips like z80-cpu keep a 32 KB
|
||||
// ROM + 32 KB RAM buffer as static data, which alone needs >2 pages once
|
||||
// the WASM stack is added. Grows up to 16 pages on demand.
|
||||
this.memory = new WebAssembly.Memory({ initial: 4, maximum: 16 });
|
||||
this.wasi.setMemory(this.memory);
|
||||
|
||||
const importObject: WebAssembly.Imports = {
|
||||
|
|
|
|||
|
|
@ -145,7 +145,13 @@ helpers = '''
|
|||
/* ─── External ROM + internal RAM + MMIO state ───────────────────────── */
|
||||
#define ROM_MAX 0x8000
|
||||
#define RAM_BASE 0x8000
|
||||
#define RAM_SIZE 0x4000
|
||||
/* RAM spans 0x8000-0xFFFF (32 KB) so SDCC's default crt0 — which sets SP to
|
||||
0x0000 and makes its first push at 0xFFFF — lands in real RAM. Without this
|
||||
a plain C program crashes in crt0 (before main) on this chip. The MMIO
|
||||
window below is carved out of the RAM range and checked first. */
|
||||
#define RAM_SIZE 0x8000
|
||||
#define MMIO_BASE 0xC000
|
||||
#define MMIO_END 0xC0FF
|
||||
#define MMIO_LED_OUT 0xC000
|
||||
#define MMIO_UART_DATA 0xC001
|
||||
#define MMIO_UART_STAT 0xC002
|
||||
|
|
@ -193,24 +199,31 @@ static void drive_leds(uint8_t v) {
|
|||
|
||||
static uint8_t bus_mem_read(uint16_t addr) {
|
||||
if (addr < ROMSZ) return ROMBUF[addr];
|
||||
if (addr >= RAM_BASE && addr < RAM_BASE + RAM_SIZE) return RAMBUF[addr - RAM_BASE];
|
||||
switch (addr) {
|
||||
case MMIO_UART_DATA: return rx_has() ? rx_pop() : 0;
|
||||
case MMIO_UART_STAT: { uint8_t s = 0x01; if (rx_has()) s |= 0x02; return s; }
|
||||
case MMIO_BTN_IN: return read_btn_bitmap();
|
||||
case MMIO_EDGE_FLAGS: { uint8_t v = edge_latch; edge_latch = 0; return v; }
|
||||
default: return 0xFF;
|
||||
/* MMIO window has priority over RAM (it is carved out of the RAM range). */
|
||||
if (addr >= MMIO_BASE && addr <= MMIO_END) {
|
||||
switch (addr) {
|
||||
case MMIO_UART_DATA: return rx_has() ? rx_pop() : 0;
|
||||
case MMIO_UART_STAT: { uint8_t s = 0x01; if (rx_has()) s |= 0x02; return s; }
|
||||
case MMIO_BTN_IN: return read_btn_bitmap();
|
||||
case MMIO_EDGE_FLAGS: { uint8_t v = edge_latch; edge_latch = 0; return v; }
|
||||
default: return 0xFF;
|
||||
}
|
||||
}
|
||||
if (addr >= RAM_BASE) return RAMBUF[addr - RAM_BASE]; /* 0x8000-0xFFFF */
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
static void bus_mem_write(uint16_t addr, uint8_t v) {
|
||||
if (addr >= RAM_BASE && addr < RAM_BASE + RAM_SIZE) {
|
||||
RAMBUF[addr - RAM_BASE] = v; return;
|
||||
/* MMIO window has priority over RAM (it is carved out of the RAM range). */
|
||||
if (addr >= MMIO_BASE && addr <= MMIO_END) {
|
||||
switch (addr) {
|
||||
case MMIO_LED_OUT: drive_leds(v); return;
|
||||
case MMIO_UART_DATA: vx_uart_write(g_uart, &v, 1); return;
|
||||
default: return;
|
||||
}
|
||||
}
|
||||
switch (addr) {
|
||||
case MMIO_LED_OUT: drive_leds(v); return;
|
||||
case MMIO_UART_DATA: vx_uart_write(g_uart, &v, 1); return;
|
||||
default: return;
|
||||
if (addr >= RAM_BASE) { /* 0x8000-0xFFFF */
|
||||
RAMBUF[addr - RAM_BASE] = v; return;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue