c64: fixed issue #203, added openroms-compatible cc65 library overrides for _cgetc and PLOT

This commit is contained in:
Steven Hugg committed 2026-08-22 14:22:03 +02:00
1 parent 3e462b0bcd
commit e32e731612
6 files changed
+144 -4

No files matched your search

+5 -2
View File
@@ -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);
+13
View File
@@ -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
Binary file not shown.
+123
View File
@@ -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
+2 -1
View File
@@ -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'],
},
+1 -1
View File
@@ -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'));