import { AcceptsPaddleInput, Probeable } from "../common/devices"; import { dumpRAM, KeyFlags } from "../common/emu"; import { clamp, hex, lpad } from "../common/util"; // https://www.c64-wiki.com/wiki/C64 // http://www.zimmers.net/cbmpics/cbm/c64/vic-ii.txt // http://www.zimmers.net/cbmpics/cbm/c64/c64prg.txt // http://sta.c64.org/cbm64mem.html // http://hitmen.c02.at/temp/palstuff/ //// WASM Machine import { Machine } from "../common/baseplatform"; import { TrapCondition } from "../common/devices"; import { BaseWASMMachine } from "../common/wasmplatform"; export class C64_WASMMachine extends BaseWASMMachine implements Machine, Probeable, AcceptsPaddleInput { numTotalScanlines = 312; cpuCyclesPerLine = 63; prgstart : number; joymask0 = 0; joymask1 = 0; lightpen_x = 0; 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() { super.reset(); // clear keyboard for (var ch=0; ch<128; ch++) { this.exports.machine_key_up(this.sys, ch); } // load rom if (this.romptr && this.romlen) { this.exports.machine_load_rom(this.sys, this.romptr, this.romlen); this.prgstart = this.romarr[0] + (this.romarr[1]<<8); // get load address // look for BASIC program start if (this.prgstart == 0x801) { // decode SYS address from decimal? if (this.romarr[6] == 0x9e) { var addr = 0; for (var i=0; i<5; i++) { var ch = this.romarr[7+i]; if (ch == 0x9b || ch == 0) break; addr = addr * 10 + (ch & 0xf); } this.prgstart = addr; console.log("SYS", addr, hex(addr)); } else { this.prgstart = this.romarr[2] + (this.romarr[3]<<8) + 2; // point to after BASIC program console.log("RUN", this.prgstart, hex(this.prgstart)); } } // is program loaded into RAM? if (this.prgstart < 0x8000) { // advance BIOS a few frames this.exports.machine_exec(this.sys, 250000); // type in command (SYS 2061) var cmd = "\rSYS "+this.prgstart; for (var i=0; i , case 191: key = flags & KeyFlags.Shift ? 0x3f : 0x2f; break; // ? / case 222: key = flags & KeyFlags.Shift ? 0x22 : 0x27; break; // " ' case 219: key = flags & KeyFlags.Shift ? 0x7b : 0x5b; break; // [ case 221: key = flags & KeyFlags.Shift ? 0x7d : 0x5d; break; // ] case 48: if (flags & KeyFlags.Shift) key = 0x29; break; // ) case 49: if (flags & KeyFlags.Shift) key = 0x21; break; // ! case 50: if (flags & KeyFlags.Shift) key = 0x40; break; // @ case 51: if (flags & KeyFlags.Shift) key = 0x23; break; // # case 52: if (flags & KeyFlags.Shift) key = 0x24; break; // $ case 53: if (flags & KeyFlags.Shift) key = 0x25; break; // % case 54: if (flags & KeyFlags.Shift) key = 0x5e; break; // ^ case 55: if (flags & KeyFlags.Shift) key = 0x26; break; // & case 56: if (flags & KeyFlags.Shift) key = 0x2a; break; // * case 57: if (flags & KeyFlags.Shift) key = 0x28; break; // ( case 59: if (flags & KeyFlags.Shift) key = 0x3a; break; // ; case 61: if (flags & KeyFlags.Shift) key = 0x2b; break; // + case 173: key = flags & KeyFlags.Shift ? 0x5f : 0x2d; break; // _ - } if (flags & KeyFlags.KeyDown) { this.exports.machine_key_down(this.sys, key); this.joymask0 |= mask; this.joymask1 |= mask2; } else if (flags & KeyFlags.KeyUp) { this.exports.machine_key_up(this.sys, key); this.joymask0 &= ~mask; this.joymask1 &= ~mask2; } this.exports.c64_joystick(this.sys, this.joymask0, this.joymask1); } getRasterX() { return this.statearr[0xf4]; } getRasterY() { return this.exports.machine_get_raster_line(this.sys); } getRasterCanvasPosition() { return { x: this.getRasterX() * 392/63, y: this.getRasterY() - 14, } } getDebugStateOffset(index: number) { var p = this.exports.machine_get_debug_pointer(this.sys, index); return p - this.sys; } getDebugCategories() { return ['CPU','ZPRAM','Stack','PLA','CIA','VIC','SID']; } getDebugInfo(category:string, state:any) { switch (category) { case 'PLA': { let s = ""; let iomapped = state.pla[0]; let port = state.pla[3]; s += `$0000 - $9FFF RAM\n`; s += `$A000 - $BFFF ${(port&3)==3 ? 'BASIC ROM' : 'RAM'}\n`; s += `$C000 - $CFFF RAM\n`; s += `$D000 - $DFFF ${iomapped ? 'I/O' : (port&3)!=0 ? 'CHAR ROM' : 'RAM'}\n`; s += `$E000 - $FFFF ${(port&2)==2 ? 'KERNAL ROM' : 'RAM'}\n`; return s; } case 'CIA': { let s = ""; for (let i=0; i<2; i++) { let m = i ? state.cia2 : state.cia1; s += `CIA ${i+1}\n`; s += ` A: Data ${hex(m[0])} DDR ${hex(m[1])} Input ${hex(m[2])}`; s += ` Timer ${hex(m[10]+m[11]*256, 4)}\n`; s += ` B: Data ${hex(m[4])} DDR ${hex(m[5])} Input ${hex(m[6])}`; s += ` Timer ${hex(m[10+10]+m[11+10]*256, 4)}\n`; //s += ` IMR ${hex(m[48])} ICR ${hex(m[50])}\n` } return s; } case 'VIC': { let m = state.vic; let s = ''; let vicbank = ((state.cia2[0] & 3) ^ 3) * 0x4000; let charmem = vicbank + (state.vic[0x18] & 14) * 0x400; let screen = vicbank + (state.vic[0x18] >> 4) * 0x400; let isbitmap = state.vic[0x11] & 0x20; let ischar = (state.cia2[0]&1)==1 && (state.vic[0x18]&12)==4; let rasterX = state.state[0xf4]; let rasterY = this.getRasterY(); s += 'Mode:'; if (state.vic[0x11] & 0x20) s += ' BITMAP'; else s += ' CHAR'; if (state.vic[0x16] & 0x10) s += ' MULTICOLOR'; if (state.vic[0x11] & 0x40) s += ' EXTENDED'; s += "\n"; s += `Raster: (${lpad(rasterX,3)}, ${lpad(rasterY,3)}) `; s += `Scroll: (${state.vic[0x16] & 7}, ${state.vic[0x11] & 7})`; s += "\n"; s += `VIC Bank: $${hex(vicbank,4)} Scrn: $${hex(screen,4)} `; if (isbitmap) s += `Bitmap: $${hex(charmem&0xe000,4)}` else if (ischar) s += `Char: ROM $${hex(charmem,4)}`; else s += `Char: $${hex(charmem,4)}`; s += "\n"; s += dumpRAM(m, 0xd000, 64); return s; } case 'SID': { let m = state.sid; let s = '' s += dumpRAM(m, 0xd400, 32); return s; } } } setPaddleInput(controller: number, value: number): void { if (controller == 0) this.lightpen_x = value; if (controller == 1) this.lightpen_y = value; const x1 = 22; const y1 = 36; const x2 = 228; const y2 = 220; let x = clamp(0, 255, (this.lightpen_x - x1) / (x2 - x1) * 160 + 24); let y = clamp(0, 255, (this.lightpen_y - y1) / (y2 - y1) * 200 + 50); this.exports.machine_set_mouse(this.sys, x, y); } }