diff --git a/src/machine/c64.ts b/src/machine/c64.ts index 0b108b34..0d282aec 100644 --- a/src/machine/c64.ts +++ b/src/machine/c64.ts @@ -28,8 +28,6 @@ export class C64_WASMMachine extends BaseWASMMachine lightpen_y = 0; loadBIOS(srcArray: Uint8Array) { - var patch1ofs = 0xea24 - 0xe000 + 0x3000; - if (srcArray[patch1ofs] == 0xc4) srcArray[patch1ofs] = 0x60; // cursor move, KIL -> RTS super.loadBIOS(srcArray); } reset() { @@ -79,6 +77,11 @@ export class C64_WASMMachine extends BaseWASMMachine for (var i=0; i<100000 && this.getPC() != this.prgstart; i++) { this.exports.machine_tick(this.sys); } + // release RETURN key + // (otherwise it stays stuck down and every keyboard read returns CR, + // e.g. conio cgetc() always returning a bogus char - GH issue #203) + this.exports.machine_key_up(this.sys, 13); + this.exports.machine_exec(this.sys, 1); // chips/kbd.c has a "sticky counter" } else { // get out of reset this.exports.machine_exec(this.sys, 100); diff --git a/src/worker/lib/c64/Makefile b/src/worker/lib/c64/Makefile new file mode 100644 index 00000000..9637077e --- /dev/null +++ b/src/worker/lib/c64/Makefile @@ -0,0 +1,13 @@ +# Rebuild the Open ROMs compatibility object for cc65 on C64. +# +# openroms-compat.o - linked before c64.lib (see src/worker/platforms.ts, +# libargs for the 'c64' platform) so it overrides +# cc65 runtime routines that depend on KERNAL entry +# points not implemented by the Open ROMs BIOS: +# _cgetc (KBDREAD $E5B4) +# PLOT ($FFF0 / UPDCRAMPTR $EA24) + +all: openroms-compat.o + +openroms-compat.o: openroms-compat.s + ca65 -I /Users/sehugg/8bitworkshop-compilers/cc65/asminc openroms-compat.s diff --git a/src/worker/lib/c64/openroms-compat.o b/src/worker/lib/c64/openroms-compat.o new file mode 100644 index 00000000..1aea1349 Binary files /dev/null and b/src/worker/lib/c64/openroms-compat.o differ diff --git a/src/worker/lib/c64/openroms-compat.s b/src/worker/lib/c64/openroms-compat.s new file mode 100644 index 00000000..3f6dd020 --- /dev/null +++ b/src/worker/lib/c64/openroms-compat.s @@ -0,0 +1,123 @@ +; +; Open ROMs compatibility overrides for the cc65 runtime library. +; +; The Open ROMs KERNAL used by 8bitworkshop is not binary-compatible +; with the original Commodore KERNAL at some undocumented entry points +; that cc65 routines call directly. This object is linked before +; c64.lib so ld65 resolves the overridden symbols here and never pulls +; the incompatible library members (cgetc.o, kplot.o). +; +; Only entry points that are actually broken in Open ROMs are +; overridden; everything else (CLRSCR $E544, SCREEN $FFED, BSOUT, +; etc.) works and is left to the KERNAL. +; +; Safe to use with the original C64 KERNAL as well: _cgetc only +; depends on the documented keyboard buffer interface (KEY_COUNT / +; KEY_BUF), and PLOT is a pure RAM-pointer implementation. +; + + .export _cgetc + .export PLOT + + .include "c64.inc" + +KEY_BUF := $0277 ; keyboard buffer (10 bytes, FIFO) + +; ---------------------------------------------------------------------------- +; char cgetc (void) +; +; cc65's stock version jumps to KBDREAD ($E5B4), which is "get character from +; keyboard buffer" on the original KERNAL but unimplemented VIC-II init code +; inside Open ROMs - so cgetc() returned garbage instead of blocking/reading. +; Read the buffer directly instead (same thing the stock KERNAL GETIN path does). +; +_cgetc: + lda KEY_COUNT + beq _cgetc ; wait until a character is available + sei ; IRQ must not touch the buffer while we shift + lda KEY_BUF ; first character (to be returned) + pha + ldx #$00 +L: lda KEY_BUF+1,x ; shift remaining characters down + sta KEY_BUF,x + inx + cpx #$09 ; buffer holds 10 characters + bne L + dec KEY_COUNT + cli + pla + ldx #$00 ; cc65 char return: char in A, high byte in X + rts + +; ---------------------------------------------------------------------------- +; PLOT - set/read cursor position (replaces kplot.o) +; +; cc65's kplot.o calls the original ROM PLOT ($FFF0) and UPDCRAMPTR ($EA24) +; directly. In Open ROMs, that path runs into unimplemented code ($EA24 is a +; KIL instruction), and the previous workaround (patching $EA24 to RTS in the +; BIOS) left CRAM_PTR stale, breaking text colors. This replacement computes +; SCREEN_PTR/CRAM_PTR directly - no KERNAL calls at all. +; +; Entry: C=0: set position (X = row, Y = column), like the KERNAL routine. +; C=1: read position (returns X = row, Y = column, C = 0). +; +ZP_ROW := $FB ; temp: 16-bit offset (row*40 + col) +ZP_ROWHI := $FC +ZP_ROW8 := $FD ; temp: row*8 +ZP_ROW8HI := $FE + +.proc PLOT + bcs @read + + ; offset = row*40 + column + stx ZP_ROW + lda #$00 + sta ZP_ROWHI + asl ZP_ROW + rol ZP_ROWHI ; *2 + asl ZP_ROW + rol ZP_ROWHI ; *4 + asl ZP_ROW + rol ZP_ROWHI ; *8 + lda ZP_ROW + sta ZP_ROW8 + lda ZP_ROWHI + sta ZP_ROW8HI ; ZP_ROW8/HI = row*8 + asl ZP_ROW + rol ZP_ROWHI ; *16 + asl ZP_ROW + rol ZP_ROWHI ; *32 + lda ZP_ROW + clc + adc ZP_ROW8 + sta ZP_ROW ; + row*8 = row*40 + lda ZP_ROWHI + adc ZP_ROW8HI + sta ZP_ROWHI + ; NOTE: no column offset here - the KERNAL PLOT convention is that + ; SCREEN_PTR/CRAM_PTR point to the START of the cursor row; the + ; column is applied by callers via indexed addressing + ; (e.g. putchar: sta (SCREEN_PTR),y with Y = CURS_X). + ; SCREEN_PTR = $0400 + row*40 + lda #<$0400 + clc + adc ZP_ROW + sta SCREEN_PTR + lda #>$0400 + adc ZP_ROWHI + sta SCREEN_PTR+1 + ; CRAM_PTR = $D800 + offset + lda #<$D800 + clc + adc ZP_ROW + sta CRAM_PTR + lda #>$D800 + adc ZP_ROWHI + sta CRAM_PTR+1 + rts + +@read: ldx CURS_Y + ldy CURS_X + clc + rts +.endproc diff --git a/src/worker/platforms.ts b/src/worker/platforms.ts index c0a29cb1..1b0539ed 100644 --- a/src/worker/platforms.ts +++ b/src/worker/platforms.ts @@ -285,7 +285,8 @@ export var PLATFORM_PARAMS = { arch: '6502', define: ['__CBM__', '__C64__'], cfgfile: 'c64.cfg', // SYS 2061 - libargs: ['c64.lib'], + libargs: ['openroms-compat.o', 'c64.lib'], + extra_link_files: ['openroms-compat.o'], acmeargs: ['-f', 'cbm'], //extra_link_files: ['c64-cart.cfg'], }, diff --git a/test/cli/testworker.js b/test/cli/testworker.js index 99dd69b6..c2a49923 100644 --- a/test/cli/testworker.js +++ b/test/cli/testworker.js @@ -318,7 +318,7 @@ describe('Worker', function() { it('should compile C64 cc65 skeleton', function(done) { var csource = ab2str(fs.readFileSync('presets/c64/skeleton.cc65')); csource = csource.replace('#include "','//'); - compile('cc65', csource, 'c64.wasm', done, 3001, 3, 0); + compile('cc65', csource, 'c64.wasm', done, 3112, 3, 0); }); it('should compile zmachine inform6 skeleton', function(done) { var csource = ab2str(fs.readFileSync('presets/zmachine/skeleton.inform6'));