eighty-eighty-js
Version:
A nice little Intel 8080 emulator for Node.js and Browser!
1,621 lines (1,194 loc) • 32.4 kB
text/typescript
import { u16, u8 } from 'typed-numbers';
import { Bit, Debug, Opcode } from '../helpers';
import { Device } from '../types';
import { Memory } from './Memory';
import { Register } from './Register';
/**
* Represents the Intel 8080 CPU.
*/
export class Cpu {
/** Clock Frequency. */
public static readonly CLOCK_FREQUENCY = 2_000_000;
/** Time a single step takes in milliseconds. */
public static readonly STEP_TIME = 16;
/** Amount of CPU cycles a single step takes. */
public static readonly STEP_CYCLES = (Cpu.STEP_TIME / (1000 / Cpu.CLOCK_FREQUENCY));
/** CPU Register. */
public reg = new Register();
/** CPU Memory. */
public mem: Memory;
/** Whether the CPU is halted. */
public halted = false;
/** Whether Flip-Flop instructions are interrupted. */
public interruptEnabled = false;
/** Whether debugging is enabled. */
public debugEnabled = false;
/** Device that handles input/output operations. */
public device: Device;
/** Keeps track of the amount CPU cycles of the current step. */
protected stepCycles = 0;
/** Keeps track of the start time of a step. */
protected stepZero = Date.now();
/**
* Constructor.
* @param mem - An instance of the Memory class, with the program already loaded in.
* @param device - A class that implements Device.
* @param [debugEnabled = false] - Enable debugging.
*/
constructor(mem: Memory, device: Device, debugEnabled: boolean = false) {
this.mem = mem;
this.device = device;
this.debugEnabled = debugEnabled;
}
public next() {
if (this.halted) return 0;
const opcode = this.getNextByte();
if (this.debugEnabled) Debug.printOperation(opcode, this);
let extraCycles = 0;
switch (opcode) {
/**********************
* NOP - No Operation *
**********************/
case 0x00:
case 0x08:
case 0x10:
case 0x18:
case 0x20:
case 0x28:
case 0x30:
case 0x38:
break;
/**************************
* Carry Bit Instructions *
**************************/
case 0x3f:
this.reg.setFlagC(!this.reg.getFlagC());
break;
case 0x37:
this.reg.setFlagC(true);
break;
/**************************************
* INR - Increment Register or Memory *
**************************************/
case 0x04:
this.reg.b = this.inr(this.reg.b);
break;
case 0x0c:
this.reg.c = this.inr(this.reg.c);
break;
case 0x14:
this.reg.d = this.inr(this.reg.d);
break;
case 0x1c:
this.reg.e = this.inr(this.reg.e);
break;
case 0x24:
this.reg.h = this.inr(this.reg.h);
break;
case 0x2c:
this.reg.l = this.inr(this.reg.l);
break;
case 0x34: {
const regM = this.getM();
const result = this.inr(regM);
this.setM(result);
break;
}
case 0x3c:
this.reg.a = this.inr(this.reg.a);
break;
/**************************************
* DCR - Decrement Register or Memory *
**************************************/
case 0x05:
this.reg.b = this.dcr(this.reg.b);
break;
case 0x0d:
this.reg.c = this.dcr(this.reg.c);
break;
case 0x15:
this.reg.d = this.dcr(this.reg.d);
break;
case 0x1d:
this.reg.e = this.dcr(this.reg.e);
break;
case 0x25:
this.reg.h = this.dcr(this.reg.h);
break;
case 0x2d:
this.reg.l = this.dcr(this.reg.l);
break;
case 0x35: {
const regM = this.getM();
const result = this.dcr(regM);
this.setM(result);
break;
}
case 0x3d:
this.reg.a = this.dcr(this.reg.a);
break;
/********************************
* CMA - Complement Accumulator *
********************************/
case 0x2f:
this.reg.a = u8(~this.reg.a);
break;
/************************************
* DAA - Decimal Adjust Accumulator *
************************************/
case 0x27:
this.daa();
break;
/***************************
* MOV - Move Instructions *
***************************/
case 0x40:
// this.reg.b = this.reg.b;
break;
case 0x41:
this.reg.b = this.reg.c;
break;
case 0x42:
this.reg.b = this.reg.d;
break;
case 0x43:
this.reg.b = this.reg.e;
break;
case 0x44:
this.reg.b = this.reg.h;
break;
case 0x45:
this.reg.b = this.reg.l;
break;
case 0x46:
this.reg.b = this.getM();
break;
case 0x47:
this.reg.b = this.reg.a;
break;
case 0x48:
this.reg.c = this.reg.b;
break;
case 0x49:
// this.reg.c = this.reg.c;
break;
case 0x4a:
this.reg.c = this.reg.d;
break;
case 0x4b:
this.reg.c = this.reg.e;
break;
case 0x4c:
this.reg.c = this.reg.h;
break;
case 0x4d:
this.reg.c = this.reg.l;
break;
case 0x4e:
this.reg.c = this.getM();
break;
case 0x4f:
this.reg.c = this.reg.a;
break;
case 0x50:
this.reg.d = this.reg.b;
break;
case 0x51:
this.reg.d = this.reg.c;
break;
case 0x52:
// this.reg.d = this.reg.d;
break;
case 0x53:
this.reg.d = this.reg.e;
break;
case 0x54:
this.reg.d = this.reg.h;
break;
case 0x55:
this.reg.d = this.reg.l;
break;
case 0x56:
this.reg.d = this.getM();
break;
case 0x57:
this.reg.d = this.reg.a;
break;
case 0x58:
this.reg.e = this.reg.b;
break;
case 0x59:
this.reg.e = this.reg.c;
break;
case 0x5a:
this.reg.e = this.reg.d;
break;
case 0x5b:
// this.reg.e = this.reg.e;
break;
case 0x5c:
this.reg.e = this.reg.h;
break;
case 0x5d:
this.reg.e = this.reg.l;
break;
case 0x5e:
this.reg.e = this.getM();
break;
case 0x5f:
this.reg.e = this.reg.a;
break;
case 0x60:
this.reg.h = this.reg.b;
break;
case 0x61:
this.reg.h = this.reg.c;
break;
case 0x62:
this.reg.h = this.reg.d;
break;
case 0x63:
this.reg.h = this.reg.e;
break;
case 0x64:
// this.reg.h = this.reg.h;
break;
case 0x65:
this.reg.h = this.reg.l;
break;
case 0x66:
this.reg.h = this.getM();
break;
case 0x67:
this.reg.h = this.reg.a;
break;
case 0x68:
this.reg.l = this.reg.b;
break;
case 0x69:
this.reg.l = this.reg.c;
break;
case 0x6a:
this.reg.l = this.reg.d;
break;
case 0x6b:
this.reg.l = this.reg.e;
break;
case 0x6c:
this.reg.l = this.reg.h;
break;
case 0x6d:
// this.reg.l = this.reg.l;
break;
case 0x6e:
this.reg.l = this.getM();
break;
case 0x6f:
this.reg.l = this.reg.a;
break;
case 0x70:
this.setM(this.reg.b);
break;
case 0x71:
this.setM(this.reg.c);
break;
case 0x72:
this.setM(this.reg.d);
break;
case 0x73:
this.setM(this.reg.e);
break;
case 0x74:
this.setM(this.reg.h);
break;
case 0x75:
this.setM(this.reg.l);
break;
case 0x77:
this.setM(this.reg.a);
break;
case 0x78:
this.reg.a = this.reg.b;
break;
case 0x79:
this.reg.a = this.reg.c;
break;
case 0x7a:
this.reg.a = this.reg.d;
break;
case 0x7b:
this.reg.a = this.reg.e;
break;
case 0x7c:
this.reg.a = this.reg.h;
break;
case 0x7d:
this.reg.a = this.reg.l;
break;
case 0x7e:
this.reg.a = this.getM();
break;
case 0x7f:
// this.reg.a = this.reg.a;
break;
/****************************
* STAX - Store Accumulator *
****************************/
case 0x02:
this.mem.set(this.reg.getBC(), this.reg.a);
break;
case 0x12:
this.mem.set(this.reg.getDE(), this.reg.a);
break;
/***************************
* LDAX - Load Accumulator *
***************************/
case 0x0a:
this.reg.a = this.mem.get(this.reg.getBC());
break;
case 0x1a:
this.reg.a = this.mem.get(this.reg.getDE());
break;
/***********************************************
* ADD - Add Register or Memory to Accumulator *
***********************************************/
case 0x80:
this.add(this.reg.b);
break;
case 0x81:
this.add(this.reg.c);
break;
case 0x82:
this.add(this.reg.d);
break;
case 0x83:
this.add(this.reg.e);
break;
case 0x84:
this.add(this.reg.h);
break;
case 0x85:
this.add(this.reg.l);
break;
case 0x86:
this.add(this.getM());
break;
case 0x87:
this.add(this.reg.a);
break;
/**********************************************************
* ADC - Add Register or Memory to Accumulator with Carry *
**********************************************************/
case 0x88:
this.adc(this.reg.b);
break;
case 0x89:
this.adc(this.reg.c);
break;
case 0x8a:
this.adc(this.reg.d);
break;
case 0x8b:
this.adc(this.reg.e);
break;
case 0x8c:
this.adc(this.reg.h);
break;
case 0x8d:
this.adc(this.reg.l);
break;
case 0x8e:
this.adc(this.getM());
break;
case 0x8f:
this.adc(this.reg.a);
break;
/******************************************************
* SUB - Subtract Register or Memory from Accumulator *
******************************************************/
case 0x90:
this.sub(this.reg.b);
break;
case 0x91:
this.sub(this.reg.c);
break;
case 0x92:
this.sub(this.reg.d);
break;
case 0x93:
this.sub(this.reg.e);
break;
case 0x94:
this.sub(this.reg.h);
break;
case 0x95:
this.sub(this.reg.l);
break;
case 0x96:
this.sub(this.getM());
break;
case 0x97:
this.sub(this.reg.a);
break;
/******************************************************************
* SBB - Subtract Register or Memory from Accumulator with Borrow *
******************************************************************/
case 0x98:
this.sbb(this.reg.b);
break;
case 0x99:
this.sbb(this.reg.c);
break;
case 0x9a:
this.sbb(this.reg.d);
break;
case 0x9b:
this.sbb(this.reg.e);
break;
case 0x9c:
this.sbb(this.reg.h);
break;
case 0x9d:
this.sbb(this.reg.l);
break;
case 0x9e:
this.sbb(this.getM());
break;
case 0x9f:
this.sbb(this.reg.a);
break;
/*********************************************************
* ANA - Logical AND Register or Memory with Accumulator *
*********************************************************/
case 0xa0:
this.ana(this.reg.b);
break;
case 0xa1:
this.ana(this.reg.c);
break;
case 0xa2:
this.ana(this.reg.d);
break;
case 0xa3:
this.ana(this.reg.e);
break;
case 0xa4:
this.ana(this.reg.h);
break;
case 0xa5:
this.ana(this.reg.l);
break;
case 0xa6:
this.ana(this.getM());
break;
case 0xa7:
this.ana(this.reg.a);
break;
/*******************************************************************************************
* XRA - Logical XOR (exclusive-or) Register or Memory with Accumulator (Zero Accumulator) *
*******************************************************************************************/
case 0xa8:
this.xra(this.reg.b);
break;
case 0xa9:
this.xra(this.reg.c);
break;
case 0xaa:
this.xra(this.reg.d);
break;
case 0xab:
this.xra(this.reg.e);
break;
case 0xac:
this.xra(this.reg.h);
break;
case 0xad:
this.xra(this.reg.l);
break;
case 0xae:
this.xra(this.getM());
break;
case 0xaf:
this.xra(this.reg.a);
break;
/********************************************************
* ORA - Logical OR Register or Memory with Accumulator *
********************************************************/
case 0xb0:
this.ora(this.reg.b);
break;
case 0xb1:
this.ora(this.reg.c);
break;
case 0xb2:
this.ora(this.reg.d);
break;
case 0xb3:
this.ora(this.reg.e);
break;
case 0xb4:
this.ora(this.reg.h);
break;
case 0xb5:
this.ora(this.reg.l);
break;
case 0xb6:
this.ora(this.getM());
break;
case 0xb7:
this.ora(this.reg.a);
break;
/*****************************************************
* CMP - Compare Register or Memory with Accumulator *
*****************************************************/
case 0xb8:
this.cmp(this.reg.b);
break;
case 0xb9:
this.cmp(this.reg.c);
break;
case 0xba:
this.cmp(this.reg.d);
break;
case 0xbb:
this.cmp(this.reg.e);
break;
case 0xbc:
this.cmp(this.reg.h);
break;
case 0xbd:
this.cmp(this.reg.l);
break;
case 0xbe:
this.cmp(this.getM());
break;
case 0xbf:
this.cmp(this.reg.a);
break;
/*********************************
* RLC - Rotate Accumulator Left *
*********************************/
case 0x07:
this.rlc();
break;
/*********************************
* RRC - Rotate Accumulator Right *
*********************************/
case 0x0f:
this.rrc();
break;
/***********************************************
* RAL - Rotate Accumulator Left Through Carry *
***********************************************/
case 0x17:
this.ral();
break;
/************************************************
* RAR - Rotate Accumulator Right Through Carry *
************************************************/
case 0x1f:
this.rar();
break;
/*******************************
* PUSH - Push Data Onto Stack *
*******************************/
case 0xc5:
this.stackAdd(this.reg.getBC());
break;
case 0xd5:
this.stackAdd(this.reg.getDE());
break;
case 0xe5:
this.stackAdd(this.reg.getHL());
break;
case 0xf5:
this.stackAdd(this.reg.getAF());
break;
/****************************
* POP - Pop Data Off Stack *
****************************/
case 0xc1:
this.reg.setBC(this.stackPop());
break;
case 0xd1:
this.reg.setDE(this.stackPop());
break;
case 0xe1:
this.reg.setHL(this.stackPop());
break;
case 0xf1:
this.reg.setAF(this.stackPop());
break;
/********************
* DAD - Double Add *
********************/
case 0x09:
this.dad(this.reg.getBC());
break;
case 0x19:
this.dad(this.reg.getDE());
break;
case 0x29:
this.dad(this.reg.getHL());
break;
case 0x39:
this.dad(this.reg.sp);
break;
/*********************************
* INX - Increment Register Pair *
*********************************/
case 0x03:
this.reg.setBC(u16(this.reg.getBC() + 1));
break;
case 0x13:
this.reg.setDE(u16(this.reg.getDE() + 1));
break;
case 0x23:
this.reg.setHL(u16(this.reg.getHL() + 1));
break;
case 0x33:
this.reg.sp = u16(this.reg.sp + 1);
break;
/*********************************
* DCX - Decrement Register Pair *
*********************************/
case 0x0b:
this.reg.setBC(u16(this.reg.getBC() - 1));
break;
case 0x1b:
this.reg.setDE(u16(this.reg.getDE() - 1));
break;
case 0x2b:
this.reg.setHL(u16(this.reg.getHL() - 1));
break;
case 0x3b:
this.reg.sp = u16(this.reg.sp - 1);
break;
/*****************************
* XCHG - Exchange Registers *
*****************************/
case 0xeb: {
const regH = this.reg.h;
this.reg.h = this.reg.d;
this.reg.d = regH;
const regL = this.reg.l;
this.reg.l = this.reg.e;
this.reg.e = regL;
break;
}
/*************************
* XTHL - Exchange Stack *
*************************/
case 0xe3: {
const memSP = this.mem.getWord(this.reg.sp);
const regHL = this.reg.getHL();
this.reg.setHL(memSP);
this.mem.setWord(this.reg.sp, regHL);
break;
}
/*******************************
* SPHL - Load SP from H and L *
*******************************/
case 0xf9:
this.reg.sp = this.reg.getHL();
break;
/*****************************
* LXI - Load Immediate Data *
*****************************/
case 0x01:
this.reg.setBC(this.getNextWord());
break;
case 0x11:
this.reg.setDE(this.getNextWord());
break;
case 0x21:
this.reg.setHL(this.getNextWord());
break;
case 0x31:
this.reg.sp = this.getNextWord();
break;
/*****************************
* MVI - Move Immediate Data *
*****************************/
case 0x06:
this.reg.b = this.getNextByte();
break;
case 0x0e:
this.reg.c = this.getNextByte();
break;
case 0x16:
this.reg.d = this.getNextByte();
break;
case 0x1e:
this.reg.e = this.getNextByte();
break;
case 0x26:
this.reg.h = this.getNextByte();
break;
case 0x2e:
this.reg.l = this.getNextByte();
break;
case 0x36:
this.setM(this.getNextByte());
break;
case 0x3e:
this.reg.a = this.getNextByte();
break;
/**************************************
* ADI - Add Immediate Data to Accumulator *
**************************************/
case 0xc6:
this.add(this.getNextByte());
break;
/*************************************************
* ACI - Add Immediate Data to Accumulator with Carry *
*************************************************/
case 0xce:
this.adc(this.getNextByte());
break;
/*********************************************
* SUI - Subtract Immediate Data from Accumulator *
*********************************************/
case 0xd6:
this.sub(this.getNextByte());
break;
/*********************************************************
* SBI - Subtract Immediate Data from Accumulator with Borrow *
*********************************************************/
case 0xde:
this.sbb(this.getNextByte());
break;
/************************************************
* ANI - Logical AND Immediate Data with Accumulator *
************************************************/
case 0xe6:
this.ana(this.getNextByte());
break;
/***************************************************************
* XRI - Logical XOR (exclusive-or) Immediate Data with Accumulator *
***************************************************************/
case 0xee:
this.xra(this.getNextByte());
break;
/****************************************************
* ORI - Logical OR Immediate Data with Accumulator *
****************************************************/
case 0xf6:
this.ora(this.getNextByte());
break;
/*************************************************
* CPI - Compare Immediate Data with Accumulator *
*************************************************/
case 0xfe:
this.cmp(this.getNextByte());
break;
/************************************
* STA - Store Accumulator Directly *
************************************/
case 0x32:
this.mem.set(this.getNextWord(), this.reg.a);
break;
/***********************************
* LDA - Load Accumulator Directly *
***********************************/
case 0x3a:
this.reg.a = this.mem.get(this.getNextWord());
break;
/********************************
* SHLD - Store Hand L Directly *
********************************/
case 0x22:
this.mem.setWord(this.getNextWord(), this.reg.getHL());
break;
/*******************************
* LHLD - Load Hand L Directly *
*******************************/
case 0x2a:
this.reg.setHL(this.mem.getWord(this.getNextWord()));
break;
/*******************************
* PCHL - Load Program Counter *
*******************************/
case 0xe9:
this.reg.pc = this.reg.getHL();
break;
/*********************
* Jump Instructions *
*********************/
case 0xc3:
case 0xda:
case 0xd2:
case 0xca:
case 0xc2:
case 0xfa:
case 0xf2:
case 0xea:
case 0xe2: {
const address = this.getNextWord();
if (this.checkBranchCondition(opcode)) {
this.reg.pc = address;
}
break;
}
/********************************
* Call Subroutine Instructions *
********************************/
case 0xcd:
case 0xdc:
case 0xd4:
case 0xcc:
case 0xc4:
case 0xec:
case 0xe4:
case 0xfc:
case 0xf4: {
const address = this.getNextWord();
if (this.checkBranchCondition(opcode)) {
extraCycles = 6;
this.stackAdd(this.reg.pc);
this.reg.pc = address;
}
break;
}
/***************************************
* Return from Subroutine Instructions *
***************************************/
case 0xc9:
case 0xd8:
case 0xd0:
case 0xc8:
case 0xc0:
case 0xf8:
case 0xf0:
case 0xe8:
case 0xe0:
if (this.checkBranchCondition(opcode)) {
extraCycles = 6;
this.reg.pc = this.stackPop();
}
break;
/****************************
* RST - Reset Instructions *
****************************/
case 0xc7:
case 0xcf:
case 0xd7:
case 0xdf:
case 0xe7:
case 0xef:
case 0xf7:
case 0xff:
this.stackAdd(this.reg.pc);
this.reg.pc = u16(opcode & 0x38);
break;
/************************************
* Interrupt Flip-Flop Instructions *
************************************/
case 0xfb:
this.interruptEnabled = true;
break;
case 0xf3:
this.interruptEnabled = false;
break;
/********************
* I/O Instructions *
********************/
case 0xdb:
this.reg.a = this.device.input(this.getNextByte());
break;
case 0xd3:
this.device.output(this.getNextByte(), this.reg.a);
break;
/**************************
* HLT - Halt Instruction *
**************************/
case 0x76:
this.halted = true;
break;
default:
throw new Cpu.UnimplementedInstructionError(opcode);
}
return Opcode.getCycles(opcode) + extraCycles;
}
/**
* Simulates a real CPU step with the Intel 8080's speed.
*/
public async step() {
if (this.stepCycles > Cpu.STEP_CYCLES) {
this.stepCycles -= Cpu.STEP_CYCLES;
const duration = Date.now() - this.stepZero;
const delay = Cpu.STEP_TIME - duration;
await new Promise(resolve => setTimeout(resolve, delay)); // Delay
this.stepZero = this.stepZero + Cpu.STEP_TIME;
}
const cycles = this.next();
this.stepCycles += cycles;
return cycles;
}
public handleInterrupt(address: u16) {
if (this.interruptEnabled) {
this.interruptEnabled = false;
this.stackAdd(this.reg.pc);
this.reg.pc = address;
this.stepCycles += Opcode.getCycles(0xcd as u8);
}
}
/************************
* IMMEDIATE ADDRESSING *
************************/
/**
* Returns the byte at the program counter and moves the program counter forwards by 1.
*/
public getNextByte(): u8 {
const value = this.mem.get(this.reg.pc);
this.reg.pc = u16(this.reg.pc + 1);
return value;
}
/**
* Returns the word at the program counter and moves the program counter forwards by 2.
*/
public getNextWord(): u16 {
const value = this.mem.getWord(this.reg.pc);
this.reg.pc = u16(this.reg.pc + 2);
return value;
}
/*******************
* MEMORY REGISTER *
*******************/
/**
* Get value of register M, which is just the memory.
*/
public getM(): u8 {
const address = this.reg.getHL();
return this.mem.get(address);
}
/**
* Set value of register M, which is just the memory.
* @param value - The new value.
*/
public setM(value: u8) {
const address = this.reg.getHL();
this.mem.set(address, value);
}
/********************
* STACK OPERATIONS *
********************/
/**
* Add a value to the top of the stack.
* @param value - The value to add.
*/
public stackAdd(value: u16) {
this.reg.sp = u16(this.reg.sp - 2);
this.mem.setWord(this.reg.sp, value);
}
/**
* Pop a value from the top of the stack.
* @return - The value from the stack.
*/
public stackPop(): u16 {
const value = this.mem.getWord(this.reg.sp);
this.reg.sp = u16(this.reg.sp + 2);
return value;
}
/*****************************
* BRANCH CONDITION CHECKING *
*****************************/
/**
* Checks branching condition for Jump/Call/Return operations
* @param opcode - The opcode of the operation.
*/
public checkBranchCondition(opcode: u8): boolean {
switch (opcode) {
case 0xc3: // JMP - Jump
case 0xcd: // CALL - Call
case 0xc9: // RET - Return
return true;
case 0xda: // JC - Jump if Carry
case 0xdc: // CC - Call if Carry
case 0xd8: // RC - Return if Carry
return this.reg.getFlagC();
case 0xd2: // JNC - Jump if No Carry
case 0xd4: // CNC - Call if No Carry
case 0xd0: // RNC - Return if No Carry
return !this.reg.getFlagC();
case 0xca: // JZ - Jump if Zero
case 0xcc: // CZ - Call if Zero
case 0xc8: // RZ - Return if Zero
return this.reg.getFlagZ();
case 0xc2: // JNZ - Jump if Not Zero
case 0xc4: // CNZ - Call if Not Zero
case 0xc0: // RNZ - Return if Not Zero
return !this.reg.getFlagZ();
case 0xfa: // JM - Jump if Minus
case 0xfc: // CM - Call if Minus
case 0xf8: // RM - Return if Minus
return this.reg.getFlagS();
case 0xf2: // JP - Jump if Plus
case 0xf4: // CP - Call if Plus
case 0xf0: // RP - Return if Plus
return !this.reg.getFlagS();
case 0xea: // JPE - Jump if Parity Even
case 0xec: // CPE - Call if Parity Even
case 0xe8: // RPE - Return if Parity Even
return this.reg.getFlagP();
case 0xe2: // JPO - Jump if Parity Odd
case 0xe4: // CPO - Call if Parity Odd
case 0xe0: // RPO - Return if Parity Odd
return !this.reg.getFlagP();
default:
throw new Cpu.UnimplementedInstructionError(opcode);
}
}
/******************
* ALU OPERATIONS *
******************/
/** */
public inr(num: u8): u8 {
const result = u8(num + 1);
this.reg.setFlagS(result);
this.reg.setFlagZ(result);
this.reg.setFlagP(result);
this.reg.setFlagA((num & 0x0f) + 0x01 > 0x0f);
return result;
}
public dcr(num: u8): u8 {
const result = u8(num - 1);
this.reg.setFlagS(result);
this.reg.setFlagZ(result);
this.reg.setFlagP(result);
this.reg.setFlagA((result & 0x0f) != 0x0f);
return result;
}
public daa() {
let accumulator = 0 as u8;
let flagC = this.reg.getFlagC();
const lsb = this.reg.a & 0xf;
const msb = (this.reg.a >> 4) & 0xf;
// If the least significant four bits of the accumulator represents a number greater than 9, or if the auxiliary carry flag is active, ...
if ((lsb > 9) || this.reg.getFlagA()) {
// ... the least significant four bits of the accumulator are incremented by six.
accumulator = u8(accumulator + 0x06);
}
// If the most significant four bits of the accumulator now represent a number greater than 9, or if the carry flag is active, ...
if ((msb > 9) || this.reg.getFlagC() || (msb >= 9 && lsb > 9)) {
// ... the most significant four bits of the accumulator are incremented by six.
accumulator = u8(accumulator + 0x60);
flagC = true;
}
this.add(accumulator);
this.reg.setFlagC(flagC);
}
public add(num: u8) {
const regA = this.reg.a;
const result = u8(regA + num);
this.reg.setFlagS(result);
this.reg.setFlagZ(result);
this.reg.setFlagP(result);
this.reg.setFlagA((regA & 0x0f) + (num & 0x0f) > 0x0f);
this.reg.setFlagC(regA + num > 0xff);
this.reg.a = result;
}
public adc(num: u8) {
const flagC = +this.reg.getFlagC();
const regA = this.reg.a;
const result = u8(regA + num + flagC);
this.reg.setFlagS(result);
this.reg.setFlagZ(result);
this.reg.setFlagP(result);
this.reg.setFlagA((regA & 0x0f) + (num & 0x0f) + flagC > 0x0f);
this.reg.setFlagC(regA + num + flagC > 0xff);
this.reg.a = result;
}
public sub(num: u8) {
const regA = this.reg.a;
const result = u8(regA - num);
this.reg.setFlagS(result);
this.reg.setFlagZ(result);
this.reg.setFlagP(result);
this.reg.setFlagA((regA & 0x0f) - (num & 0x0f) >= 0x00);
this.reg.setFlagC(regA < num);
this.reg.a = result;
}
public sbb(num: u8) {
const flagC = +this.reg.getFlagC();
const regA = this.reg.a;
const result = u8(regA - num - flagC);
this.reg.setFlagS(result);
this.reg.setFlagZ(result);
this.reg.setFlagP(result);
this.reg.setFlagA((regA & 0x0f) - (num & 0x0f) - flagC >= 0x00);
this.reg.setFlagC(regA < num + flagC);
this.reg.a = result;
}
public ana(num: u8) {
const result = u8(this.reg.a & num);
this.reg.setFlagS(result);
this.reg.setFlagZ(result);
this.reg.setFlagP(result);
this.reg.setFlagA(((this.reg.a | num) & 0x08) != 0);
this.reg.setFlagC(false);
this.reg.a = result;
}
public xra(num: u8) {
const result = u8(this.reg.a ^ num);
this.reg.setFlagS(result);
this.reg.setFlagZ(result);
this.reg.setFlagP(result);
this.reg.setFlagA(false);
this.reg.setFlagC(false);
this.reg.a = result;
}
public ora(num: u8) {
const result = u8(this.reg.a | num);
this.reg.setFlagS(result);
this.reg.setFlagZ(result);
this.reg.setFlagP(result);
this.reg.setFlagA(false);
this.reg.setFlagC(false);
this.reg.a = result;
}
public cmp(num: u8) {
const regA = this.reg.a;
this.sub(num);
this.reg.a = regA;
}
public rlc() {
const flagC = Bit.get(this.reg.a, 7);
const result = u8((this.reg.a << 1) | +flagC);
this.reg.setFlagC(flagC);
this.reg.a = result;
}
public rrc() {
const flagC = Bit.get(this.reg.a, 0);
const result = flagC ? u8(0x80 | (this.reg.a >> 1)) : u8(this.reg.a >> 1);
this.reg.setFlagC(flagC);
this.reg.a = result;
}
public ral() {
const flagC = Bit.get(this.reg.a, 7);
const result = u8((this.reg.a << 1) | +this.reg.getFlagC());
this.reg.setFlagC(flagC);
this.reg.a = result;
}
public rar() {
const flagC = Bit.get(this.reg.a, 0);
const result = this.reg.getFlagC() ? u8(0x80 | (this.reg.a >> 1)) : u8(this.reg.a >> 1);
this.reg.setFlagC(flagC);
this.reg.a = result;
}
public dad(num: u16) {
const regHL = this.reg.getHL();
const result = u16(regHL + num);
this.reg.setFlagC(regHL > 0xffff - num);
this.reg.setHL(result);
}
}
export namespace Cpu {
export class UnimplementedInstructionError extends Error {
public opcode: u8;
constructor(opcode: u8) {
super(`Unimplemented instruction: ${Opcode.toString(opcode)} (0x${Debug.toHexStr(opcode)})`);
this.opcode = opcode;
}
}
}