diff --git a/src/baseplatform.ts b/src/baseplatform.ts index 01da2b2b..7256d08b 100644 --- a/src/baseplatform.ts +++ b/src/baseplatform.ts @@ -1001,6 +1001,11 @@ export abstract class BaseMachinePlatform extends BaseDebugPl this.video = new RasterVideo(this.mainElement, vp.width, vp.height, {overscan:!!vp.overscan,rotate:vp.rotate|0}); this.video.create(); m.connectVideo(this.video.getFrameData()); + // TODO: support keyboard w/o video? + if (hasKeyInput(m)) { + this.video.setKeyboardEvents(m.setKeyInput.bind(m)); + this.poller = new ControllerPoller(m.setKeyInput.bind(m)); + } } if (hasAudio(m)) { var ap = m.getAudioParams(); @@ -1008,10 +1013,6 @@ export abstract class BaseMachinePlatform extends BaseDebugPl this.audio.start(); m.connectAudio(this.audio); } - if (hasKeyInput(m)) { - this.video.setKeyboardEvents(m.setKeyInput.bind(m)); - this.poller = new ControllerPoller(m.setKeyInput.bind(m)); - } if (hasPaddleInput(m)) { this.video.setupMouseEvents(); } @@ -1042,7 +1043,7 @@ export abstract class BaseMachinePlatform extends BaseDebugPl advance(novideo:boolean) { this.machine.advanceFrame(999999, this.getDebugCallback()); - if (!novideo) this.video.updateFrame(); + if (!novideo && this.video) this.video.updateFrame(); } isRunning() { diff --git a/src/devices.ts b/src/devices.ts index 48c64fb0..e70fd038 100644 --- a/src/devices.ts +++ b/src/devices.ts @@ -231,29 +231,19 @@ export interface BasicMachineState extends BasicMachineControlsState { ram: Uint8Array; } -export abstract class BasicMachine implements HasCPU, Bus, SampledAudioSource, AcceptsROM, Probeable, +export abstract class BasicHeadlessMachine implements HasCPU, Bus, AcceptsROM, Probeable, SavesState, SavesInputState { abstract cpuFrequency : number; - abstract canvasWidth : number; - abstract numVisibleScanlines : number; abstract defaultROMSize : number; - abstract sampleRate : number; - overscan : boolean = false; - rotate : number = 0; - + abstract cpu : CPU; - abstract ram : Uint8Array; - + abstract ram : Uint8Array; + rom : Uint8Array; - pixels : Uint32Array; - audio : SampledAudioSink; inputs : Uint8Array = new Uint8Array(32); handler : (key,code,flags) => void; // keyboard handler - scanline : number; - frameCycles : number; - nullProbe = new NullProbe(); probe : ProbeAll = this.nullProbe; @@ -263,18 +253,6 @@ export abstract class BasicMachine implements HasCPU, Bus, SampledAudioSource, A setKeyInput(key:number, code:number, flags:number) : void { this.handler && this.handler(key,code,flags); } - getAudioParams() : SampledAudioParams { - return {sampleRate:this.sampleRate, stereo:false}; - } - connectAudio(audio : SampledAudioSink) : void { - this.audio = audio; - } - getVideoParams() : VideoParams { - return {width:this.canvasWidth, height:this.numVisibleScanlines, overscan:this.overscan, rotate:this.rotate}; - } - connectVideo(pixels:Uint32Array) : void { - this.pixels = pixels; - } connectProbe(probe: ProbeAll) : void { this.probe = probe || this.nullProbe; } @@ -348,6 +326,33 @@ export abstract class BasicMachine implements HasCPU, Bus, SampledAudioSource, A } } +export abstract class BasicMachine extends BasicHeadlessMachine implements SampledAudioSource { + + abstract canvasWidth : number; + abstract numVisibleScanlines : number; + abstract sampleRate : number; + overscan : boolean = false; + rotate : number = 0; + + pixels : Uint32Array; + audio : SampledAudioSink; + + scanline : number; + + getAudioParams() : SampledAudioParams { + return {sampleRate:this.sampleRate, stereo:false}; + } + connectAudio(audio : SampledAudioSink) : void { + this.audio = audio; + } + getVideoParams() : VideoParams { + return {width:this.canvasWidth, height:this.numVisibleScanlines, overscan:this.overscan, rotate:this.rotate}; + } + connectVideo(pixels:Uint32Array) : void { + this.pixels = pixels; + } +} + export abstract class BasicScanlineMachine extends BasicMachine implements RasterFrameBased { abstract numTotalScanlines : number; @@ -355,6 +360,8 @@ export abstract class BasicScanlineMachine extends BasicMachine implements Raste abstract startScanline() : void; abstract drawScanline() : void; + + frameCycles : number; advanceFrame(maxClocks:number, trap) : number { this.preFrame(); diff --git a/src/machine/kim1.ts b/src/machine/kim1.ts new file mode 100644 index 00000000..168b9304 --- /dev/null +++ b/src/machine/kim1.ts @@ -0,0 +1,148 @@ + +import { MOS6502, MOS6502State } from "../cpu/MOS6502"; +import { BasicHeadlessMachine } from "../devices"; +import { padBytes, Keys, KeyFlags, newAddressDecoder } from "../emu"; // TODO +import { hex, stringToByteArray, lzgmini } from "../util"; + +const KIM1_KEYMATRIX_NOSHIFT = [ + Keys.VK_DELETE, Keys.VK_ENTER, Keys.VK_RIGHT, Keys.VK_F7, Keys.VK_F1, Keys.VK_F3, Keys.VK_F5, Keys.VK_DOWN, + Keys.VK_3, Keys.VK_W, Keys.VK_A, Keys.VK_4, Keys.VK_Z, Keys.VK_S, Keys.VK_E, Keys.VK_SHIFT, + Keys.VK_5, Keys.VK_R, Keys.VK_D, Keys.VK_6, Keys.VK_C, Keys.VK_F, Keys.VK_T, Keys.VK_X, + Keys.VK_7, Keys.VK_Y, Keys.VK_G, Keys.VK_8, Keys.VK_B, Keys.VK_H, Keys.VK_U, Keys.VK_V, + Keys.VK_9, Keys.VK_I, Keys.VK_J, Keys.VK_0, Keys.VK_M, Keys.VK_K, Keys.VK_O, Keys.VK_N, + null/*Keys.VK_PLUS*/, Keys.VK_P, Keys.VK_L, Keys.VK_MINUS, Keys.VK_PERIOD, null/*Keys.VK_COLON*/, null/*Keys.VK_AT*/, Keys.VK_COMMA, + null/*Keys.VK_POUND*/, null/*TIMES*/, Keys.VK_SEMICOLON, Keys.VK_HOME, Keys.VK_SHIFT/*right*/, Keys.VK_EQUALS, Keys.VK_TILDE, Keys.VK_SLASH, + Keys.VK_1, Keys.VK_LEFT, Keys.VK_CONTROL, Keys.VK_2, Keys.VK_SPACE, Keys.VK_ALT, Keys.VK_Q, null/*STOP*/, +]; + +const KEYBOARD_ROW_0 = 0; + +class RRIOT_6530 { + + regs = new Uint8Array(16); + ina : number = 0; + inb : number = 0; + + read(a:number) : number { + //console.log('read', hex(a), hex(this.regs[a])); + return this.regs[a]; + } + + write(a:number,v:number) { + this.regs[a] = v; + //console.log('write', hex(a), hex(v)); + } + + input_a() { return this.ina & ~this.regs[1]; } + input_b() { return this.inb & ~this.regs[1]; } + output_a() { return (this.regs[0] ^ 0xff) | this.regs[1]; } + output_b() { return (this.regs[2] ^ 0xff) | this.regs[3]; } +} + +export class KIM1 extends BasicHeadlessMachine { + cpuFrequency = 1000000; + defaultROMSize = 0x1000; + + cpu = new MOS6502(); + ram = new Uint8Array(0x1800); + bios : Uint8Array; + + rriot1 : RRIOT_6530 = new RRIOT_6530(); + rriot2 : RRIOT_6530 = new RRIOT_6530(); + digits = []; + + constructor() { + super(); + this.bios = new lzgmini().decode(stringToByteArray(atob(KIM1_BIOS_LZG))); + this.connectCPUMemoryBus(this); + } + + read = newAddressDecoder([ + [0x1700, 0x173f, 0x000f, (a) => { return this.readIO_1(a); }], + [0x1740, 0x177f, 0x000f, (a) => { return this.readIO_2(a); }], + [0x0000, 0x17ff, 0x1fff, (a) => { return this.ram[a]; }], + [0x1800, 0x1fff, 0x07ff, (a) => { return this.bios[a]; }], + ], {gmask:0x1fff}); + + write = newAddressDecoder([ + [0x1700, 0x173f, 0x000f, (a,v) => { return this.writeIO_1(a,v); }], + [0x1740, 0x177f, 0x000f, (a,v) => { return this.writeIO_2(a,v); }], + [0x0000, 0x17ff, 0x1fff, (a,v) => { this.ram[a] = v; }], + ], {gmask:0x1fff}); + + readConst(a:number) : number { + return this.read(a); + } + + readIO_1(a:number) : number { + return this.rriot1.read(a); + } + + writeIO_1(a:number, v:number) { + this.rriot1.write(a,v); + } + + readIO_2(a:number) : number { + switch (a & 0xf) { + case 0x0: + let cols = 0; + for (let i=0; i<8; i++) + if ((this.rriot2.regs[0] & (1<> 3; + let col = i & 7; + // is column selected? + if (flags & KeyFlags.KeyDown) { + this.inputs[KEYBOARD_ROW_0 + row] |= (1< implements Platform { -const KIM1_KEYMATRIX_NOSHIFT = [ - Keys.VK_DELETE, Keys.VK_ENTER, Keys.VK_RIGHT, Keys.VK_F7, Keys.VK_F1, Keys.VK_F3, Keys.VK_F5, Keys.VK_DOWN, - Keys.VK_3, Keys.VK_W, Keys.VK_A, Keys.VK_4, Keys.VK_Z, Keys.VK_S, Keys.VK_E, Keys.VK_SHIFT, - Keys.VK_5, Keys.VK_R, Keys.VK_D, Keys.VK_6, Keys.VK_C, Keys.VK_F, Keys.VK_T, Keys.VK_X, - Keys.VK_7, Keys.VK_Y, Keys.VK_G, Keys.VK_8, Keys.VK_B, Keys.VK_H, Keys.VK_U, Keys.VK_V, - Keys.VK_9, Keys.VK_I, Keys.VK_J, Keys.VK_0, Keys.VK_M, Keys.VK_K, Keys.VK_O, Keys.VK_N, - null/*Keys.VK_PLUS*/, Keys.VK_P, Keys.VK_L, Keys.VK_MINUS, Keys.VK_PERIOD, null/*Keys.VK_COLON*/, null/*Keys.VK_AT*/, Keys.VK_COMMA, - null/*Keys.VK_POUND*/, null/*TIMES*/, Keys.VK_SEMICOLON, Keys.VK_HOME, Keys.VK_SHIFT/*right*/, Keys.VK_EQUALS, Keys.VK_TILDE, Keys.VK_SLASH, - Keys.VK_1, Keys.VK_LEFT, Keys.VK_CONTROL, Keys.VK_2, Keys.VK_SPACE, Keys.VK_ALT, Keys.VK_Q, null/*STOP*/, -]; + newMachine() { return new KIM1(); } + getPresets() { return KIM1_PRESETS; } + getDefaultExtension() { return ".dasm"; }; + readAddress(a) { return this.machine.readConst(a); } -const KEYBOARD_ROW_0 = 0; - -const cpuFrequency = 1000000; -const romLength = 0x1000; - -class RRIOT_6530 { - - regs = new Uint8Array(16); - ina : number = 0; - inb : number = 0; - - read(a:number) : number { - //console.log('read', hex(a), hex(this.regs[a])); - return this.regs[a]; - } - - write(a:number,v:number) { - this.regs[a] = v; - //console.log('write', hex(a), hex(v)); - } - - input_a() { return this.ina & ~this.regs[1]; } - input_b() { return this.inb & ~this.regs[1]; } - output_a() { return (this.regs[0] ^ 0xff) | this.regs[1]; } - output_b() { return (this.regs[2] ^ 0xff) | this.regs[3]; } -} - -class KIM1Platform extends Base6502Platform implements Platform { - - mainElement : HTMLElement; - cpu; - ram : Uint8Array; - bios : Uint8Array; - bus; - timer : AnimationTimer; - inputs = new Uint8Array(16); - rriot1 : RRIOT_6530 = new RRIOT_6530(); - rriot2 : RRIOT_6530 = new RRIOT_6530(); - digits = []; - - constructor(mainElement : HTMLElement) { - super(); - this.mainElement = mainElement; - } - - getPresets() { - return KIM1_PRESETS; - } - - getKeyboardMap() { return null; /* TODO: KIM1_KEYCODE_MAP;*/ } - - getKeyboardFunction() { - return (key,code,flags) => { - //console.log(key,code,flags); - var keymap = KIM1_KEYMATRIX_NOSHIFT; - for (var i=0; i> 3; - let col = i & 7; - // is column selected? - if (flags & KeyFlags.KeyDown) { - this.inputs[KEYBOARD_ROW_0 + row] |= (1< { return this.readIO_1(a); }], - [0x1740, 0x177f, 0x000f, (a) => { return this.readIO_2(a); }], - [0x0000, 0x17ff, 0x1fff, (a) => { return this.ram[a]; }], - [0x1800, 0x1fff, 0x07ff, (a) => { return this.bios[a]; }], - ], {gmask:0x1fff}), - write: newAddressDecoder([ - [0x1700, 0x173f, 0x000f, (a,v) => { return this.writeIO_1(a,v); }], - [0x1740, 0x177f, 0x000f, (a,v) => { return this.writeIO_2(a,v); }], - [0x0000, 0x17ff, 0x1fff, (a,v) => { this.ram[a] = v; }], - ], {gmask:0x1fff}), - }; - this.cpu.connectBus(this.bus); - this.timer = new AnimationTimer(60, this.nextFrame.bind(this)); - // create digits display - let div = $('
').appendTo(this.mainElement); - div[0].tabIndex = -1; // Make it focusable - for (let i=0; i<6; i++) { - let id = "kim_digit_" + i; - let el = $('0').attr('id',id).appendTo(div); - this.digits.push(el); - } - _setKeyboardEvents(div[0], this.getKeyboardFunction()); - } - - advance(novideo : boolean) { - var debugCond = this.getDebugCallback(); - for (var i=0; i { + }).catch( (e) => { + console.log(e); alertError('Platform "' + platform_id + '" not supported.'); }); }