mirror of
https://github.com/romychs/Ocean-240.2-Emulator.git
synced 2026-04-21 11:03:21 +03:00
Test FUSE implemented. c99.z80 passed this test now
This commit is contained in:
parent
db6ca6e0f8
commit
fc91bab922
119
z80/c99/cpu.go
119
z80/c99/cpu.go
@ -2,30 +2,6 @@ package c99
|
||||
|
||||
import "okemu/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)
|
||||
@ -58,11 +34,11 @@ type Z80 struct {
|
||||
int_pending bool
|
||||
nmi_pending bool
|
||||
|
||||
core MemIoRW
|
||||
core z80.MemIoRW
|
||||
}
|
||||
|
||||
// New initializes a Z80 instance and return pointer to it
|
||||
func New(core MemIoRW) *Z80 {
|
||||
func New(core z80.MemIoRW) *Z80 {
|
||||
z := Z80{}
|
||||
z.core = core
|
||||
|
||||
@ -129,28 +105,76 @@ func (z *Z80) RunInstruction() uint64 {
|
||||
return z.cyc - pre
|
||||
}
|
||||
|
||||
func (z *Z80) SetState(state *z80.Z80CPU) {
|
||||
z.cyc = 0
|
||||
z.a = state.A
|
||||
z.b = state.B
|
||||
z.c = state.C
|
||||
z.d = state.D
|
||||
z.e = state.E
|
||||
z.h = state.H
|
||||
z.l = state.L
|
||||
|
||||
z.a_ = state.AAlt
|
||||
z.b_ = state.BAlt
|
||||
z.c_ = state.CAlt
|
||||
z.d_ = state.DAlt
|
||||
z.e_ = state.EAlt
|
||||
z.h_ = state.HAlt
|
||||
z.l_ = state.LAlt
|
||||
|
||||
z.pc = state.PC
|
||||
z.sp = state.SP
|
||||
z.ix = state.IX
|
||||
z.iy = state.IY
|
||||
z.i = state.I
|
||||
z.r = state.R
|
||||
z.mem_ptr = state.MemPtr
|
||||
|
||||
z.sf = state.Flags.S
|
||||
z.zf = state.Flags.Z
|
||||
z.yf = state.Flags.Y
|
||||
z.hf = state.Flags.H
|
||||
z.xf = state.Flags.X
|
||||
z.pf = state.Flags.P
|
||||
z.nf = state.Flags.N
|
||||
z.cf = state.Flags.C
|
||||
|
||||
z.f_ = state.FlagsAlt.GetFlags()
|
||||
|
||||
//z.iff_delay = 0
|
||||
z.interrupt_mode = state.IMode
|
||||
z.iff1 = state.Iff1
|
||||
z.iff2 = state.Iff2
|
||||
z.halted = state.Halted
|
||||
z.int_pending = state.InterruptOccurred
|
||||
z.nmi_pending = false
|
||||
z.int_data = 0
|
||||
}
|
||||
func (z *Z80) GetState() *z80.Z80CPU {
|
||||
return &z80.Z80CPU{
|
||||
A: z.a,
|
||||
B: z.b,
|
||||
C: z.c,
|
||||
D: z.d,
|
||||
E: z.e,
|
||||
H: z.h,
|
||||
L: z.l,
|
||||
AAlt: z.a_,
|
||||
BAlt: z.b_,
|
||||
CAlt: z.c_,
|
||||
DAlt: z.d_,
|
||||
EAlt: z.e_,
|
||||
HAlt: z.h_,
|
||||
LAlt: z.l_,
|
||||
IX: z.ix,
|
||||
IY: z.iy,
|
||||
I: z.i,
|
||||
R: z.r,
|
||||
SP: z.sp,
|
||||
PC: z.pc,
|
||||
A: z.a,
|
||||
B: z.b,
|
||||
C: z.c,
|
||||
D: z.d,
|
||||
E: z.e,
|
||||
H: z.h,
|
||||
L: z.l,
|
||||
AAlt: z.a_,
|
||||
BAlt: z.b_,
|
||||
CAlt: z.c_,
|
||||
DAlt: z.d_,
|
||||
EAlt: z.e_,
|
||||
HAlt: z.h_,
|
||||
LAlt: z.l_,
|
||||
|
||||
IX: z.ix,
|
||||
IY: z.iy,
|
||||
I: z.i,
|
||||
R: z.r,
|
||||
SP: z.sp,
|
||||
PC: z.pc,
|
||||
|
||||
Flags: z.getFlags(),
|
||||
FlagsAlt: z.getAltFlags(),
|
||||
IMode: z.interrupt_mode,
|
||||
@ -160,7 +184,8 @@ func (z *Z80) GetState() *z80.Z80CPU {
|
||||
DoDelayedDI: z.int_pending,
|
||||
DoDelayedEI: z.int_pending,
|
||||
CycleCounter: z.inst_cyc,
|
||||
InterruptOccurred: false,
|
||||
InterruptOccurred: z.int_pending,
|
||||
MemPtr: z.mem_ptr,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -48,8 +48,12 @@ func (z *Z80) cond_ret(condition bool) {
|
||||
}
|
||||
}
|
||||
|
||||
func (z *Z80) jr(displacement byte) {
|
||||
z.pc += uint16(displacement)
|
||||
func (z *Z80) jr(offset byte) {
|
||||
if offset&0x80 != 0 {
|
||||
z.pc += 0xFF00 | uint16(offset)
|
||||
} else {
|
||||
z.pc += uint16(offset)
|
||||
}
|
||||
z.mem_ptr = z.pc
|
||||
}
|
||||
|
||||
@ -393,6 +397,33 @@ func (z *Z80) cpd() {
|
||||
z.mem_ptr -= 2
|
||||
}
|
||||
|
||||
var halfCarrySubTable = []bool{false, false, true, false, true, false, true, true}
|
||||
|
||||
func (z *Z80) cpir() {
|
||||
value := z.rb(z.get_hl())
|
||||
diff := uint8(uint16(z.a) - uint16(value))
|
||||
lookup := ((z.a & 0x08) >> 3) | ((value & 0x08) >> 2) | ((diff & 0x08) >> 1)
|
||||
z.set_bc(z.get_bc() - 1)
|
||||
z.hf = halfCarrySubTable[lookup]
|
||||
z.pf = z.get_bc() != 0 // V=P
|
||||
z.nf = true
|
||||
z.zf = diff == 0
|
||||
z.sf = diff&0x80 != 0
|
||||
if z.hf {
|
||||
diff--
|
||||
}
|
||||
z.xf = (diff & 0x08) != 0
|
||||
z.yf = (diff & 0x02) != 0
|
||||
if z.pf && !z.zf {
|
||||
z.cyc += 5
|
||||
z.pc -= 2
|
||||
z.mem_ptr = z.pc + 1
|
||||
} else {
|
||||
z.mem_ptr++
|
||||
}
|
||||
z.set_hl(z.get_hl() + 1)
|
||||
}
|
||||
|
||||
func (z *Z80) in_r_c(r *byte) {
|
||||
*r = z.core.IORead(z.get_bc())
|
||||
z.zf = *r == 0
|
||||
@ -405,32 +436,88 @@ func (z *Z80) in_r_c(r *byte) {
|
||||
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
|
||||
z.b--
|
||||
|
||||
other := val + z.c + 1
|
||||
if other < val {
|
||||
z.hf = true
|
||||
z.cf = true
|
||||
} else {
|
||||
z.hf = false
|
||||
z.cf = false
|
||||
}
|
||||
z.nf = val&0x80 != 0
|
||||
z.pf = parity((other & 0x07) ^ z.b)
|
||||
z.sf = z.b&0x80 != 0
|
||||
z.zf = z.b == 0
|
||||
z.updateXY(z.b)
|
||||
z.set_hl(z.get_hl() + 1)
|
||||
}
|
||||
|
||||
func (z *Z80) ind() {
|
||||
z.ini()
|
||||
z.set_hl(z.get_hl() - 2)
|
||||
z.mem_ptr = z.get_bc() - 2
|
||||
val := z.core.IORead(z.get_bc())
|
||||
z.wb(z.get_hl(), val)
|
||||
z.mem_ptr = z.get_bc() - 1
|
||||
z.b--
|
||||
|
||||
other := val + z.c - 1
|
||||
z.nf = val&0x80 != 0
|
||||
if other < val {
|
||||
z.hf = true
|
||||
z.cf = true
|
||||
} else {
|
||||
z.hf = false
|
||||
z.cf = false
|
||||
}
|
||||
z.pf = parity((other & 0x07) ^ z.b)
|
||||
|
||||
z.sf = z.b&0x80 != 0
|
||||
z.zf = z.b == 0
|
||||
z.updateXY(z.b)
|
||||
z.set_hl(z.get_hl() - 1)
|
||||
}
|
||||
|
||||
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
|
||||
val := z.rb(z.get_hl())
|
||||
z.b--
|
||||
z.mem_ptr = z.get_bc() + 1
|
||||
z.core.IOWrite(z.get_bc(), val)
|
||||
z.set_hl(z.get_hl() + 1)
|
||||
other := val + z.l
|
||||
z.nf = val&0x80 != 0
|
||||
if other < val {
|
||||
z.hf = true
|
||||
z.cf = true
|
||||
} else {
|
||||
z.hf = false
|
||||
z.cf = false
|
||||
}
|
||||
z.pf = parity((other & 0x07) ^ z.b)
|
||||
z.zf = z.b == 0
|
||||
z.sf = z.b&0x80 != 0
|
||||
z.updateXY(z.b)
|
||||
}
|
||||
|
||||
func (z *Z80) outd() {
|
||||
z.outi()
|
||||
z.set_hl(z.get_hl() - 2)
|
||||
z.mem_ptr = z.get_bc() - 2
|
||||
val := z.rb(z.get_hl())
|
||||
z.b--
|
||||
z.mem_ptr = z.get_bc() - 1
|
||||
z.core.IOWrite(z.get_bc(), val)
|
||||
z.set_hl(z.get_hl() - 1)
|
||||
other := val + z.l
|
||||
z.nf = val&0x80 != 0
|
||||
if other < val {
|
||||
z.hf = true
|
||||
z.cf = true
|
||||
} else {
|
||||
z.hf = false
|
||||
z.cf = false
|
||||
}
|
||||
z.pf = parity((other & 0x07) ^ z.b)
|
||||
z.zf = z.b == 0
|
||||
z.sf = z.b&0x80 != 0
|
||||
z.updateXY(z.b)
|
||||
}
|
||||
|
||||
func (z *Z80) daa() {
|
||||
@ -468,8 +555,14 @@ func (z *Z80) daa() {
|
||||
z.updateXY(z.a)
|
||||
}
|
||||
|
||||
func (z *Z80) displace(base_addr uint16, displacement byte) uint16 {
|
||||
addr := base_addr + uint16(displacement)
|
||||
func (z *Z80) displace(base_addr uint16, offset byte) uint16 {
|
||||
addr := base_addr
|
||||
if offset&0x80 == 0x80 {
|
||||
addr += 0xff00 | uint16(offset)
|
||||
} else {
|
||||
addr += uint16(offset)
|
||||
}
|
||||
//addr := base_addr + uint16(displacement)
|
||||
z.mem_ptr = addr
|
||||
return addr
|
||||
}
|
||||
@ -841,7 +934,7 @@ func (z *Z80) exec_opcode(opcode byte) {
|
||||
case 0x00: // nop
|
||||
case 0x76:
|
||||
z.halted = true // halt
|
||||
|
||||
z.pc--
|
||||
case 0x3C:
|
||||
z.a = z.inc(z.a) // inc a
|
||||
case 0x04:
|
||||
@ -907,13 +1000,13 @@ func (z *Z80) exec_opcode(opcode byte) {
|
||||
z.cf = true
|
||||
z.nf = false
|
||||
z.hf = false
|
||||
z.updateXY(z.a)
|
||||
z.updateXY(z.a | z.get_f())
|
||||
case 0x3F:
|
||||
// ccf
|
||||
z.hf = z.cf
|
||||
z.cf = !z.cf
|
||||
z.nf = false
|
||||
z.updateXY(z.a)
|
||||
z.updateXY(z.a | z.get_f())
|
||||
case 0x07:
|
||||
// rlca (rotate left)
|
||||
z.cf = z.a&0x80 != 0
|
||||
@ -1044,6 +1137,7 @@ func (z *Z80) exec_opcode(opcode byte) {
|
||||
z.cond_jr(z.b != 0) // djnz *
|
||||
case 0x18:
|
||||
z.pc += uint16(z.nextb()) // jr *
|
||||
z.mem_ptr = z.pc
|
||||
case 0x20:
|
||||
z.cond_jr(!z.zf) // jr nz, *
|
||||
case 0x28:
|
||||
@ -1133,15 +1227,14 @@ func (z *Z80) exec_opcode(opcode byte) {
|
||||
z.set_f(byte(val))
|
||||
case 0xDB:
|
||||
// in a,(n)
|
||||
port := uint16(z.nextb())
|
||||
a := z.a
|
||||
port := (uint16(z.a) << 8) | uint16(z.nextb())
|
||||
z.a = z.core.IORead(port)
|
||||
z.mem_ptr = (uint16(a) << 8) | uint16(z.a+1)
|
||||
z.mem_ptr = port + 1 // (uint16(a) << 8) | (uint16(z.a+1) & 0x00ff)
|
||||
case 0xD3:
|
||||
// out (n), a
|
||||
port := uint16(z.nextb())
|
||||
z.core.IOWrite(port, z.a)
|
||||
z.mem_ptr = (port + 1) | (uint16(z.a) << 8)
|
||||
z.mem_ptr = ((port + 1) & 0x00ff) | (uint16(z.a) << 8)
|
||||
case 0x08:
|
||||
// ex af,af'
|
||||
a := z.a
|
||||
|
||||
@ -10,10 +10,10 @@ func (z *Z80) exec_opcode_ddfd(opcode byte, iz *uint16) {
|
||||
*iz = z.popw() // pop iz
|
||||
case 0xE5:
|
||||
z.pushw(*iz) // push iz
|
||||
|
||||
case 0xE9:
|
||||
z.jump(*iz) // jp iz
|
||||
|
||||
// jp iz
|
||||
z.pc = *iz
|
||||
//z.jump(*iz)
|
||||
case 0x09:
|
||||
z.addiz(iz, z.get_bc()) // add iz,bc
|
||||
case 0x19:
|
||||
@ -94,9 +94,13 @@ func (z *Z80) exec_opcode_ddfd(opcode byte, iz *uint16) {
|
||||
case 0x2D:
|
||||
*iz = (*iz & 0xff00) | uint16(z.dec(byte(*iz))) // dec izl
|
||||
case 0x2A:
|
||||
*iz = z.rw(z.nextw()) // ld iz,(**)
|
||||
addr := z.nextw()
|
||||
*iz = z.rw(addr) // ld iz,(**)
|
||||
z.mem_ptr = addr + 1
|
||||
case 0x22:
|
||||
z.ww(z.nextw(), *iz) // ld (**),iz
|
||||
addr := z.nextw()
|
||||
z.ww(addr, *iz) // ld (**),iz
|
||||
z.mem_ptr = addr + 1
|
||||
case 0x21:
|
||||
*iz = z.nextw() // ld iz,**
|
||||
case 0x36:
|
||||
|
||||
@ -19,6 +19,7 @@ func (z *Z80) exec_opcode_ed(opcode byte) {
|
||||
z.hf = false
|
||||
z.nf = false
|
||||
z.pf = z.iff2
|
||||
z.updateXY(z.a)
|
||||
case 0x5F:
|
||||
// ld a,r
|
||||
z.a = z.r
|
||||
@ -65,47 +66,61 @@ func (z *Z80) exec_opcode_ed(opcode byte) {
|
||||
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
|
||||
// cpir
|
||||
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++
|
||||
}
|
||||
//z.cpir()
|
||||
case 0xB9:
|
||||
{
|
||||
z.cpd()
|
||||
if z.get_bc() != 0 && !z.zf {
|
||||
z.pc -= 2
|
||||
z.cyc += 5
|
||||
} else {
|
||||
z.mem_ptr += 1
|
||||
}
|
||||
} // cpdr
|
||||
|
||||
// cpdr
|
||||
z.cpd()
|
||||
if z.get_bc() != 0 && !z.zf {
|
||||
z.pc -= 2
|
||||
z.cyc += 5
|
||||
z.mem_ptr = z.pc + 1
|
||||
} else {
|
||||
//z.mem_ptr++
|
||||
}
|
||||
case 0x40:
|
||||
z.in_r_c(&z.b) // in b, (c)
|
||||
z.mem_ptr = z.get_bc() + 1
|
||||
case 0x48:
|
||||
z.mem_ptr = z.get_bc() + 1
|
||||
z.in_r_c(&z.c) // in c, (c)
|
||||
z.updateXY(z.c)
|
||||
//case 0x4e:
|
||||
// ld c,(iy+dd)
|
||||
|
||||
case 0x50:
|
||||
z.in_r_c(&z.d) // in d, (c)
|
||||
z.mem_ptr = z.get_bc() + 1
|
||||
case 0x58:
|
||||
z.in_r_c(&z.e) // in e, (c)
|
||||
// in e, (c)
|
||||
z.in_r_c(&z.e)
|
||||
z.mem_ptr = z.get_bc() + 1
|
||||
z.updateXY(z.e)
|
||||
case 0x60:
|
||||
z.in_r_c(&z.h) // in h, (c)
|
||||
z.mem_ptr = z.get_bc() + 1
|
||||
case 0x68:
|
||||
z.in_r_c(&z.l) // in l, (c)
|
||||
z.mem_ptr = z.get_bc() + 1
|
||||
z.updateXY(z.l)
|
||||
case 0x70:
|
||||
// in (c)
|
||||
var val byte
|
||||
z.in_r_c(&val)
|
||||
z.mem_ptr = z.get_bc() + 1
|
||||
case 0x78:
|
||||
// in a, (c)
|
||||
z.in_r_c(&z.a)
|
||||
z.mem_ptr = z.get_bc() + 1
|
||||
z.updateXY(z.a)
|
||||
case 0xA2:
|
||||
z.ini() // ini
|
||||
case 0xB2:
|
||||
@ -116,7 +131,8 @@ func (z *Z80) exec_opcode_ed(opcode byte) {
|
||||
z.cyc += 5
|
||||
}
|
||||
case 0xAA:
|
||||
z.ind() // ind
|
||||
// ind
|
||||
z.ind()
|
||||
case 0xBA:
|
||||
// indr
|
||||
z.ind()
|
||||
@ -126,18 +142,25 @@ func (z *Z80) exec_opcode_ed(opcode byte) {
|
||||
}
|
||||
case 0x41:
|
||||
z.core.IOWrite(z.get_bc(), z.b) // out (c), b
|
||||
z.mem_ptr = z.get_bc() + 1
|
||||
case 0x49:
|
||||
z.core.IOWrite(z.get_bc(), z.c) // out (c), c
|
||||
z.mem_ptr = z.get_bc() + 1
|
||||
case 0x51:
|
||||
z.core.IOWrite(z.get_bc(), z.d) // out (c), d
|
||||
z.mem_ptr = z.get_bc() + 1
|
||||
case 0x59:
|
||||
z.core.IOWrite(z.get_bc(), z.e) // out (c), e
|
||||
z.mem_ptr = z.get_bc() + 1
|
||||
case 0x61:
|
||||
z.core.IOWrite(z.get_bc(), z.h) // out (c), h
|
||||
z.mem_ptr = z.get_bc() + 1
|
||||
case 0x69:
|
||||
z.core.IOWrite(z.get_bc(), z.l) // out (c), l
|
||||
z.mem_ptr = z.get_bc() + 1
|
||||
case 0x71:
|
||||
z.core.IOWrite(z.get_bc(), 0) // out (c), 0
|
||||
z.mem_ptr = z.get_bc() + 1
|
||||
case 0x79:
|
||||
// out (c), a
|
||||
z.core.IOWrite(z.get_bc(), z.a)
|
||||
@ -157,6 +180,7 @@ func (z *Z80) exec_opcode_ed(opcode byte) {
|
||||
// otdr
|
||||
z.outd()
|
||||
if z.b > 0 {
|
||||
z.cyc += 5
|
||||
z.pc -= 2
|
||||
}
|
||||
|
||||
@ -218,7 +242,7 @@ func (z *Z80) exec_opcode_ed(opcode byte) {
|
||||
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:
|
||||
case 0x46, 0x4e, 0x66, 0x6e:
|
||||
z.interrupt_mode = 0 // im 0
|
||||
case 0x56, 0x76:
|
||||
z.interrupt_mode = 1 // im 1
|
||||
|
||||
54
z80/cpu.go
54
z80/cpu.go
@ -70,5 +70,59 @@ type Z80CPU struct {
|
||||
DoDelayedEI bool
|
||||
CycleCounter byte
|
||||
InterruptOccurred bool
|
||||
MemPtr uint16
|
||||
//core MemIoRW
|
||||
}
|
||||
|
||||
func (f *FlagsType) GetFlags() byte {
|
||||
var flags byte = 0
|
||||
if f.S {
|
||||
flags |= 0x80
|
||||
}
|
||||
if f.Z {
|
||||
flags |= 0x40
|
||||
}
|
||||
if f.Y {
|
||||
flags |= 0x20
|
||||
}
|
||||
if f.H {
|
||||
flags |= 0x10
|
||||
}
|
||||
if f.X {
|
||||
flags |= 0x08
|
||||
}
|
||||
if f.P {
|
||||
flags |= 0x04
|
||||
}
|
||||
if f.N {
|
||||
flags |= 0x02
|
||||
}
|
||||
if f.C {
|
||||
flags |= 0x01
|
||||
}
|
||||
return flags
|
||||
}
|
||||
|
||||
func GetFlags(f byte) FlagsType {
|
||||
return FlagsType{
|
||||
S: f&0x80 != 0,
|
||||
Z: f&0x40 != 0,
|
||||
Y: f&0x20 != 0,
|
||||
H: f&0x10 != 0,
|
||||
X: f&0x08 != 0,
|
||||
P: f&0x04 != 0,
|
||||
N: f&0x02 != 0,
|
||||
C: f&0x01 != 0,
|
||||
}
|
||||
}
|
||||
|
||||
func (f *FlagsType) SetFlags(flags byte) {
|
||||
f.S = flags&0x80 != 0
|
||||
f.Z = flags&0x40 != 0
|
||||
f.Y = flags&0x20 != 0
|
||||
f.H = flags&0x10 != 0
|
||||
f.X = flags&0x08 != 0
|
||||
f.P = flags&0x04 != 0
|
||||
f.N = flags&0x02 != 0
|
||||
f.C = flags&0x01 != 0
|
||||
}
|
||||
|
||||
611
z80/cpu_test.go
Normal file
611
z80/cpu_test.go
Normal file
@ -0,0 +1,611 @@
|
||||
package z80_test
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"okemu/z80"
|
||||
"okemu/z80/c99"
|
||||
|
||||
// "okemu/z80/c99"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const (
|
||||
ScanNone int = iota
|
||||
ScanDescr
|
||||
ScanEvent
|
||||
ScanRegs
|
||||
ScanState
|
||||
ScanMem
|
||||
ScanEnd
|
||||
)
|
||||
|
||||
type Registers struct {
|
||||
AF uint16
|
||||
BC uint16
|
||||
DE uint16
|
||||
HL uint16
|
||||
AFa uint16
|
||||
BCa uint16
|
||||
DEa uint16
|
||||
HLa uint16
|
||||
IX uint16
|
||||
IY uint16
|
||||
SP uint16
|
||||
PC uint16
|
||||
MemPtr uint16
|
||||
}
|
||||
|
||||
type State struct {
|
||||
I byte
|
||||
R byte
|
||||
IFF1 bool
|
||||
IFF2 bool
|
||||
IM byte
|
||||
isHalted bool
|
||||
tStates uint16
|
||||
}
|
||||
|
||||
type MemorySetup struct {
|
||||
addr uint16
|
||||
values []byte
|
||||
}
|
||||
|
||||
type Event struct {
|
||||
time uint16
|
||||
typ string
|
||||
addr uint16
|
||||
data byte
|
||||
}
|
||||
|
||||
type Z80TestIn struct {
|
||||
// description string
|
||||
registers Registers
|
||||
state State
|
||||
memorySetup []MemorySetup
|
||||
}
|
||||
|
||||
type Z80TestExpected struct {
|
||||
expect Expect
|
||||
}
|
||||
|
||||
type Expect struct {
|
||||
events []Event
|
||||
registers Registers
|
||||
state State
|
||||
memory []MemorySetup
|
||||
}
|
||||
|
||||
//go:embed tests.in
|
||||
var testIn []byte
|
||||
|
||||
//go:embed tests.expected
|
||||
var testExpected []byte
|
||||
|
||||
type Computer struct {
|
||||
cpu *c99.Z80
|
||||
memory [65536]byte
|
||||
ports [256]byte
|
||||
}
|
||||
|
||||
var z80TestsIn map[string]Z80TestIn
|
||||
var z80TestsExpected map[string]Expect
|
||||
|
||||
var computer Computer
|
||||
|
||||
var testNames []string
|
||||
|
||||
//var z80 *c99.Z80
|
||||
|
||||
func init() {
|
||||
z80TestsIn = make(map[string]Z80TestIn)
|
||||
z80TestsExpected = make(map[string]Expect)
|
||||
parseTestIn()
|
||||
parseTestExpected()
|
||||
for addr := 0; addr < 65535; addr++ {
|
||||
computer.memory[addr] = 0x00
|
||||
}
|
||||
for addr := 0; addr < 255; addr++ {
|
||||
computer.ports[addr] = 0
|
||||
}
|
||||
computer.cpu = c99.New(&computer)
|
||||
}
|
||||
|
||||
func (c *Computer) M1MemRead(addr uint16) byte {
|
||||
return c.memory[addr]
|
||||
}
|
||||
|
||||
func (c *Computer) MemRead(addr uint16) byte {
|
||||
return c.memory[addr]
|
||||
}
|
||||
|
||||
func (c *Computer) MemWrite(addr uint16, val byte) {
|
||||
c.memory[addr] = val
|
||||
}
|
||||
|
||||
func (c *Computer) IOWrite(port uint16, val byte) {
|
||||
c.ports[port&0x00ff] = val
|
||||
}
|
||||
|
||||
func (c *Computer) IORead(port uint16) byte {
|
||||
return byte(port >> 8) //c.ports[port&0x00ff]
|
||||
}
|
||||
|
||||
func parseTestExpected() {
|
||||
exScanner := bufio.NewScanner(bytes.NewReader(testExpected))
|
||||
scanState := ScanNone
|
||||
|
||||
testName := ""
|
||||
var events []Event
|
||||
registers := Registers{}
|
||||
state := State{}
|
||||
var memorySetup []MemorySetup
|
||||
|
||||
for exScanner.Scan() {
|
||||
line := exScanner.Text()
|
||||
if len(strings.TrimSpace(line)) == 0 {
|
||||
if scanState == ScanMem {
|
||||
scanState = ScanEnd
|
||||
} else {
|
||||
scanState = ScanNone
|
||||
continue
|
||||
}
|
||||
}
|
||||
if ScanNone == scanState {
|
||||
scanState = ScanDescr
|
||||
} else if len(line) > 0 && line[0] == ' ' {
|
||||
scanState = ScanEvent
|
||||
}
|
||||
//else {
|
||||
// if scanState == ScanEvent {
|
||||
// scanState = ScanRegs
|
||||
// }
|
||||
//}
|
||||
|
||||
switch scanState {
|
||||
case ScanDescr:
|
||||
testName = line
|
||||
scanState = ScanRegs
|
||||
case ScanEvent:
|
||||
events = append([]Event{}, *parseEvent(line))
|
||||
scanState = ScanRegs
|
||||
case ScanRegs:
|
||||
registers = *parseRegisters(line)
|
||||
scanState = ScanState
|
||||
case ScanState:
|
||||
state = *parseState(line)
|
||||
scanState = ScanMem
|
||||
case ScanMem:
|
||||
memorySetup = append(memorySetup, *parseMemory(line))
|
||||
//scanState = ScanMem
|
||||
case ScanEnd:
|
||||
z80TestsExpected[testName] = Expect{
|
||||
events: events,
|
||||
registers: registers,
|
||||
state: state,
|
||||
memory: memorySetup,
|
||||
}
|
||||
events = []Event{}
|
||||
memorySetup = []MemorySetup{}
|
||||
scanState = ScanNone
|
||||
default:
|
||||
panic("unhandled default case")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func parseTestIn() {
|
||||
inScanner := bufio.NewScanner(bytes.NewReader(testIn))
|
||||
scanState := ScanNone
|
||||
testName := ""
|
||||
registers := Registers{}
|
||||
state := State{}
|
||||
var memorySetup []MemorySetup
|
||||
for inScanner.Scan() {
|
||||
line := inScanner.Text()
|
||||
if len(line) == 0 || strings.TrimSpace(line) == "" {
|
||||
scanState = ScanNone
|
||||
continue
|
||||
}
|
||||
if ScanNone == scanState {
|
||||
scanState = ScanDescr
|
||||
} else if line == "-1" {
|
||||
scanState = ScanEnd
|
||||
}
|
||||
switch scanState {
|
||||
case ScanDescr:
|
||||
testName = line
|
||||
scanState = ScanRegs
|
||||
case ScanRegs:
|
||||
registers = *parseRegisters(line)
|
||||
scanState = ScanState
|
||||
case ScanState:
|
||||
state = *parseState(line)
|
||||
scanState = ScanMem
|
||||
case ScanMem:
|
||||
memorySetup = append(memorySetup, *parseMemory(line))
|
||||
//scanState = ScanMem
|
||||
case ScanEnd:
|
||||
test := Z80TestIn{
|
||||
registers: registers,
|
||||
state: state,
|
||||
memorySetup: memorySetup,
|
||||
}
|
||||
testNames = append(testNames, testName)
|
||||
z80TestsIn[testName] = test
|
||||
scanState = ScanNone
|
||||
default:
|
||||
panic("unhandled default case")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func parseEvent(event string) *Event {
|
||||
e := Event{}
|
||||
line := strings.TrimSpace(event)
|
||||
//4 MR 0000 00
|
||||
//012345678
|
||||
if len(line) < 9 {
|
||||
log.Errorf("Invalid event: %s", line)
|
||||
return nil
|
||||
}
|
||||
sp := strings.Index(line, " ")
|
||||
if sp == -1 {
|
||||
log.Errorf("Invalid event: %s", line)
|
||||
return nil
|
||||
}
|
||||
e.time = parseDecW(line[:sp])
|
||||
e.typ = line[sp+1 : sp+3]
|
||||
e.addr = parseHexW(line[sp+4 : sp+8])
|
||||
if len(line) > sp+9 {
|
||||
//println("Event: ", line)
|
||||
|
||||
e.data = parseHexB(line[sp+9:])
|
||||
}
|
||||
|
||||
return &e
|
||||
}
|
||||
|
||||
func parseMemory(line string) *MemorySetup {
|
||||
m := MemorySetup{}
|
||||
//0000 00 -1
|
||||
//0123456789
|
||||
m.addr = parseHexW(line[0:4])
|
||||
mem := line[5:]
|
||||
for {
|
||||
st := mem[:2]
|
||||
if st == "-1" {
|
||||
break
|
||||
}
|
||||
m.values = append(m.values, parseHexB(st))
|
||||
mem = strings.TrimSpace(mem[2:])
|
||||
}
|
||||
return &m
|
||||
}
|
||||
|
||||
func parseRegisters(line string) *Registers {
|
||||
r := Registers{}
|
||||
line = strings.TrimSpace(line)
|
||||
if len(line) != 64 {
|
||||
log.Errorf("Invalid register line: %s", line)
|
||||
} else {
|
||||
// 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
|
||||
for ctr := 0; ctr < 13; ctr++ {
|
||||
hexString := line[ctr*5 : ctr*5+4]
|
||||
v, err := strconv.ParseUint(hexString, 16, 16)
|
||||
if err != nil {
|
||||
log.Errorf("Invalid register value: %s in line %s", hexString, line)
|
||||
break
|
||||
}
|
||||
switch ctr {
|
||||
case 0:
|
||||
r.AF = uint16(v)
|
||||
case 1:
|
||||
r.BC = uint16(v)
|
||||
case 2:
|
||||
r.DE = uint16(v)
|
||||
case 3:
|
||||
r.HL = uint16(v)
|
||||
case 4:
|
||||
r.AFa = uint16(v)
|
||||
case 5:
|
||||
r.BCa = uint16(v)
|
||||
case 6:
|
||||
r.DEa = uint16(v)
|
||||
case 7:
|
||||
r.HLa = uint16(v)
|
||||
case 8:
|
||||
r.IX = uint16(v)
|
||||
case 9:
|
||||
r.IY = uint16(v)
|
||||
case 10:
|
||||
r.SP = uint16(v)
|
||||
case 11:
|
||||
r.PC = uint16(v)
|
||||
case 12:
|
||||
r.MemPtr = uint16(v)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return &r
|
||||
}
|
||||
|
||||
func parseHexB(line string) byte {
|
||||
v, err := strconv.ParseUint(line, 16, 8)
|
||||
if err != nil {
|
||||
log.Errorf("Invalid HexB value: %s", line)
|
||||
}
|
||||
return byte(v)
|
||||
}
|
||||
|
||||
func parseHexW(line string) uint16 {
|
||||
v, err := strconv.ParseUint(line, 16, 16)
|
||||
if err != nil {
|
||||
log.Errorf("Invalid HexW value: %s", line)
|
||||
}
|
||||
return uint16(v)
|
||||
}
|
||||
|
||||
func parseDecB(line string) byte {
|
||||
v, err := strconv.ParseUint(line, 10, 8)
|
||||
if err != nil {
|
||||
log.Errorf("Invalid B value: %s", line)
|
||||
}
|
||||
return byte(v)
|
||||
}
|
||||
|
||||
func parseDecW(line string) uint16 {
|
||||
v, err := strconv.ParseUint(line, 10, 16)
|
||||
if err != nil {
|
||||
log.Errorf("Invalid W value: %s", line)
|
||||
}
|
||||
return uint16(v)
|
||||
}
|
||||
|
||||
func parseBoolB(line string) bool {
|
||||
v, err := strconv.ParseUint(line, 10, 8)
|
||||
if err != nil {
|
||||
log.Errorf("Invalid state I value: %s", line)
|
||||
}
|
||||
return v != 0
|
||||
}
|
||||
|
||||
func parseState(line string) *State {
|
||||
s := State{}
|
||||
line = strings.TrimSpace(line)
|
||||
if len(line) < 15 {
|
||||
log.Errorf("Invalid state line: %s", line)
|
||||
} else {
|
||||
//00 00 0 0 0 0 1
|
||||
//0123456789012345678
|
||||
s.I = parseHexB(line[0:2])
|
||||
s.R = parseHexB(line[3:5])
|
||||
s.IFF1 = parseBoolB(line[6:7])
|
||||
s.IFF2 = parseBoolB(line[8:9])
|
||||
s.IM = parseDecB(line[10:11])
|
||||
s.isHalted = parseBoolB(line[12:13])
|
||||
s.tStates = parseDecW(strings.TrimSpace(line[13:]))
|
||||
}
|
||||
return &s
|
||||
}
|
||||
|
||||
func TestZ80Fuse(t *testing.T) {
|
||||
t.Logf("Fuse-type Z80 emulator test")
|
||||
computer.cpu.Reset()
|
||||
for _, name := range testNames {
|
||||
z80Test := z80TestsIn[name]
|
||||
//t.Logf("Run test: %s", name)
|
||||
setComputerState(z80Test)
|
||||
//if name == "edb9" {
|
||||
// t.Logf("stop test")
|
||||
//}
|
||||
|
||||
exp, exists := z80TestsExpected[name]
|
||||
if !exists {
|
||||
t.Errorf("Expected values for test %s not exists!", name)
|
||||
return
|
||||
}
|
||||
cy := uint64(0)
|
||||
for {
|
||||
cy += computer.cpu.RunInstruction()
|
||||
if cy >= uint64(exp.state.tStates) {
|
||||
break
|
||||
}
|
||||
}
|
||||
checkComputerState(t, name)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func setComputerState(test Z80TestIn) {
|
||||
state := z80.Z80CPU{
|
||||
A: byte(test.registers.AF >> 8),
|
||||
B: byte(test.registers.BC >> 8),
|
||||
C: byte(test.registers.BC),
|
||||
D: byte(test.registers.DE >> 8),
|
||||
E: byte(test.registers.DE),
|
||||
H: byte(test.registers.HL >> 8),
|
||||
L: byte(test.registers.HL),
|
||||
AAlt: byte(test.registers.AFa >> 8),
|
||||
BAlt: byte(test.registers.BCa >> 8),
|
||||
CAlt: byte(test.registers.BCa),
|
||||
DAlt: byte(test.registers.DEa >> 8),
|
||||
EAlt: byte(test.registers.DEa),
|
||||
HAlt: byte(test.registers.HLa >> 8),
|
||||
LAlt: byte(test.registers.HLa),
|
||||
IX: test.registers.IX,
|
||||
IY: test.registers.IY,
|
||||
I: test.state.I,
|
||||
R: test.state.R,
|
||||
SP: test.registers.SP,
|
||||
PC: test.registers.PC,
|
||||
Flags: z80.GetFlags(byte(test.registers.AF)),
|
||||
FlagsAlt: z80.GetFlags(byte(test.registers.AFa)),
|
||||
IMode: test.state.IM,
|
||||
Iff1: test.state.IFF1,
|
||||
Iff2: test.state.IFF2,
|
||||
Halted: test.state.isHalted,
|
||||
DoDelayedDI: false,
|
||||
DoDelayedEI: false,
|
||||
CycleCounter: 0,
|
||||
InterruptOccurred: false,
|
||||
MemPtr: test.registers.MemPtr,
|
||||
}
|
||||
|
||||
// Setup CPU
|
||||
computer.cpu.SetState(&state)
|
||||
|
||||
// Setup Memory
|
||||
for _, ms := range test.memorySetup {
|
||||
addr := ms.addr
|
||||
for _, b := range ms.values {
|
||||
computer.memory[addr] = b
|
||||
addr++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func lo(w uint16) byte {
|
||||
return byte(w)
|
||||
}
|
||||
|
||||
func hi(w uint16) byte {
|
||||
return byte(w >> 8)
|
||||
}
|
||||
|
||||
func checkComputerState(t *testing.T, name string) {
|
||||
state := computer.cpu.GetState()
|
||||
exp, exists := z80TestsExpected[name]
|
||||
if !exists {
|
||||
t.Errorf("Expected values for test %s not exists!", name)
|
||||
return
|
||||
}
|
||||
|
||||
// A,B,C,D,E,H,L
|
||||
if hi(exp.registers.AF) != state.A {
|
||||
t.Errorf("%s: Expected A to be %x, got %x", name, hi(exp.registers.AF), state.A)
|
||||
}
|
||||
if hi(exp.registers.BC) != state.B {
|
||||
t.Errorf("%s: Expected B to be %x, got %x", name, hi(exp.registers.BC), state.B)
|
||||
computer.cpu.DebugOutput()
|
||||
}
|
||||
|
||||
if lo(exp.registers.BC) != state.C {
|
||||
t.Errorf("%s: Expected C to be %x, got %x", name, lo(exp.registers.BC), state.C)
|
||||
}
|
||||
|
||||
if hi(exp.registers.DE) != state.D {
|
||||
t.Errorf("%s: Expected D to be %x, got %x", name, hi(exp.registers.DE), state.D)
|
||||
}
|
||||
|
||||
if lo(exp.registers.DE) != state.E {
|
||||
t.Errorf("%s: Expected E to be %x, got %x", name, lo(exp.registers.DE), state.E)
|
||||
}
|
||||
|
||||
if hi(exp.registers.HL) != state.H {
|
||||
t.Errorf("%s: Expected H to be %x, got %x", name, hi(exp.registers.HL), state.H)
|
||||
}
|
||||
|
||||
if lo(exp.registers.HL) != state.L {
|
||||
t.Errorf("%s: Expected L to be %x, got %x", name, lo(exp.registers.BC), state.L)
|
||||
}
|
||||
|
||||
// Alt A,B,C,D,E,H,L
|
||||
if hi(exp.registers.AFa) != state.AAlt {
|
||||
t.Errorf("%s: Expected A' to be %x, got %x", name, hi(exp.registers.AFa), state.AAlt)
|
||||
}
|
||||
if hi(exp.registers.BCa) != state.BAlt {
|
||||
t.Errorf("%s: Expected B' to be %x, got %x", name, hi(exp.registers.BCa), state.BAlt)
|
||||
}
|
||||
|
||||
if lo(exp.registers.BCa) != state.CAlt {
|
||||
t.Errorf("%s: Expected C' to be %x, got %x", name, lo(exp.registers.BCa), state.CAlt)
|
||||
}
|
||||
|
||||
if hi(exp.registers.DEa) != state.DAlt {
|
||||
t.Errorf("%s: Expected D' to be %x, got %x", name, hi(exp.registers.DEa), state.DAlt)
|
||||
}
|
||||
|
||||
if lo(exp.registers.DEa) != state.EAlt {
|
||||
t.Errorf("%s: Expected E' to be %x, got %x", name, lo(exp.registers.DEa), state.EAlt)
|
||||
}
|
||||
|
||||
if hi(exp.registers.HLa) != state.HAlt {
|
||||
t.Errorf("%s: Expected H' to be %x, got %x", name, hi(exp.registers.HLa), state.HAlt)
|
||||
}
|
||||
|
||||
if lo(exp.registers.HLa) != state.LAlt {
|
||||
t.Errorf("%s: Expected L' to be %x, got %x", name, lo(exp.registers.BCa), state.LAlt)
|
||||
}
|
||||
|
||||
// 16b regs PC, SP, Meme, R, I
|
||||
|
||||
if exp.registers.IX != state.IX {
|
||||
t.Errorf("%s: Expected IX to be %x, got %x", name, exp.registers.IX, state.IX)
|
||||
}
|
||||
|
||||
if exp.registers.IY != state.IY {
|
||||
t.Errorf("%s: Expected IX to be %x, got %x", name, exp.registers.IX, state.IX)
|
||||
}
|
||||
|
||||
if exp.registers.PC != state.PC {
|
||||
t.Errorf("%s: Expected PC to be %x, got %x", name, exp.registers.PC, state.PC)
|
||||
}
|
||||
|
||||
if exp.registers.SP != state.SP {
|
||||
t.Errorf("%s: Expected SP to be %x, got %x", name, exp.registers.SP, state.SP)
|
||||
}
|
||||
|
||||
if exp.registers.MemPtr != state.MemPtr {
|
||||
t.Errorf("%s: Expected MemPtr to be %x, got %x", name, exp.registers.MemPtr, state.MemPtr)
|
||||
}
|
||||
|
||||
// State
|
||||
if exp.state.I != state.I {
|
||||
t.Errorf("%s: Expected I to be %x, got %x", name, exp.state.I, state.I)
|
||||
}
|
||||
|
||||
if exp.state.IM != state.IMode {
|
||||
t.Errorf("%s: Expected IM to be %d, got %d", name, exp.state.IM, state.IMode)
|
||||
}
|
||||
|
||||
if exp.state.IFF1 != state.Iff1 {
|
||||
t.Errorf("%s: Expected IIF1 to be %t, got %t", name, exp.state.IFF1, state.Iff1)
|
||||
}
|
||||
|
||||
if exp.state.IFF2 != state.Iff2 {
|
||||
t.Errorf("%s: Expected IIF2 to be %t, got %t", name, exp.state.IFF2, state.Iff2)
|
||||
}
|
||||
|
||||
if exp.state.isHalted != state.Halted {
|
||||
t.Errorf("%s: Expected isHalted to be %t, got %t", name, exp.state.isHalted, state.Halted)
|
||||
}
|
||||
|
||||
// FLAGS
|
||||
if lo(exp.registers.AF) != state.Flags.GetFlags() {
|
||||
t.Errorf("%s: Expected Flags to be %08b, got %08b", name, lo(exp.registers.AF), state.Flags.GetFlags())
|
||||
}
|
||||
|
||||
if lo(exp.registers.AFa) != state.FlagsAlt.GetFlags() {
|
||||
t.Errorf("%s: Expected Flags' to be %08b, got %08b", name, lo(exp.registers.AFa), state.FlagsAlt.GetFlags())
|
||||
}
|
||||
|
||||
// Check memory
|
||||
for _, mExpect := range exp.memory {
|
||||
addr := mExpect.addr
|
||||
for _, b := range mExpect.values {
|
||||
if computer.memory[addr] != b {
|
||||
t.Errorf("%s: Expected memory[%x] to be %x, got %x", name, addr, b, computer.memory[addr])
|
||||
}
|
||||
addr++
|
||||
}
|
||||
}
|
||||
}
|
||||
112
z80/js/cpu.go
112
z80/js/cpu.go
@ -7,7 +7,6 @@ import (
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// const SpDefault uint16 = 0xffff
|
||||
type Z80 struct {
|
||||
z80.Z80CPU
|
||||
core z80.MemIoRW
|
||||
@ -18,7 +17,7 @@ func (z *Z80) Reset() {
|
||||
z.R = 0
|
||||
z.SP = 0xff
|
||||
z.PC = 0
|
||||
z.setFlagsRegister(0xff)
|
||||
z.Flags.SetFlags(0xff)
|
||||
// Interrupts disabled
|
||||
z.IMode = 0
|
||||
z.Iff1 = false
|
||||
@ -116,13 +115,15 @@ func New(memIoRW z80.MemIoRW) *Z80 {
|
||||
HAlt: 0,
|
||||
IX: 0,
|
||||
IY: 0,
|
||||
I: 0,
|
||||
|
||||
R: 0,
|
||||
SP: 0xffff,
|
||||
PC: 0,
|
||||
Flags: z80.FlagsType{S: true, Z: true, Y: true, H: true, X: true, P: true, N: true, C: true},
|
||||
FlagsAlt: z80.FlagsType{S: true, Z: true, Y: true, H: true, X: true, P: true, N: true, C: true},
|
||||
I: 0,
|
||||
R: 0,
|
||||
SP: 0xffff,
|
||||
PC: 0,
|
||||
|
||||
Flags: z80.FlagsType{S: true, Z: true, Y: true, H: true, X: true, P: true, N: true, C: true},
|
||||
FlagsAlt: z80.FlagsType{S: true, Z: true, Y: true, H: true, X: true, P: true, N: true, C: true},
|
||||
|
||||
IMode: 0,
|
||||
Iff1: false,
|
||||
Iff2: false,
|
||||
@ -137,9 +138,7 @@ func New(memIoRW z80.MemIoRW) *Z80 {
|
||||
}
|
||||
|
||||
func (z *Z80) RunInstruction() uint64 {
|
||||
|
||||
z.incR()
|
||||
|
||||
if !z.Halted {
|
||||
// If the previous instruction was a DI or an EI,
|
||||
// we'll need to disable or enable interrupts
|
||||
@ -153,16 +152,13 @@ func (z *Z80) RunInstruction() uint64 {
|
||||
z.DoDelayedEI = false
|
||||
doingDelayedEi = true
|
||||
}
|
||||
|
||||
// Read the byte at the PC and run the instruction it encodes.
|
||||
opcode := z.core.M1MemRead(z.PC)
|
||||
z.decodeInstruction(opcode)
|
||||
|
||||
// HALT does not increase the PC
|
||||
if !z.Halted {
|
||||
z.PC++
|
||||
}
|
||||
|
||||
// Actually do the delayed interrupt disable/enable if we have one.
|
||||
if doingDelayedDi {
|
||||
z.Iff1 = false
|
||||
@ -171,19 +167,16 @@ func (z *Z80) RunInstruction() uint64 {
|
||||
z.Iff1 = true
|
||||
z.Iff2 = true
|
||||
}
|
||||
|
||||
// And finally clear out the cycle counter for the next instruction
|
||||
// before returning it to the emulator core.
|
||||
cycleCounter := z.CycleCounter
|
||||
z.CycleCounter = 0
|
||||
return uint64(cycleCounter)
|
||||
}
|
||||
|
||||
// HALTED
|
||||
// During HALT, NOPs are executed which is 4T
|
||||
z.core.M1MemRead(z.PC) // HALT does a normal M1 fetch to keep the memory refresh active. The result is ignored (NOP).
|
||||
return 4
|
||||
|
||||
}
|
||||
|
||||
// Simulates pulsing the processor's INT (or NMI) pin
|
||||
@ -346,65 +339,6 @@ func (z *Z80) alu8bit(opcode byte, operand byte) {
|
||||
}
|
||||
}
|
||||
|
||||
// getFlagsRegister return whole F register
|
||||
func (z *Z80) getFlagsRegister() byte {
|
||||
return getFlags(&z.Flags)
|
||||
}
|
||||
|
||||
// getFlagsRegister return whole F' register
|
||||
func (z *Z80) getFlagsPrimeRegister() byte {
|
||||
return getFlags(&z.FlagsAlt)
|
||||
}
|
||||
|
||||
func getFlags(f *z80.FlagsType) byte {
|
||||
var flags byte = 0
|
||||
if f.S {
|
||||
flags |= 0x80
|
||||
}
|
||||
if f.Z {
|
||||
flags |= 0x40
|
||||
}
|
||||
if f.Y {
|
||||
flags |= 0x20
|
||||
}
|
||||
if f.H {
|
||||
flags |= 0x10
|
||||
}
|
||||
if f.X {
|
||||
flags |= 0x08
|
||||
}
|
||||
if f.P {
|
||||
flags |= 0x04
|
||||
}
|
||||
if f.N {
|
||||
flags |= 0x02
|
||||
}
|
||||
if f.C {
|
||||
flags |= 0x01
|
||||
}
|
||||
return flags
|
||||
|
||||
}
|
||||
|
||||
func (z *Z80) setFlagsRegister(flags byte) {
|
||||
setFlags(flags, &z.Flags)
|
||||
}
|
||||
|
||||
func (z *Z80) setFlagsPrimeRegister(flags byte) {
|
||||
setFlags(flags, &z.FlagsAlt)
|
||||
}
|
||||
|
||||
func setFlags(flags byte, f *z80.FlagsType) {
|
||||
f.S = flags&0x80 != 0
|
||||
f.Z = flags&0x40 != 0
|
||||
f.Y = flags&0x20 != 0
|
||||
f.H = flags&0x10 != 0
|
||||
f.X = flags&0x08 != 0
|
||||
f.P = flags&0x04 != 0
|
||||
f.N = flags&0x02 != 0
|
||||
f.C = flags&0x01 != 0
|
||||
}
|
||||
|
||||
// updateXYFlags Set flags X and Y based on result bits
|
||||
func (z *Z80) updateXYFlags(result byte) {
|
||||
z.Flags.Y = result&0x20 != 0
|
||||
@ -458,7 +392,6 @@ func (z *Z80) doConditionalRelativeJump(condition bool) {
|
||||
} else {
|
||||
z.PC = z.PC + uint16(offset)
|
||||
}
|
||||
|
||||
}
|
||||
z.PC++
|
||||
}
|
||||
@ -491,14 +424,12 @@ func (z *Z80) doReset(address uint16) {
|
||||
// doAdd Handle ADD A, [operand] instructions.
|
||||
func (z *Z80) doAdd(operand byte) {
|
||||
var result = uint16(z.A) + uint16(operand)
|
||||
|
||||
z.Flags.S = result&0x80 != 0
|
||||
z.Flags.Z = result&0x00ff == 0
|
||||
z.Flags.H = (((operand & 0x0f) + (z.A & 0x0f)) & 0x10) != 0
|
||||
z.Flags.P = ((z.A & 0x80) == (operand & 0x80)) && (z.A&0x80 != byte(result&0x80))
|
||||
z.Flags.N = false
|
||||
z.Flags.C = result&0x0100 != 0
|
||||
|
||||
z.A = byte(result & 0xff)
|
||||
z.updateXYFlags(z.A)
|
||||
}
|
||||
@ -510,14 +441,12 @@ func (z *Z80) doAdc(operand byte) {
|
||||
add = 1
|
||||
}
|
||||
var result = uint16(z.A) + uint16(operand) + uint16(add)
|
||||
|
||||
z.Flags.S = result&0x80 != 0
|
||||
z.Flags.Z = result&0x00ff == 0
|
||||
z.Flags.H = (((operand & 0x0f) + (z.A & 0x0f) + add) & 0x10) != 0
|
||||
z.Flags.P = ((z.A & 0x80) == (operand & 0x80)) && (z.A&0x80 != byte(result&0x80))
|
||||
z.Flags.N = false
|
||||
z.Flags.C = result&0x0100 != 0
|
||||
|
||||
z.A = byte(result & 0xff)
|
||||
z.updateXYFlags(z.A)
|
||||
}
|
||||
@ -525,14 +454,12 @@ func (z *Z80) doAdc(operand byte) {
|
||||
// doSub Handle SUB A, [operand] instructions.
|
||||
func (z *Z80) doSub(operand byte) {
|
||||
var result = uint16(z.A) - uint16(operand)
|
||||
|
||||
z.Flags.S = result&0x80 != 0
|
||||
z.Flags.Z = result&0x00ff == 0
|
||||
z.Flags.H = (((z.A & 0x0f) - (operand & 0x0f)) & 0x10) != 0
|
||||
z.Flags.P = ((z.A & 0x80) != (operand & 0x80)) && ((z.A & 0x80) != byte(result&0x80))
|
||||
z.Flags.N = true
|
||||
z.Flags.C = result&0x0100 != 0
|
||||
|
||||
z.A = byte(result & 0xff)
|
||||
z.updateXYFlags(z.A)
|
||||
}
|
||||
@ -544,14 +471,12 @@ func (z *Z80) doSbc(operand byte) {
|
||||
sub = 1
|
||||
}
|
||||
var result = uint16(z.A) - uint16(operand) - uint16(sub)
|
||||
|
||||
z.Flags.S = result&0x80 != 0
|
||||
z.Flags.Z = result&0x00ff == 0
|
||||
z.Flags.H = (((z.A & 0x0f) - (operand & 0x0f) - sub) & 0x10) != 0
|
||||
z.Flags.P = ((z.A & 0x80) != (operand & 0x80)) && (z.A&0x80 != byte(result&0x80))
|
||||
z.Flags.N = true
|
||||
z.Flags.C = result&0x0100 != 0
|
||||
|
||||
z.A = byte(result & 0xff)
|
||||
z.updateXYFlags(z.A)
|
||||
}
|
||||
@ -601,13 +526,11 @@ func (z *Z80) doCp(operand byte) {
|
||||
func (z *Z80) doInc(operand byte) byte {
|
||||
var result = uint16(operand) + 1
|
||||
r8 := byte(result & 0xff)
|
||||
|
||||
z.Flags.S = r8&0x80 != 0
|
||||
z.Flags.Z = r8 == 0
|
||||
z.Flags.H = (operand & 0x0f) == 0x0f
|
||||
z.Flags.P = operand == 0x7f
|
||||
z.Flags.N = false
|
||||
|
||||
z.updateXYFlags(r8)
|
||||
return r8
|
||||
}
|
||||
@ -616,13 +539,11 @@ func (z *Z80) doInc(operand byte) byte {
|
||||
func (z *Z80) doDec(operand byte) byte {
|
||||
var result = uint16(operand) - 1
|
||||
r8 := byte(result & 0xff)
|
||||
|
||||
z.Flags.S = r8&0x80 != 0
|
||||
z.Flags.Z = r8 == 0
|
||||
z.Flags.H = (operand & 0x0f) == 0x00
|
||||
z.Flags.P = operand == 0x80
|
||||
z.Flags.N = true
|
||||
|
||||
z.updateXYFlags(r8)
|
||||
return r8
|
||||
}
|
||||
@ -636,11 +557,7 @@ func (z *Z80) doHlAdd(operand uint16) {
|
||||
z.Flags.N = false
|
||||
z.Flags.C = result > 0xffff
|
||||
z.Flags.H = ((hl&0x0fff)+(operand&0x0fff))&0x1000 > 0
|
||||
|
||||
z.setHl(uint16(result))
|
||||
//z.L = byte(result & 0xff)
|
||||
//z.H = byte((result & 0xff00) >> 8)
|
||||
|
||||
z.updateXYFlags(z.H)
|
||||
}
|
||||
|
||||
@ -649,21 +566,15 @@ func (z *Z80) doHlAdc(operand uint16) {
|
||||
if z.Flags.C {
|
||||
operand++
|
||||
}
|
||||
|
||||
hl := z.hl()
|
||||
result := uint32(hl) + uint32(operand)
|
||||
|
||||
z.Flags.S = (result & 0x8000) != 0
|
||||
z.Flags.Z = result&0xffff == 0
|
||||
z.Flags.H = (((hl & 0x0fff) + (operand & 0x0fff)) & 0x1000) != 0
|
||||
z.Flags.P = ((hl & 0x8000) == (operand & 0x8000)) && (uint16(result&0x8000) != (hl & 0x8000))
|
||||
z.Flags.N = false
|
||||
z.Flags.C = result > 0xffff
|
||||
|
||||
z.setHl(uint16(result))
|
||||
//z.L = byte(result & 0xff)
|
||||
//z.H = byte((result & 0xff00) >> 8)
|
||||
|
||||
z.updateXYFlags(z.H)
|
||||
}
|
||||
|
||||
@ -684,8 +595,6 @@ func (z *Z80) doHlSbc(operand uint16) {
|
||||
z.Flags.C = result > 0xffff
|
||||
|
||||
z.setHl(uint16(result))
|
||||
//z.L = byte(result & 0xff)
|
||||
//z.H = byte((result & 0xff00) >> 8)
|
||||
|
||||
z.updateXYFlags(z.H)
|
||||
}
|
||||
@ -983,8 +892,7 @@ func (z *Z80) setWord(address uint16, value uint16) {
|
||||
|
||||
func (z *Z80) debugOutput() {
|
||||
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.getFlagsRegister()), z.bc(), z.de(), z.hl(), z.SP,
|
||||
z.IX, z.IY, z.I, z.R)
|
||||
z.PC, (uint16(z.A)<<8)|uint16(z.Flags.GetFlags()), z.bc(), z.de(), z.hl(), z.SP, z.IX, z.IY, z.I, z.R)
|
||||
|
||||
log.Debugf("\t(%02X %02X %02X %02X), cyc: %d\n", z.core.MemRead(z.PC), z.core.MemRead(z.PC+1),
|
||||
z.core.MemRead(z.PC+2), z.core.MemRead(z.PC+3), z.CycleCounter)
|
||||
|
||||
@ -50,9 +50,9 @@ var instructions = []func(s *Z80){
|
||||
// 0x08 : EX AF, AF'
|
||||
0x08: func(s *Z80) {
|
||||
s.A, s.AAlt = s.AAlt, s.A
|
||||
temp := s.getFlagsRegister()
|
||||
s.setFlagsRegister(s.getFlagsPrimeRegister())
|
||||
s.setFlagsPrimeRegister(temp)
|
||||
temp := s.Flags.GetFlags()
|
||||
s.Flags.SetFlags(s.FlagsAlt.GetFlags()) //setFlagsRegister(s.getFlagsPrimeRegister())
|
||||
s.FlagsAlt.SetFlags(temp)
|
||||
},
|
||||
// 0x09 : ADD HL, BC
|
||||
0x09: func(s *Z80) {
|
||||
@ -583,7 +583,7 @@ var instructions = []func(s *Z80){
|
||||
// 0xf1 : POP AF
|
||||
0xF1: func(s *Z80) {
|
||||
var result = s.PopWord()
|
||||
s.setFlagsRegister(byte(result & 0xff))
|
||||
s.Flags.SetFlags(byte(result & 0xff))
|
||||
s.A = byte((result & 0xff00) >> 8)
|
||||
},
|
||||
// 0xf2 : JP P, nn
|
||||
@ -601,7 +601,7 @@ var instructions = []func(s *Z80){
|
||||
},
|
||||
// 0xf5 : PUSH AF
|
||||
0xF5: func(s *Z80) {
|
||||
s.pushWord(uint16(s.getFlagsRegister()) | (uint16(s.A) << 8))
|
||||
s.pushWord(uint16(s.Flags.GetFlags()) | (uint16(s.A) << 8))
|
||||
},
|
||||
// 0xf6 : OR n
|
||||
0xF6: func(s *Z80) {
|
||||
|
||||
18913
z80/tests.expected
Normal file
18913
z80/tests.expected
Normal file
File diff suppressed because it is too large
Load Diff
9153
z80/tests.in
Normal file
9153
z80/tests.in
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user