import { Z80, Z80State } from "../common/cpu/ZilogZ80"; import { BasicScanlineMachine } from "../common/devices"; import { BaseZ80VDPBasedMachine } from "./vdp_z80"; import { KeyFlags, newAddressDecoder, padBytes, Keys, makeKeycodeMap, newKeyboardHandler } from "../common/emu"; import { hex, lzgmini, stringToByteArray } from "../common/util"; import { TssChannelAdapter, MasterAudio, AY38910_Audio } from "../common/audio"; import { TMS9918A } from "../common/video/tms9918a"; var MSX_KEYCODE_MAP = makeKeycodeMap([ [Keys.UP, 0, 0x1], [Keys.DOWN, 0, 0x2], [Keys.LEFT, 0, 0x4], [Keys.RIGHT, 0, 0x8], [Keys.A, 0, 0x10], [Keys.B, 0, 0x20], [Keys.P2_UP, 1, 0x1], [Keys.P2_DOWN, 1, 0x2], [Keys.P2_LEFT, 1, 0x4], [Keys.P2_RIGHT, 1, 0x8], [Keys.P2_A, 1, 0x10], [Keys.P2_B, 1, 0x20], [Keys.ANYKEY, 2, 0x0], ]); const JOY_INPUT_0 = 0; const JOY_INPUT_1 = 1; const KEYBOARD_ROW_0 = 16; const MSX_KEYMATRIX_INTL_NOSHIFT = [ Keys.VK_7, Keys.VK_6, Keys.VK_5, Keys.VK_4, Keys.VK_3, Keys.VK_2, Keys.VK_1, Keys.VK_0, Keys.VK_SEMICOLON, Keys.VK_CLOSE_BRACKET, Keys.VK_OPEN_BRACKET, Keys.VK_BACK_SLASH, Keys.VK_EQUALS, Keys.VK_MINUS, Keys.VK_9, Keys.VK_8, Keys.VK_B, Keys.VK_A, null/*DEAD*/, Keys.VK_SLASH, Keys.VK_PERIOD, Keys.VK_COMMA, Keys.VK_ACUTE, Keys.VK_QUOTE, Keys.VK_J, Keys.VK_I, Keys.VK_H, Keys.VK_G, Keys.VK_F, Keys.VK_E, Keys.VK_D, Keys.VK_C, Keys.VK_R, Keys.VK_Q, Keys.VK_P, Keys.VK_O, Keys.VK_N, Keys.VK_M, Keys.VK_L, Keys.VK_K, Keys.VK_Z, Keys.VK_Y, Keys.VK_X, Keys.VK_W, Keys.VK_V, Keys.VK_U, Keys.VK_T, Keys.VK_S, Keys.VK_F3, Keys.VK_F2, Keys.VK_F1, null, Keys.VK_CAPS_LOCK, null, Keys.VK_CONTROL, Keys.VK_SHIFT, Keys.VK_ENTER, null, Keys.VK_BACK_SPACE, null, Keys.VK_TAB, Keys.VK_ESCAPE, Keys.VK_F5, Keys.VK_F4, Keys.VK_RIGHT, Keys.VK_DOWN, Keys.VK_UP, Keys.VK_LEFT, Keys.VK_DELETE, Keys.VK_INSERT, Keys.VK_HOME, Keys.VK_SPACE, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, // TODO: null keycodes ]; /// standard emulator interface MSXSlot { read(addr:number) : number; write(addr:number, val:number) : void; } export class MSX1 extends BaseZ80VDPBasedMachine { numVisibleScanlines = 240; defaultROMSize = 0x8000; ram = new Uint8Array(0x10000); vdp : TMS9918A; bios : Uint8Array; slots : MSXSlot[]; slotmask : number = 0; ppi_c : number = 0; constructor() { super(); this.init(this, this.newIOBus(), new AY38910_Audio(new MasterAudio())); this.bios = new lzgmini().decode(stringToByteArray(atob(MSX1_BIOS_LZG))); // skip splash screen this.bios[0xdd5] = 0; this.bios[0xdd6] = 0; this.bios[0xdd7] = 0; // slot definitions this.slots = [ // slot 0 : BIOS { read: (a) => { return this.bios[a] | 0; }, write: (a,v) => { } }, // slot 1: cartridge { read: (a) => { return this.rom[a - 0x4000] | 0; }, write: (a,v) => { } }, // slot 2: cartridge { read: (a) => { return this.rom[a - 0x4000] | 0; }, write: (a,v) => { } }, // slot 3 : RAM { read: (a) => { return this.ram[a] | 0; }, write: (a,v) => { this.ram[a] = v; } }, ]; } loadBIOS(bios: Uint8Array) { this.bios = bios; } getKeyboardMap() { return MSX_KEYCODE_MAP; } // http://map.grauw.nl/articles/keymatrix.php getKeyboardFunction() { return (o,key,code,flags) => { //console.log(o,key,code,flags); var keymap = MSX_KEYMATRIX_INTL_NOSHIFT; for (var i=0; i> 3; let bit = 7 - (i & 7); //console.log(key, row, bit); if (flags & KeyFlags.KeyDown) { this.inputs[KEYBOARD_ROW_0+row] |= (1< { let shift = (a >> 14) << 1; let slotnum = (this.slotmask >> shift) & 3; let slot = this.slots[slotnum]; return slot ? slot.read(a) : 0; }; write = (a,v) => { let shift = (a >> 14) << 1; let slotnum = (this.slotmask >> shift) & 3; let slot = this.slots[slotnum]; if (slot) slot.write(a, v); }; newIOBus() { return { read: (addr) => { addr &= 0xff; //console.log('IO read', hex(addr,4)); switch (addr) { case 0x98: return this.vdp.readData(); case 0x99: return this.vdp.readStatus(); case 0xa2: if (this.psg.currentRegister() == 14) return ~this.inputs[JOY_INPUT_0]; // TODO: joy 1? else return this.psg.readData(); case 0xa8: return this.slotmask; case 0xa9: return ~this.inputs[KEYBOARD_ROW_0 + (this.ppi_c & 15)]; case 0xaa: return this.ppi_c; // TODO? //default: throw new EmuHalt("Read I/O " + hex(addr)); } return 0; }, write: (addr, val) => { addr &= 0xff; val &= 0xff; //console.log('IO write', hex(addr,4), hex(val,2)); switch (addr) { case 0x98: this.vdp.writeData(val); break; case 0x99: this.vdp.writeAddress(val); break; case 0xa8: this.slotmask = val; break; case 0xaa: this.ppi_c = val; break; case 0xab: // command register, modifies PPI C let ibit = (val >> 1) & 7; this.ppi_c = (this.ppi_c & ~(1<