mz700-js
Version:
MZ-700 Full JavaScript Emulator
1,268 lines (1,260 loc) • 242 kB
text/typescript
"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);