From: Nick Downing Date: Wed, 3 Dec 2025 09:03:38 +0000 (+1100) Subject: Implement our own 8080 emulator, works but need to check AC flag, DAA instruction... X-Git-Url: https://git.ndcode.org/public/gitweb.cgi?a=commitdiff_plain;h=1969258bb2c530214786a35689cbc9de91e72577;p=multi_emu.git Implement our own 8080 emulator, works but need to check AC flag, DAA instruction, remove Z80-only registers and interrupt handling, etc --- diff --git a/.gitignore b/.gitignore index 3972a8e..fa5bbaa 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,8 @@ /emu_68000_alt /emu_6809 /emu_6809_alt +/emu_8080 +/emu_8080_alt /emu_8086 /emu_8086_alt /emu_mips diff --git a/.gitmodules b/.gitmodules index c4d83a0..da21646 100644 --- a/.gitmodules +++ b/.gitmodules @@ -49,3 +49,6 @@ [submodule "intel-8080"] path = intel-8080 url = https://github.com/nickd4/intel-8080.git +[submodule "jefftranter_8080"] + path = jefftranter_8080 + url = https://github.com/nickd4/jefftranter_8080.git diff --git a/Makefile b/Makefile index 498b535..d042b92 100644 --- a/Makefile +++ b/Makefile @@ -35,8 +35,8 @@ emu_z80_alt \ zexall.ihx \ zexdoc.ihx \ mbasic.ihx \ +emu_8080 \ emu_8080_alt -#emu_8080 #emu_mips #emu_pdp11 #emu_68000 @@ -300,12 +300,12 @@ zexdoc.ihx: ZEXALL/zexdoc.com rm __temp__.ihx # 8080 -#emu_8080: emu_8080.o cpu_8080.o -# ${CC} ${CFLAGS} -o $@ $^ +emu_8080: emu_8080.o cpu_8080.o + ${CC} ${CFLAGS} -o $@ $^ -#emu_8080.o: cpu_8080.h +emu_8080.o: cpu_8080.h -#cpu_8080.o: cpu_8080.h +cpu_8080.o: cpu_8080.h emu_8080_alt: emu_8080_alt.o 8080_cpu.o ${CC} ${CFLAGS} -o $@ $^ diff --git a/cpu_8080.c b/cpu_8080.c new file mode 100644 index 0000000..4f8b820 --- /dev/null +++ b/cpu_8080.c @@ -0,0 +1,856 @@ +#include +#include +#include +#include +#include "cpu_8080.h" + +// initialization +void cpu_8080_init( + struct cpu_8080 *self, + int (*read_byte)(void *context, int addr), + void *read_byte_context, + void (*write_byte)(void *context, int addr, int data), + void *write_byte_context, + int (*in_byte)(void *context, int addr), + void *in_byte_context, + void (*out_byte)(void *context, int addr, int data), + void *out_byte_context +) { + memset(self, 0, sizeof(struct cpu_8080)); + self->read_byte = read_byte; + self->read_byte_context = read_byte_context; + self->write_byte = write_byte; + self->write_byte_context = write_byte_context; + self->in_byte = in_byte; + self->in_byte_context = in_byte_context; + self->out_byte = out_byte; + self->out_byte_context = out_byte_context; +} + +void cpu_8080_reset(struct cpu_8080 *self) { + self->regs.word.pc = CPU_8080_RESET_ADDR; + self->regs.word.psw = 0; //0xffff; + self->regs.word.sp = 0xffff; + self->regs.word.iflags = 0; +} + +// instruction decode +void cpu_8080_execute(struct cpu_8080 *self) { + if (self->regs.bit.nmi_pending) { + self->regs.bit.nmi_pending = false; + + self->regs.word.pc += self->regs.bit.halt_flag; + self->regs.bit.halt_flag = false; + + self->regs.bit.iff1 = false; + cpu_8080_call(self, CPU_8080_NMI_ADDR); + return; + } + + if ( + self->regs.bit.irq_pending && + self->regs.bit.iff1 && + !self->regs.bit.ei_flag + ) { + self->regs.bit.irq_pending = false; + + self->regs.word.pc += self->regs.bit.halt_flag; + self->regs.bit.halt_flag = false; + + self->regs.bit.iff1 = false; + self->regs.bit.iff2 = false; + switch (self->regs.bit.im) { + case 0: + // self->regs.byte.v contains the opcode placed on bus by peripheral + // only RST instructions are supported at the moment (systems with + // 8259 / 8228 could also use CALL, that would be extra complexity) + assert((self->regs.byte.v & 0xc7) == 0xc7); + cpu_8080_call(self, self->regs.byte.v & 0x38); + break; + case 1: + cpu_8080_call(self, CPU_8080_IRQ_ADDR); + break; + case 2: + // self->regs.byte.v contains the vector placed on bus by peripheral + // it is combined with self->regs.byte.i to create vector word "iv" + cpu_8080_call(self, cpu_8080_read_word(self, self->regs.word.iv & 0xfffe)); + break; + default: + abort(); + } + return; + } + + self->regs.bit.halt_flag = false; + self->regs.bit.ei_flag = false; + switch (cpu_8080_fetch_byte(self)) { + case 0x00: + cpu_8080_nop(self); + break; + case 0x01: + cpu_8080_lxi(self, CPU_8080_EA_BC, cpu_8080_fetch_word(self)); + break; + case 0x02: + cpu_8080_stax(self, self->regs.word.bc); + break; + case 0x03: + cpu_8080_inx(self, CPU_8080_EA_BC); + break; + case 0x04: + cpu_8080_inr(self, CPU_8080_EA_B); + break; + case 0x05: + cpu_8080_dcr(self, CPU_8080_EA_B); + break; + case 0x06: + cpu_8080_mvi(self, CPU_8080_EA_B, cpu_8080_fetch_byte(self)); + break; + case 0x07: + cpu_8080_rlc(self); + break; + case 0x08: + cpu_8080_nop(self); + break; + case 0x09: + cpu_8080_dad(self, self->regs.word.bc); + break; + case 0x0a: + cpu_8080_ldax(self, self->regs.word.bc); + break; + case 0x0b: + cpu_8080_dcx(self, CPU_8080_EA_BC); + break; + case 0x0c: + cpu_8080_inr(self, CPU_8080_EA_C); + break; + case 0x0d: + cpu_8080_dcr(self, CPU_8080_EA_C); + break; + case 0x0e: + cpu_8080_mvi(self, CPU_8080_EA_C, cpu_8080_fetch_byte(self)); + break; + case 0x0f: + cpu_8080_rrc(self); + break; + case 0x10: + cpu_8080_nop(self); + break; + case 0x11: + cpu_8080_lxi(self, CPU_8080_EA_DE, cpu_8080_fetch_word(self)); + break; + case 0x12: + cpu_8080_stax(self, self->regs.word.de); + break; + case 0x13: + cpu_8080_inx(self, CPU_8080_EA_DE); + break; + case 0x14: + cpu_8080_inr(self, CPU_8080_EA_D); + break; + case 0x15: + cpu_8080_dcr(self, CPU_8080_EA_D); + break; + case 0x16: + cpu_8080_mvi(self, CPU_8080_EA_D, cpu_8080_fetch_byte(self)); + break; + case 0x17: + cpu_8080_ral(self); + break; + case 0x18: + cpu_8080_nop(self); + break; + case 0x19: + cpu_8080_dad(self, self->regs.word.de); + break; + case 0x1a: + cpu_8080_ldax(self, self->regs.word.de); + break; + case 0x1b: + cpu_8080_dcx(self, CPU_8080_EA_DE); + break; + case 0x1c: + cpu_8080_inr(self, CPU_8080_EA_E); + break; + case 0x1d: + cpu_8080_dcr(self, CPU_8080_EA_E); + break; + case 0x1e: + cpu_8080_mvi(self, CPU_8080_EA_E, cpu_8080_fetch_byte(self)); + break; + case 0x1f: + cpu_8080_rar(self); + break; + case 0x20: + cpu_8080_nop(self); + break; + case 0x21: + cpu_8080_lxi(self, CPU_8080_EA_HL, cpu_8080_fetch_word(self)); + break; + case 0x22: + cpu_8080_shld(self, cpu_8080_fetch_word(self)); + break; + case 0x23: + cpu_8080_inx(self, CPU_8080_EA_HL); + break; + case 0x24: + cpu_8080_inr(self, CPU_8080_EA_H); + break; + case 0x25: + cpu_8080_dcr(self, CPU_8080_EA_H); + break; + case 0x26: + cpu_8080_mvi(self, CPU_8080_EA_H, cpu_8080_fetch_byte(self)); + break; + case 0x27: + cpu_8080_daa(self); + break; + case 0x28: + cpu_8080_nop(self); + break; + case 0x29: + cpu_8080_dad(self, self->regs.word.hl); + break; + case 0x2a: + cpu_8080_lhld(self, cpu_8080_fetch_word(self)); + break; + case 0x2b: + cpu_8080_dcx(self, CPU_8080_EA_HL); + break; + case 0x2c: + cpu_8080_inr(self, CPU_8080_EA_L); + break; + case 0x2d: + cpu_8080_dcr(self, CPU_8080_EA_L); + break; + case 0x2e: + cpu_8080_mvi(self, CPU_8080_EA_L, cpu_8080_fetch_byte(self)); + break; + case 0x2f: + cpu_8080_cma(self); + break; + case 0x30: + cpu_8080_nop(self); + break; + case 0x31: + cpu_8080_lxi(self, CPU_8080_EA_SP, cpu_8080_fetch_word(self)); + break; + case 0x32: + cpu_8080_sta(self, cpu_8080_fetch_word(self)); + break; + case 0x33: + cpu_8080_inx(self, CPU_8080_EA_SP); + break; + case 0x34: + cpu_8080_inr(self, self->regs.word.hl); + break; + case 0x35: + cpu_8080_dcr(self, self->regs.word.hl); + break; + case 0x36: + cpu_8080_mvi(self, self->regs.word.hl, cpu_8080_fetch_byte(self)); + break; + case 0x37: + cpu_8080_stc(self); + break; + case 0x38: + cpu_8080_nop(self); + break; + case 0x39: + cpu_8080_dad(self, self->regs.word.sp); + break; + case 0x3a: + cpu_8080_lda(self, cpu_8080_fetch_word(self)); + break; + case 0x3b: + cpu_8080_dcx(self, CPU_8080_EA_SP); + break; + case 0x3c: + cpu_8080_inr(self, CPU_8080_EA_A); + break; + case 0x3d: + cpu_8080_dcr(self, CPU_8080_EA_A); + break; + case 0x3e: + cpu_8080_mvi(self, CPU_8080_EA_A, cpu_8080_fetch_byte(self)); + break; + case 0x3f: + cpu_8080_cmc(self); + break; + case 0x40: + cpu_8080_mov(self, CPU_8080_EA_B, self->regs.byte.b); + break; + case 0x41: + cpu_8080_mov(self, CPU_8080_EA_B, self->regs.byte.c); + break; + case 0x42: + cpu_8080_mov(self, CPU_8080_EA_B, self->regs.byte.d); + break; + case 0x43: + cpu_8080_mov(self, CPU_8080_EA_B, self->regs.byte.e); + break; + case 0x44: + cpu_8080_mov(self, CPU_8080_EA_B, self->regs.byte.h); + break; + case 0x45: + cpu_8080_mov(self, CPU_8080_EA_B, self->regs.byte.l); + break; + case 0x46: + cpu_8080_mov(self, CPU_8080_EA_B, cpu_8080_read_byte(self, self->regs.word.hl)); + break; + case 0x47: + cpu_8080_mov(self, CPU_8080_EA_B, self->regs.byte.a); + break; + case 0x48: + cpu_8080_mov(self, CPU_8080_EA_C, self->regs.byte.b); + break; + case 0x49: + cpu_8080_mov(self, CPU_8080_EA_C, self->regs.byte.c); + break; + case 0x4a: + cpu_8080_mov(self, CPU_8080_EA_C, self->regs.byte.d); + break; + case 0x4b: + cpu_8080_mov(self, CPU_8080_EA_C, self->regs.byte.e); + break; + case 0x4c: + cpu_8080_mov(self, CPU_8080_EA_C, self->regs.byte.h); + break; + case 0x4d: + cpu_8080_mov(self, CPU_8080_EA_C, self->regs.byte.l); + break; + case 0x4e: + cpu_8080_mov(self, CPU_8080_EA_C, cpu_8080_read_byte(self, self->regs.word.hl)); + break; + case 0x4f: + cpu_8080_mov(self, CPU_8080_EA_C, self->regs.byte.a); + break; + case 0x50: + cpu_8080_mov(self, CPU_8080_EA_D, self->regs.byte.b); + break; + case 0x51: + cpu_8080_mov(self, CPU_8080_EA_D, self->regs.byte.c); + break; + case 0x52: + cpu_8080_mov(self, CPU_8080_EA_D, self->regs.byte.d); + break; + case 0x53: + cpu_8080_mov(self, CPU_8080_EA_D, self->regs.byte.e); + break; + case 0x54: + cpu_8080_mov(self, CPU_8080_EA_D, self->regs.byte.h); + break; + case 0x55: + cpu_8080_mov(self, CPU_8080_EA_D, self->regs.byte.l); + break; + case 0x56: + cpu_8080_mov(self, CPU_8080_EA_D, cpu_8080_read_byte(self, self->regs.word.hl)); + break; + case 0x57: + cpu_8080_mov(self, CPU_8080_EA_D, self->regs.byte.a); + break; + case 0x58: + cpu_8080_mov(self, CPU_8080_EA_E, self->regs.byte.b); + break; + case 0x59: + cpu_8080_mov(self, CPU_8080_EA_E, self->regs.byte.c); + break; + case 0x5a: + cpu_8080_mov(self, CPU_8080_EA_E, self->regs.byte.d); + break; + case 0x5b: + cpu_8080_mov(self, CPU_8080_EA_E, self->regs.byte.e); + break; + case 0x5c: + cpu_8080_mov(self, CPU_8080_EA_E, self->regs.byte.h); + break; + case 0x5d: + cpu_8080_mov(self, CPU_8080_EA_E, self->regs.byte.l); + break; + case 0x5e: + cpu_8080_mov(self, CPU_8080_EA_E, cpu_8080_read_byte(self, self->regs.word.hl)); + break; + case 0x5f: + cpu_8080_mov(self, CPU_8080_EA_E, self->regs.byte.a); + break; + case 0x60: + cpu_8080_mov(self, CPU_8080_EA_H, self->regs.byte.b); + break; + case 0x61: + cpu_8080_mov(self, CPU_8080_EA_H, self->regs.byte.c); + break; + case 0x62: + cpu_8080_mov(self, CPU_8080_EA_H, self->regs.byte.d); + break; + case 0x63: + cpu_8080_mov(self, CPU_8080_EA_H, self->regs.byte.e); + break; + case 0x64: + cpu_8080_mov(self, CPU_8080_EA_H, self->regs.byte.h); + break; + case 0x65: + cpu_8080_mov(self, CPU_8080_EA_H, self->regs.byte.l); + break; + case 0x66: + cpu_8080_mov(self, CPU_8080_EA_H, cpu_8080_read_byte(self, self->regs.word.hl)); + break; + case 0x67: + cpu_8080_mov(self, CPU_8080_EA_H, self->regs.byte.a); + break; + case 0x68: + cpu_8080_mov(self, CPU_8080_EA_L, self->regs.byte.b); + break; + case 0x69: + cpu_8080_mov(self, CPU_8080_EA_L, self->regs.byte.c); + break; + case 0x6a: + cpu_8080_mov(self, CPU_8080_EA_L, self->regs.byte.d); + break; + case 0x6b: + cpu_8080_mov(self, CPU_8080_EA_L, self->regs.byte.e); + break; + case 0x6c: + cpu_8080_mov(self, CPU_8080_EA_L, self->regs.byte.h); + break; + case 0x6d: + cpu_8080_mov(self, CPU_8080_EA_L, self->regs.byte.l); + break; + case 0x6e: + cpu_8080_mov(self, CPU_8080_EA_L, cpu_8080_read_byte(self, self->regs.word.hl)); + break; + case 0x6f: + cpu_8080_mov(self, CPU_8080_EA_L, self->regs.byte.a); + break; + case 0x70: + cpu_8080_mov(self, self->regs.word.hl, self->regs.byte.b); + break; + case 0x71: + cpu_8080_mov(self, self->regs.word.hl, self->regs.byte.c); + break; + case 0x72: + cpu_8080_mov(self, self->regs.word.hl, self->regs.byte.d); + break; + case 0x73: + cpu_8080_mov(self, self->regs.word.hl, self->regs.byte.e); + break; + case 0x74: + cpu_8080_mov(self, self->regs.word.hl, self->regs.byte.h); + break; + case 0x75: + cpu_8080_mov(self, self->regs.word.hl, self->regs.byte.l); + break; + case 0x76: + cpu_8080_hlt(self); + break; + case 0x77: + cpu_8080_mov(self, self->regs.word.hl, self->regs.byte.a); + break; + case 0x78: + cpu_8080_mov(self, CPU_8080_EA_A, self->regs.byte.b); + break; + case 0x79: + cpu_8080_mov(self, CPU_8080_EA_A, self->regs.byte.c); + break; + case 0x7a: + cpu_8080_mov(self, CPU_8080_EA_A, self->regs.byte.d); + break; + case 0x7b: + cpu_8080_mov(self, CPU_8080_EA_A, self->regs.byte.e); + break; + case 0x7c: + cpu_8080_mov(self, CPU_8080_EA_A, self->regs.byte.h); + break; + case 0x7d: + cpu_8080_mov(self, CPU_8080_EA_A, self->regs.byte.l); + break; + case 0x7e: + cpu_8080_mov(self, CPU_8080_EA_A, cpu_8080_read_byte(self, self->regs.word.hl)); + break; + case 0x7f: + cpu_8080_mov(self, CPU_8080_EA_A, self->regs.byte.a); + break; + case 0x80: + cpu_8080_add(self, self->regs.byte.b); + break; + case 0x81: + cpu_8080_add(self, self->regs.byte.c); + break; + case 0x82: + cpu_8080_add(self, self->regs.byte.d); + break; + case 0x83: + cpu_8080_add(self, self->regs.byte.e); + break; + case 0x84: + cpu_8080_add(self, self->regs.byte.h); + break; + case 0x85: + cpu_8080_add(self, self->regs.byte.l); + break; + case 0x86: + cpu_8080_add(self, cpu_8080_read_byte(self, self->regs.word.hl)); + break; + case 0x87: + cpu_8080_add(self, self->regs.byte.a); + break; + case 0x88: + cpu_8080_adc(self, self->regs.byte.b); + break; + case 0x89: + cpu_8080_adc(self, self->regs.byte.c); + break; + case 0x8a: + cpu_8080_adc(self, self->regs.byte.d); + break; + case 0x8b: + cpu_8080_adc(self, self->regs.byte.e); + break; + case 0x8c: + cpu_8080_adc(self, self->regs.byte.h); + break; + case 0x8d: + cpu_8080_adc(self, self->regs.byte.l); + break; + case 0x8e: + cpu_8080_adc(self, cpu_8080_read_byte(self, self->regs.word.hl)); + break; + case 0x8f: + cpu_8080_adc(self, self->regs.byte.a); + break; + case 0x90: + cpu_8080_sub(self, self->regs.byte.b); + break; + case 0x91: + cpu_8080_sub(self, self->regs.byte.c); + break; + case 0x92: + cpu_8080_sub(self, self->regs.byte.d); + break; + case 0x93: + cpu_8080_sub(self, self->regs.byte.e); + break; + case 0x94: + cpu_8080_sub(self, self->regs.byte.h); + break; + case 0x95: + cpu_8080_sub(self, self->regs.byte.l); + break; + case 0x96: + cpu_8080_sub(self, cpu_8080_read_byte(self, self->regs.word.hl)); + break; + case 0x97: + cpu_8080_sub(self, self->regs.byte.a); + break; + case 0x98: + cpu_8080_sbb(self, self->regs.byte.b); + break; + case 0x99: + cpu_8080_sbb(self, self->regs.byte.c); + break; + case 0x9a: + cpu_8080_sbb(self, self->regs.byte.d); + break; + case 0x9b: + cpu_8080_sbb(self, self->regs.byte.e); + break; + case 0x9c: + cpu_8080_sbb(self, self->regs.byte.h); + break; + case 0x9d: + cpu_8080_sbb(self, self->regs.byte.l); + break; + case 0x9e: + cpu_8080_sbb(self, cpu_8080_read_byte(self, self->regs.word.hl)); + break; + case 0x9f: + cpu_8080_sbb(self, self->regs.byte.a); + break; + case 0xa0: + cpu_8080_ana(self, self->regs.byte.b); + break; + case 0xa1: + cpu_8080_ana(self, self->regs.byte.c); + break; + case 0xa2: + cpu_8080_ana(self, self->regs.byte.d); + break; + case 0xa3: + cpu_8080_ana(self, self->regs.byte.e); + break; + case 0xa4: + cpu_8080_ana(self, self->regs.byte.h); + break; + case 0xa5: + cpu_8080_ana(self, self->regs.byte.l); + break; + case 0xa6: + cpu_8080_ana(self, cpu_8080_read_byte(self, self->regs.word.hl)); + break; + case 0xa7: + cpu_8080_ana(self, self->regs.byte.a); + break; + case 0xa8: + cpu_8080_xra(self, self->regs.byte.b); + break; + case 0xa9: + cpu_8080_xra(self, self->regs.byte.c); + break; + case 0xaa: + cpu_8080_xra(self, self->regs.byte.d); + break; + case 0xab: + cpu_8080_xra(self, self->regs.byte.e); + break; + case 0xac: + cpu_8080_xra(self, self->regs.byte.h); + break; + case 0xad: + cpu_8080_xra(self, self->regs.byte.l); + break; + case 0xae: + cpu_8080_xra(self, cpu_8080_read_byte(self, self->regs.word.hl)); + break; + case 0xaf: + cpu_8080_xra(self, self->regs.byte.a); + break; + case 0xb0: + cpu_8080_ora(self, self->regs.byte.b); + break; + case 0xb1: + cpu_8080_ora(self, self->regs.byte.c); + break; + case 0xb2: + cpu_8080_ora(self, self->regs.byte.d); + break; + case 0xb3: + cpu_8080_ora(self, self->regs.byte.e); + break; + case 0xb4: + cpu_8080_ora(self, self->regs.byte.h); + break; + case 0xb5: + cpu_8080_ora(self, self->regs.byte.l); + break; + case 0xb6: + cpu_8080_ora(self, cpu_8080_read_byte(self, self->regs.word.hl)); + break; + case 0xb7: + cpu_8080_ora(self, self->regs.byte.a); + break; + case 0xb8: + cpu_8080_cmp(self, self->regs.byte.b); + break; + case 0xb9: + cpu_8080_cmp(self, self->regs.byte.c); + break; + case 0xba: + cpu_8080_cmp(self, self->regs.byte.d); + break; + case 0xbb: + cpu_8080_cmp(self, self->regs.byte.e); + break; + case 0xbc: + cpu_8080_cmp(self, self->regs.byte.h); + break; + case 0xbd: + cpu_8080_cmp(self, self->regs.byte.l); + break; + case 0xbe: + cpu_8080_cmp(self, cpu_8080_read_byte(self, self->regs.word.hl)); + break; + case 0xbf: + cpu_8080_cmp(self, self->regs.byte.a); + break; + case 0xc0: + cpu_8080_rnz(self); + break; + case 0xc1: + cpu_8080_pop(self, CPU_8080_EA_BC); + break; + case 0xc2: + cpu_8080_jnz(self, cpu_8080_fetch_word(self)); + break; + case 0xc3: + cpu_8080_jmp(self, cpu_8080_fetch_word(self)); + break; + case 0xc4: + cpu_8080_cnz(self, cpu_8080_fetch_word(self)); + break; + case 0xc5: + cpu_8080_push(self, self->regs.word.bc); + break; + case 0xc6: + cpu_8080_adi(self, cpu_8080_fetch_byte(self)); + break; + case 0xc7: + cpu_8080_rst(self, 0); + break; + case 0xc8: + cpu_8080_rz(self); + break; + case 0xc9: + cpu_8080_ret(self); + break; + case 0xca: + cpu_8080_jz(self, cpu_8080_fetch_word(self)); + break; + case 0xcb: + cpu_8080_jmp(self, cpu_8080_fetch_word(self)); + break; + case 0xcc: + cpu_8080_cz(self, cpu_8080_fetch_word(self)); + break; + case 0xcd: + cpu_8080_call(self, cpu_8080_fetch_word(self)); + break; + case 0xce: + cpu_8080_aci(self, cpu_8080_fetch_byte(self)); + break; + case 0xcf: + cpu_8080_rst(self, 1); + break; + case 0xd0: + cpu_8080_rnc(self); + break; + case 0xd1: + cpu_8080_pop(self, CPU_8080_EA_DE); + break; + case 0xd2: + cpu_8080_jnc(self, cpu_8080_fetch_word(self)); + break; + case 0xd3: + cpu_8080_out(self, cpu_8080_fetch_byte(self)); + break; + case 0xd4: + cpu_8080_cnc(self, cpu_8080_fetch_word(self)); + break; + case 0xd5: + cpu_8080_push(self, self->regs.word.de); + break; + case 0xd6: + cpu_8080_sui(self, cpu_8080_fetch_byte(self)); + break; + case 0xd7: + cpu_8080_rst(self, 2); + break; + case 0xd8: + cpu_8080_rc(self); + break; + case 0xd9: + cpu_8080_ret(self); + break; + case 0xda: + cpu_8080_jc(self, cpu_8080_fetch_word(self)); + break; + case 0xdb: + cpu_8080_in(self, cpu_8080_fetch_byte(self)); + break; + case 0xdc: + cpu_8080_cc(self, cpu_8080_fetch_word(self)); + break; + case 0xdd: + cpu_8080_call(self, cpu_8080_fetch_word(self)); + break; + case 0xde: + cpu_8080_sbi(self, cpu_8080_fetch_byte(self)); + break; + case 0xdf: + cpu_8080_rst(self, 3); + break; + case 0xe0: + cpu_8080_rpo(self); + break; + case 0xe1: + cpu_8080_pop(self, CPU_8080_EA_HL); + break; + case 0xe2: + cpu_8080_jpo(self, cpu_8080_fetch_word(self)); + break; + case 0xe3: + cpu_8080_xthl(self); + break; + case 0xe4: + cpu_8080_cpo(self, cpu_8080_fetch_word(self)); + break; + case 0xe5: + cpu_8080_push(self, self->regs.word.hl); + break; + case 0xe6: + cpu_8080_ani(self, cpu_8080_fetch_byte(self)); + break; + case 0xe7: + cpu_8080_rst(self, 4); + break; + case 0xe8: + cpu_8080_rpe(self); + break; + case 0xe9: + cpu_8080_pchl(self); + break; + case 0xea: + cpu_8080_jpe(self, cpu_8080_fetch_word(self)); + break; + case 0xeb: + cpu_8080_xchg(self); + break; + case 0xec: + cpu_8080_cpe(self, cpu_8080_fetch_word(self)); + break; + case 0xed: + cpu_8080_call(self, cpu_8080_fetch_word(self)); + break; + case 0xee: + cpu_8080_xri(self, cpu_8080_fetch_byte(self)); + break; + case 0xef: + cpu_8080_rst(self, 5); + break; + case 0xf0: + cpu_8080_rp(self); + break; + case 0xf1: + cpu_8080_pop(self, CPU_8080_EA_PSW); + break; + case 0xf2: + cpu_8080_jp(self, cpu_8080_fetch_word(self)); + break; + case 0xf3: + cpu_8080_di(self); + break; + case 0xf4: + cpu_8080_cp(self, cpu_8080_fetch_word(self)); + break; + case 0xf5: + cpu_8080_push(self, self->regs.word.psw); + break; + case 0xf6: + cpu_8080_ori(self, cpu_8080_fetch_byte(self)); + break; + case 0xf7: + cpu_8080_rst(self, 6); + break; + case 0xf8: + cpu_8080_rm(self); + break; + case 0xf9: + cpu_8080_sphl(self); + break; + case 0xfa: + cpu_8080_jm(self, cpu_8080_fetch_word(self)); + break; + case 0xfb: + cpu_8080_ei(self); + break; + case 0xfc: + cpu_8080_cm(self, cpu_8080_fetch_word(self)); + break; + case 0xfd: + cpu_8080_call(self, cpu_8080_fetch_word(self)); + break; + case 0xfe: + cpu_8080_cpi(self, cpu_8080_fetch_byte(self)); + break; + case 0xff: + cpu_8080_rst(self, 7); + break; + } +} diff --git a/cpu_8080.h b/cpu_8080.h new file mode 100644 index 0000000..01c4d8f --- /dev/null +++ b/cpu_8080.h @@ -0,0 +1,991 @@ +#ifndef _CPU_8080_H +#define _CPU_8080_H + +#include +#include +#include +#include + +// gcc specific +#ifndef ALWAYS_INLINE +#define ALWAYS_INLINE __attribute__((always_inline)) +#endif + +#define CPU_8080_NMI_ADDR 0x66 +#define CPU_8080_RESET_ADDR 0 +#define CPU_8080_IRQ_ADDR 0x38 + +// bits within REG_F +#define CPU_8080_REG_F_BIT_C 0 +#define CPU_8080_REG_F_BIT_P 2 +#define CPU_8080_REG_F_BIT_AC 4 +#define CPU_8080_REG_F_BIT_Z 6 +#define CPU_8080_REG_F_BIT_S 7 + +// special memory locations (negative address) +// note: CPU_8080_EA_SINK is a byte-wide sink for implementing the "in f,(c)" +// and DDCB/FDCB undocumented instructions, to store into and never read back +#define CPU_8080_EA_PC (-0x1e) +#define CPU_8080_EA_PSW (-0x1c) +#define CPU_8080_EA_BC (-0x1a) +#define CPU_8080_EA_DE (-0x18) +#define CPU_8080_EA_HL (-0x16) +#define CPU_8080_EA_SP (-0x14) +#define CPU_8080_EA_IX (-0x12) +#define CPU_8080_EA_IY (-0x10) +#define CPU_8080_EA_AF_PRIME (-0xe) +#define CPU_8080_EA_BC_PRIME (-0xc) +#define CPU_8080_EA_DE_PRIME (-0xa) +#define CPU_8080_EA_HL_PRIME (-8) +#define CPU_8080_EA_IFLAGS (-6) +#define CPU_8080_EA_IV (-4) + +// pc (-0x1e) +#define CPU_8080_EA_F (-0x1c) +#define CPU_8080_EA_A (-0x1b) +#define CPU_8080_EA_C (-0x1a) +#define CPU_8080_EA_B (-0x19) +#define CPU_8080_EA_E (-0x18) +#define CPU_8080_EA_D (-0x17) +#define CPU_8080_EA_L (-0x16) +#define CPU_8080_EA_H (-0x15) +// sp (-0x14) +#define CPU_8080_EA_IXL (-0x12) +#define CPU_8080_EA_IXH (-0x11) +#define CPU_8080_EA_IYL (-0x10) +#define CPU_8080_EA_IYH (-0xf) +// af_prime (-0xe) +// bc_prime (-0xc) +// de_prime (-0xa) +// hl_prime (-8) +// iflags (-6) +#define CPU_8080_EA_V (-4) +#define CPU_8080_EA_I (-3) +#define CPU_8080_EA_R (-2) +#define CPU_8080_EA_SINK (-1) + +// registers, in same order as special memory locations, but reversed on +// big endian hardware where special memory address will be complemented +// (this allows special memory to always look like it is little endian) +union cpu_8080_regs { +#if __BYTE_ORDER == __BIG_ENDIAN + struct { + uint8_t _fill_sink; + uint8_t _fill_r; + uint16_t _fill_iv; + uint16_t _fill_iflags : 7; + uint16_t reti_flag : 1; + uint16_t ei_flag : 1; + uint16_t halt_flag : 1; + uint16_t nmi_pending : 1; + uint16_t irq_pending : 1; + uint16_t iff2 : 1; + uint16_t iff1 : 1; + uint16_t im : 2; + uint16_t _fill_hl_prime; + uint16_t _fill_de_prime; + uint16_t _fill_bc_prime; + uint16_t _fill_af_prime; + uint16_t _fill_iy; + uint16_t _fill_ix; + uint16_t _fill_sp; + uint16_t _fill_hl; + uint16_t _fill_de; + uint16_t _fill_bc; + uint8_t _fill_a; + uint8_t sf : 1; + uint8_t zf : 1; + uint8_t _fill_5f: 1; + uint8_t acf : 1; + uint8_t _fill_3f: 1; + uint8_t pf: 1; + uint8_t _fill_1f : 1; + uint8_t cf : 1; + uint16_t _fill_pc; + } bit; + struct { + uint8_t sink; + uint8_t r; + uint8_t i; + uint8_t v; + uint16_t _fill_iflags; + uint16_t _fill_hl_prime; + uint16_t _fill_de_prime; + uint16_t _fill_bc_prime; + uint16_t _fill_af_prime; + uint8_t r; + uint8_t i; + uint8_t iyh; + uint8_t iyl; + uint8_t ixh; + uint8_t ixl; + uint16_t _fill_sp; + uint8_t h; + uint8_t l; + uint8_t d; + uint8_t e; + uint8_t b; + uint8_t c; + uint8_t a; + uint8_t f; + uint16_t _fill_pc; + } byte; + struct { + uint8_t _fill_sink; + uint8_t _fill_r; + uint16_t iv; + uint16_t iflags; + uint16_t hl_prime; + uint16_t de_prime; + uint16_t bc_prime; + uint16_t af_prime; + uint16_t iy; + uint16_t ix; + uint16_t sp; + uint16_t hl; + uint16_t de; + uint16_t bc; + uint16_t psw; + uint16_t pc; + } word; + uint8_t mem_be[0x1e]; +#else + struct { + uint16_t _fill_pc; + uint8_t cf : 1; + uint8_t _fill_1f : 1; + uint8_t pf: 1; + uint8_t _fill_3f: 1; + uint8_t acf : 1; + uint8_t _fill_5f: 1; + uint8_t zf : 1; + uint8_t sf : 1; + uint8_t _fill_a; + uint16_t _fill_bc; + uint16_t _fill_de; + uint16_t _fill_hl; + uint16_t _fill_sp; + uint16_t _fill_ix; + uint16_t _fill_iy; + uint16_t _fill_af_prime; + uint16_t _fill_bc_prime; + uint16_t _fill_de_prime; + uint16_t _fill_hl_prime; + uint16_t im : 2; + uint16_t iff1 : 1; + uint16_t iff2 : 1; + uint16_t irq_pending : 1; + uint16_t nmi_pending : 1; + uint16_t halt_flag : 1; + uint16_t ei_flag : 1; + uint16_t reti_flag : 1; + uint16_t _fill_iflags : 7; + uint16_t _fill_iv; + uint8_t _fill_r; + uint8_t _fill_sink; + } bit; + struct { + uint16_t _fill_pc; + uint8_t f; + uint8_t a; + uint8_t c; + uint8_t b; + uint8_t e; + uint8_t d; + uint8_t l; + uint8_t h; + uint16_t _fill_sp; + uint8_t ixl; + uint8_t ixh; + uint8_t iyl; + uint8_t iyh; + uint16_t _fill_af_prime; + uint16_t _fill_bc_prime; + uint16_t _fill_de_prime; + uint16_t _fill_hl_prime; + uint16_t _fill_iflags; + uint8_t v; + uint8_t i; + uint8_t r; + uint8_t sink; + } byte; + struct { + uint16_t pc; + uint16_t psw; + uint16_t bc; + uint16_t de; + uint16_t hl; + uint16_t sp; + uint16_t ix; + uint16_t iy; + uint16_t af_prime; + uint16_t bc_prime; + uint16_t de_prime; + uint16_t hl_prime; + uint16_t iflags; + uint16_t iv; + uint8_t _fill_r; + uint8_t _fill_sink; + } word; + uint8_t mem_le[0x1e]; +#endif +}; + +struct cpu_8080 { + int cycles; + int (*read_byte)(void *context, int addr); + void *read_byte_context; + void (*write_byte)(void *context, int addr, int data); + void *write_byte_context; + int (*in_byte)(void *context, int addr); + void *in_byte_context; + void (*out_byte)(void *context, int addr, int data); + void *out_byte_context; + union cpu_8080_regs regs; +}; + +// memory or special memory access +static ALWAYS_INLINE int cpu_8080_read_byte(struct cpu_8080 *self, int addr) { + if (addr < 0) +#if __BYTE_ORDER == __BIG_ENDIAN + return self->regs.mem_be[~addr]; +#else + return self->regs.mem_le[sizeof(union cpu_8080_regs) + addr]; +#endif + self->cycles += 1; + return self->read_byte(self->read_byte_context, addr & 0xffff); +} + +static ALWAYS_INLINE int cpu_8080_read_word(struct cpu_8080 *self, int addr) { + int data = cpu_8080_read_byte(self, addr); + return data | (cpu_8080_read_byte(self, addr + 1) << 8); +} + +static ALWAYS_INLINE void cpu_8080_write_byte(struct cpu_8080 *self, int addr, int data) { + self->cycles += 1; + if (addr < 0) +#if __BYTE_ORDER == __BIG_ENDIAN + self->regs.mem_be[~addr] = data; +#else + self->regs.mem_le[sizeof(union cpu_8080_regs) + addr] = data; +#endif + else + self->write_byte(self->write_byte_context, addr, data); +} + +static ALWAYS_INLINE void cpu_8080_write_word(struct cpu_8080 *self, int addr, int data) { + cpu_8080_write_byte(self, addr, data & 0xff); + cpu_8080_write_byte(self, addr + 1, data >> 8); +} + +static ALWAYS_INLINE int cpu_8080_fetch_byte(struct cpu_8080 *self) { + int data = cpu_8080_read_byte(self, self->regs.word.pc++); + return data; +} + +static ALWAYS_INLINE int cpu_8080_fetch_word(struct cpu_8080 *self) { + int data = cpu_8080_fetch_byte(self); + return data | (cpu_8080_fetch_byte(self) << 8); +} + +static ALWAYS_INLINE void cpu_8080_push_word(struct cpu_8080 *self, int data) { + self->regs.word.sp -= 2; + cpu_8080_write_word(self, self->regs.word.sp, data); +} + +static ALWAYS_INLINE int cpu_8080_pop_word(struct cpu_8080 *self) { + int data = cpu_8080_read_word(self, self->regs.word.sp); + self->regs.word.sp += 2; + return data; +} + +static ALWAYS_INLINE int cpu_8080_in_byte(struct cpu_8080 *self, int addr) { + self->cycles += 1; + return self->in_byte(self->in_byte_context, addr); +} + +static ALWAYS_INLINE void cpu_8080_out_byte(struct cpu_8080 *self, int addr, int data) { + self->cycles += 1; + self->out_byte(self->out_byte_context, addr, data); +} + +// effective address calculation +static ALWAYS_INLINE int cpu_8080_relative(struct cpu_8080 *self) { + return (int8_t)cpu_8080_fetch_byte(self); +} + +static ALWAYS_INLINE int cpu_8080_displacement(struct cpu_8080 *self, int base) { + return (base + (int8_t)cpu_8080_fetch_byte(self)) & 0xffff; +} + +// byte-addressed ports are extended to word using a as high 8 bits +static ALWAYS_INLINE int cpu_8080_port_word(struct cpu_8080 *self) { + return cpu_8080_fetch_byte(self) | (self->regs.byte.a << 8); +} + +// instruction execute +static ALWAYS_INLINE void cpu_8080_aci(struct cpu_8080 *self, int rvalue) { + int result0 = (self->regs.byte.a & 0xf) + (rvalue & 0xf) + self->regs.bit.cf; + int result1 = result0 + (self->regs.byte.a & 0x70) + (rvalue & 0x70); + int result2 = result1 + (self->regs.byte.a & 0x80) + (rvalue & 0x80); + + int parity = result2; + parity ^= parity >> 4; + parity ^= parity >> 2; + parity ^= parity >> 1; + + self->regs.byte.a = result2 & 0xff; + self->regs.bit.cf = result2 >> 8; + self->regs.bit.pf = (parity & 1) == 0; + self->regs.bit.acf = result0 >> 4; + self->regs.bit.zf = (result2 & 0xff) == 0; + self->regs.bit.sf = (result2 >> 7) & 1; +} + +static ALWAYS_INLINE void cpu_8080_adc(struct cpu_8080 *self, int rvalue) { + int result0 = (self->regs.byte.a & 0xf) + (rvalue & 0xf) + self->regs.bit.cf; + int result1 = result0 + (self->regs.byte.a & 0x70) + (rvalue & 0x70); + int result2 = result1 + (self->regs.byte.a & 0x80) + (rvalue & 0x80); + + int parity = result2; + parity ^= parity >> 4; + parity ^= parity >> 2; + parity ^= parity >> 1; + + self->regs.byte.a = result2 & 0xff; + self->regs.bit.cf = result2 >> 8; + self->regs.bit.pf = (parity & 1) == 0; + self->regs.bit.acf = result0 >> 4; + self->regs.bit.zf = (result2 & 0xff) == 0; + self->regs.bit.sf = (result2 >> 7) & 1; +} + +static ALWAYS_INLINE void cpu_8080_add(struct cpu_8080 *self, int rvalue) { + int result0 = (self->regs.byte.a & 0xf) + (rvalue & 0xf); + int result1 = result0 + (self->regs.byte.a & 0x70) + (rvalue & 0x70); + int result2 = result1 + (self->regs.byte.a & 0x80) + (rvalue & 0x80); + + int parity = result2; + parity ^= parity >> 4; + parity ^= parity >> 2; + parity ^= parity >> 1; + + self->regs.byte.a = result2 & 0xff; + self->regs.bit.cf = result2 >> 8; + self->regs.bit.pf = (parity & 1) == 0; + self->regs.bit.acf = result0 >> 4; + self->regs.bit.zf = (result2 & 0xff) == 0; + self->regs.bit.sf = (result2 >> 7) & 1; +} + +static ALWAYS_INLINE void cpu_8080_adi(struct cpu_8080 *self, int rvalue) { + int result0 = (self->regs.byte.a & 0xf) + (rvalue & 0xf); + int result1 = result0 + (self->regs.byte.a & 0x70) + (rvalue & 0x70); + int result2 = result1 + (self->regs.byte.a & 0x80) + (rvalue & 0x80); + + int parity = result2; + parity ^= parity >> 4; + parity ^= parity >> 2; + parity ^= parity >> 1; + + self->regs.byte.a = result2 & 0xff; + self->regs.bit.cf = result2 >> 8; + self->regs.bit.pf = (parity & 1) == 0; + self->regs.bit.acf = result0 >> 4; + self->regs.bit.zf = (result2 & 0xff) == 0; + self->regs.bit.sf = (result2 >> 7) & 1; +} + +static ALWAYS_INLINE void cpu_8080_ana(struct cpu_8080 *self, int rvalue) { + int result = self->regs.byte.a & rvalue; + + int parity = result; + parity ^= parity >> 4; + parity ^= parity >> 2; + parity ^= parity >> 1; + + self->regs.byte.a = result; + self->regs.bit.cf = false; + self->regs.bit.pf = (parity & 1) == 0; + self->regs.bit.acf = true; + self->regs.bit.zf = result == 0; + self->regs.bit.sf = result >> 7; +} + +static ALWAYS_INLINE void cpu_8080_ani(struct cpu_8080 *self, int rvalue) { + int result = self->regs.byte.a & rvalue; + + int parity = result; + parity ^= parity >> 4; + parity ^= parity >> 2; + parity ^= parity >> 1; + + self->regs.byte.a = result; + self->regs.bit.cf = false; + self->regs.bit.pf = (parity & 1) == 0; + self->regs.bit.acf = true; + self->regs.bit.zf = result == 0; + self->regs.bit.sf = result >> 7; +} + +static ALWAYS_INLINE void cpu_8080_call(struct cpu_8080 *self, int rvalue) { + cpu_8080_push_word(self, self->regs.word.pc); + self->regs.word.pc = rvalue; +} + +static ALWAYS_INLINE void cpu_8080_cc(struct cpu_8080 *self, int rvalue) { + if (self->regs.bit.cf) { + cpu_8080_push_word(self, self->regs.word.pc); + self->regs.word.pc = rvalue; + } +} + +static ALWAYS_INLINE void cpu_8080_cm(struct cpu_8080 *self, int rvalue) { + if (self->regs.bit.sf) { + cpu_8080_push_word(self, self->regs.word.pc); + self->regs.word.pc = rvalue; + } +} + +static ALWAYS_INLINE void cpu_8080_cma(struct cpu_8080 *self) { + self->regs.byte.a = ~self->regs.byte.a; + self->regs.bit.acf = true; +} + +static ALWAYS_INLINE void cpu_8080_cmc(struct cpu_8080 *self) { + self->regs.bit.acf = self->regs.bit.cf; + self->regs.bit.cf = !self->regs.bit.cf; +} + +static ALWAYS_INLINE void cpu_8080_cmp(struct cpu_8080 *self, int rvalue) { + int result0 = (self->regs.byte.a & 0xf) - (rvalue & 0xf); + int result1 = result0 + (self->regs.byte.a & 0x70) - (rvalue & 0x70); + int result2 = result1 + (self->regs.byte.a & 0x80) - (rvalue & 0x80); + + int parity = result2; + parity ^= parity >> 4; + parity ^= parity >> 2; + parity ^= parity >> 1; + + self->regs.bit.cf = (result2 >> 8) & 1; + self->regs.bit.pf = (parity & 1) == 0; + self->regs.bit.acf = (result0 >> 4) & 1; + self->regs.bit.zf = (result2 & 0xff) == 0; + self->regs.bit.sf = (result2 >> 7) & 1; +} + +static ALWAYS_INLINE void cpu_8080_cnc(struct cpu_8080 *self, int rvalue) { + if (!self->regs.bit.cf) { + cpu_8080_push_word(self, self->regs.word.pc); + self->regs.word.pc = rvalue; + } +} + +static ALWAYS_INLINE void cpu_8080_cnz(struct cpu_8080 *self, int rvalue) { + if (!self->regs.bit.zf) { + cpu_8080_push_word(self, self->regs.word.pc); + self->regs.word.pc = rvalue; + } +} + +static ALWAYS_INLINE void cpu_8080_cp(struct cpu_8080 *self, int rvalue) { + if (!self->regs.bit.sf) { + cpu_8080_push_word(self, self->regs.word.pc); + self->regs.word.pc = rvalue; + } +} + +static ALWAYS_INLINE void cpu_8080_cpe(struct cpu_8080 *self, int rvalue) { + if (self->regs.bit.pf) { + cpu_8080_push_word(self, self->regs.word.pc); + self->regs.word.pc = rvalue; + } +} + +static ALWAYS_INLINE void cpu_8080_cpi(struct cpu_8080 *self, int rvalue) { + int result0 = (self->regs.byte.a & 0xf) - (rvalue & 0xf); + int result1 = result0 + (self->regs.byte.a & 0x70) - (rvalue & 0x70); + int result2 = result1 + (self->regs.byte.a & 0x80) - (rvalue & 0x80); + + int parity = result2; + parity ^= parity >> 4; + parity ^= parity >> 2; + parity ^= parity >> 1; + + self->regs.bit.cf = (result2 >> 8) & 1; + self->regs.bit.pf = (parity & 1) == 0; + self->regs.bit.acf = (result0 >> 4) & 1; + self->regs.bit.zf = (result2 & 0xff) == 0; + self->regs.bit.sf = (result2 >> 7) & 1; +} + +static ALWAYS_INLINE void cpu_8080_cpo(struct cpu_8080 *self, int rvalue) { + if (!self->regs.bit.pf) { + cpu_8080_push_word(self, self->regs.word.pc); + self->regs.word.pc = rvalue; + } +} + +static ALWAYS_INLINE void cpu_8080_cz(struct cpu_8080 *self, int rvalue) { + if (self->regs.bit.zf) { + cpu_8080_push_word(self, self->regs.word.pc); + self->regs.word.pc = rvalue; + } +} + +static ALWAYS_INLINE void cpu_8080_daa(struct cpu_8080 *self) { + int correction = 0; + if (self->regs.bit.acf || (self->regs.byte.a & 0xf) >= 0xa) + correction = 6; + if (self->regs.bit.cf || self->regs.byte.a >= 0x9a) { + correction |= 0x60; + self->regs.bit.cf = true; + } + + int result0 = (self->regs.byte.a & 0xf) + (correction & 0xf); + int result1 = result0 + (self->regs.byte.a & 0xf0) + (correction & 0xf0); + + int parity = result1; + parity ^= parity >> 4; + parity ^= parity >> 2; + parity ^= parity >> 1; + + self->regs.byte.a = result1 & 0xff; + //self->regs.bit.cf |= (result1 >> 8) & 1; + self->regs.bit.pf = (parity & 1) == 0; + self->regs.bit.acf = (result0 >> 4) & 1; + self->regs.bit.zf = (result1 & 0xff) == 0; + self->regs.bit.sf = (result1 >> 7) & 1; +} + +static ALWAYS_INLINE void cpu_8080_dad(struct cpu_8080 *self, int rvalue) { + int result0 = (self->regs.word.hl & 0xfff) + (rvalue & 0xfff); + int result1 = result0 + (self->regs.word.hl & 0xf000) + (rvalue & 0xf000); + + self->regs.word.hl = result1 & 0xffff; + self->regs.bit.cf = result1 >> 16; + self->regs.bit.acf = result0 >> 12; +} + +static ALWAYS_INLINE void cpu_8080_dcr(struct cpu_8080 *self, int lvalue) { + int data = cpu_8080_read_byte(self, lvalue); + + int result0 = (data & 0xf) - 1; + int result1 = result0 + (data & 0x70); + int result2 = result1 + (data & 0x80); + + int parity = result2; + parity ^= parity >> 4; + parity ^= parity >> 2; + parity ^= parity >> 1; + + cpu_8080_write_byte(self, lvalue, result2 & 0xff); + self->regs.bit.pf = (parity & 1) == 0; + self->regs.bit.acf = (result0 >> 4) & 1; + self->regs.bit.zf = (result2 & 0xff) == 0; + self->regs.bit.sf = (result2 >> 7) & 1; +} + +static ALWAYS_INLINE void cpu_8080_dcx(struct cpu_8080 *self, int lvalue) { + int result = (cpu_8080_read_word(self, lvalue) - 1) & 0xffff; + cpu_8080_write_word(self, lvalue, result); +} + +static ALWAYS_INLINE void cpu_8080_di(struct cpu_8080 *self) { + self->regs.bit.iff1 = false; + self->regs.bit.iff2 = false; +} + +static ALWAYS_INLINE void cpu_8080_ei(struct cpu_8080 *self) { + self->regs.bit.iff1 = true; + self->regs.bit.iff2 = true; + self->regs.bit.ei_flag = true; // causes one-cycle interrupt delay +} + +static ALWAYS_INLINE void cpu_8080_hlt(struct cpu_8080 *self) { + self->regs.bit.halt_flag = true; + --self->regs.word.pc; +} + +static ALWAYS_INLINE void cpu_8080_in(struct cpu_8080 *self, int rvalue) { + self->regs.byte.a = cpu_8080_in_byte(self, rvalue); +} + +static ALWAYS_INLINE void cpu_8080_inr(struct cpu_8080 *self, int lvalue) { + int data = cpu_8080_read_byte(self, lvalue); + + int result0 = (data & 0xf) + 1; + int result1 = result0 + (data & 0x70); + int result2 = result1 + (data & 0x80); + + int parity = result2; + parity ^= parity >> 4; + parity ^= parity >> 2; + parity ^= parity >> 1; + + cpu_8080_write_byte(self, lvalue, result2 & 0xff); + self->regs.bit.pf = (parity & 1) == 0; + self->regs.bit.acf = result0 >> 4; + self->regs.bit.zf = (result2 & 0xff) == 0; + self->regs.bit.sf = (result2 >> 7) & 1; +} + +static ALWAYS_INLINE void cpu_8080_inx(struct cpu_8080 *self, int lvalue) { + int result = (cpu_8080_read_word(self, lvalue) + 1) & 0xffff; + cpu_8080_write_word(self, lvalue, result); +} + +static ALWAYS_INLINE void cpu_8080_jc(struct cpu_8080 *self, int rvalue) { + if (self->regs.bit.cf) + self->regs.word.pc = rvalue; +} + +static ALWAYS_INLINE void cpu_8080_jm(struct cpu_8080 *self, int rvalue) { + if (self->regs.bit.sf) + self->regs.word.pc = rvalue; +} + + +static ALWAYS_INLINE void cpu_8080_jmp(struct cpu_8080 *self, int rvalue) { + self->regs.word.pc = rvalue; +} + +static ALWAYS_INLINE void cpu_8080_jnc(struct cpu_8080 *self, int rvalue) { + if (!self->regs.bit.cf) + self->regs.word.pc = rvalue; +} + +static ALWAYS_INLINE void cpu_8080_jnz(struct cpu_8080 *self, int rvalue) { + if (!self->regs.bit.zf) + self->regs.word.pc = rvalue; +} + +static ALWAYS_INLINE void cpu_8080_jp(struct cpu_8080 *self, int rvalue) { + if (!self->regs.bit.sf) + self->regs.word.pc = rvalue; +} + +static ALWAYS_INLINE void cpu_8080_jpe(struct cpu_8080 *self, int rvalue) { + if (self->regs.bit.pf) + self->regs.word.pc = rvalue; +} + +static ALWAYS_INLINE void cpu_8080_jpo(struct cpu_8080 *self, int rvalue) { + if (!self->regs.bit.pf) + self->regs.word.pc = rvalue; +} + +static ALWAYS_INLINE void cpu_8080_jz(struct cpu_8080 *self, int rvalue) { + if (self->regs.bit.zf) + self->regs.word.pc = rvalue; +} + +static ALWAYS_INLINE void cpu_8080_lda(struct cpu_8080 *self, int rvalue) { + self->regs.byte.a = cpu_8080_read_byte(self, rvalue); +} + +static ALWAYS_INLINE void cpu_8080_ldax(struct cpu_8080 *self, int rvalue) { + self->regs.byte.a = cpu_8080_read_byte(self, rvalue); +} + +static ALWAYS_INLINE void cpu_8080_lhld(struct cpu_8080 *self, int rvalue) { + self->regs.word.hl = cpu_8080_read_word(self, rvalue); +} + +static ALWAYS_INLINE void cpu_8080_lxi(struct cpu_8080 *self, int lvalue, int rvalue) { + cpu_8080_write_word(self, lvalue, rvalue); +} + +static ALWAYS_INLINE void cpu_8080_mov(struct cpu_8080 *self, int lvalue, int rvalue) { + cpu_8080_write_byte(self, lvalue, rvalue); +} + +static ALWAYS_INLINE void cpu_8080_mvi(struct cpu_8080 *self, int lvalue, int rvalue) { + cpu_8080_write_byte(self, lvalue, rvalue); +} + +static ALWAYS_INLINE void cpu_8080_nop(struct cpu_8080 *self) { +} + +static ALWAYS_INLINE void cpu_8080_ora(struct cpu_8080 *self, int rvalue) { + int result = self->regs.byte.a | rvalue; + + int parity = result; + parity ^= parity >> 4; + parity ^= parity >> 2; + parity ^= parity >> 1; + + self->regs.byte.a = result; + self->regs.bit.cf = false; + self->regs.bit.pf = (parity & 1) == 0; + self->regs.bit.acf = false; + self->regs.bit.zf = result == 0; + self->regs.bit.sf = result >> 7; +} + +static ALWAYS_INLINE void cpu_8080_ori(struct cpu_8080 *self, int rvalue) { + int result = self->regs.byte.a | rvalue; + + int parity = result; + parity ^= parity >> 4; + parity ^= parity >> 2; + parity ^= parity >> 1; + + self->regs.byte.a = result; + self->regs.bit.cf = false; + self->regs.bit.pf = (parity & 1) == 0; + self->regs.bit.acf = false; + self->regs.bit.zf = result == 0; + self->regs.bit.sf = result >> 7; +} + +static ALWAYS_INLINE void cpu_8080_out(struct cpu_8080 *self, int rvalue) { + cpu_8080_out_byte(self, rvalue, self->regs.byte.a); +} + +static ALWAYS_INLINE void cpu_8080_pchl(struct cpu_8080 *self) { + self->regs.word.pc = self->regs.word.hl; +} + +static ALWAYS_INLINE void cpu_8080_pop(struct cpu_8080 *self, int lvalue) { + cpu_8080_write_word(self, lvalue, cpu_8080_pop_word(self)); +} + +static ALWAYS_INLINE void cpu_8080_push(struct cpu_8080 *self, int rvalue) { + cpu_8080_push_word(self, rvalue); +} + +static ALWAYS_INLINE void cpu_8080_ral(struct cpu_8080 *self) { + int result = self->regs.bit.cf | (self->regs.byte.a << 1); + + self->regs.byte.a = result & 0xff; + self->regs.bit.cf = result >> 8; + self->regs.bit.acf = false; +} + +static ALWAYS_INLINE void cpu_8080_rar(struct cpu_8080 *self) { + int result = self->regs.byte.a | (self->regs.bit.cf << 8); + + self->regs.byte.a = result >> 1; + self->regs.bit.cf = result & 1; + self->regs.bit.acf = false; +} + +static ALWAYS_INLINE void cpu_8080_rc(struct cpu_8080 *self) { + if (self->regs.bit.cf) + self->regs.word.pc = cpu_8080_pop_word(self); +} + +static ALWAYS_INLINE void cpu_8080_ret(struct cpu_8080 *self) { + self->regs.word.pc = cpu_8080_pop_word(self); +} + +static ALWAYS_INLINE void cpu_8080_rlc(struct cpu_8080 *self) { + int result = (self->regs.byte.a >> 7) | (self->regs.byte.a << 1); + + self->regs.byte.a = result & 0xff; + self->regs.bit.cf = result >> 8; + self->regs.bit.acf = false; +} + +static ALWAYS_INLINE void cpu_8080_rm(struct cpu_8080 *self) { + if (self->regs.bit.sf) + self->regs.word.pc = cpu_8080_pop_word(self); +} + +static ALWAYS_INLINE void cpu_8080_rnc(struct cpu_8080 *self) { + if (!self->regs.bit.cf) + self->regs.word.pc = cpu_8080_pop_word(self); +} + +static ALWAYS_INLINE void cpu_8080_rnz(struct cpu_8080 *self) { + if (!self->regs.bit.zf) + self->regs.word.pc = cpu_8080_pop_word(self); +} + +static ALWAYS_INLINE void cpu_8080_rp(struct cpu_8080 *self) { + if (!self->regs.bit.sf) + self->regs.word.pc = cpu_8080_pop_word(self); +} + +static ALWAYS_INLINE void cpu_8080_rpe(struct cpu_8080 *self) { + if (self->regs.bit.pf) + self->regs.word.pc = cpu_8080_pop_word(self); +} + +static ALWAYS_INLINE void cpu_8080_rpo(struct cpu_8080 *self) { + if (!self->regs.bit.pf) + self->regs.word.pc = cpu_8080_pop_word(self); +} + +static ALWAYS_INLINE void cpu_8080_rrc(struct cpu_8080 *self) { + int result = self->regs.byte.a | ((self->regs.byte.a & 1) << 8); + + self->regs.byte.a = result >> 1; + self->regs.bit.cf = result & 1; + self->regs.bit.acf = false; +} + +static ALWAYS_INLINE void cpu_8080_rst(struct cpu_8080 *self, int rvalue) { + cpu_8080_push_word(self, self->regs.word.pc); + self->regs.word.pc = rvalue << 3; +} + +static ALWAYS_INLINE void cpu_8080_rz(struct cpu_8080 *self) { + if (self->regs.bit.zf) + self->regs.word.pc = cpu_8080_pop_word(self); +} + +static ALWAYS_INLINE void cpu_8080_sbb(struct cpu_8080 *self, int rvalue) { + int result0 = (self->regs.byte.a & 0xf) - (rvalue & 0xf) - self->regs.bit.cf; + int result1 = result0 + (self->regs.byte.a & 0x70) - (rvalue & 0x70); + int result2 = result1 + (self->regs.byte.a & 0x80) - (rvalue & 0x80); + + int parity = result2; + parity ^= parity >> 4; + parity ^= parity >> 2; + parity ^= parity >> 1; + + self->regs.byte.a = result2 & 0xff; + self->regs.bit.cf = (result2 >> 8) & 1; + self->regs.bit.pf = (parity & 1) == 0; + self->regs.bit.acf = (result0 >> 4) & 1; + self->regs.bit.zf = (result2 & 0xff) == 0; + self->regs.bit.sf = (result2 >> 7) & 1; +} + +static ALWAYS_INLINE void cpu_8080_sbi(struct cpu_8080 *self, int rvalue) { + int result0 = (self->regs.byte.a & 0xf) - (rvalue & 0xf) - self->regs.bit.cf; + int result1 = result0 + (self->regs.byte.a & 0x70) - (rvalue & 0x70); + int result2 = result1 + (self->regs.byte.a & 0x80) - (rvalue & 0x80); + + int parity = result2; + parity ^= parity >> 4; + parity ^= parity >> 2; + parity ^= parity >> 1; + + self->regs.byte.a = result2 & 0xff; + self->regs.bit.cf = (result2 >> 8) & 1; + self->regs.bit.pf = (parity & 1) == 0; + self->regs.bit.acf = (result0 >> 4) & 1; + self->regs.bit.zf = (result2 & 0xff) == 0; + self->regs.bit.sf = (result2 >> 7) & 1; +} + +static ALWAYS_INLINE void cpu_8080_shld(struct cpu_8080 *self, int rvalue) { + cpu_8080_write_word(self, rvalue, self->regs.word.hl); +} + +static ALWAYS_INLINE void cpu_8080_sphl(struct cpu_8080 *self) { + self->regs.word.sp = self->regs.word.hl; +} + +static ALWAYS_INLINE void cpu_8080_sta(struct cpu_8080 *self, int rvalue) { + cpu_8080_write_byte(self, rvalue, self->regs.byte.a); +} + +static ALWAYS_INLINE void cpu_8080_stax(struct cpu_8080 *self, int rvalue) { + cpu_8080_write_byte(self, rvalue, self->regs.byte.a); +} + +static ALWAYS_INLINE void cpu_8080_stc(struct cpu_8080 *self) { + self->regs.bit.cf = true; + self->regs.bit.acf = false; +} + +static ALWAYS_INLINE void cpu_8080_sub(struct cpu_8080 *self, int rvalue) { + int result0 = (self->regs.byte.a & 0xf) - (rvalue & 0xf); + int result1 = result0 + (self->regs.byte.a & 0x70) - (rvalue & 0x70); + int result2 = result1 + (self->regs.byte.a & 0x80) - (rvalue & 0x80); + + int parity = result2; + parity ^= parity >> 4; + parity ^= parity >> 2; + parity ^= parity >> 1; + + self->regs.byte.a = result2 & 0xff; + self->regs.bit.cf = (result2 >> 8) & 1; + self->regs.bit.pf = (parity & 1) == 0; + self->regs.bit.acf = (result0 >> 4) & 1; + self->regs.bit.zf = (result2 & 0xff) == 0; + self->regs.bit.sf = (result2 >> 7) & 1; +} + +static ALWAYS_INLINE void cpu_8080_sui(struct cpu_8080 *self, int rvalue) { + int result0 = (self->regs.byte.a & 0xf) - (rvalue & 0xf); + int result1 = result0 + (self->regs.byte.a & 0x70) - (rvalue & 0x70); + int result2 = result1 + (self->regs.byte.a & 0x80) - (rvalue & 0x80); + + int parity = result2; + parity ^= parity >> 4; + parity ^= parity >> 2; + parity ^= parity >> 1; + + self->regs.byte.a = result2 & 0xff; + self->regs.bit.cf = (result2 >> 8) & 1; + self->regs.bit.pf = (parity & 1) == 0; + self->regs.bit.acf = (result0 >> 4) & 1; + self->regs.bit.zf = (result2 & 0xff) == 0; + self->regs.bit.sf = (result2 >> 7) & 1; +} + +static ALWAYS_INLINE void cpu_8080_xchg(struct cpu_8080 *self) { + int data = self->regs.word.hl; + self->regs.word.hl = self->regs.word.de; + self->regs.word.de = data; +} + +static ALWAYS_INLINE void cpu_8080_xra(struct cpu_8080 *self, int rvalue) { + int result = self->regs.byte.a ^ rvalue; + + int parity = result; + parity ^= parity >> 4; + parity ^= parity >> 2; + parity ^= parity >> 1; + + self->regs.byte.a = result; + self->regs.bit.cf = false; + self->regs.bit.pf = (parity & 1) == 0; + self->regs.bit.acf = false; + self->regs.bit.zf = result == 0; + self->regs.bit.sf = result >> 7; +} + +static ALWAYS_INLINE void cpu_8080_xri(struct cpu_8080 *self, int rvalue) { + int result = self->regs.byte.a ^ rvalue; + + int parity = result; + parity ^= parity >> 4; + parity ^= parity >> 2; + parity ^= parity >> 1; + + self->regs.byte.a = result; + self->regs.bit.cf = false; + self->regs.bit.pf = (parity & 1) == 0; + self->regs.bit.acf = false; + self->regs.bit.zf = result == 0; + self->regs.bit.sf = result >> 7; +} + +static ALWAYS_INLINE void cpu_8080_xthl(struct cpu_8080 *self) { + int data = cpu_8080_read_word(self, self->regs.word.sp); + cpu_8080_write_word(self, self->regs.word.sp, self->regs.word.hl); + self->regs.word.hl = data; +} + +// prototypes +void cpu_8080_init( + struct cpu_8080 *self, + int (*read_byte)(void *context, int addr), + void *read_byte_context, + void (*write_byte)(void *context, int addr, int data), + void *write_byte_context, + int (*in_byte)(void *context, int addr), + void *in_byte_context, + void (*out_byte)(void *context, int addr, int data), + void *out_byte_context +); +void cpu_8080_reset(struct cpu_8080 *self); +void cpu_8080_execute(struct cpu_8080 *self); + +#endif diff --git a/cpu_z80.c b/cpu_z80.c index e312c74..67f1763 100644 --- a/cpu_z80.c +++ b/cpu_z80.c @@ -5506,4 +5506,3 @@ void cpu_z80_execute_fd_cb(struct cpu_z80 *self) { break; } } - diff --git a/decode_8080.py b/decode_8080.py new file mode 100755 index 0000000..4475cdc --- /dev/null +++ b/decode_8080.py @@ -0,0 +1,179 @@ +#!/usr/bin/env python3 + +import sys + +# end-relative range of operands that are rvalues +rvalue_opcodes = { + 'aci': -1, + 'adc': -1, + 'add': -1, + 'adi': -1, + 'ana': -1, + 'ani': -1, + 'call': -1, + 'cc': -1, + 'cm': -1, + 'cmp': -1, + 'cnc': -1, + 'cnz': -1, + 'cp': -1, + 'cpe': -1, + 'cpi': -1, + 'cpo': -1, + 'cz': -1, + 'dad': -1, + 'in': -1, + 'jc': -1, + 'jm': -1, + 'jmp': -1, + 'jnc': -1, + 'jnz': -1, + 'jp': -1, + 'jpe': -1, + 'jpo': -1, + 'jz': -1, + 'lda': -1, + 'ldax': -1, + 'lhld': -1, + 'lxi': -1, + 'mov': -1, + 'mvi': -1, + 'ora': -1, + 'ori': -1, + 'out': -1, + 'push': -1, + 'rst': -1, + 'sbb': -1, + 'sbi': -1, + 'shld': -1, + 'sta': -1, + 'stax': -1, + 'sub': -1, + 'sui': -1, + 'xra': -1, + 'xri': -1, +} + +byte_opcodes = { + 'aci', + 'adc', + 'add', + 'adi', + 'ana', + 'ani', + 'cmp', + 'cpi', + 'dcr', + 'in', + 'inr', + 'mov', + 'mvi', + 'ora', + 'ori', + 'out', + 'rst', + 'sbb', + 'sbi', + 'sub', + 'sui', + 'xra', + 'xri', +} +byte_rvalue_modes = { + '0x12': 'cpu_8080_fetch_byte(self)', + 'a': 'self->regs.byte.a', + 'b': 'self->regs.byte.b', + 'c': 'self->regs.byte.c', + 'd': 'self->regs.byte.d', + 'e': 'self->regs.byte.e', + 'l': 'self->regs.byte.l', + 'h': 'self->regs.byte.h', +} +byte_lvalue_modes = { + 'a': 'CPU_8080_EA_A', + 'b': 'CPU_8080_EA_B', + 'c': 'CPU_8080_EA_C', + 'd': 'CPU_8080_EA_D', + 'e': 'CPU_8080_EA_E', + 'l': 'CPU_8080_EA_L', + 'h': 'CPU_8080_EA_H', + 'm': 'self->regs.word.hl', +} + +word_rvalue_modes = { + '0x3412': 'cpu_8080_fetch_word(self)', + 'psw': 'self->regs.word.psw', + 'b': 'self->regs.word.bc', + 'd': 'self->regs.word.de', + 'h': 'self->regs.word.hl', + 'sp': 'self->regs.word.sp', +} +word_lvalue_modes = { + 'psw': 'CPU_8080_EA_PSW', + 'b': 'CPU_8080_EA_BC', + 'd': 'CPU_8080_EA_DE', + 'h': 'CPU_8080_EA_HL', + 'sp': 'CPU_8080_EA_SP', +} + +print('void cpu_8080_execute(struct cpu_8080 *self) {') +print(' switch (cpu_8080_fetch_byte(self)) {') + +for i in range(0x100): + line = sys.stdin.readline().strip() + + print(f' case 0x{i:02x}:') + instr = line.split() + if len(instr) == 0: + instr = ['ill'] + elif len(instr) >= 2: + instr[1:] = [l for j in instr[1:] for l in j.split(',')] + #print('xxx', instr) + + # work out which operands are rvalue + j = len(instr) + rvalue_opcodes.get(instr[0], 0) + + # translate operands + if instr[0] in byte_opcodes: + # operands [1, j) are lvalue + for l in range(1, j): + if instr[l] in byte_lvalue_modes: + instr[l] = byte_lvalue_modes[instr[l]] + + # operands [j, n) are rvalue + for l in range(j, len(instr)): + if instr[l] in byte_rvalue_modes: + instr[l] = byte_rvalue_modes[instr[l]] + elif instr[l] in byte_lvalue_modes: + instr[l] = 'cpu_8080_read_byte(self, {0:s})'.format( + byte_lvalue_modes[instr[l]] + ) + else: + # operands [1, j] are lvalue + for l in range(1, j): + if instr[l] in word_lvalue_modes: + instr[l] = word_lvalue_modes[instr[l]] + + # operands [j, n) are rvalue + for l in range(j, len(instr)): + if instr[l] in word_rvalue_modes: + instr[l] = word_rvalue_modes[instr[l]] + elif instr[l] in word_lvalue_modes: + instr[l] = 'cpu_8080_read_word(self, {0:s})'.format( + word_lvalue_modes[instr[l]] + ) + + print( + ' cpu_8080_{0:s}(self{1:s});'.format( + instr[0], + ''.join([', ' + j for j in instr[1:]]) + ) + ) + print(' break;') + +line = sys.stdin.readline().strip() +assert len(line) == 0 + +print(' }') +print('}') +print() diff --git a/doc/Optable.pdf b/doc/Optable.pdf new file mode 100644 index 0000000..04695ed Binary files /dev/null and b/doc/Optable.pdf differ diff --git a/emu_8080.c b/emu_8080.c new file mode 100644 index 0000000..b718006 --- /dev/null +++ b/emu_8080.c @@ -0,0 +1,541 @@ +#include +#include +#include +#include +#include +#if ALT_BACKEND +#include "intel-8080/cpu.h" +#else +#include "cpu_8080.h" +#endif + +#define REG_TRACE 0 +#define MEM_TRACE 0 +#define IO_TRACE 0 + +#define BDOS 0xffbb // ret +#define JMP_BOOT 0xffbc // -3: Cold start routine +#define JMP_WBOOT 0xffbf // 0: Warm boot - reload command processor +#define JMP_CONST 0xffc2 // 3: Console status +#define JMP_CONIN 0xffc5 // 6: Console input +#define JMP_CONOUT 0xffc8 // 9: Console output +#define JMP_LIST 0xffcb // 12: Printer output +#define JMP_PUNCH 0xffce // 15: Paper tape punch output +#define JMP_READER 0xffd1 // 18: Paper tape reader input +#define JMP_HOME 0xffd4 // 21: Move disc head to track 0 +#define JMP_SELDSK 0xffd7 // 24: Select disc drive +#define JMP_SETTRK 0xffda // 27: Set track number +#define JMP_SETSEC 0xffdd // 30: Set sector number +#define JMP_SETDMA 0xffe0 // 33: Set DMA address +#define JMP_READ 0xffe3 // 36: Read a sector +#define JMP_WRITE 0xffe6 // 39: Write a sector +#define JMP_LISTST 0xffe9 // 42: Status of list device +#define JMP_SECTRAN 0xffec // 45: Sector translation for skewing +#define BIOS_BOOT 0xffef // ret +#define BIOS_WBOOT 0xfff0 // ret +#define BIOS_CONST 0xfff1 // ret +#define BIOS_CONIN 0xfff2 // ret +#define BIOS_CONOUT 0xfff3 // ret +#define BIOS_LIST 0xfff4 // ret +#define BIOS_PUNCH 0xfff5 // ret +#define BIOS_READER 0xfff6 // ret +#define BIOS_HOME 0xfff7 // ret +#define BIOS_SELDSK 0xfff8 // ret +#define BIOS_SETTRK 0xfff9 // ret +#define BIOS_SETSEC 0xfffa // ret +#define BIOS_SETDMA 0xfffb // ret +#define BIOS_READ 0xfffc // ret +#define BIOS_WRITE 0xfffd // ret +#define BIOS_LISTST 0xfffe // ret +#define BIOS_SECTRAN 0xffff // ret + +#define MEM_SIZE 0x10000 +uint8_t mem[MEM_SIZE]; + +// read and write are separated for I/O +#define IO_SIZE 0x100 +uint8_t io_read[IO_SIZE]; +uint8_t io_write[IO_SIZE]; + +int load_ihx(char *name) { + FILE *fp = fopen(name, "r"); + if (fp == NULL) { + perror(name); + exit(EXIT_FAILURE); + } + + int base = 0, entry_point = 0; + bool had_eof = false; + char line[0x100]; + while (fgets(line, 0x100, fp)) { + for (char *p = line; *p; ++p) + if (*p == '\n') { + *p = 0; + break; + } + + if (had_eof) { + fprintf(stderr, "garbage after EOF record: %s\n", line); + exit(EXIT_FAILURE); + } + + if (line[0] != ':') { + fprintf(stderr, "require colon: %s\n", line); + exit(EXIT_FAILURE); + } + + uint8_t buf[0x7f]; + int len; + for (len = 0; len < 0x7f; ++len) { + char *p = line + 1 + len * 2; + if (*p == 0) + break; + if (*p == '\n') { + *p = 0; + break; + } + uint8_t c = p[2]; + p[2] = 0; + + char *q; + buf[len] = (uint8_t)strtol(p, &q, 16); + p[2] = c; + if (q != p + 2) { + fprintf(stderr, "not hex byte: %s\n", p); + exit(EXIT_FAILURE); + } + } + + if (len == 0) { + fprintf(stderr, "empty line: %s\n", line); + exit(EXIT_FAILURE); + } + + uint8_t checksum = 0; + for (int i = 0; i < len; ++i) + checksum += buf[i]; + if (checksum) { + checksum -= buf[len - 1]; + fprintf( + stderr, + "checksum %02x, should be %02x\n", + checksum, + buf[len - 1] + ); + exit(EXIT_FAILURE); + } + + len -= 5; + if (len != buf[0]) { + fprintf(stderr, "incorrect length: %s\n", line); + exit(EXIT_FAILURE); + } + + int addr = (buf[1] << 8) | buf[2], end_addr; + switch (buf[3]) { + case 0: + addr += base; + end_addr = addr + len; + if (end_addr < addr || end_addr > MEM_SIZE) { + fprintf(stderr, "invalid load range: [0x%x, 0x%x)\n", addr, end_addr); + exit(EXIT_FAILURE); + } + memcpy(mem + addr, buf + 4, len); + break; + case 1: + had_eof = true; + break; + case 4: + if (len < 2) { + fprintf(stderr, "invalid extended linear address record: %s\n", line); + exit(EXIT_FAILURE); + } + base = (buf[4] << 24) | (buf[5] << 16); + break; + case 5: + if (len < 4) { + fprintf(stderr, "invalid start linear address record: %s\n", line); + exit(EXIT_FAILURE); + } + entry_point = (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7]; + break; + default: + fprintf(stderr, "unknown record type: 0x%x\n", buf[3]); + exit(EXIT_FAILURE); + } + } + if (!had_eof) { + fprintf(stderr, "no EOF record\n"); + exit(EXIT_FAILURE); + } + + fclose(fp); + return entry_point; +} + +int bdos(int a, int c, int de, uint16_t *hl) { + switch (c) { + case 2: + // Character output + de &= 0xff; + switch (de) { + case '\n': + break; + case '\r': + de = '\n'; + default: + putchar(de); + break; + } + break; +#if 0 // unlike MS-DOS version, the CP/M BASIC-80 uses direct BIOS calls + case 6: + // Direct console I/O + de &= 0xff; + if (de == 0xff) { + int data = getchar(); + switch (data) { + case '\n': + data = '\r'; + goto avail; + case 0x7f: + data = '\b'; + //goto avail; + default: + avail: + a = data; + break; + case EOF: + a = 0; + break; + } + break; + } + switch (de) { + case '\n': + break; + case '\r': + de = '\n'; + default: + putchar(de); + break; + } + break; +#endif + case 9: + // Display string + for (int data; (data = mem[de]) != '$'; de = (de + 1) & 0xffff) + switch (data) { + case '\n': + break; + case '\r': + data = '\n'; + default: + putchar(data); + break; + } + break; + case 0xc: + // Get CP/M version + *hl = 0x22; + break; + case 0x29: // DRV_ROVEC - Return bitmap of read-only drives + *hl = 0; + break; + default: + fprintf(stderr, "unimplemented BDOS call 0x%02x\n", c); + exit(EXIT_FAILURE); + } + return a; +} + +int bios(int pc, int a, int c) { + switch (pc) { + case BIOS_WBOOT: + exit(EXIT_SUCCESS); + case BIOS_CONST: + a = 0; // defeat BASIC Ctrl-C checking + break; + case BIOS_CONIN: + { + int data = getchar(); + switch (data) { + case '\n': + data = '\r'; + goto avail; + case 0x7f: + data = '\b'; + //goto avail; + default: + avail: + a = data; + break; + case EOF: + a = 0; + break; + } + } + break; + case BIOS_CONOUT: + switch (c) { + case '\n': + break; + case '\r': + c = '\n'; + default: + putchar(c); + break; + } + break; + default: + fprintf(stderr, "unimplemented BIOS call 0x%04x\n", pc); + exit(EXIT_FAILURE); + } + return a; +} + +#if ALT_BACKEND +uint8_t read_byte(uint16_t addr) +#else +int read_byte(void *context, int addr) +#endif +{ +#if MEM_TRACE + int data = mem[addr]; + fprintf(stderr, "addr=%04x rd=%02x\n", addr, data); + return data; +#else + return mem[addr]; +#endif +} + +#if ALT_BACKEND +void write_byte(uint16_t addr, uint8_t data) +#else +void write_byte(void *context, int addr, int data) +#endif +{ +#if MEM_TRACE + fprintf(stderr, "addr=%04x wr=%02x\n", addr, data); +#endif + mem[addr] = data; +} + +int in_byte(void *context, int addr) { + addr &= 0xff; +#if IO_TRACE + int data = io_read[addr]; + printf("io=%02x rd=%02x\n", addr, data); + return data; +#else + return io_read[addr]; +#endif +} + +void out_byte(void *context, int addr, int data) { + addr &= 0xff; +#if IO_TRACE + printf("io=%02x wr=%02x\n", addr, data); +#endif + io_write[addr] = data; +} + +int main(int argc, char **argv) { + if (argc < 2) { + printf("usage: %s image.ihx\n", argv[0]); + exit(EXIT_FAILURE); + } + int entry_point = load_ihx(argv[1]); + + mem[0] = 0xc3; // jmp + mem[1] = (uint8_t)JMP_WBOOT; + mem[2] = (uint8_t)(JMP_WBOOT >> 8); + + mem[5] = 0xc3; // jmp + mem[6] = (uint8_t)BDOS; + mem[7] = (uint8_t)(BDOS >> 8); + + mem[BDOS] = 0xc9; // ret + + mem[JMP_BOOT] = 0xc3; // jmp + mem[JMP_BOOT + 1] = (uint8_t)BIOS_BOOT; + mem[JMP_BOOT + 2] = (uint8_t)(BIOS_BOOT >> 8); + + mem[JMP_WBOOT] = 0xc3; // jmp + mem[JMP_WBOOT + 1] = (uint8_t)BIOS_WBOOT; + mem[JMP_WBOOT + 2] = (uint8_t)(BIOS_WBOOT >> 8); + + mem[JMP_CONST] = 0xc3; // jmp + mem[JMP_CONST + 1] = (uint8_t)BIOS_CONST; + mem[JMP_CONST + 2] = (uint8_t)(BIOS_CONST >> 8); + + mem[JMP_CONIN] = 0xc3; // jmp + mem[JMP_CONIN + 1] = (uint8_t)BIOS_CONIN; + mem[JMP_CONIN + 2] = (uint8_t)(BIOS_CONIN >> 8); + + mem[JMP_CONOUT] = 0xc3; // jmp + mem[JMP_CONOUT + 1] = (uint8_t)BIOS_CONOUT; + mem[JMP_CONOUT + 2] = (uint8_t)(BIOS_CONOUT >> 8); + + mem[JMP_LIST] = 0xc3; // jmp + mem[JMP_LIST + 1] = (uint8_t)BIOS_LIST; + mem[JMP_LIST + 2] = (uint8_t)(BIOS_LIST >> 8); + + mem[JMP_PUNCH] = 0xc3; // jmp + mem[JMP_PUNCH + 1] = (uint8_t)BIOS_PUNCH; + mem[JMP_PUNCH + 2] = (uint8_t)(BIOS_PUNCH >> 8); + + mem[JMP_READER] = 0xc3; // jmp + mem[JMP_READER + 1] = (uint8_t)BIOS_READER; + mem[JMP_READER + 2] = (uint8_t)(BIOS_READER >> 8); + + mem[JMP_HOME] = 0xc3; // jmp + mem[JMP_HOME + 1] = (uint8_t)BIOS_HOME; + mem[JMP_HOME + 2] = (uint8_t)(BIOS_HOME >> 8); + + mem[JMP_SELDSK] = 0xc3; // jmp + mem[JMP_SELDSK + 1] = (uint8_t)BIOS_SELDSK; + mem[JMP_SELDSK + 2] = (uint8_t)(BIOS_SELDSK >> 8); + + mem[JMP_SETTRK] = 0xc3; // jmp + mem[JMP_SETTRK + 1] = (uint8_t)BIOS_SETTRK; + mem[JMP_SETTRK + 2] = (uint8_t)(BIOS_SETTRK >> 8); + + mem[JMP_SETSEC] = 0xc3; // jmp + mem[JMP_SETSEC + 1] = (uint8_t)BIOS_SETSEC; + mem[JMP_SETSEC + 2] = (uint8_t)(BIOS_SETSEC >> 8); + + mem[JMP_SETDMA] = 0xc3; // jmp + mem[JMP_SETDMA + 1] = (uint8_t)BIOS_SETDMA; + mem[JMP_SETDMA + 2] = (uint8_t)(BIOS_SETDMA >> 8); + + mem[JMP_READ] = 0xc3; // jmp + mem[JMP_READ + 1] = (uint8_t)BIOS_READ; + mem[JMP_READ + 2] = (uint8_t)(BIOS_READ >> 8); + + mem[JMP_WRITE] = 0xc3; // jmp + mem[JMP_WRITE + 1] = (uint8_t)BIOS_WRITE; + mem[JMP_WRITE + 2] = (uint8_t)(BIOS_WRITE >> 8); + + mem[JMP_LISTST] = 0xc3; // jmp + mem[JMP_LISTST + 1] = (uint8_t)BIOS_LISTST; + mem[JMP_LISTST + 2] = (uint8_t)(BIOS_LISTST >> 8); + + mem[JMP_SECTRAN] = 0xc3; // jmp + mem[JMP_SECTRAN + 1] = (uint8_t)BIOS_SECTRAN; + mem[JMP_SECTRAN + 2] = (uint8_t)(BIOS_SECTRAN >> 8); + + mem[BIOS_BOOT] = 0xc9; // ret + mem[BIOS_WBOOT] = 0xc9; // ret + mem[BIOS_CONST] = 0xc9; // ret + mem[BIOS_CONIN] = 0xc9; // ret + mem[BIOS_CONOUT] = 0xc9; // ret + mem[BIOS_LIST] = 0xc9; // ret + mem[BIOS_PUNCH] = 0xc9; // ret + mem[BIOS_READER] = 0xc9; // ret + mem[BIOS_HOME] = 0xc9; // ret + mem[BIOS_SELDSK] = 0xc9; // ret + mem[BIOS_SETTRK] = 0xc9; // ret + mem[BIOS_SETSEC] = 0xc9; // ret + mem[BIOS_SETDMA] = 0xc9; // ret + mem[BIOS_READ] = 0xc9; // ret + mem[BIOS_WRITE] = 0xc9; // ret + mem[BIOS_LISTST] = 0xc9; // ret + mem[BIOS_SECTRAN] = 0xc9; // ret + +#if ALT_BACKEND + regs.pc = entry_point; + regs.sp = BDOS - 2; // assume word at BDOS - 2 is initialized to 0 + + while (true) { +#if REG_TRACE + fprintf( + stderr, + "pc=%04x psw=%04x bc=%04x de=%04x hl=%04x sp=%04x cf=%d pf=%d acf=%d zf=%d sf=%d\n", + regs.pc, + regs.cf | + (regs.pf << 2) | + (regs.acf << 4) | + (regs.zf << 6) | + (regs.sf << 7) | + (regs.a << 8), + regs.c | (regs.b << 8), + regs.e | (regs.d << 8), + regs.l | (regs.h << 8), + regs.sp, + regs.cf, + regs.pf, + regs.acf, + regs.zf, + regs.sf + ); +#endif + + if (regs.pc == BDOS) { + int de = regs.e | (regs.d << 8); + uint16_t hl = regs.l | (regs.h << 8); + regs.a = bdos(regs.a, regs.c, de, &hl); + regs.l = (uint8_t)hl; + regs.h = (uint8_t)(hl >> 8); + } + else if (regs.pc >= BIOS_BOOT) + regs.a = bios(regs.pc, regs.a, regs.c); + + instruction(read_next_byte()); // should check return code (HLT, RST) + } +#else + struct cpu_8080 cpu; + cpu_8080_init( + &cpu, + read_byte, + NULL, + write_byte, + NULL, + in_byte, + NULL, + out_byte, + NULL + ); + cpu_8080_reset(&cpu); + cpu.regs.word.pc = entry_point; + cpu.regs.word.sp = BDOS - 2; // assume word at BDOS - 2 is initialized to 0 + + while (true) { +#if REG_TRACE + fprintf( + stderr, + "pc=%04x psw=%04x bc=%04x de=%04x hl=%04x sp=%04x cf=%d pf=%d acf=%d zf=%d sf=%d\n", + cpu.regs.word.pc, + cpu.regs.word.psw, + cpu.regs.word.bc, + cpu.regs.word.de, + cpu.regs.word.hl, + cpu.regs.word.sp, + cpu.regs.bit.cf, + cpu.regs.bit.pf, + cpu.regs.bit.acf, + cpu.regs.bit.zf, + cpu.regs.bit.sf + ); +#endif + + if (cpu.regs.word.pc == BDOS) + cpu.regs.byte.a = bdos( + cpu.regs.byte.a, + cpu.regs.byte.c, + cpu.regs.word.de, + &cpu.regs.word.hl + ); + else if (cpu.regs.word.pc >= BIOS_BOOT) + cpu.regs.byte.a = bios( + cpu.regs.word.pc, + cpu.regs.byte.a, + cpu.regs.byte.c + ); + + cpu_8080_execute(&cpu); + } +#endif + + return 0; +} diff --git a/instr_8080.txt b/instr_8080.txt new file mode 100644 index 0000000..53509ff --- /dev/null +++ b/instr_8080.txt @@ -0,0 +1,256 @@ +nop +lxi b,0x3412 +stax b +inx b +inr b +dcr b +mvi b,0x12 +rlc +nop +dad b +ldax b +dcx b +inr c +dcr c +mvi c,0x12 +rrc +nop +lxi d,0x3412 +stax d +inx d +inr d +dcr d +mvi d,0x12 +ral +nop +dad d +ldax d +dcx d +inr e +dcr e +mvi e,0x12 +rar +nop +lxi h,0x3412 +shld 0x3412 +inx h +inr h +dcr h +mvi h,0x12 +daa +nop +dad h +lhld 0x3412 +dcx h +inr l +dcr l +mvi l,0x12 +cma +nop +lxi sp,0x3412 +sta 0x3412 +inx sp +inr m +dcr m +mvi m,0x12 +stc +nop +dad sp +lda 0x3412 +dcx sp +inr a +dcr a +mvi a,0x12 +cmc +mov b,b +mov b,c +mov b,d +mov b,e +mov b,h +mov b,l +mov b,m +mov b,a +mov c,b +mov c,c +mov c,d +mov c,e +mov c,h +mov c,l +mov c,m +mov c,a +mov d,b +mov d,c +mov d,d +mov d,e +mov d,h +mov d,l +mov d,m +mov d,a +mov e,b +mov e,c +mov e,d +mov e,e +mov e,h +mov e,l +mov e,m +mov e,a +mov h,b +mov h,c +mov h,d +mov h,e +mov h,h +mov h,l +mov h,m +mov h,a +mov l,b +mov l,c +mov l,d +mov l,e +mov l,h +mov l,l +mov l,m +mov l,a +mov m,b +mov m,c +mov m,d +mov m,e +mov m,h +mov m,l +hlt +mov m,a +mov a,b +mov a,c +mov a,d +mov a,e +mov a,h +mov a,l +mov a,m +mov a,a +add b +add c +add d +add e +add h +add l +add m +add a +adc b +adc c +adc d +adc e +adc h +adc l +adc m +adc a +sub b +sub c +sub d +sub e +sub h +sub l +sub m +sub a +sbb b +sbb c +sbb d +sbb e +sbb h +sbb l +sbb m +sbb a +ana b +ana c +ana d +ana e +ana h +ana l +ana m +ana a +xra b +xra c +xra d +xra e +xra h +xra l +xra m +xra a +ora b +ora c +ora d +ora e +ora h +ora l +ora m +ora a +cmp b +cmp c +cmp d +cmp e +cmp h +cmp l +cmp m +cmp a +rnz +pop b +jnz 0x3412 +jmp 0x3412 +cnz 0x3412 +push b +adi 0x12 +rst 0 +rz +ret +jz 0x3412 +jmp 0x3412 +cz 0x3412 +call 0x3412 +aci 0x12 +rst 1 +rnc +pop d +jnc 0x3412 +out 0x12 +cnc 0x3412 +push d +sui 0x12 +rst 2 +rc +ret +jc 0x3412 +in 0x12 +cc 0x3412 +call 0x3412 +sbi 0x12 +rst 3 +rpo +pop h +jpo 0x3412 +xthl +cpo 0x3412 +push h +ani 0x12 +rst 4 +rpe +pchl +jpe 0x3412 +xchg +cpe 0x3412 +call 0x3412 +xri 0x12 +rst 5 +rp +pop psw +jp 0x3412 +di +cp 0x3412 +push psw +ori 0x12 +rst 6 +rm +sphl +jm 0x3412 +ei +cm 0x3412 +call 0x3412 +cpi 0x12 +rst 7 diff --git a/jefftranter_8080 b/jefftranter_8080 new file mode 160000 index 0000000..12a4ffb --- /dev/null +++ b/jefftranter_8080 @@ -0,0 +1 @@ +Subproject commit 12a4ffb689b4499b4a835206182ab1a49bbcb09c