From 0742e9969cd93e8a7d3926c905cc096d80c66ff1 Mon Sep 17 00:00:00 2001 From: Roman Boykov Date: Mon, 9 Mar 2026 13:26:32 +0300 Subject: [PATCH] zx80c99 implemented --- main.go | 4 +- okean240/usart/usart8251.go | 28 +- z80/constants.go | 58 ++ z80/cpu.go | 174 +++++ z80/helper.go | 165 +++++ z80/opcodes.go | 1188 +++++++++++++++++++++++++++++++++++ z80/opcodesCB.go | 162 +++++ z80/opcodesDDFD.go | 202 ++++++ z80/opcodesED.go | 257 ++++++++ z80em/z80em.go | 20 +- 10 files changed, 2232 insertions(+), 26 deletions(-) create mode 100644 z80/constants.go create mode 100644 z80/cpu.go create mode 100644 z80/helper.go create mode 100644 z80/opcodes.go create mode 100644 z80/opcodesCB.go create mode 100644 z80/opcodesDDFD.go create mode 100644 z80/opcodesED.go diff --git a/main.go b/main.go index 763700a..4d20b6e 100644 --- a/main.go +++ b/main.go @@ -50,7 +50,7 @@ func main() { w, raster, label := mainWindow(computer) go emulator(computer) - go screen(computer, raster, label, conf) + go screen(computer, raster, label) (*w).ShowAndRun() } @@ -131,7 +131,7 @@ func mainWindow(computer *okean240.ComputerType) (*fyne.Window, *canvas.Raster, return &w, raster, label } -func screen(computer *okean240.ComputerType, raster *canvas.Raster, label *widget.Label, emuConfig *config.OkEmuConfig) { +func screen(computer *okean240.ComputerType, raster *canvas.Raster, label *widget.Label) { ticker := time.NewTicker(20 * time.Millisecond) frame := 0 var pre uint64 = 0 diff --git a/okean240/usart/usart8251.go b/okean240/usart/usart8251.go index 4b669cf..e91a0ee 100644 --- a/okean240/usart/usart8251.go +++ b/okean240/usart/usart8251.go @@ -1,19 +1,17 @@ +// Package usart +// Universal Serial Asynchronous Receiver/Transmitter +// i8051, MSM82C51, КР580ВВ51 +// By Romych, 2025.03.04 package usart import log "github.com/sirupsen/logrus" -/** -Universal Serial Asynchronous Receiver/Transmitter -i8051, MSM82C51, КР580ВВ51 +//const I8251DSRFlag = 0x80 +//const I8251SynDetFlag = 0x40 +//const I8251FrameErrorFlag = 0x20 +//const I8251OverrunErrorFlag = 0x10 +//const I8251ParityErrorFlag = 0x08 -By Romych, 2025.03.04 -*/ - -const I8251DSRFlag = 0x80 -const I8251SynDetFlag = 0x40 -const I8251FrameErrorFlag = 0x20 -const I8251OverrunErrorFlag = 0x10 -const I8251ParityErrorFlag = 0x08 const I8251TxEnableFlag = 0x04 const I8251RxReadyFlag = 0x02 const I8251TxReadyFlag = 0x01 @@ -131,20 +129,22 @@ func (s *I8251) Command(value byte) { func (s *I8251) Send(value byte) { if s.txe { s.bufferTx = append(s.bufferTx, value) + log.Debugf("Send byte: %x", value) } } func (s *I8251) Receive() byte { - if s.rxe { if len(s.bufferRx) > 0 { res := s.bufferRx[0] s.bufferRx = s.bufferRx[1:] - log.Debugf("ReceiveByte: %x", res) + log.Debugf("Receive: %x", res) return res } + log.Debugf("Receive: empty buffer") + } else { + log.Debugf("Receive: receiver disabled") } - log.Debugf("ReceiveByte: empty buffer") return 0 } diff --git a/z80/constants.go b/z80/constants.go new file mode 100644 index 0000000..922d7be --- /dev/null +++ b/z80/constants.go @@ -0,0 +1,58 @@ +package z80 + +var cyc_00 = [256]byte{ + 4, 10, 7, 6, 4, 4, 7, 4, 4, 11, 7, 6, 4, 4, 7, 4, + 8, 10, 7, 6, 4, 4, 7, 4, 12, 11, 7, 6, 4, 4, 7, 4, + 7, 10, 16, 6, 4, 4, 7, 4, 7, 11, 16, 6, 4, 4, 7, 4, + 7, 10, 13, 6, 11, 11, 10, 4, 7, 11, 13, 6, 4, 4, 7, 4, + 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, + 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, + 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, + 7, 7, 7, 7, 7, 7, 4, 7, 4, 4, 4, 4, 4, 4, 7, 4, + 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, + 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, + 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, + 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, + 5, 10, 10, 10, 10, 11, 7, 11, 5, 10, 10, 0, 10, 17, 7, 11, + 5, 10, 10, 11, 10, 11, 7, 11, 5, 4, 10, 11, 10, 0, 7, 11, + 5, 10, 10, 19, 10, 11, 7, 11, 5, 4, 10, 4, 10, 0, 7, 11, + 5, 10, 10, 4, 10, 11, 7, 11, 5, 6, 10, 4, 10, 0, 7, 11, +} + +var cyc_ed = [256]byte{ + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 12, 12, 15, 20, 8, 14, 8, 9, 12, 12, 15, 20, 8, 14, 8, 9, + 12, 12, 15, 20, 8, 14, 8, 9, 12, 12, 15, 20, 8, 14, 8, 9, + 12, 12, 15, 20, 8, 14, 8, 18, 12, 12, 15, 20, 8, 14, 8, 18, + 12, 12, 15, 20, 8, 14, 8, 8, 12, 12, 15, 20, 8, 14, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 16, 16, 16, 16, 8, 8, 8, 8, 16, 16, 16, 16, 8, 8, 8, 8, + 16, 16, 16, 16, 8, 8, 8, 8, 16, 16, 16, 16, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, +} + +var cyc_ddfd = [256]byte{ + 4, 4, 4, 4, 4, 4, 4, 4, 4, 15, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 15, 4, 4, 4, 4, 4, 4, + 4, 14, 20, 10, 8, 8, 11, 4, 4, 15, 20, 10, 8, 8, 11, 4, + 4, 4, 4, 4, 23, 23, 19, 4, 4, 15, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 8, 8, 19, 4, 4, 4, 4, 4, 8, 8, 19, 4, + 4, 4, 4, 4, 8, 8, 19, 4, 4, 4, 4, 4, 8, 8, 19, 4, + 8, 8, 8, 8, 8, 8, 19, 8, 8, 8, 8, 8, 8, 8, 19, 8, + 19, 19, 19, 19, 19, 19, 4, 19, 4, 4, 4, 4, 8, 8, 19, 4, + 4, 4, 4, 4, 8, 8, 19, 4, 4, 4, 4, 4, 8, 8, 19, 4, + 4, 4, 4, 4, 8, 8, 19, 4, 4, 4, 4, 4, 8, 8, 19, 4, + 4, 4, 4, 4, 8, 8, 19, 4, 4, 4, 4, 4, 8, 8, 19, 4, + 4, 4, 4, 4, 8, 8, 19, 4, 4, 4, 4, 4, 8, 8, 19, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 14, 4, 23, 4, 15, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 10, 4, 4, 4, 4, 4, 4, +} diff --git a/z80/cpu.go b/z80/cpu.go new file mode 100644 index 0000000..e31f028 --- /dev/null +++ b/z80/cpu.go @@ -0,0 +1,174 @@ +package z80 + +type MemIoRW interface { + // M1MemRead Read byte from memory for specified address @ M1 cycle + M1MemRead(addr uint16) byte + // MemRead Read byte from memory for specified address + MemRead(addr uint16) byte + // MemWrite Write byte to memory to specified address + MemWrite(addr uint16, val byte) + // IORead Read byte from specified port + IORead(port uint16) byte + // IOWrite Write byte to specified port + IOWrite(port uint16, val byte) +} + +type CPUInterface interface { + // Reset CPU to initial state + Reset() + // RunInstruction Run single instruction, return number of CPU cycles + RunInstruction() byte + // GetState Get current CPU state + GetState() *Z80 + // SetState Set current CPU state + SetState(state *Z80) +} + +type Z80 struct { + + // cycle count (t-states) + cyc uint64 + + // special purpose registers + pc, sp, ix, iy uint16 + + // "wz" register + mem_ptr uint16 + + // main registers + a, b, c, d, e, h, l byte + + // alternate registers + a_, b_, c_, d_, e_, h_, l_, f_ byte + + // interrupt vector, memory refresh + i, r byte + + // flags: sign, zero, yf, half-carry, xf, parity/overflow, negative, carry + sf, zf, yf, hf, xf, pf, nf, cf bool + iff_delay byte + interrupt_mode byte + int_data byte + iff1 bool + iff2 bool + halted bool + int_pending bool + nmi_pending bool + + core MemIoRW +} + +// New initializes a Z80 instance and return pointer to it +func New(core MemIoRW) *Z80 { + z := Z80{} + z.core = core + + z.cyc = 0 + + z.pc = 0 + z.sp = 0xFFFF + z.ix = 0 + z.iy = 0 + z.mem_ptr = 0 + + // af and sp are set to 0xFFFF after reset, + // and the other values are undefined (z80-documented) + z.a = 0xFF + z.b = 0 + z.c = 0 + z.d = 0 + z.e = 0 + z.h = 0 + z.l = 0 + + z.a_ = 0 + z.b_ = 0 + z.c_ = 0 + z.d_ = 0 + z.e_ = 0 + z.h_ = 0 + z.l_ = 0 + z.f_ = 0 + + z.i = 0 + z.r = 0 + + z.sf = true + z.zf = true + z.yf = true + z.hf = true + z.xf = true + z.pf = true + z.nf = true + z.cf = true + + z.iff_delay = 0 + z.interrupt_mode = 0 + z.iff1 = false + z.iff2 = false + z.halted = false + z.int_pending = false + z.nmi_pending = false + z.int_data = 0 + return &z +} + +// RunInstruction executes the next instruction in memory + handles interrupts +func (z *Z80) RunInstruction() { + if z.halted { + z.exec_opcode(0x00) + } else { + opcode := z.nextb() + z.exec_opcode(opcode) + } + z.process_interrupts() +} + +func (z *Z80) GetState() *Z80 { + return &Z80{ + cyc: z.cyc, + pc: z.pc, + sp: z.sp, + ix: z.ix, + iy: z.iy, + mem_ptr: z.mem_ptr, + + a: z.a, + b: z.b, + c: z.c, + d: z.d, + e: z.e, + h: z.h, + l: z.l, + a_: z.a_, + b_: z.b_, + c_: z.c_, + d_: z.d_, + e_: z.e_, + h_: z.h_, + l_: z.l_, + f_: z.f_, + + i: z.i, + r: z.r, + + sf: z.sf, + zf: z.zf, + yf: z.yf, + hf: z.hf, + xf: z.xf, + pf: z.pf, + nf: z.nf, + cf: z.cf, + + iff1: z.iff1, + iff2: z.iff2, + + iff_delay: z.iff_delay, + interrupt_mode: z.interrupt_mode, + halted: z.halted, + int_pending: z.int_pending, + nmi_pending: z.nmi_pending, + core: z.core, + } +} diff --git a/z80/helper.go b/z80/helper.go new file mode 100644 index 0000000..f21ca5d --- /dev/null +++ b/z80/helper.go @@ -0,0 +1,165 @@ +package z80 + +import log "github.com/sirupsen/logrus" + +// GetBit return bit "n" from byte "val" +func getBit(n byte, val byte) byte { + return ((val) >> (n)) & 1 +} + +func getBit3(val byte) bool { + return (val & 0x08) != 0 +} + +func getBit5(val byte) bool { + return (val & 0x20) != 0 +} + +func (z *Z80) rb(addr uint16) byte { + return z.core.MemRead(addr) +} + +func (z *Z80) wb(addr uint16, val byte) { + z.core.MemWrite(addr, val) +} + +func (z *Z80) rw(addr uint16) uint16 { + return (uint16(z.core.MemRead(addr+1)) << 8) | uint16(z.core.MemRead(addr)) +} + +func (z *Z80) ww(addr uint16, val uint16) { + z.core.MemWrite(addr, byte(val)) + z.core.MemWrite(addr+1, byte(val>>8)) +} + +func (z *Z80) pushw(val uint16) { + z.sp -= 2 + z.ww(z.sp, val) +} + +func (z *Z80) popw() uint16 { + z.sp += 2 + return z.rw(z.sp - 2) +} + +func (z *Z80) nextb() byte { + b := z.rb(z.pc) + z.pc++ + return b +} + +func (z *Z80) nextw() uint16 { + w := z.rw(z.pc) + z.pc += 2 + return w +} + +func (z *Z80) get_bc() uint16 { + return (uint16(z.b) << 8) | uint16(z.c) +} + +func (z *Z80) get_de() uint16 { + return (uint16(z.d) << 8) | uint16(z.e) +} + +func (z *Z80) get_hl() uint16 { + return (uint16(z.h) << 8) | uint16(z.l) +} + +func (z *Z80) set_bc(val uint16) { + z.b = byte(val >> 8) + z.c = byte(val) +} + +func (z *Z80) set_de(val uint16) { + z.d = byte(val >> 8) + z.e = byte(val) +} + +func (z *Z80) set_hl(val uint16) { + z.h = byte(val >> 8) + z.l = byte(val) +} + +func (z *Z80) get_f() byte { + val := byte(0) + if z.cf { + val |= 0x01 + } + if z.nf { + val |= 0x02 + } + if z.pf { + val |= 0x04 + } + if z.xf { + val |= 0x08 + } + if z.hf { + val |= 0x10 + } + if z.yf { + val |= 0x20 + } + if z.zf { + val |= 0x40 + } + if z.sf { + val |= 0x80 + } + return val +} + +func (z *Z80) set_f(val byte) { + z.cf = val&1 != 0 + z.nf = (val>>1)&1 != 0 + z.pf = (val>>2)&1 != 0 + z.xf = (val>>3)&1 != 0 + z.hf = (val>>4)&1 != 0 + z.yf = (val>>5)&1 != 0 + z.zf = (val>>6)&1 != 0 + z.sf = (val>>7)&1 != 0 +} + +// increments R, keeping the highest byte intact +func (z *Z80) inc_r() { + z.r = (z.r & 0x80) | ((z.r + 1) & 0x7f) +} + +func boolToInt32(b bool) int32 { + if b { + return 1 + } + return 0 +} + +// returns if there was a carry between bit "bit_no" and "bit_no - 1" when +// executing "a + b + cy" +func carry(bit_no int, a uint16, b uint16, cy bool) bool { + result := int32(a) + int32(b) + boolToInt32(cy) + carry := result ^ int32(a) ^ int32(b) + return (carry & (1 << bit_no)) != 0 +} + +// returns the parity of byte: 0 if number of 1 bits in `val` is odd, else 1 +func parity(val byte) bool { + ones := byte(0) + for i := 0; i < 8; i++ { + ones += (val >> i) & 1 + } + return (ones & 1) == 0 +} + +func (z *Z80) updateXY(result byte) { + z.yf = result&0x20 != 0 + z.xf = result&0x08 != 0 +} + +func (z *Z80) debug_output() { + log.Debugf("PC: %04X, AF: %04X, BC: %04X, DE: %04X, HL: %04X, SP: %04X, IX: %04X, IY: %04X, I: %02X, R: %02X", + z.pc, (uint16(z.a)<<8)|uint16(z.get_f()), z.get_bc(), z.get_de(), z.get_hl(), z.sp, + z.ix, z.iy, z.i, z.r) + + log.Debugf("\t(%02X %02X %02X %02X), cyc: %d\n", z.rb(z.pc), z.rb(z.pc+1), + z.rb(z.pc+2), z.rb(z.pc+3), z.cyc) +} diff --git a/z80/opcodes.go b/z80/opcodes.go new file mode 100644 index 0000000..4f1cbce --- /dev/null +++ b/z80/opcodes.go @@ -0,0 +1,1188 @@ +package z80 + +import log "github.com/sirupsen/logrus" + +// jumps to an address +func (z *Z80) jump(addr uint16) { + z.pc = addr + z.mem_ptr = addr +} + +// jumps to next word in memory if condition is true +func (z *Z80) cond_jump(condition bool) { + addr := z.nextw() + if condition { + z.jump(addr) + } + z.mem_ptr = addr +} + +// calls to next word in memory +func (z *Z80) call(addr uint16) { + z.pushw(z.pc) + z.pc = addr + z.mem_ptr = addr +} + +// calls to next word in memory if condition is true +func (z *Z80) cond_call(condition bool) { + addr := z.nextw() + if condition { + z.call(addr) + z.cyc += 7 + } + z.mem_ptr = addr +} + +// returns from subroutine +func (z *Z80) ret() { + z.pc = z.popw() + z.mem_ptr = z.pc +} + +// returns from subroutine if condition is true +func (z *Z80) cond_ret(condition bool) { + if condition { + z.ret() + z.cyc += 6 + } +} + +func (z *Z80) jr(displacement byte) { + z.pc += uint16(displacement) + z.mem_ptr = z.pc +} + +func (z *Z80) cond_jr(condition bool) { + b := z.nextb() + if condition { + z.jr(b) + z.cyc += 5 + } +} + +func bToByte(cond bool) byte { + if cond { + return byte(1) + } + return byte(0) +} + +// ADD Byte: adds two bytes together +func (z *Z80) addb(a byte, b byte, cy bool) byte { + result := a + b + bToByte(cy) + z.sf = result&0x80 != 0 + z.zf = result == 0 + z.hf = carry(4, uint16(a), uint16(b), cy) + z.pf = carry(7, uint16(a), uint16(b), cy) != carry(8, uint16(a), uint16(b), cy) + z.cf = carry(8, uint16(a), uint16(b), cy) + z.nf = false + z.updateXY(result) + return result +} + +// SUBtract Byte: subtracts two bytes (with optional carry) +func (z *Z80) subb(a byte, b byte, cy bool) byte { + val := z.addb(a, ^b, !cy) + z.cf = !z.cf + z.hf = !z.hf + z.nf = true + return val +} + +// ADD Word: adds two words together +func (z *Z80) addw(a uint16, b uint16, cy bool) uint16 { + lsb := z.addb(byte(a), byte(b), cy) + msb := z.addb(byte(a>>8), byte(b>>8), z.cf) + result := (uint16(msb) << 8) | uint16(lsb) + z.zf = result == 0 + z.mem_ptr = a + 1 + return result +} + +// SUBtract Word: subtracts two words (with optional carry) +func (z *Z80) subw(a uint16, b uint16, cy bool) uint16 { + lsb := z.subb(byte(a), byte(b), cy) + msb := z.subb(byte(a>>8), byte(b>>8), z.cf) + result := (uint16(msb) << 8) | uint16(lsb) + z.zf = result == 0 + z.mem_ptr = a + 1 + return result +} + +// Adds a word to HL +func (z *Z80) addhl(val uint16) { + sf := z.sf + zf := z.zf + pf := z.pf + result := z.addw(z.get_hl(), val, false) + z.set_hl(result) + z.sf = sf + z.zf = zf + z.pf = pf +} + +// adds a word to IX or IY +func (z *Z80) addiz(reg *uint16, val uint16) { + sf := z.sf + zf := z.zf + pf := z.pf + result := z.addw(*reg, val, false) + *reg = result + z.sf = sf + z.zf = zf + z.pf = pf +} + +// adchl adds a word (+ carry) to HL +func (z *Z80) adchl(val uint16) { + result := z.addw(z.get_hl(), val, z.cf) + z.sf = result&0x8000 != 0 + z.zf = result == 0 + z.set_hl(result) +} + +// sbchl subtracts a word (+ carry) to HL +func (z *Z80) sbchl(val uint16) { + result := z.subw(z.get_hl(), val, z.cf) + z.sf = result&0x8000 != 0 + z.zf = result == 0 + z.set_hl(result) +} + +// increments a byte value +func (z *Z80) inc(a byte) byte { + cf := z.cf + result := z.addb(a, 1, false) + z.cf = cf + return result +} + +// decrements a byte value +func (z *Z80) dec(a byte) byte { + cf := z.cf + result := z.subb(a, 1, false) + z.cf = cf + return result +} + +// executes a logic "and" between register A and a byte, then stores the +// result in register A +func (z *Z80) land(val byte) { + result := z.a & val + z.sf = result&0x80 != 0 + z.zf = result == 0 + z.hf = true + z.pf = parity(result) + z.nf = false + z.cf = false + z.updateXY(result) + z.a = result +} + +// executes a logic "xor" between register A and a byte, then stores the +// result in register A +func (z *Z80) lxor(val byte) { + result := z.a ^ val + z.sf = result&0x80 != 0 + z.zf = result == 0 + z.hf = false + z.pf = parity(result) + z.nf = false + z.cf = false + z.updateXY(result) + z.a = result +} + +// executes a logic "or" between register A and a byte, then stores the +// result in register A +func (z *Z80) lor(val byte) { + result := z.a | val + + z.sf = result&0x80 != 0 + z.zf = result == 0 + z.hf = false + z.pf = parity(result) + z.nf = false + z.cf = false + z.updateXY(result) + z.a = result +} + +// compares a value with register A +func (z *Z80) cp(val byte) { + z.subb(z.a, val, false) + + // the only difference between cp and sub is that + // the xf/yf are taken from the value to be subtracted, + // not the result + + z.updateXY(val) +} + +// 0xCB opcodes +// rotate left with carry +func (z *Z80) cb_rlc(val byte) byte { + old := val >> 7 + val = (val << 1) | old + z.sf = val&0x80 != 0 + z.zf = val == 0 + z.pf = parity(val) + z.nf = false + z.hf = false + z.cf = old != 0 + z.updateXY(val) + return val +} + +// rotate right with carry +func (z *Z80) cb_rrc(val byte) byte { + old := val & 1 + val = (val >> 1) | (old << 7) + z.sf = val&0x80 != 0 + z.zf = val == 0 + z.nf = false + z.hf = false + z.cf = old != 0 + z.pf = parity(val) + z.updateXY(val) + return val +} + +// rotate left (simple) +func (z *Z80) cb_rl(val byte) byte { + cf := z.cf + z.cf = val>>7 != 0 + val = (val << 1) | bToByte(cf) + z.sf = val&0x80 != 0 + z.zf = val == 0 + z.nf = false + z.hf = false + z.pf = parity(val) + z.updateXY(val) + return val +} + +// rotate right (simple) +func (z *Z80) cb_rr(val byte) byte { + c := z.cf + z.cf = (val & 1) != 0 + val = (val >> 1) | (bToByte(c) << 7) + z.sf = val&0x80 != 0 + z.zf = val == 0 + z.nf = false + z.hf = false + z.pf = parity(val) + z.updateXY(val) + return val +} + +// shift left preserving sign +func (z *Z80) cb_sla(val byte) byte { + z.cf = (val >> 7) != 0 + val <<= 1 + z.sf = val&0x80 != 0 + z.zf = val == 0 + z.nf = false + z.hf = false + z.pf = parity(val) + z.updateXY(val) + return val +} + +// SLL (exactly like SLA, but sets the first bit to 1) +func (z *Z80) cb_sll(val byte) byte { + z.cf = val&0x80 != 0 + val <<= 1 + val |= 1 + z.sf = val&0x80 != 0 + z.zf = val == 0 + z.nf = false + z.hf = false + z.pf = parity(val) + z.updateXY(val) + return val +} + +// shift right preserving sign +func (z *Z80) cb_sra(val byte) byte { + z.cf = (val & 1) != 0 + val = (val >> 1) | (val & 0x80) // 0b10000000 + z.sf = val&0x80 != 0 + z.zf = val == 0 + z.nf = false + z.hf = false + z.pf = parity(val) + z.updateXY(val) + return val +} + +// shift register right +func (z *Z80) cb_srl(val byte) byte { + z.cf = (val & 1) != 0 + val >>= 1 + z.sf = val&0x80 != 0 + z.zf = val == 0 + z.nf = false + z.hf = false + z.pf = parity(val) + z.updateXY(val) + return val +} + +// tests bit "n" from a byte +func (z *Z80) cb_bit(val byte, n byte) byte { + result := val & (1 << n) + z.sf = result&0x80 != 0 + z.zf = result == 0 + z.hf = true + z.updateXY(val) + z.pf = z.zf + z.nf = false + return result +} + +func (z *Z80) ldi() { + de := z.get_de() + hl := z.get_hl() + val := z.rb(hl) + z.wb(de, val) + + z.set_hl(z.get_hl() + 1) + z.set_de(z.get_de() + 1) + z.set_bc(z.get_bc() - 1) + + // see https://wikiti.brandonw.net/index.php?title=Z80_Instruction_Set + // for the calculation of xf/yf on LDI + result := val + z.a + + z.updateXY(result) + + z.nf = false + z.hf = false + z.pf = z.get_bc() > 0 + +} + +func (z *Z80) ldd() { + z.ldi() + // same as ldi but HL and DE are decremented instead of incremented + z.set_hl(z.get_hl() - 2) + z.set_de(z.get_de() - 2) +} + +func (z *Z80) cpi() { + cf := z.cf + result := z.subb(z.a, z.rb(z.get_hl()), false) + z.set_hl(z.get_hl() + 1) + z.set_bc(z.get_bc() - 1) + + val := result - bToByte(z.hf) + z.xf = val&0x08 != 0 + z.yf = val&0x02 != 0 + z.pf = z.get_bc() != 0 + z.cf = cf + z.mem_ptr += 1 +} + +func (z *Z80) cpd() { + z.cpi() + // same as cpi but HL is decremented instead of incremented + z.set_hl(z.get_hl() - 2) + z.mem_ptr -= 2 +} + +func (z *Z80) in_r_c(r *byte) { + *r = z.core.IORead(z.get_bc()) + z.zf = *r == 0 + z.sf = *r&0x80 != 0 + z.pf = parity(*r) + z.nf = false + z.hf = false +} + +func (z *Z80) ini() { + val := z.core.IORead(z.get_bc()) + z.wb(z.get_hl(), val) + z.set_hl(z.get_hl() + 1) + z.b -= 1 + z.zf = z.b == 0 + z.nf = true + z.mem_ptr = z.get_bc() + 1 +} + +func (z *Z80) ind() { + z.ini() + z.set_hl(z.get_hl() - 2) + z.mem_ptr = z.get_bc() - 2 +} + +func (z *Z80) outi() { + z.core.IOWrite(z.get_bc(), z.rb(z.get_hl())) + z.set_hl(z.get_hl() + 1) + z.b -= 1 + z.zf = z.b == 0 + z.nf = true + z.mem_ptr = z.get_bc() + 1 +} + +func (z *Z80) outd() { + z.outi() + z.set_hl(z.get_hl() - 2) + z.mem_ptr = z.get_bc() - 2 +} + +func (z *Z80) daa() { + // "When this instruction is executed, the A register is BCD corrected + // using the contents of the flags. The exact process is the following: + // if the least significant four bits of A contain a non-BCD digit + // (i. e. it is greater than 9) or the H flag is set, then $06 is + // added to the register. Then the four most significant bits are + // checked. If this more significant digit also happens to be greater + // than 9 or the C flag is set, then $60 is added." + // > http://z80-heaven.wikidot.com/instructions-set:daa + correction := byte(0) + + if (z.a&0x0F) > 0x09 || z.hf { + correction += 0x06 + } + + if z.a > 0x99 || z.cf { + correction += 0x60 + z.cf = true + } + + substraction := z.nf + if substraction { + z.hf = z.hf && (z.a&0x0F) < 0x06 + z.a -= correction + } else { + z.hf = (z.a & 0x0F) > 0x09 + z.a += correction + } + + z.sf = z.a&0x80 != 0 + z.zf = z.a == 0 + z.pf = parity(z.a) + z.updateXY(z.a) +} + +func (z *Z80) displace(base_addr uint16, displacement byte) uint16 { + addr := base_addr + uint16(displacement) + z.mem_ptr = addr + return addr +} + +func (z *Z80) process_interrupts() { + // "When an EI instruction is executed, any pending interrupt request + // is not accepted until after the instruction following EI is executed." + if z.iff_delay > 0 { + z.iff_delay -= 1 + if z.iff_delay == 0 { + z.iff1 = true + z.iff2 = true + } + return + } + + if z.nmi_pending { + z.nmi_pending = false + z.halted = false + z.iff1 = false + z.inc_r() + + z.cyc += 11 + z.call(0x0066) + return + } + + if z.int_pending && z.iff1 { + z.int_pending = false + z.halted = false + z.iff1 = false + z.iff2 = false + z.inc_r() + + switch z.interrupt_mode { + case 0: + z.cyc += 11 + z.exec_opcode(z.int_data) + case 1: + z.cyc += 13 + z.call(0x38) + case 2: + z.cyc += 19 + z.call(z.rw((uint16(z.i) << 8) | uint16(z.int_data))) + default: + log.Errorf("Unsupported interrupt mode %d\n", z.interrupt_mode) + } + return + } +} + +// z80_gen_nmi function to call when an NMI is to be serviced +func (z *Z80) z80_gen_nmi() { + z.nmi_pending = true +} + +// z80_gen_int function to call when an INT is to be serviced +func (z *Z80) z80_gen_int(data byte) { + z.int_pending = true + z.int_data = data +} + +// executes a non-prefixed opcode +func (z *Z80) exec_opcode(opcode byte) { + z.cyc += uint64(cyc_00[opcode]) + z.inc_r() + + switch opcode { + case 0x7F: + z.a = z.a // ld a,a + case 0x78: + z.a = z.b // ld a,b + case 0x79: + z.a = z.c // ld a,c + case 0x7A: + z.a = z.d // ld a,d + case 0x7B: + z.a = z.e // ld a,e + case 0x7C: + z.a = z.h // ld a,h + case 0x7D: + z.a = z.l // ld a,l + + case 0x47: + z.b = z.a // ld b,a + case 0x40: + z.b = z.b // ld b,b + case 0x41: + z.b = z.c // ld b,c + case 0x42: + z.b = z.d // ld b,d + case 0x43: + z.b = z.e // ld b,e + case 0x44: + z.b = z.h // ld b,h + case 0x45: + z.b = z.l // ld b,l + + case 0x4F: + z.c = z.a // ld c,a + case 0x48: + z.c = z.b // ld c,b + case 0x49: + z.c = z.c // ld c,c + case 0x4A: + z.c = z.d // ld c,d + case 0x4B: + z.c = z.e // ld c,e + case 0x4C: + z.c = z.h // ld c,h + case 0x4D: + z.c = z.l // ld c,l + + case 0x57: + z.d = z.a // ld d,a + case 0x50: + z.d = z.b // ld d,b + case 0x51: + z.d = z.c // ld d,c + case 0x52: + z.d = z.d // ld d,d + case 0x53: + z.d = z.e // ld d,e + case 0x54: + z.d = z.h // ld d,h + case 0x55: + z.d = z.l // ld d,l + + case 0x5F: + z.e = z.a // ld e,a + case 0x58: + z.e = z.b // ld e,b + case 0x59: + z.e = z.c // ld e,c + case 0x5A: + z.e = z.d // ld e,d + case 0x5B: + z.e = z.e // ld e,e + case 0x5C: + z.e = z.h // ld e,h + case 0x5D: + z.e = z.l // ld e,l + + case 0x67: + z.h = z.a // ld h,a + case 0x60: + z.h = z.b // ld h,b + case 0x61: + z.h = z.c // ld h,c + case 0x62: + z.h = z.d // ld h,d + case 0x63: + z.h = z.e // ld h,e + case 0x64: + z.h = z.h // ld h,h + case 0x65: + z.h = z.l // ld h,l + + case 0x6F: + z.l = z.a // ld l,a + case 0x68: + z.l = z.b // ld l,b + case 0x69: + z.l = z.c // ld l,c + case 0x6A: + z.l = z.d // ld l,d + case 0x6B: + z.l = z.e // ld l,e + case 0x6C: + z.l = z.h // ld l,h + case 0x6D: + z.l = z.l // ld l,l + + case 0x7E: + z.a = z.rb(z.get_hl()) // ld a,(hl) + case 0x46: + z.b = z.rb(z.get_hl()) // ld b,(hl) + case 0x4E: + z.c = z.rb(z.get_hl()) // ld c,(hl) + case 0x56: + z.d = z.rb(z.get_hl()) // ld d,(hl) + case 0x5E: + z.e = z.rb(z.get_hl()) // ld e,(hl) + case 0x66: + z.h = z.rb(z.get_hl()) // ld h,(hl) + case 0x6E: + z.l = z.rb(z.get_hl()) // ld l,(hl) + + case 0x77: + z.wb(z.get_hl(), z.a) // ld (hl),a + case 0x70: + z.wb(z.get_hl(), z.b) // ld (hl),b + case 0x71: + z.wb(z.get_hl(), z.c) // ld (hl),c + case 0x72: + z.wb(z.get_hl(), z.d) // ld (hl),d + case 0x73: + z.wb(z.get_hl(), z.e) // ld (hl),e + case 0x74: + z.wb(z.get_hl(), z.h) // ld (hl),h + case 0x75: + z.wb(z.get_hl(), z.l) // ld (hl),l + + case 0x3E: + z.a = z.nextb() // ld a,* + case 0x06: + z.b = z.nextb() // ld b,* + case 0x0E: + z.c = z.nextb() // ld c,* + case 0x16: + z.d = z.nextb() // ld d,* + case 0x1E: + z.e = z.nextb() // ld e,* + case 0x26: + z.h = z.nextb() // ld h,* + case 0x2E: + z.l = z.nextb() // ld l,* + case 0x36: + z.wb(z.get_hl(), z.nextb()) // ld (hl),* + case 0x0A: + // ld a,(bc) + z.a = z.rb(z.get_bc()) + z.mem_ptr = z.get_bc() + 1 + case 0x1A: + // ld a,(de) + z.a = z.rb(z.get_de()) + z.mem_ptr = z.get_de() + 1 + case 0x3A: + // ld a,(**) + addr := z.nextw() + z.a = z.rb(addr) + z.mem_ptr = addr + 1 + case 0x02: + // ld (bc),a + z.wb(z.get_bc(), z.a) + z.mem_ptr = (uint16(z.a) << 8) | ((z.get_bc() + 1) & 0xFF) + case 0x12: + // ld (de),a + z.wb(z.get_de(), z.a) + z.mem_ptr = (uint16(z.a) << 8) | ((z.get_de() + 1) & 0xFF) + case 0x32: + // ld (**),a + addr := z.nextw() + z.wb(addr, z.a) + z.mem_ptr = (uint16(z.a) << 8) | ((addr + 1) & 0xFF) + case 0x01: + z.set_bc(z.nextw()) // ld bc,** + case 0x11: + z.set_de(z.nextw()) // ld de,** + case 0x21: + z.set_hl(z.nextw()) // ld hl,** + case 0x31: + z.sp = z.nextw() // ld sp,** + + case 0x2A: + // ld hl,(**) + addr := z.nextw() + z.set_hl(z.rw(addr)) + z.mem_ptr = addr + 1 + case 0x22: + // ld (**),hl + addr := z.nextw() + z.ww(addr, z.get_hl()) + z.mem_ptr = addr + 1 + case 0xF9: + z.sp = z.get_hl() // ld sp,hl + + case 0xEB: + // ex de,hl + de := z.get_de() + z.set_de(z.get_hl()) + z.set_hl(de) + case 0xE3: + // ex (sp),hl + val := z.rw(z.sp) + z.ww(z.sp, z.get_hl()) + z.set_hl(val) + z.mem_ptr = val + case 0x87: + z.a = z.addb(z.a, z.a, false) // add a,a + case 0x80: + z.a = z.addb(z.a, z.b, false) // add a,b + case 0x81: + z.a = z.addb(z.a, z.c, false) // add a,c + case 0x82: + z.a = z.addb(z.a, z.d, false) // add a,d + case 0x83: + z.a = z.addb(z.a, z.e, false) // add a,e + case 0x84: + z.a = z.addb(z.a, z.h, false) // add a,h + case 0x85: + z.a = z.addb(z.a, z.l, false) // add a,l + case 0x86: + z.a = z.addb(z.a, z.rb(z.get_hl()), false) // add a,(hl) + case 0xC6: + z.a = z.addb(z.a, z.nextb(), false) // add a,* + + case 0x8F: + z.a = z.addb(z.a, z.a, z.cf) // adc a,a + case 0x88: + z.a = z.addb(z.a, z.b, z.cf) // adc a,b + case 0x89: + z.a = z.addb(z.a, z.c, z.cf) // adc a,c + case 0x8A: + z.a = z.addb(z.a, z.d, z.cf) // adc a,d + case 0x8B: + z.a = z.addb(z.a, z.e, z.cf) // adc a,e + case 0x8C: + z.a = z.addb(z.a, z.h, z.cf) // adc a,h + case 0x8D: + z.a = z.addb(z.a, z.l, z.cf) // adc a,l + case 0x8E: + z.a = z.addb(z.a, z.rb(z.get_hl()), z.cf) // adc a,(hl) + case 0xCE: + z.a = z.addb(z.a, z.nextb(), z.cf) // adc a,* + + case 0x97: + z.a = z.subb(z.a, z.a, false) // sub a,a + case 0x90: + z.a = z.subb(z.a, z.b, false) // sub a,b + case 0x91: + z.a = z.subb(z.a, z.c, false) // sub a,c + case 0x92: + z.a = z.subb(z.a, z.d, false) // sub a,d + case 0x93: + z.a = z.subb(z.a, z.e, false) // sub a,e + case 0x94: + z.a = z.subb(z.a, z.h, false) // sub a,h + case 0x95: + z.a = z.subb(z.a, z.l, false) // sub a,l + case 0x96: + z.a = z.subb(z.a, z.rb(z.get_hl()), false) // sub a,(hl) + case 0xD6: + z.a = z.subb(z.a, z.nextb(), false) // sub a,* + + case 0x9F: + z.a = z.subb(z.a, z.a, z.cf) // sbc a,a + case 0x98: + z.a = z.subb(z.a, z.b, z.cf) // sbc a,b + case 0x99: + z.a = z.subb(z.a, z.c, z.cf) // sbc a,c + case 0x9A: + z.a = z.subb(z.a, z.d, z.cf) // sbc a,d + case 0x9B: + z.a = z.subb(z.a, z.e, z.cf) // sbc a,e + case 0x9C: + z.a = z.subb(z.a, z.h, z.cf) // sbc a,h + case 0x9D: + z.a = z.subb(z.a, z.l, z.cf) // sbc a,l + case 0x9E: + z.a = z.subb(z.a, z.rb(z.get_hl()), z.cf) // sbc a,(hl) + case 0xDE: + z.a = z.subb(z.a, z.nextb(), z.cf) // sbc a,* + + case 0x09: + z.addhl(z.get_bc()) // add hl,bc + case 0x19: + z.addhl(z.get_de()) // add hl,de + case 0x29: + z.addhl(z.get_hl()) // add hl,hl + case 0x39: + z.addhl(z.sp) // add hl,sp + + case 0xF3: + z.iff1 = false + z.iff2 = false // di + case 0xFB: + z.iff_delay = 1 // ei + case 0x00: // nop + case 0x76: + z.halted = true // halt + + case 0x3C: + z.a = z.inc(z.a) // inc a + case 0x04: + z.b = z.inc(z.b) // inc b + case 0x0C: + z.c = z.inc(z.c) // inc c + case 0x14: + z.d = z.inc(z.d) // inc d + case 0x1C: + z.e = z.inc(z.e) // inc e + case 0x24: + z.h = z.inc(z.h) // inc h + case 0x2C: + z.l = z.inc(z.l) // inc l + case 0x34: + // inc (hl) + result := z.inc(z.rb(z.get_hl())) + z.wb(z.get_hl(), result) + case 0x3D: + z.a = z.dec(z.a) // dec a + case 0x05: + z.b = z.dec(z.b) // dec b + case 0x0D: + z.c = z.dec(z.c) // dec c + case 0x15: + z.d = z.dec(z.d) // dec d + case 0x1D: + z.e = z.dec(z.e) // dec e + case 0x25: + z.h = z.dec(z.h) // dec h + case 0x2D: + z.l = z.dec(z.l) // dec l + case 0x35: + // dec (hl) + result := z.dec(z.rb(z.get_hl())) + z.wb(z.get_hl(), result) + case 0x03: + z.set_bc(z.get_bc() + 1) // inc bc + case 0x13: + z.set_de(z.get_de() + 1) // inc de + case 0x23: + z.set_hl(z.get_hl() + 1) // inc hl + case 0x33: + z.sp = z.sp + 1 // inc sp + case 0x0B: + z.set_bc(z.get_bc() - 1) // dec bc + case 0x1B: + z.set_de(z.get_de() - 1) // dec de + case 0x2B: + z.set_hl(z.get_hl() - 1) // dec hl + case 0x3B: + z.sp = z.sp - 1 // dec sp + case 0x27: + z.daa() // daa + case 0x2F: + // cpl + z.a = ^z.a + z.nf = true + z.hf = true + z.updateXY(z.a) + case 0x37: + // scf + z.cf = true + z.nf = false + z.hf = false + z.updateXY(z.a) + case 0x3F: + // ccf + z.hf = z.cf + z.cf = !z.cf + z.nf = false + z.updateXY(z.a) + case 0x07: + // rlca (rotate left) + z.cf = z.a&0x80 != 0 + z.a = (z.a << 1) | bToByte(z.cf) + z.nf = false + z.hf = false + z.updateXY(z.a) + case 0x0F: + // rrca (rotate right) + z.cf = z.a&1 != 0 + z.a = (z.a >> 1) | (bToByte(z.cf) << 7) + z.nf = false + z.hf = false + z.updateXY(z.a) + case 0x17: + // rla + cy := bToByte(z.cf) + z.cf = z.a&0x80 != 0 + z.a = (z.a << 1) | cy + z.nf = false + z.hf = false + z.updateXY(z.a) + case 0x1F: + // rra + cy := bToByte(z.cf) + z.cf = z.a&1 != 0 + z.a = (z.a >> 1) | (cy << 7) + z.nf = false + z.hf = false + z.updateXY(z.a) + case 0xA7: + z.land(z.a) // and a + case 0xA0: + z.land(z.b) // and b + case 0xA1: + z.land(z.c) // and c + case 0xA2: + z.land(z.d) // and d + case 0xA3: + z.land(z.e) // and e + case 0xA4: + z.land(z.h) // and h + case 0xA5: + z.land(z.l) // and l + case 0xA6: + z.land(z.rb(z.get_hl())) // and (hl) + case 0xE6: + z.land(z.nextb()) // and * + + case 0xAF: + z.lxor(z.a) // xor a + case 0xA8: + z.lxor(z.b) // xor b + case 0xA9: + z.lxor(z.c) // xor c + case 0xAA: + z.lxor(z.d) // xor d + case 0xAB: + z.lxor(z.e) // xor e + case 0xAC: + z.lxor(z.h) // xor h + case 0xAD: + z.lxor(z.l) // xor l + case 0xAE: + z.lxor(z.rb(z.get_hl())) // xor (hl) + case 0xEE: + z.lxor(z.nextb()) // xor * + + case 0xB7: + z.lor(z.a) // or a + case 0xB0: + z.lor(z.b) // or b + case 0xB1: + z.lor(z.c) // or c + case 0xB2: + z.lor(z.d) // or d + case 0xB3: + z.lor(z.e) // or e + case 0xB4: + z.lor(z.h) // or h + case 0xB5: + z.lor(z.l) // or l + case 0xB6: + z.lor(z.rb(z.get_hl())) // or (hl) + case 0xF6: + z.lor(z.nextb()) // or * + + case 0xBF: + z.cp(z.a) // cp a + case 0xB8: + z.cp(z.b) // cp b + case 0xB9: + z.cp(z.c) // cp c + case 0xBA: + z.cp(z.d) // cp d + case 0xBB: + z.cp(z.e) // cp e + case 0xBC: + z.cp(z.h) // cp h + case 0xBD: + z.cp(z.l) // cp l + case 0xBE: + z.cp(z.rb(z.get_hl())) // cp (hl) + case 0xFE: + z.cp(z.nextb()) // cp * + + case 0xC3: + z.jump(z.nextw()) // jm ** + case 0xC2: + z.cond_jump(!z.zf) // jp nz, ** + case 0xCA: + z.cond_jump(z.zf) // jp z, ** + case 0xD2: + z.cond_jump(!z.cf) // jp nc, ** + case 0xDA: + z.cond_jump(z.cf) // jp c, ** + case 0xE2: + z.cond_jump(!z.pf) // jp po, ** + case 0xEA: + z.cond_jump(z.pf) // jp pe, ** + case 0xF2: + z.cond_jump(!z.sf) // jp p, ** + case 0xFA: + z.cond_jump(z.sf) // jp m, ** + + case 0x10: + z.b-- + z.cond_jr(z.b != 0) // djnz * + case 0x18: + z.pc += uint16(z.nextb()) // jr * + case 0x20: + z.cond_jr(!z.zf) // jr nz, * + case 0x28: + z.cond_jr(z.zf) // jr z, * + case 0x30: + z.cond_jr(!z.cf) // jr nc, * + case 0x38: + z.cond_jr(z.cf) // jr c, * + + case 0xE9: + z.pc = z.get_hl() // jp (hl) + case 0xCD: + z.call(z.nextw()) // call + + case 0xC4: + z.cond_call(!z.zf) // cnz + case 0xCC: + z.cond_call(z.zf) // cz + case 0xD4: + z.cond_call(!z.cf) // cnc + case 0xDC: + z.cond_call(z.cf) // cc + case 0xE4: + z.cond_call(!z.pf) // cpo + case 0xEC: + z.cond_call(z.pf) // cpe + case 0xF4: + z.cond_call(!z.sf) // cp + case 0xFC: + z.cond_call(z.sf) // cm + + case 0xC9: + z.ret() // ret + case 0xC0: + z.cond_ret(!z.zf) // ret nz + case 0xC8: + z.cond_ret(z.zf) // ret z + case 0xD0: + z.cond_ret(!z.cf) // ret nc + case 0xD8: + z.cond_ret(z.cf) // ret c + case 0xE0: + z.cond_ret(!z.pf) // ret po + case 0xE8: + z.cond_ret(z.pf) // ret pe + case 0xF0: + z.cond_ret(!z.sf) // ret p + case 0xF8: + z.cond_ret(z.sf) // ret m + + case 0xC7: + z.call(0x00) // rst 0 + case 0xCF: + z.call(0x08) // rst 1 + case 0xD7: + z.call(0x10) // rst 2 + case 0xDF: + z.call(0x18) // rst 3 + case 0xE7: + z.call(0x20) // rst 4 + case 0xEF: + z.call(0x28) // rst 5 + case 0xF7: + z.call(0x30) // rst 6 + case 0xFF: + z.call(0x38) // rst 7 + + case 0xC5: + z.pushw(z.get_bc()) // push bc + case 0xD5: + z.pushw(z.get_de()) // push de + case 0xE5: + z.pushw(z.get_hl()) // push hl + case 0xF5: + z.pushw((uint16(z.a) << 8) | uint16(z.get_f())) // push af + + case 0xC1: + z.set_bc(z.popw()) // pop bc + case 0xD1: + z.set_de(z.popw()) // pop de + case 0xE1: + z.set_hl(z.popw()) // pop hl + case 0xF1: + // pop af + val := z.popw() + z.a = byte(val >> 8) + z.set_f(byte(val)) + case 0xDB: + // in a,(n) + port := uint16(z.nextb()) + a := z.a + z.a = z.core.IORead(port) + z.mem_ptr = (uint16(a) << 8) | uint16(z.a+1) + case 0xD3: + // out (n), a + port := uint16(z.nextb()) + z.core.IOWrite(port, z.a) + z.mem_ptr = (port + 1) | (uint16(z.a) << 8) + case 0x08: + // ex af,af' + a := z.a + f := z.get_f() + + z.a = z.a_ + z.set_f(z.f_) + + z.a_ = a + z.f_ = f + case 0xD9: + // exx + b := z.b + c := z.c + d := z.d + e := z.e + h := z.h + l := z.l + + z.b = z.b_ + z.c = z.c_ + z.d = z.d_ + z.e = z.e_ + z.h = z.h_ + z.l = z.l_ + + z.b_ = b + z.c_ = c + z.d_ = d + z.e_ = e + z.h_ = h + z.l_ = l + case 0xCB: + z.exec_opcode_cb(z.nextb()) + case 0xED: + z.exec_opcode_ed(z.nextb()) + case 0xDD: + z.exec_opcode_ddfd(z.nextb(), &z.ix) + case 0xFD: + z.exec_opcode_ddfd(z.nextb(), &z.iy) + + default: + log.Errorf("Unknown opcode %02X\n", opcode) + } +} diff --git a/z80/opcodesCB.go b/z80/opcodesCB.go new file mode 100644 index 0000000..d2c6ad4 --- /dev/null +++ b/z80/opcodesCB.go @@ -0,0 +1,162 @@ +package z80 + +import log "github.com/sirupsen/logrus" + +// executes a CB opcode +func (z *Z80) exec_opcode_cb(opcode byte) { + z.cyc += 8 + z.inc_r() + + // decoding instructions from http://z80.info/decoding.htm#cb + x_ := (opcode >> 6) & 3 // 0b11 + y_ := (opcode >> 3) & 7 // 0b111 + z_ := opcode & 7 // 0b111 + + var hl byte + v := byte(0) + reg := &v + switch z_ { + case 0: + reg = &z.b + case 1: + reg = &z.c + case 2: + reg = &z.d + case 3: + reg = &z.e + case 4: + reg = &z.h + case 5: + reg = &z.l + case 6: + hl = z.rb(z.get_hl()) + reg = &hl + case 7: + reg = &z.a + } + + switch x_ { + case 0: + // rot[y] r[z] + switch y_ { + case 0: + *reg = z.cb_rlc(*reg) + case 1: + *reg = z.cb_rrc(*reg) + case 2: + *reg = z.cb_rl(*reg) + case 3: + *reg = z.cb_rr(*reg) + case 4: + *reg = z.cb_sla(*reg) + case 5: + *reg = z.cb_sra(*reg) + case 6: + *reg = z.cb_sll(*reg) + case 7: + *reg = z.cb_srl(*reg) + } + + case 1: + // BIT y, r[z] + z.cb_bit(*reg, y_) + + // in bit (hl), x/y flags are handled differently: + if z_ == 6 { + z.updateXY(byte(z.mem_ptr >> 8)) + z.cyc += 4 + } + + case 2: + *reg &= ^(1 << y_) // RES y, r[z] + case 3: + *reg |= 1 << y_ // SET y, r[z] + } + + if (x_ == 0 || x_ == 2 || x_ == 3) && z_ == 6 { + z.cyc += 7 + } + + if reg == &hl { + z.wb(z.get_hl(), hl) + } +} + +// exec_opcode_dcb executes a displaced CB opcode (DDCB or FDCB) +func (z *Z80) exec_opcode_dcb(opcode byte, addr uint16) { + val := z.rb(addr) + result := byte(0) + + // decoding instructions from http://z80.info/decoding.htm#ddcb + x_ := (opcode >> 6) & 3 // 0b11 + y_ := (opcode >> 3) & 7 // 0b111 + z_ := opcode & 7 // 0b111 + + switch x_ { + case 0: + // rot[y] (iz+d) + switch y_ { + case 0: + result = z.cb_rlc(val) + case 1: + result = z.cb_rrc(val) + case 2: + result = z.cb_rl(val) + case 3: + result = z.cb_rr(val) + case 4: + result = z.cb_sla(val) + case 5: + result = z.cb_sra(val) + case 6: + result = z.cb_sll(val) + case 7: + result = z.cb_srl(val) + } + + case 1: + // bit y,(iz+d) + result = z.cb_bit(val, y_) + z.updateXY(byte(addr >> 8)) + case 2: + result = val & ^(1 << y_) // res y, (iz+d) + case 3: + result = val | (1 << y_) // set y, (iz+d) + + default: + log.Errorf("Unknown XYCB opcode: %02X\n", opcode) + } + + // ld r[z], rot[y] (iz+d) + // ld r[z], res y,(iz+d) + // ld r[z], set y,(iz+d) + if x_ != 1 && z_ != 6 { + switch z_ { + case 0: + z.b = result + case 1: + z.c = result + case 2: + z.d = result + case 3: + z.e = result + case 4: + z.h = result + case 5: + z.l = result + // always false + //case 6: + // z.wb(z.get_hl(), result) + case 7: + z.a = result + } + } + + if x_ == 1 { + // bit instructions take 20 cycles, others take 23 + z.cyc += 20 + } else { + z.wb(addr, result) + z.cyc += 23 + } +} diff --git a/z80/opcodesDDFD.go b/z80/opcodesDDFD.go new file mode 100644 index 0000000..6d74423 --- /dev/null +++ b/z80/opcodesDDFD.go @@ -0,0 +1,202 @@ +package z80 + +// executes a DD/FD opcode (IZ = IX or IY) +func (z *Z80) exec_opcode_ddfd(opcode byte, iz *uint16) { + z.cyc += uint64(cyc_ddfd[opcode]) + z.inc_r() + + switch opcode { + case 0xE1: + *iz = z.popw() // pop iz + case 0xE5: + z.pushw(*iz) // push iz + + case 0xE9: + z.jump(*iz) // jp iz + + case 0x09: + z.addiz(iz, z.get_bc()) // add iz,bc + case 0x19: + z.addiz(iz, z.get_de()) // add iz,de + case 0x29: + z.addiz(iz, *iz) // add iz,iz + case 0x39: + z.addiz(iz, z.sp) // add iz,sp + + case 0x84: + z.a = z.addb(z.a, byte(*iz>>8), false) // add a,izh + case 0x85: + z.a = z.addb(z.a, byte(*iz), false) // add a,izl + case 0x8C: + z.a = z.addb(z.a, byte(*iz>>8), z.cf) // adc a,izh + case 0x8D: + z.a = z.addb(z.a, byte(*iz), z.cf) // adc a,izl + case 0x86: + z.a = z.addb(z.a, z.rb(z.displace(*iz, z.nextb())), false) // add a,(iz+*) + case 0x8E: + z.a = z.addb(z.a, z.rb(z.displace(*iz, z.nextb())), z.cf) // adc a,(iz+*) + case 0x96: + z.a = z.subb(z.a, z.rb(z.displace(*iz, z.nextb())), false) // sub (iz+*) + case 0x9E: + z.a = z.subb(z.a, z.rb(z.displace(*iz, z.nextb())), z.cf) // sbc (iz+*) + case 0x94: + z.a = z.subb(z.a, byte(*iz>>8), false) // sub izh + case 0x95: + z.a = z.subb(z.a, byte(*iz), false) // sub izl + case 0x9C: + z.a = z.subb(z.a, byte(*iz>>8), z.cf) // sbc izh + case 0x9D: + z.a = z.subb(z.a, byte(*iz), z.cf) // sbc izl + + case 0xA6: + z.land(z.rb(z.displace(*iz, z.nextb()))) // and (iz+*) + case 0xA4: + z.land(byte(*iz >> 8)) // and izh + case 0xA5: + z.land(byte(*iz)) // and izl + + case 0xAE: + z.lxor(z.rb(z.displace(*iz, z.nextb()))) // xor (iz+*) + case 0xAC: + z.lxor(byte(*iz >> 8)) // xor izh + case 0xAD: + z.lxor(byte(*iz)) // xor izl + case 0xB6: + z.lor(z.rb(z.displace(*iz, z.nextb()))) // or (iz+*) + case 0xB4: + z.lor(byte(*iz >> 8)) // or izh + case 0xB5: + z.lor(byte(*iz)) // or izl + case 0xBE: + z.cp(z.rb(z.displace(*iz, z.nextb()))) // cp (iz+*) + case 0xBC: + z.cp(byte(*iz >> 8)) // cp izh + case 0xBD: + z.cp(byte(*iz)) // cp izl + case 0x23: + *iz += 1 // inc iz + case 0x2B: + *iz -= 1 // dec iz + case 0x34: + // inc (iz+*) + addr := z.displace(*iz, z.nextb()) + z.wb(addr, z.inc(z.rb(addr))) + case 0x35: + // dec (iz+*) + addr := z.displace(*iz, z.nextb()) + z.wb(addr, z.dec(z.rb(addr))) + case 0x24: + *iz = (*iz & 0x00ff) | (uint16(z.inc(byte(*iz>>8))) << 8) // inc izh + case 0x25: + *iz = (*iz & 0x00ff) | (uint16(z.dec(byte(*iz>>8))) << 8) // dec izh + case 0x2C: + *iz = (*iz & 0xff00) | uint16(z.inc(byte(*iz))) // inc izl + case 0x2D: + *iz = (*iz & 0xff00) | uint16(z.dec(byte(*iz))) // dec izl + case 0x2A: + *iz = z.rw(z.nextw()) // ld iz,(**) + case 0x22: + z.ww(z.nextw(), *iz) // ld (**),iz + case 0x21: + *iz = z.nextw() // ld iz,** + case 0x36: + // ld (iz+*),* + addr := z.displace(*iz, z.nextb()) + z.wb(addr, z.nextb()) + case 0x70: + z.wb(z.displace(*iz, z.nextb()), z.b) // ld (iz+*),b + case 0x71: + z.wb(z.displace(*iz, z.nextb()), z.c) // ld (iz+*),c + case 0x72: + z.wb(z.displace(*iz, z.nextb()), z.d) // ld (iz+*),d + case 0x73: + z.wb(z.displace(*iz, z.nextb()), z.e) // ld (iz+*),e + case 0x74: + z.wb(z.displace(*iz, z.nextb()), z.h) // ld (iz+*),h + case 0x75: + z.wb(z.displace(*iz, z.nextb()), z.l) // ld (iz+*),l + case 0x77: + z.wb(z.displace(*iz, z.nextb()), z.a) // ld (iz+*),a + case 0x46: + z.b = z.rb(z.displace(*iz, z.nextb())) // ld b,(iz+*) + case 0x4E: + z.c = z.rb(z.displace(*iz, z.nextb())) // ld c,(iz+*) + case 0x56: + z.d = z.rb(z.displace(*iz, z.nextb())) // ld d,(iz+*) + case 0x5E: + z.e = z.rb(z.displace(*iz, z.nextb())) // ld e,(iz+*) + case 0x66: + z.h = z.rb(z.displace(*iz, z.nextb())) // ld h,(iz+*) + case 0x6E: + z.l = z.rb(z.displace(*iz, z.nextb())) // ld l,(iz+*) + case 0x7E: + z.a = z.rb(z.displace(*iz, z.nextb())) // ld a,(iz+*) + case 0x44: + z.b = byte(*iz >> 8) // ld b,izh + case 0x4C: + z.c = byte(*iz >> 8) // ld c,izh + case 0x54: + z.d = byte(*iz >> 8) // ld d,izh + case 0x5C: + z.e = byte(*iz >> 8) // ld e,izh + case 0x7C: + z.a = byte(*iz >> 8) // ld a,izh + case 0x45: + z.b = byte(*iz) // ld b,izl + case 0x4D: + z.c = byte(*iz) // ld c,izl + case 0x55: + z.d = byte(*iz) // ld d,izl + case 0x5D: + z.e = byte(*iz) // ld e,izl + case 0x7D: + z.a = byte(*iz) // ld a,izl + case 0x60: + *iz = (*iz & 0x00ff) | (uint16(z.b) << 8) // ld izh,b + case 0x61: + *iz = (*iz & 0x00ff) | (uint16(z.c) << 8) // ld izh,c + case 0x62: + *iz = (*iz & 0x00ff) | (uint16(z.d) << 8) // ld izh,d + case 0x63: + *iz = (*iz & 0x00ff) | (uint16(z.e) << 8) // ld izh,e + case 0x64: // ld izh,izh + case 0x65: + *iz = ((*iz & 0x00ff) << 8) | (*iz & 0x00ff) // ld izh,izl + case 0x67: + *iz = (uint16(z.a) << 8) | (*iz & 0x00ff) // ld izh,a + case 0x26: + *iz = (uint16(z.nextb()) << 8) | (*iz & 0x00ff) // ld izh,* + case 0x68: + *iz = (*iz & 0xff00) | uint16(z.b) // ld izl,b + case 0x69: + *iz = (*iz & 0xff00) | uint16(z.c) // ld izl,c + case 0x6A: + *iz = (*iz & 0xff00) | uint16(z.d) // ld izl,d + case 0x6B: + *iz = (*iz & 0xff00) | uint16(z.e) // ld izl,e + case 0x6C: + *iz = (*iz & 0xff00) | (*iz >> 8) // ld izl,izh + case 0x6D: // ld izl,izl + case 0x6F: + *iz = (*iz & 0xff00) | uint16(z.a) // ld izl,a + case 0x2E: + *iz = (*iz & 0xff00) | uint16(z.nextb()) // ld izl,* + case 0xF9: + z.sp = *iz // ld sp,iz + case 0xE3: + // ex (sp),iz + val := z.rw(z.sp) + z.ww(z.sp, *iz) + *iz = val + z.mem_ptr = val + case 0xCB: + addr := z.displace(*iz, z.nextb()) + op := z.nextb() + z.exec_opcode_dcb(op, addr) + default: + // any other FD/DD opcode behaves as a non-prefixed opcode: + z.exec_opcode(opcode) + // R should not be incremented twice: + z.inc_r() + } +} diff --git a/z80/opcodesED.go b/z80/opcodesED.go new file mode 100644 index 0000000..e323949 --- /dev/null +++ b/z80/opcodesED.go @@ -0,0 +1,257 @@ +package z80 + +import log "github.com/sirupsen/logrus" + +// executes a ED opcode +func (z *Z80) exec_opcode_ed(opcode byte) { + z.cyc += uint64(cyc_ed[opcode]) + z.inc_r() + switch opcode { + case 0x47: + z.i = z.a // ld i,a + case 0x4F: + z.r = z.a // ld r,a + case 0x57: + // ld a,i + z.a = z.i + z.sf = z.a&0x80 != 0 + z.zf = z.a == 0 + z.hf = false + z.nf = false + z.pf = z.iff2 + case 0x5F: + // ld a,r + z.a = z.r + z.sf = z.a&0x80 != 0 + z.zf = z.a == 0 + z.hf = false + z.nf = false + z.pf = z.iff2 + case 0x45, 0x55, 0x5D, 0x65, 0x6D, 0x75, 0x7D: + // retn + z.iff1 = z.iff2 + z.ret() + case 0x4D: + z.ret() // reti + + case 0xA0: + z.ldi() // ldi + case 0xB0: + { + z.ldi() + + if z.get_bc() != 0 { + z.pc -= 2 + z.cyc += 5 + z.mem_ptr = z.pc + 1 + } + } // ldir + + case 0xA8: + z.ldd() // ldd + case 0xB8: + { + z.ldd() + + if z.get_bc() != 0 { + z.pc -= 2 + z.cyc += 5 + z.mem_ptr = z.pc + 1 + } + } // lddr + + case 0xA1: + z.cpi() // cpi + case 0xA9: + z.cpd() // cpd + case 0xB1: + { + z.cpi() + if z.get_bc() != 0 && !z.zf { + z.pc -= 2 + z.cyc += 5 + z.mem_ptr = z.pc + 1 + } else { + z.mem_ptr += 1 + } + } // cpir + case 0xB9: + { + z.cpd() + if z.get_bc() != 0 && !z.zf { + z.pc -= 2 + z.cyc += 5 + } else { + z.mem_ptr += 1 + } + } // cpdr + + case 0x40: + z.in_r_c(&z.b) // in b, (c) + case 0x48: + z.in_r_c(&z.c) // in c, (c) + case 0x50: + z.in_r_c(&z.d) // in d, (c) + case 0x58: + z.in_r_c(&z.e) // in e, (c) + case 0x60: + z.in_r_c(&z.h) // in h, (c) + case 0x68: + z.in_r_c(&z.l) // in l, (c) + case 0x70: + // in (c) + var val byte + z.in_r_c(&val) + case 0x78: + // in a, (c) + z.in_r_c(&z.a) + z.mem_ptr = z.get_bc() + 1 + case 0xA2: + z.ini() // ini + case 0xB2: + // inir + z.ini() + if z.b > 0 { + z.pc -= 2 + z.cyc += 5 + } + case 0xAA: + z.ind() // ind + case 0xBA: + // indr + z.ind() + if z.b > 0 { + z.pc -= 2 + z.cyc += 5 + } + case 0x41: + z.core.IOWrite(z.get_bc(), z.b) // out (c), b + case 0x49: + z.core.IOWrite(z.get_bc(), z.c) // out (c), c + case 0x51: + z.core.IOWrite(z.get_bc(), z.d) // out (c), d + case 0x59: + z.core.IOWrite(z.get_bc(), z.e) // out (c), e + case 0x61: + z.core.IOWrite(z.get_bc(), z.h) // out (c), h + case 0x69: + z.core.IOWrite(z.get_bc(), z.l) // out (c), l + case 0x71: + z.core.IOWrite(z.get_bc(), 0) // out (c), 0 + case 0x79: + // out (c), a + z.core.IOWrite(z.get_bc(), z.a) + z.mem_ptr = z.get_bc() + 1 + case 0xA3: + z.outi() // outi + case 0xB3: + // otir + z.outi() + if z.b > 0 { + z.pc -= 2 + z.cyc += 5 + } + case 0xAB: + z.outd() // outd + case 0xBB: + // otdr + z.outd() + if z.b > 0 { + z.pc -= 2 + } + + case 0x42: + z.sbchl(z.get_bc()) // sbc hl,bc + case 0x52: + z.sbchl(z.get_de()) // sbc hl,de + case 0x62: + z.sbchl(z.get_hl()) // sbc hl,hl + case 0x72: + z.sbchl(z.sp) // sbc hl,sp + case 0x4A: + z.adchl(z.get_bc()) // adc hl,bc + case 0x5A: + z.adchl(z.get_de()) // adc hl,de + case 0x6A: + z.adchl(z.get_hl()) // adc hl,hl + case 0x7A: + z.adchl(z.sp) // adc hl,sp + case 0x43: + // ld (**), bc + addr := z.nextw() + z.ww(addr, z.get_bc()) + z.mem_ptr = addr + 1 + case 0x53: + // ld (**), de + addr := z.nextw() + z.ww(addr, z.get_de()) + z.mem_ptr = addr + 1 + case 0x63: + // ld (**), hl + addr := z.nextw() + z.ww(addr, z.get_hl()) + z.mem_ptr = addr + 1 + case 0x73: + // ld (**), hl + addr := z.nextw() + z.ww(addr, z.sp) + z.mem_ptr = addr + 1 + case 0x4B: + // ld bc, (**) + addr := z.nextw() + z.set_bc(z.rw(addr)) + z.mem_ptr = addr + 1 + case 0x5B: + // ld de, (**) + addr := z.nextw() + z.set_de(z.rw(addr)) + z.mem_ptr = addr + 1 + case 0x6B: + // ld hl, (**) + addr := z.nextw() + z.set_hl(z.rw(addr)) + z.mem_ptr = addr + 1 + case 0x7B: + // ld sp,(**) + addr := z.nextw() + z.sp = z.rw(addr) + z.mem_ptr = addr + 1 + case 0x44, 0x54, 0x64, 0x74, 0x4C, 0x5C, 0x6C, 0x7C: + z.a = z.subb(0, z.a, false) // neg + case 0x46, 0x66: + z.interrupt_mode = 0 // im 0 + case 0x56, 0x76: + z.interrupt_mode = 1 // im 1 + case 0x5E, 0x7E: + z.interrupt_mode = 2 // im 2 + case 0x67: + // rrd + a := z.a + val := z.rb(z.get_hl()) + z.a = (a & 0xF0) | (val & 0xF) + z.wb(z.get_hl(), (val>>4)|(a<<4)) + + z.nf = false + z.hf = false + z.updateXY(z.a) + z.zf = z.a == 0 + z.sf = z.a&0x80 != 0 + z.pf = parity(z.a) + z.mem_ptr = z.get_hl() + 1 + case 0x6F: + // rld + a := z.a + val := z.rb(z.get_hl()) + z.a = (a & 0xF0) | (val >> 4) + z.wb(z.get_hl(), (val<<4)|(a&0xF)) + z.nf = false + z.hf = false + z.updateXY(z.a) + z.zf = z.a == 0 + z.sf = z.a&0x80 != 0 + z.pf = parity(z.a) + z.mem_ptr = z.get_hl() + 1 + default: + log.Errorf("Unknown ED opcode: %02X\n", opcode) + } +} diff --git a/z80em/z80em.go b/z80em/z80em.go index 00f48e9..6104b0a 100644 --- a/z80em/z80em.go +++ b/z80em/z80em.go @@ -2,7 +2,7 @@ package z80em import "fmt" -const SpDefault uint16 = 0xdff0 +//const SpDefault uint16 = 0xffff // FlagsType - Processor flags type FlagsType struct { @@ -78,9 +78,9 @@ type CPUInterface interface { func (z *Z80Type) Reset() { z.A = 0 z.R = 0 - z.SP = SpDefault + z.SP = 0xff z.PC = 0 - z.setFlagsRegister(0) + z.setFlagsRegister(0xff) // Interrupts disabled z.IMode = 0 z.Iff1 = 0 @@ -180,10 +180,10 @@ func New(memIoRW MemIoRW) *Z80Type { I: 0, R: 0, - SP: SpDefault, + SP: 0xffff, PC: 0, - Flags: FlagsType{false, false, false, false, false, false, false, false}, - FlagsAlt: FlagsType{false, false, false, false, false, false, false, false}, + Flags: FlagsType{true, true, true, true, true, true, true, true}, + FlagsAlt: FlagsType{true, true, true, true, true, true, true, true}, IMode: 0, Iff1: 0, Iff2: 0, @@ -808,8 +808,8 @@ func (z *Z80Type) doCpi() { z.Flags.C = tempCarry fh := z.fhv() - z.Flags.Y = ((z.A-readValue-fh)&0x02)>>1 != 0 - z.Flags.X = ((z.A-readValue-fh)&0x08)>>3 != 0 + z.Flags.Y = ((z.A - readValue - fh) & 0x02) != 0 + z.Flags.X = ((z.A - readValue - fh) & 0x08) != 0 z.incHl() z.decBc() z.Flags.P = (z.B | z.C) != 0 @@ -838,8 +838,8 @@ func (z *Z80Type) doLdd() { z.decHl() z.decBc() z.Flags.P = (z.C | z.B) != 0 - z.Flags.Y = ((z.A+readValue)&0x02)>>1 != 0 - z.Flags.X = ((z.A+readValue)&0x08)>>3 != 0 + z.Flags.Y = ((z.A + readValue) & 0x02) != 0 + z.Flags.X = ((z.A + readValue) & 0x08) != 0 } func (z *Z80Type) doCpd() {