UNPKG

mz700-js

Version:
1,268 lines (1,260 loc) 242 kB
"use strict"; import oct from "../lib/oct"; import IMem from "./imem"; import MemoryBlock from "./memory-block"; import Z80_Register from "./register"; import BinUtil from "./bin-util"; import Z80LineAssembler from "./Z80-line-assembler"; import NumberUtil from "../lib/number-util"; /* tslint:disable:no-console no-bitwise no-string-throw */ export default class Z80 { memory: IMem; IFF1 = 0; IFF2 = 0; IM = 0; HALT = 0; ioPort:number[]; reg:Z80_Register; regB:Z80_Register; bpmap:boolean[]; consumedTCycle = 0; _onReadIoPort:((value:number)=>void)[]; _onWriteIoPort:((value:number)=>void)[]; exec:()=>void = Z80.exec; opecodeTable:{ mnemonic: string|(()=>Record<string, string>[])|null, cycle?:number|string, proc: ()=>void, disasm: (mem:IMem, addr:number)=>{ code: number[], mnemonic: string[], refAddrTo?:number, } }[]; /** * Execute the instruction at current program counter. * @returns {undefined} */ static exec:()=>void = function():void { this.reg.R = (this.reg.R + 1) & 255; const instruction = this.opecodeTable[this.fetch()]; const cycle = instruction.proc() || instruction.cycle || 4; this.consumedTCycle += cycle; if(this.bpmap[this.reg.PC] != null) { console.log("*** BREAK AT $" + NumberUtil.HEX(this.reg.PC, 4)); throw "break"; } }; /** * Z80 emulator class. * @param {object} opt The options to create. * @constructor */ constructor(opt?:{memory?:IMem}) { opt = opt || { memory: null, }; this.memory = opt.memory; if (opt.memory == null) { this.memory = new MemoryBlock(); this.memory.create(); } this.ioPort = (new Array(256)).fill(0); this.reg = new Z80_Register(); this.regB = new Z80_Register(); this.bpmap = new Array(0x10000); this.consumedTCycle = 0; this.createOpecodeTable(); this._onReadIoPort = (new Array(256)).fill(()=>{ /* empty */ }); this._onWriteIoPort = (new Array(256)).fill(()=>{ /* empty */ }); } /** * Read a value fron I/O port. * @param {number} port A port number(0 to 255). * @returns {number} A 8-bit value that was read. */ readIoPort(port:number):number { const value = this.ioPort[port]; this.reg.onReadIoPort(value); this._onReadIoPort[port](value); return value; } /** * Write I/O port. * @param {number} port A port number(0 to 255). * @param {number} value A 8-bit value to be written. * @returns {undefined} */ writeIoPort(port:number, value:number):void { this.ioPort[port] = value; this._onWriteIoPort[port](value); } /** * Reset. * @returns {undefined} */ reset():void { this.IFF1 = 0; this.IFF2 = 0; this.IM = 0; this.HALT = 0; this.reg.clear(); this.regB.clear(); this.exec = Z80.exec; this.consumedTCycle = 0; } /** * Interrupt. * @returns {undefined} */ interrupt():void { if (this.IFF1) { this.pushPair(this.reg.PC); this.reg.PC = 0x0038; } } /** * Clear the break points. * @returns {undefined} */ clearBreakPoints():void { this.bpmap = new Array(0x10000); for (let i = 0; i < 0x10000; i++) { this.bpmap[i] = null; } } /** * Get the break points. * @returns {Array<boolean>} The array that the index is * an address and the element is a status whether it * is a break point. */ getBreakPoints():boolean[] { return this.bpmap; } /** * Remove the break points. * @param {number} address The staring address. * @param {number} size The area size. * @returns {undefined} */ removeBreak(address:number, size:number):void { for (let i = 0; i < size; i++) { this.bpmap[address + i] = null; } } /** * Remove the break points. * @param {number} address The staring address. * @param {number} size The area size. * @returns {undefined} */ setBreak(address:number, size:number):void { for (let i = 0; i < size; i++) { this.bpmap[address + i] = true; } } /** * Get a 8-bit unsigned value from memory pointed by PC. * And the PC goes forward with 1 byte. * @returns {number} A 8-bit value. */ fetch():number { const PC = this.reg.PC; this.reg.PC = (PC + 1) & 0xffff; return this.memory.peek(PC); } /** * Get a 8-bit signed value from memory pointed by PC. * And the PC goes forward with 1 byte. * @returns {number} A 8-bit value. */ fetchSigned():number { return NumberUtil.to8bitSigned(this.fetch()); } /** * Get a 16-bit value from memory pointed by PC. * And the PC goes forward with 2 bytes. * @returns {number} A 16-bit value. */ fetchPair():number { const PC = this.reg.PC; this.reg.PC = (PC + 2) & 0xffff; return this.memory.peekPair(PC); } /** * Push 16-bit value to stack pointer(SP). * And SP goes back with 2 bytes. * @param {number} nn 16 bit integer. * @returns {undefined} */ pushPair(nn:number):void { this.memory.poke(--this.reg.SP, BinUtil.hibyte(nn)); this.memory.poke(--this.reg.SP, BinUtil.lobyte(nn)); } /** * Pop 16-bit value from stack pointer(SP). * And SP goes forward with 2 bytes. * @returns {number} 16 bit integer that was read. */ popPair():number { const lo = this.memory.peek(this.reg.SP++); const hi = this.memory.peek(this.reg.SP++); return BinUtil.pair(hi, lo); } /** * Increment the specific address value. * @param {number} addr A address to increment. * @returns {undefined} */ incrementAt(addr:number):void { this.memory.poke(addr, this.reg.getINCValue(this.memory.peek(addr))); } /** * Decrement the specific address value. * @param {number} addr A address to increment. * @returns {undefined} */ decrementAt(addr:number):void { this.memory.poke(addr, this.reg.getDECValue(this.memory.peek(addr))); } /** * Disassemble one operation code of Z80. * * @param {number} addr The starting address * @param {number} lastAddr The last address to assemble. * @returns {Z80LineAssembler} A disassembled result. */ disassemble(addr:number, lastAddr:number):Z80LineAssembler { let disasm = null; let errmsg = ""; const opecode = this.memory.peek(addr); try { const opecodeEntry = this.opecodeTable[opecode]; if (opecodeEntry == null) { errmsg = "UNKNOWN OPECODE"; } else if (opecodeEntry.disasm == null) { errmsg = "NO DISASSEMBLER"; } else { const dasmEntry = opecodeEntry.disasm(this.memory, addr); if (dasmEntry == null) { errmsg = "NULL RETURNED"; } else if (addr + dasmEntry.code.length > lastAddr) { disasm = null; } else { disasm = Z80LineAssembler.create(dasmEntry.mnemonic[0], dasmEntry.mnemonic.slice(1).join(","), dasmEntry.code); if ("refAddrTo" in dasmEntry) { disasm.setRefAddrTo(dasmEntry.refAddrTo); } } } } catch (e) { errmsg = "EXCEPTION THROWN"; } if (disasm == null) { disasm = Z80LineAssembler.create("DEFB", NumberUtil.HEX(opecode, 2) + "H", [opecode]); disasm.setComment(";*** DISASSEMBLE FAIL: " + errmsg); } return disasm; } /* * ----------------------------------------------------------------------------------- * ニーモニック 実行内容 命令コード1 命令コード2 命令コード3 命令コード4 * 76 543 210 76 543 210 76 543 210 76 543 210 * ----------------------------------------------------------------------------------- * 8ビットロードグループ * ----------------------------------------------------------------------------------- OK * LD r,n r<-n 00 r 110 <- n -> OK * LD (BC),A (BC)<-A 00 000 010 OK * LD A,(BC) A<-(BC) 00 001 010 OK * LD (DE),A (DE)<-A 00 010 010 OK * LD A,(DE) A<-(DE) 00 011 010 OK * LD (nn),A (nn)<-A 00 110 010 <- n -> <- n -> OK * LD (HL),n (HL)<-n 00 110 110 <- n -> OK * LD A,(nn) A<-(nn) 00 111 010 <- n -> <- n -> OK * LD r,r' r<-r' 01 r r' OK * LD r,(HL) r<-(HL) 01 r 110 OK * LD (HL),r (HL)<-r 01 110 r * LD r,(IX+d) r<-(IX+d) 11 011 101 01 r 110 <- d -> * LD (IX+d),r (IX+d)<-r 11 011 101 01 110 r <- d -> * LD (IX+d),n (IX+d)<-n 11 011 101 00 110 110 <- d -> <- n -> * LD A,I A<-I 11 101 101 01 010 111 * LD A,R A<-R 11 101 101 01 011 111 * LD I,A I<-A 11 101 101 01 000 111 * LD R,A R<-A 11 101 101 01 001 111 * LD (IY+d),n (IY+d)<-n 11 111 101 00 110 110 <- d -> <- n -> * LD r,(IY+d) r<-(IY+d) 11 111 101 01 r 110 <- d -> * LD (IY+d),r (IY+d)<-r 11 111 101 01 110 r <- d -> * ----------------------------------------------------------------------------------- * ニーモニック 実行内容 命令コード1 命令コード2 命令コード3 命令コード4 * 76 543 210 76 543 210 76 543 210 76 543 210 * ----------------------------------------------------------------------------------- * 16ビットロードグループ * ----------------------------------------------------------------------------------- OK * LD (nn),HL (nn+1)<-H,(nn)<-L 00 100 010 <- n -> <- n -> OK * LD HL,(nn) H<-(nn+1),L<-(nn) 00 101 010 <- n -> <- n -> OK * LD dd,nn dd<-nn 00 dd0 001 <- n -> <- n -> OK * PUSH qq (SP-2)<-qqL,(SP-1)<-qqH 11 qq0 101 OK * POP qq qqL<-(SP-2),qqH<-(SP-1) 11 qq0 001 OK * LD SP,HL SP<-HL 11 111 001 * LD IX,nn IX<-nn 11 011 101 00 100 001 <- n -> <- n -> * LD (nn),IX (nn+1)<-IXH,(nn)<-IXL 11 011 101 00 100 010 <- n -> <- n -> * LD IX,(nn) IXH<-(nn+1),IXL<-(nn) 11 011 101 00 101 010 <- n -> <- n -> * POP IX IXL<-(SP-2),IXH<-(SP-1) 11 011 101 11 100 001 * PUSH IX (SP-2)<-IXL,(SP-1)<-IXH 11 011 101 11 100 101 * LD SP,IX SP<-IX 11 011 101 11 111 001 * LD (nn),dd (nn+1)<-ddH,(nn)<-ddL 11 101 101 01 dd0 011 <- n -> <- n -> * LD dd,(nn) ddH<-(nn+1),ddL<-(nn) 11 101 101 01 dd1 011 <- n -> <- n -> * LD IY,nn IY<-nn 11 111 101 00 100 001 <- n -> <- n -> * LD (nn),IY (nn+1)<-IYH,(nn)<-IYL 11 111 101 00 100 010 <- n -> <- n -> * LD IY,(nn) IYH<-(nn+1),IYL<-(nn) 11 111 101 00 101 010 <- n -> <- n -> * POP IY IYL<-(SP-2),IYH<-(SP-1) 11 111 101 11 100 001 * PUSH IY (SP-2)<-IYL,(SP-1)<-IYH 11 111 101 11 100 101 * LD SP,IY SP<-IY 11 111 101 11 111 001 * */ createOpecodeTable():void { this.opecodeTable = new Array(256); const opeIX = new Array(256); const opeIY = new Array(256); const opeRotate = new Array(256); const opeRotateIX = new Array(256); const opeRotateIY = new Array(256); const opeMisc = new Array(256); const fetch = Z80.prototype.fetch.bind(this); const fetchSigned = Z80.prototype.fetchSigned.bind(this); const fetchPair = Z80.prototype.fetchPair.bind(this); const peek = this.memory.peek.bind(this.memory); const peekPair = this.memory.peekPair.bind(this.memory); const poke = this.memory.poke.bind(this.memory); const pushPair = Z80.prototype.pushPair.bind(this); const popPair = Z80.prototype.popPair.bind(this); for (let ii = 0; ii < 256; ii++) { this.opecodeTable[ii] = { mnemonic: null, proc: () => { throw "ILLEGAL OPCODE"; }, disasm: ((i) => (( /*mem, addr*/) => ({ code: [i], mnemonic: ["DEFB", NumberUtil.HEX(i, 2) + "H; *** UNKNOWN OPCODE"] })))(ii) }; opeIX[ii] = { mnemonic: null, proc: ((i) => (() => { throw "ILLEGAL OPCODE DD " + NumberUtil.HEX(i, 2) + " for IX command subset"; }))(ii), disasm: ((i) => (( /*mem, addr*/) => ({ code: [0xDD], mnemonic: ["DEFB", "DDh; *** UNKNOWN OPCODE " + NumberUtil.HEX(i, 2) + "H"] })))(ii) }; opeIY[ii] = { mnemonic: null, proc: ((i) => (() => { throw "ILLEGAL OPCODE FD " + NumberUtil.HEX(i, 2) + " for IY command subset"; }))(ii), disasm: ((i) => (( /*mem, addr*/) => ({ code: [0xFD], mnemonic: ["DEFB", "FDh; *** UNKNOWN OPCODE " + NumberUtil.HEX(i, 2) + "H"] })))(ii) }; opeRotate[ii] = { mnemonic: null, proc: ((i) => (() => { throw "ILLEGAL OPCODE CB " + NumberUtil.HEX(i, 2) + " for Rotate command subset"; }))(ii), disasm: ((i) => (( /*mem, addr*/) => ({ code: [0xCB], mnemonic: ["DEFB", "CBh; *** UNKNOWN OPCODE " + NumberUtil.HEX(i, 2) + "H"] })))(ii) }; opeRotateIX[ii] = { mnemonic: null, proc: ((i) => (() => { throw "ILLEGAL OPCODE DD CB " + NumberUtil.HEX(i, 2) + " for Rotate IX command subset"; }))(ii), disasm: ((i) => (( /*mem, addr*/) => ({ code: [0xDD, 0xCB], mnemonic: ["DEFW", "CBDDh; *** UNKNOWN OPCODE " + NumberUtil.HEX(i, 2) + "H"] })))(ii) }; opeRotateIY[ii] = { mnemonic: null, proc: ((i) => (() => { throw "ILLEGAL OPCODE FD CB " + NumberUtil.HEX(i, 2) + " for Rotate IY command subset"; }))(ii), disasm: ((i) => (( /*mem, addr*/) => ({ code: [0xFD, 0xCB], mnemonic: ["DEFW", "CBFDh; *** UNKNOWN OPCODE " + NumberUtil.HEX(i, 2) + "H"] })))(ii) }; opeMisc[ii] = { mnemonic: null, proc: ((i) => (() => { throw "ILLEGAL OPCODE ED " + NumberUtil.HEX(i, 2) + " for Misc command subset"; }))(ii), disasm: ((i) => (( /*mem, addr*/) => ({ code: [0xED], mnemonic: ["DEFB", "EDh; *** UNKNOWN OPCODE " + NumberUtil.HEX(i, 2) + "H"] })))(ii) }; } // IX command this.opecodeTable[0xDD] = { mnemonic: () => opeIX, proc: () => { opeIX[fetch()].proc(); }, disasm: (mem, addr) => opeIX[mem.peek(addr + 1)].disasm(mem, addr), }; // IY command this.opecodeTable[0xFD] = { mnemonic: () => opeIY, proc: () => { opeIY[fetch()].proc(); }, disasm: (mem, addr) => opeIY[mem.peek(addr + 1)].disasm(mem, addr), }; // Rotate this.opecodeTable[0xCB] = { mnemonic: () => opeRotate, proc: () => { opeRotate[fetch()].proc(); }, disasm: (mem, addr) => opeRotate[mem.peek(addr + 1)].disasm(mem, addr), }; // Misc this.opecodeTable[0xED] = { mnemonic: () => opeMisc, proc: () => { opeMisc[fetch()].proc(); }, disasm: (mem, addr) => opeMisc[mem.peek(addr + 1)].disasm(mem, addr), }; // ================================================================================= // // 8bit load group // // ================================================================================= // --------------------------------------------------------------------------------- // LD r,r' r<-r' 01 r r' // --------------------------------------------------------------------------------- const reg = this.reg; const getB = this.reg.getB.bind(this.reg); const getC = this.reg.getC.bind(this.reg); const getD = this.reg.getD.bind(this.reg); const getE = this.reg.getE.bind(this.reg); const getH = this.reg.getH.bind(this.reg); const getL = this.reg.getL.bind(this.reg); const getA = this.reg.getA.bind(this.reg); const setB = this.reg.setB.bind(this.reg); const setC = this.reg.setC.bind(this.reg); const setD = this.reg.setD.bind(this.reg); const setE = this.reg.setE.bind(this.reg); const setH = this.reg.setH.bind(this.reg); const setL = this.reg.setL.bind(this.reg); const setA = this.reg.setA.bind(this.reg); const getHL = this.reg.getHL.bind(this.reg); const getBC = this.reg.getBC.bind(this.reg); const getDE = this.reg.getDE.bind(this.reg); const procsLdRr = { "LD B,B": () => { setB(getB()); }, "LD B,C": () => { setB(getC()); }, "LD B,D": () => { setB(getD()); }, "LD B,E": () => { setB(getE()); }, "LD B,H": () => { setB(getH()); }, "LD B,L": () => { setB(getL()); }, "LD B,A": () => { setB(getA()); }, "LD C,B": () => { setC(getB()); }, "LD C,C": () => { setC(getC()); }, "LD C,D": () => { setC(getD()); }, "LD C,E": () => { setC(getE()); }, "LD C,H": () => { setC(getH()); }, "LD C,L": () => { setC(getL()); }, "LD C,A": () => { setC(getA()); }, "LD D,B": () => { setD(getB()); }, "LD D,C": () => { setD(getC()); }, "LD D,D": () => { setD(getD()); }, "LD D,E": () => { setD(getE()); }, "LD D,H": () => { setD(getH()); }, "LD D,L": () => { setD(getL()); }, "LD D,A": () => { setD(getA()); }, "LD E,B": () => { setE(getB()); }, "LD E,C": () => { setE(getC()); }, "LD E,D": () => { setE(getD()); }, "LD E,E": () => { setE(getE()); }, "LD E,H": () => { setE(getH()); }, "LD E,L": () => { setE(getL()); }, "LD E,A": () => { setE(getA()); }, "LD H,B": () => { setH(getB()); }, "LD H,C": () => { setH(getC()); }, "LD H,D": () => { setH(getD()); }, "LD H,E": () => { setH(getE()); }, "LD H,H": () => { setH(getH()); }, "LD H,L": () => { setH(getL()); }, "LD H,A": () => { setH(getA()); }, "LD L,B": () => { setL(getB()); }, "LD L,C": () => { setL(getC()); }, "LD L,D": () => { setL(getD()); }, "LD L,E": () => { setL(getE()); }, "LD L,H": () => { setL(getH()); }, "LD L,L": () => { setL(getL()); }, "LD L,A": () => { setL(getA()); }, "LD A,B": () => { setA(getB()); }, "LD A,C": () => { setA(getC()); }, "LD A,D": () => { setA(getD()); }, "LD A,E": () => { setA(getE()); }, "LD A,H": () => { setA(getH()); }, "LD A,L": () => { setA(getL()); }, "LD A,A": () => { setA(getA()); }, }; for (const dstRegId of Object.keys(Z80_Register.REG_R_ID2NAME)) { const nDstRegId = parseInt(dstRegId, 10); const dstRegName = Z80_Register.REG_R_ID2NAME[nDstRegId]; for (const srcRegId of Object.keys(Z80_Register.REG_R_ID2NAME)) { const srcRegName = Z80_Register.REG_R_ID2NAME[srcRegId]; const opecode = (0x01 << 6) | (nDstRegId << 3) | parseInt(srcRegId, 10); this.opecodeTable[opecode] = { mnemonic: "LD " + dstRegName + "," + srcRegName, proc: procsLdRr[`LD ${dstRegName},${srcRegName}`], "cycle": 4, disasm: ((oc, dst, src) => (( /*mem, addr*/) => ({ code: [oc], mnemonic: ["LD", dst, src], })))(opecode, dstRegName, srcRegName) }; } } // --------------------------------------------------------------------------------- // LD r,n r<-n 00 r 110 <- n -> // LD r,(HL) r<-(HL) 01 r 110 // LD (HL),r (HL)<-r 01 110 r // LD (HL),n (HL)<-n 00 110 110 <- n -> // --------------------------------------------------------------------------------- const disasmLdReg8N = (mem, addr) => { const opecode = mem.peek(addr); const code = [opecode]; const x = ((opecode & 0x40) !== 0) ? 1 : 0; const r1 = (opecode >> 3) & 0x07; const r2 = (opecode >> 0) & 0x07; const operand = ["???", "???"]; let n; operand[0] = ((r1 === 6) ? "(HL)" : Z80_Register.REG_R_ID2NAME[r1]); switch (x) { case 0: n = mem.peek(addr + 1); code.push(n); operand[1] = NumberUtil.HEX(n, 2) + "H"; break; case 1: operand[1] = ((r2 === 6) ? "(HL)" : Z80_Register.REG_R_ID2NAME[r2]); break; } return {code, mnemonic: ["LD", operand[0], operand[1]]}; }; // --------------------------------------------------------------------------------- // LD r,n r<-n 00 r 110 <- n -> // --------------------------------------------------------------------------------- this.opecodeTable[oct("0006")] = { mnemonic: "LD B,n", proc: () => { setB(fetch()); }, "cycle": 7, disasm: disasmLdReg8N, }; this.opecodeTable[oct("0016")] = { mnemonic: "LD C,n", proc: () => { setC(fetch()); }, "cycle": 7, disasm: disasmLdReg8N, }; this.opecodeTable[oct("0026")] = { mnemonic: "LD D,n", proc: () => { setD(fetch()); }, "cycle": 7, disasm: disasmLdReg8N, }; this.opecodeTable[oct("0036")] = { mnemonic: "LD E,n", proc: () => { setE(fetch()); }, "cycle": 7, disasm: disasmLdReg8N, }; this.opecodeTable[oct("0046")] = { mnemonic: "LD H,n", proc: () => { setH(fetch()); }, "cycle": 7, disasm: disasmLdReg8N, }; this.opecodeTable[oct("0056")] = { mnemonic: "LD L,n", proc: () => { setL(fetch()); }, "cycle": 7, disasm: disasmLdReg8N, }; this.opecodeTable[oct("0076")] = { mnemonic: "LD A,n", proc: () => { setA(fetch()); }, "cycle": 7, disasm: disasmLdReg8N, }; // --------------------------------------------------------------------------------- // LD r,(HL) r<-(HL) 01 r 110 // --------------------------------------------------------------------------------- this.opecodeTable[oct("0106")] = { mnemonic: "LD B,(HL)", proc: () => { setB(peek(getHL())); }, "cycle": 7, disasm: disasmLdReg8N, }; this.opecodeTable[oct("0116")] = { mnemonic: "LD C,(HL)", proc: () => { setC(peek(getHL())); }, "cycle": 7, disasm: disasmLdReg8N, }; this.opecodeTable[oct("0126")] = { mnemonic: "LD D,(HL)", proc: () => { setD(peek(getHL())); }, "cycle": 7, disasm: disasmLdReg8N, }; this.opecodeTable[oct("0136")] = { mnemonic: "LD E,(HL)", proc: () => { setE(peek(getHL())); }, "cycle": 7, disasm: disasmLdReg8N, }; this.opecodeTable[oct("0146")] = { mnemonic: "LD H,(HL)", proc: () => { setH(peek(getHL())); }, "cycle": 7, disasm: disasmLdReg8N, }; this.opecodeTable[oct("0156")] = { mnemonic: "LD L,(HL)", proc: () => { setL(peek(getHL())); }, "cycle": 7, disasm: disasmLdReg8N, }; this.opecodeTable[oct("0176")] = { mnemonic: "LD A,(HL)", proc: () => { setA(peek(getHL())); }, "cycle": 7, disasm: disasmLdReg8N, }; // --------------------------------------------------------------------------------- // LD (HL),r (HL)<-r 01 110 r // --------------------------------------------------------------------------------- this.opecodeTable[oct("0160")] = { mnemonic: "LD (HL),B", proc: () => { poke(getHL(), getB()); }, "cycle": 10, disasm: disasmLdReg8N, }; this.opecodeTable[oct("0161")] = { mnemonic: "LD (HL),C", proc: () => { poke(getHL(), getC()); }, "cycle": 10, disasm: disasmLdReg8N, }; this.opecodeTable[oct("0162")] = { mnemonic: "LD (HL),D", proc: () => { poke(getHL(), getD()); }, "cycle": 10, disasm: disasmLdReg8N, }; this.opecodeTable[oct("0163")] = { mnemonic: "LD (HL),E", proc: () => { poke(getHL(), getE()); }, "cycle": 10, disasm: disasmLdReg8N, }; this.opecodeTable[oct("0164")] = { mnemonic: "LD (HL),H", proc: () => { poke(getHL(), getH()); }, "cycle": 10, disasm: disasmLdReg8N, }; this.opecodeTable[oct("0165")] = { mnemonic: "LD (HL),L", proc: () => { poke(getHL(), getL()); }, "cycle": 10, disasm: disasmLdReg8N, }; this.opecodeTable[oct("0167")] = { mnemonic: "LD (HL),A", proc: () => { poke(getHL(), getA()); }, "cycle": 10, disasm: disasmLdReg8N, }; // --------------------------------------------------------------------------------- // LD (HL),n (HL)<-n 00 110 110 <- n -> // --------------------------------------------------------------------------------- this.opecodeTable[oct("0066")] = { mnemonic: "LD (HL),n", proc: () => { poke(getHL(), fetch()); }, "cycle": 10, disasm: disasmLdReg8N, }; // --------------------------------------------------------------------------------- // LD A,(BC) A<-(BC) 00 001 010 // --------------------------------------------------------------------------------- this.opecodeTable[oct("0012")] = { mnemonic: "LD A,(BC)", proc: () => { setA(peek(getBC())); }, "cycle": 7, disasm: (mem, addr) => ({code: [mem.peek(addr)], mnemonic: ["LD", "A", "(BC)"]}), }; // --------------------------------------------------------------------------------- // LD A,(DE) A<-(DE) 00 011 010 // --------------------------------------------------------------------------------- this.opecodeTable[oct("0032")] = { mnemonic: "LD A,(DE)", proc: () => { setA(peek(getDE())); }, "cycle": 7, disasm: (mem, addr) => ({code: [mem.peek(addr)], mnemonic: ["LD", "A", "(DE)"] }), }; // --------------------------------------------------------------------------------- // LD A,(nn) A<-(nn) 00 111 010 <- n -> <- n -> // --------------------------------------------------------------------------------- this.opecodeTable[oct("0072")] = { mnemonic: "LD A,(nn)", proc: () => { setA(peek(fetchPair())); }, "cycle": 13, disasm: (mem, addr) => ({ code: [mem.peek(addr), mem.peek(addr + 1), mem.peek(addr + 2)], mnemonic: ["LD", "A", "(" + NumberUtil.HEX(mem.peekPair(addr + 1), 4) + "H)"] }), }; // -------------------------------------------------------------------------------- // LD (BC),A (BC)<-A 00 000 010 // --------------------------------------------------------------------------------- this.opecodeTable[oct("0002")] = { mnemonic: "LD (BC),A", proc: () => { poke(getBC(), getA()); }, "cycle": 7, disasm: (mem, addr) => { return { code: [mem.peek(addr)], mnemonic: ["LD", "(BC)", "A"] }; } }; // --------------------------------------------------------------------------------- // LD (DE),A (DE)<-A 00 010 010 // --------------------------------------------------------------------------------- this.opecodeTable[oct("0022")] = { mnemonic: "LD (DE),A", proc: () => { poke(getDE(), getA()); }, "cycle": 7, disasm: (mem, addr) => { return { code: [mem.peek(addr)], mnemonic: ["LD", "(DE)", "A"] }; } }; // --------------------------------------------------------------------------------- // LD (nn),A (nn)<-A 00 110 010 <- n -> <- n -> // --------------------------------------------------------------------------------- this.opecodeTable[oct("0062")] = { mnemonic: "LD (nn),A", proc: () => { poke(fetchPair(), getA()); }, "cycle": 13, disasm: (mem, addr) => { return { code: [mem.peek(addr), mem.peek(addr + 1), mem.peek(addr + 2)], mnemonic: ["LD", "(" + NumberUtil.HEX(mem.peekPair(addr + 1), 4) + "H)", "A"] }; } }; // --------------------------------------------------------------------------------- // LD A,I A<-I 11 101 101 01 010 111 S,Z,H=0,P/V=IFF,N=0 // --------------------------------------------------------------------------------- opeMisc[oct("0127")] = { mnemonic: "LD A,I", proc: () => { reg.LD_A_I(this.IFF2); }, "cycle": 9, disasm: (mem, addr) => { return { code: [mem.peek(addr), mem.peek(addr + 1)], mnemonic: ["LD", "A", "I"] }; } }; // --------------------------------------------------------------------------------- // LD A,R A<-R 11 101 101 01 011 111 S,Z,H=0,P/V=IFF,N=0 // --------------------------------------------------------------------------------- opeMisc[oct("0137")] = { mnemonic: "LD A,R", proc: () => { reg.LD_A_R(this.IFF2, this.regB.R); }, "cycle": 9, disasm: (mem, addr) => { return { code: [mem.peek(addr), mem.peek(addr + 1)], mnemonic: ["LD", "A", "R"] }; } }; // --------------------------------------------------------------------------------- // LD I,A I<-A 11 101 101 01 000 111 // --------------------------------------------------------------------------------- opeMisc[oct("0107")] = { mnemonic: "LD I,A", proc: () => { reg.I = getA(); }, "cycle": 9, disasm: (mem, addr) => { return { code: [mem.peek(addr), mem.peek(addr + 1)], mnemonic: ["LD", "I", "A"] }; } }; // --------------------------------------------------------------------------------- // LD R,A R<-A 11 101 101 01 001 111 // --------------------------------------------------------------------------------- opeMisc[oct("0117")] = { mnemonic: "LD R,A", proc: () => { reg.R = this.regB.R = getA(); }, "cycle": 9, disasm: (mem, addr) => { return { code: [mem.peek(addr), mem.peek(addr + 1)], mnemonic: ["LD", "R", "A"] }; } }; // --------------------------------------------------------------------------------- // LD r, (IX+d) // --------------------------------------------------------------------------------- const disasmLdRIdxD = (mem, addr, r, idx) => { const d = mem.peek(addr + 2); return { code: [mem.peek(addr), mem.peek(addr + 1), d], mnemonic: ["LD", r, "(" + idx + "+" + NumberUtil.HEX(d, 2) + "H)"] }; }; const disasmLdRIdxDR = (mem, addr, idx, r) => { const d = mem.peek(addr + 2); const d8s = NumberUtil.to8bitSigned(d); const displacement = `${d8s>=0?"+":""}${d8s}`; return { code: [mem.peek(addr), mem.peek(addr + 1), d], mnemonic: ["LD", `(${idx}${displacement})`, r] }; }; opeIX[oct("0106")] = { mnemonic: "LD B,(IX+d)", proc: () => { setB(peek(reg.IX + fetchSigned())); }, "cycle": 19, disasm: (mem, addr) => { return disasmLdRIdxD(mem, addr, "B", "IX"); } }; opeIX[oct("0116")] = { mnemonic: "LD C,(IX+d)", proc: () => { setC(peek(reg.IX + fetchSigned())); }, "cycle": 19, disasm: (mem, addr) => { return disasmLdRIdxD(mem, addr, "C", "IX"); } }; opeIX[oct("0126")] = { mnemonic: "LD D,(IX+d)", proc: () => { setD(peek(reg.IX + fetchSigned())); }, "cycle": 19, disasm: (mem, addr) => { return disasmLdRIdxD(mem, addr, "D", "IX"); } }; opeIX[oct("0136")] = { mnemonic: "LD E,(IX+d)", proc: () => { setE(peek(reg.IX + fetchSigned())); }, "cycle": 19, disasm: (mem, addr) => { return disasmLdRIdxD(mem, addr, "E", "IX"); } }; opeIX[oct("0146")] = { mnemonic: "LD H,(IX+d)", proc: () => { setH(peek(reg.IX + fetchSigned())); }, "cycle": 19, disasm: (mem, addr) => { return disasmLdRIdxD(mem, addr, "H", "IX"); } }; opeIX[oct("0156")] = { mnemonic: "LD L,(IX+d)", proc: () => { setL(peek(reg.IX + fetchSigned())); }, "cycle": 19, disasm: (mem, addr) => { return disasmLdRIdxD(mem, addr, "L", "IX"); } }; opeIX[oct("0176")] = { mnemonic: "LD A,(IX+d)", proc: () => { setA(peek(reg.IX + fetchSigned())); }, "cycle": 19, disasm: (mem, addr) => { return disasmLdRIdxD(mem, addr, "A", "IX"); } }; // --------------------------------------------------------------------------------- // LD (IX+d), r // --------------------------------------------------------------------------------- opeIX[oct("0160")] = { mnemonic: "LD (IX+d),B", proc: () => { poke(reg.IX + fetchSigned(), getB()); }, "cycle": 19, disasm: (mem, addr) => { return disasmLdRIdxDR(mem, addr, "IX", "B"); } }; opeIX[oct("0161")] = { mnemonic: "LD (IX+d),C", proc: () => { poke(reg.IX + fetchSigned(), getC()); }, "cycle": 19, disasm: (mem, addr) => { return disasmLdRIdxDR(mem, addr, "IX", "C"); } }; opeIX[oct("0162")] = { mnemonic: "LD (IX+d),D", proc: () => { poke(reg.IX + fetchSigned(), getD()); }, "cycle": 19, disasm: (mem, addr) => { return disasmLdRIdxDR(mem, addr, "IX", "D"); } }; opeIX[oct("0163")] = { mnemonic: "LD (IX+d),E", proc: () => { poke(reg.IX + fetchSigned(), getE()); }, "cycle": 19, disasm: (mem, addr) => { return disasmLdRIdxDR(mem, addr, "IX", "E"); } }; opeIX[oct("0164")] = { mnemonic: "LD (IX+d),H", proc: () => { poke(reg.IX + fetchSigned(), getH()); }, "cycle": 19, disasm: (mem, addr) => { return disasmLdRIdxDR(mem, addr, "IX", "H"); } }; opeIX[oct("0165")] = { mnemonic: "LD (IX+d),L", proc: () => { poke(reg.IX + fetchSigned(), getL()); }, "cycle": 19, disasm: (mem, addr) => { return disasmLdRIdxDR(mem, addr, "IX", "L"); } }; opeIX[oct("0167")] = { mnemonic: "LD (IX+d),A", proc: () => { poke(reg.IX + fetchSigned(), getA()); }, "cycle": 19, disasm: (mem, addr) => { return disasmLdRIdxDR(mem, addr, "IX", "A"); } }; // --------------------------------------------------------------------------------- // LD r, (IX+d) // --------------------------------------------------------------------------------- opeIY[oct("0106")] = { mnemonic: "LD B,(IY+d)", proc: () => { setB(peek(reg.IY + fetchSigned())); }, "cycle": 19, disasm: (mem, addr) => { return disasmLdRIdxD(mem, addr, "B", "IY"); } }; opeIY[oct("0116")] = { mnemonic: "LD C,(IY+d)", proc: () => { setC(peek(reg.IY + fetchSigned())); }, "cycle": 19, disasm: (mem, addr) => { return disasmLdRIdxD(mem, addr, "C", "IY"); } }; opeIY[oct("0126")] = { mnemonic: "LD D,(IY+d)", proc: () => { setD(peek(reg.IY + fetchSigned())); }, "cycle": 19, disasm: (mem, addr) => { return disasmLdRIdxD(mem, addr, "D", "IY"); } }; opeIY[oct("0136")] = { mnemonic: "LD E,(IY+d)", proc: () => { setE(peek(reg.IY + fetchSigned())); }, "cycle": 19, disasm: (mem, addr) => { return disasmLdRIdxD(mem, addr, "E", "IY"); } }; opeIY[oct("0146")] = { mnemonic: "LD H,(IY+d)", proc: () => { setH(peek(reg.IY + fetchSigned())); }, "cycle": 19, disasm: (mem, addr) => { return disasmLdRIdxD(mem, addr, "H", "IY"); } }; opeIY[oct("0156")] = { mnemonic: "LD L,(IY+d)", proc: () => { setL(peek(reg.IY + fetchSigned())); }, "cycle": 19, disasm: (mem, addr) => { return disasmLdRIdxD(mem, addr, "L", "IY"); } }; opeIY[oct("0176")] = { mnemonic: "LD A,(IY+d)", proc: () => { setA(peek(reg.IY + fetchSigned())); }, "cycle": 19, disasm: (mem, addr) => { return disasmLdRIdxD(mem, addr, "A", "IY"); } }; // --------------------------------------------------------------------------------- // LD (IY+d), r // --------------------------------------------------------------------------------- opeIY[oct("0160")] = { mnemonic: "LD (IY+d),B", proc: () => { poke(reg.IY + fetchSigned(), getB()); }, "cycle": 19, disasm: (mem, addr) => { return disasmLdRIdxDR(mem, addr, "IY", "B"); } }; opeIY[oct("0161")] = { mnemonic: "LD (IY+d),C", proc: () => { poke(reg.IY + fetchSigned(), getC()); }, "cycle": 19, disasm: (mem, addr) => { return disasmLdRIdxDR(mem, addr, "IY", "C"); } }; opeIY[oct("0162")] = { mnemonic: "LD (IY+d),D", proc: () => { poke(reg.IY + fetchSigned(), getD()); }, "cycle": 19, disasm: (mem, addr) => { return disasmLdRIdxDR(mem, addr, "IY", "D"); } }; opeIY[oct("0163")] = { mnemonic: "LD (IY+d),E", proc: () => { poke(reg.IY + fetchSigned(), getE()); }, "cycle": 19, disasm: (mem, addr) => { return disasmLdRIdxDR(mem, addr, "IY", "E"); } }; opeIY[oct("0164")] = { mnemonic: "LD (IY+d),H", proc: () => { poke(reg.IY + fetchSigned(), getH()); }, "cycle": 19, disasm: (mem, addr) => { return disasmLdRIdxDR(mem, addr, "IY", "H"); } }; opeIY[oct("0165")] = { mnemonic: "LD (IY+d),L", proc: () => { poke(reg.IY + fetchSigned(), getL()); }, "cycle": 19, disasm: (mem, addr) => { return disasmLdRIdxDR(mem, addr, "IY", "L"); } }; opeIY[oct("0167")] = { mnemonic: "LD (IY+d),A", proc: () => { poke(reg.IY + fetchSigned(), getA()); }, "cycle": 19, disasm: (mem, addr) => { return disasmLdRIdxDR(mem, addr, "IY", "A"); } }; // ================================================================================= // // 16bit load group // // ================================================================================= // --------------------------------------------------------------------------------- // LD dd,nn dd<-nn 00 dd0 001 <- n -> <- n -> // --------------------------------------------------------------------------------- const disasmLdDdNn = (mem, addr:number) => { const opcode = mem.peek(addr); const nnL = mem.peek(addr + 1); const nnH = mem.peek(addr + 2); const nn = BinUtil.pair(nnH, nnL); const ddIndex = ((opcode >> 4) & 0x03); const reg16bitName = ["BC","DE","HL","SP"]; if(ddIndex >= reg16bitName.length) { throw "*** LD dd,nn; but unknown dd."; } const dd = reg16bitName[ddIndex]; return { code: [opcode, nnL, nnH], mnemonic: ["LD", dd, NumberUtil.HEX(nn, 4) + "H"] }; }; this.opecodeTable[oct("0001")] = { mnemonic: "LD BC,nn", cycle: 10, proc: () => { setC(fetch()); setB(fetch()); }, disasm: disasmLdDdNn }; this.opecodeTable[oct("0021")] = { mnemonic: "LD DE,nn", cycle: 10, proc: () => { setE(fetch()); setD(fetch()); }, disasm: disasmLdDdNn }; this.opecodeTable[oct("0041")] = { mnemonic: "LD HL,nn", cycle: 10, proc: () => { setL(fetch()); setH(fetch()); }, disasm: disasmLdDdNn }; this.opecodeTable[oct("0061")] = { mnemonic: "LD SP,nn", cycle: 10, proc: () => { reg.SP = fetchPair(); }, disasm: disasmLdDdNn }; // --------------------------------------------------------------------------------- // LD HL,(nn) H<-(nn+1),L<-(nn) 00 101 010 <- n -> <- n -> // --------------------------------------------------------------------------------- this.opecodeTable[oct("0052")] = { mnemonic: "LD HL,(nn)", cycle: 16, proc: () => { const nn = fetchPair(); setL(peek(nn + 0)); setH(peek(nn + 1)); }, disasm: (mem, addr) => { const opcode = mem.peek(addr); const nnL = mem.peek(addr + 1); const nnH = mem.peek(addr + 2); const nn = BinUtil.pair(nnH, nnL); return { code: [opcode, nnL, nnH], mnemonic: ["LD", "HL", "(" + NumberUtil.HEX(nn, 4) + "H)"] }; } }; opeMisc[oct("0113")] = { mnemonic: "LD BC,(nn)", cycle: 20, proc: () => { const nn = fetchPair(); setC(peek(nn + 0)); setB(peek(nn + 1)); }, disasm: (mem, addr) => { const opcode = mem.peek(addr); const operand = mem.peek(addr + 1); const nnL = mem.peek(addr + 2); const nnH = mem.peek(addr + 3); const nn = BinUtil.pair(nnH, nnL); return { code: [opcode, operand, nnL, nnH], mnemonic: ["LD", "BC", "(" + NumberUtil.HEX(nn, 4) + "H)"] }; } }; opeMisc[oct("0133")] = { mnemonic: "LD DE,(nn)", cycle: 20, proc: () => { const nn = fetchPair(); setE(peek(nn + 0)); setD(peek(nn + 1)); }, disasm: (mem, addr) => { const opcode = mem.peek(addr); const operand = mem.peek(addr + 1); const nnL = mem.peek(addr + 2); const nnH = mem.peek(addr + 3); const nn = BinUtil.pair(nnH, nnL); return { code: [opcode, operand, nnL, nnH], mnemonic: ["LD", "DE", "(" + NumberUtil.HEX(nn, 4) + "H)"] }; } }; opeMisc[oct("0153")] = { mnemonic: "LD HL,(nn)", cycle: 20, proc: () => { const nn = fetchPair(); setL(peek(nn + 0)); setH(peek(nn + 1)); }, disasm: (mem, addr) => { const opcode = mem.peek(addr); const operand = mem.peek(addr + 1); const nnL = mem.peek(addr + 2); const nnH = mem.peek(addr + 3); const nn = BinUtil.pair(nnH, nnL); return { code: [opcode, operand, nnL, nnH], mnemonic: ["LD", "HL", "(" + NumberUtil.HEX(nn, 4) + "H)"] }; } }; opeMisc[oct("0173")] = { mnemonic: "LD SP,(nn)", cycle: 20, proc: () => { reg.SP = peekPair(fetchPair()); }, disasm: (mem, addr) => { const opcode = mem.peek(addr); const operand = mem.peek(addr + 1); const nnL = mem.peek(addr + 2); const nnH = mem.peek(addr + 3); const nn = BinUtil.pair(nnH, nnL); return { code: [opcode, operand, nnL, nnH], mnemonic: ["LD", "SP", "(" + NumberUtil.HEX(nn, 4) + "H)"] }; } }; // --------------------------------------------------------------------------------- // LD (nn),HL (nn+1)<-H,(nn)<-L 00 100 010 <- n -> <- n -> // --------------------------------------------------------------------------------- this.opecodeTable[oct("0042")] = { mnemonic: "LD (nn), HL", cycle: 16, proc: () => { const nn = fetchPair(); poke(nn + 0, getL()); poke(nn + 1, getH()); }, disasm: (mem, addr) => { const opcode = mem.peek(addr); const nnL = mem.peek(addr + 1); const nnH = mem.peek(addr + 2); const nn = BinUtil.pair(nnH, nnL); return { code: [opcode, nnL, nnH], mnemonic: ["LD", "(" + NumberUtil.HEX(nn, 4) + "H)", "HL"] }; } }; opeMisc[oct("0103")] = { mnemonic: "LD (nn),BC", cycle: 20, proc: () => { const nn = fetchPair(); poke(nn + 0, getC()); poke(nn + 1, getB()); }, disasm: (mem, addr) => { const opcode = mem.peek(addr); const operand = mem.peek(addr + 1); const nnL = mem.peek(addr + 2);