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"
|
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 {
|
type Z80 struct {
|
||||||
|
|
||||||
// cycle count (t-states)
|
// cycle count (t-states)
|
||||||
@ -58,11 +34,11 @@ type Z80 struct {
|
|||||||
int_pending bool
|
int_pending bool
|
||||||
nmi_pending bool
|
nmi_pending bool
|
||||||
|
|
||||||
core MemIoRW
|
core z80.MemIoRW
|
||||||
}
|
}
|
||||||
|
|
||||||
// New initializes a Z80 instance and return pointer to it
|
// New initializes a Z80 instance and return pointer to it
|
||||||
func New(core MemIoRW) *Z80 {
|
func New(core z80.MemIoRW) *Z80 {
|
||||||
z := Z80{}
|
z := Z80{}
|
||||||
z.core = core
|
z.core = core
|
||||||
|
|
||||||
@ -129,28 +105,76 @@ func (z *Z80) RunInstruction() uint64 {
|
|||||||
return z.cyc - pre
|
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 {
|
func (z *Z80) GetState() *z80.Z80CPU {
|
||||||
return &z80.Z80CPU{
|
return &z80.Z80CPU{
|
||||||
A: z.a,
|
A: z.a,
|
||||||
B: z.b,
|
B: z.b,
|
||||||
C: z.c,
|
C: z.c,
|
||||||
D: z.d,
|
D: z.d,
|
||||||
E: z.e,
|
E: z.e,
|
||||||
H: z.h,
|
H: z.h,
|
||||||
L: z.l,
|
L: z.l,
|
||||||
AAlt: z.a_,
|
AAlt: z.a_,
|
||||||
BAlt: z.b_,
|
BAlt: z.b_,
|
||||||
CAlt: z.c_,
|
CAlt: z.c_,
|
||||||
DAlt: z.d_,
|
DAlt: z.d_,
|
||||||
EAlt: z.e_,
|
EAlt: z.e_,
|
||||||
HAlt: z.h_,
|
HAlt: z.h_,
|
||||||
LAlt: z.l_,
|
LAlt: z.l_,
|
||||||
IX: z.ix,
|
|
||||||
IY: z.iy,
|
IX: z.ix,
|
||||||
I: z.i,
|
IY: z.iy,
|
||||||
R: z.r,
|
I: z.i,
|
||||||
SP: z.sp,
|
R: z.r,
|
||||||
PC: z.pc,
|
SP: z.sp,
|
||||||
|
PC: z.pc,
|
||||||
|
|
||||||
Flags: z.getFlags(),
|
Flags: z.getFlags(),
|
||||||
FlagsAlt: z.getAltFlags(),
|
FlagsAlt: z.getAltFlags(),
|
||||||
IMode: z.interrupt_mode,
|
IMode: z.interrupt_mode,
|
||||||
@ -160,7 +184,8 @@ func (z *Z80) GetState() *z80.Z80CPU {
|
|||||||
DoDelayedDI: z.int_pending,
|
DoDelayedDI: z.int_pending,
|
||||||
DoDelayedEI: z.int_pending,
|
DoDelayedEI: z.int_pending,
|
||||||
CycleCounter: z.inst_cyc,
|
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) {
|
func (z *Z80) jr(offset byte) {
|
||||||
z.pc += uint16(displacement)
|
if offset&0x80 != 0 {
|
||||||
|
z.pc += 0xFF00 | uint16(offset)
|
||||||
|
} else {
|
||||||
|
z.pc += uint16(offset)
|
||||||
|
}
|
||||||
z.mem_ptr = z.pc
|
z.mem_ptr = z.pc
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -393,6 +397,33 @@ func (z *Z80) cpd() {
|
|||||||
z.mem_ptr -= 2
|
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) {
|
func (z *Z80) in_r_c(r *byte) {
|
||||||
*r = z.core.IORead(z.get_bc())
|
*r = z.core.IORead(z.get_bc())
|
||||||
z.zf = *r == 0
|
z.zf = *r == 0
|
||||||
@ -405,32 +436,88 @@ func (z *Z80) in_r_c(r *byte) {
|
|||||||
func (z *Z80) ini() {
|
func (z *Z80) ini() {
|
||||||
val := z.core.IORead(z.get_bc())
|
val := z.core.IORead(z.get_bc())
|
||||||
z.wb(z.get_hl(), val)
|
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.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() {
|
func (z *Z80) ind() {
|
||||||
z.ini()
|
val := z.core.IORead(z.get_bc())
|
||||||
z.set_hl(z.get_hl() - 2)
|
z.wb(z.get_hl(), val)
|
||||||
z.mem_ptr = z.get_bc() - 2
|
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() {
|
func (z *Z80) outi() {
|
||||||
z.core.IOWrite(z.get_bc(), z.rb(z.get_hl()))
|
val := z.rb(z.get_hl())
|
||||||
z.set_hl(z.get_hl() + 1)
|
z.b--
|
||||||
z.b -= 1
|
|
||||||
z.zf = z.b == 0
|
|
||||||
z.nf = true
|
|
||||||
z.mem_ptr = z.get_bc() + 1
|
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() {
|
func (z *Z80) outd() {
|
||||||
z.outi()
|
val := z.rb(z.get_hl())
|
||||||
z.set_hl(z.get_hl() - 2)
|
z.b--
|
||||||
z.mem_ptr = z.get_bc() - 2
|
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() {
|
func (z *Z80) daa() {
|
||||||
@ -468,8 +555,14 @@ func (z *Z80) daa() {
|
|||||||
z.updateXY(z.a)
|
z.updateXY(z.a)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (z *Z80) displace(base_addr uint16, displacement byte) uint16 {
|
func (z *Z80) displace(base_addr uint16, offset byte) uint16 {
|
||||||
addr := base_addr + uint16(displacement)
|
addr := base_addr
|
||||||
|
if offset&0x80 == 0x80 {
|
||||||
|
addr += 0xff00 | uint16(offset)
|
||||||
|
} else {
|
||||||
|
addr += uint16(offset)
|
||||||
|
}
|
||||||
|
//addr := base_addr + uint16(displacement)
|
||||||
z.mem_ptr = addr
|
z.mem_ptr = addr
|
||||||
return addr
|
return addr
|
||||||
}
|
}
|
||||||
@ -841,7 +934,7 @@ func (z *Z80) exec_opcode(opcode byte) {
|
|||||||
case 0x00: // nop
|
case 0x00: // nop
|
||||||
case 0x76:
|
case 0x76:
|
||||||
z.halted = true // halt
|
z.halted = true // halt
|
||||||
|
z.pc--
|
||||||
case 0x3C:
|
case 0x3C:
|
||||||
z.a = z.inc(z.a) // inc a
|
z.a = z.inc(z.a) // inc a
|
||||||
case 0x04:
|
case 0x04:
|
||||||
@ -907,13 +1000,13 @@ func (z *Z80) exec_opcode(opcode byte) {
|
|||||||
z.cf = true
|
z.cf = true
|
||||||
z.nf = false
|
z.nf = false
|
||||||
z.hf = false
|
z.hf = false
|
||||||
z.updateXY(z.a)
|
z.updateXY(z.a | z.get_f())
|
||||||
case 0x3F:
|
case 0x3F:
|
||||||
// ccf
|
// ccf
|
||||||
z.hf = z.cf
|
z.hf = z.cf
|
||||||
z.cf = !z.cf
|
z.cf = !z.cf
|
||||||
z.nf = false
|
z.nf = false
|
||||||
z.updateXY(z.a)
|
z.updateXY(z.a | z.get_f())
|
||||||
case 0x07:
|
case 0x07:
|
||||||
// rlca (rotate left)
|
// rlca (rotate left)
|
||||||
z.cf = z.a&0x80 != 0
|
z.cf = z.a&0x80 != 0
|
||||||
@ -1044,6 +1137,7 @@ func (z *Z80) exec_opcode(opcode byte) {
|
|||||||
z.cond_jr(z.b != 0) // djnz *
|
z.cond_jr(z.b != 0) // djnz *
|
||||||
case 0x18:
|
case 0x18:
|
||||||
z.pc += uint16(z.nextb()) // jr *
|
z.pc += uint16(z.nextb()) // jr *
|
||||||
|
z.mem_ptr = z.pc
|
||||||
case 0x20:
|
case 0x20:
|
||||||
z.cond_jr(!z.zf) // jr nz, *
|
z.cond_jr(!z.zf) // jr nz, *
|
||||||
case 0x28:
|
case 0x28:
|
||||||
@ -1133,15 +1227,14 @@ func (z *Z80) exec_opcode(opcode byte) {
|
|||||||
z.set_f(byte(val))
|
z.set_f(byte(val))
|
||||||
case 0xDB:
|
case 0xDB:
|
||||||
// in a,(n)
|
// in a,(n)
|
||||||
port := uint16(z.nextb())
|
port := (uint16(z.a) << 8) | uint16(z.nextb())
|
||||||
a := z.a
|
|
||||||
z.a = z.core.IORead(port)
|
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:
|
case 0xD3:
|
||||||
// out (n), a
|
// out (n), a
|
||||||
port := uint16(z.nextb())
|
port := uint16(z.nextb())
|
||||||
z.core.IOWrite(port, z.a)
|
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:
|
case 0x08:
|
||||||
// ex af,af'
|
// ex af,af'
|
||||||
a := z.a
|
a := z.a
|
||||||
|
|||||||
@ -10,10 +10,10 @@ func (z *Z80) exec_opcode_ddfd(opcode byte, iz *uint16) {
|
|||||||
*iz = z.popw() // pop iz
|
*iz = z.popw() // pop iz
|
||||||
case 0xE5:
|
case 0xE5:
|
||||||
z.pushw(*iz) // push iz
|
z.pushw(*iz) // push iz
|
||||||
|
|
||||||
case 0xE9:
|
case 0xE9:
|
||||||
z.jump(*iz) // jp iz
|
// jp iz
|
||||||
|
z.pc = *iz
|
||||||
|
//z.jump(*iz)
|
||||||
case 0x09:
|
case 0x09:
|
||||||
z.addiz(iz, z.get_bc()) // add iz,bc
|
z.addiz(iz, z.get_bc()) // add iz,bc
|
||||||
case 0x19:
|
case 0x19:
|
||||||
@ -94,9 +94,13 @@ func (z *Z80) exec_opcode_ddfd(opcode byte, iz *uint16) {
|
|||||||
case 0x2D:
|
case 0x2D:
|
||||||
*iz = (*iz & 0xff00) | uint16(z.dec(byte(*iz))) // dec izl
|
*iz = (*iz & 0xff00) | uint16(z.dec(byte(*iz))) // dec izl
|
||||||
case 0x2A:
|
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:
|
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:
|
case 0x21:
|
||||||
*iz = z.nextw() // ld iz,**
|
*iz = z.nextw() // ld iz,**
|
||||||
case 0x36:
|
case 0x36:
|
||||||
|
|||||||
@ -19,6 +19,7 @@ func (z *Z80) exec_opcode_ed(opcode byte) {
|
|||||||
z.hf = false
|
z.hf = false
|
||||||
z.nf = false
|
z.nf = false
|
||||||
z.pf = z.iff2
|
z.pf = z.iff2
|
||||||
|
z.updateXY(z.a)
|
||||||
case 0x5F:
|
case 0x5F:
|
||||||
// ld a,r
|
// ld a,r
|
||||||
z.a = z.r
|
z.a = z.r
|
||||||
@ -65,47 +66,61 @@ func (z *Z80) exec_opcode_ed(opcode byte) {
|
|||||||
case 0xA9:
|
case 0xA9:
|
||||||
z.cpd() // cpd
|
z.cpd() // cpd
|
||||||
case 0xB1:
|
case 0xB1:
|
||||||
{
|
// cpir
|
||||||
z.cpi()
|
z.cpi()
|
||||||
if z.get_bc() != 0 && !z.zf {
|
if z.get_bc() != 0 && !z.zf {
|
||||||
z.pc -= 2
|
z.pc -= 2
|
||||||
z.cyc += 5
|
z.cyc += 5
|
||||||
z.mem_ptr = z.pc + 1
|
z.mem_ptr = z.pc + 1
|
||||||
} else {
|
} else {
|
||||||
z.mem_ptr += 1
|
//z.mem_ptr++
|
||||||
}
|
}
|
||||||
} // cpir
|
//z.cpir()
|
||||||
case 0xB9:
|
case 0xB9:
|
||||||
{
|
// cpdr
|
||||||
z.cpd()
|
z.cpd()
|
||||||
if z.get_bc() != 0 && !z.zf {
|
if z.get_bc() != 0 && !z.zf {
|
||||||
z.pc -= 2
|
z.pc -= 2
|
||||||
z.cyc += 5
|
z.cyc += 5
|
||||||
} else {
|
z.mem_ptr = z.pc + 1
|
||||||
z.mem_ptr += 1
|
} else {
|
||||||
}
|
//z.mem_ptr++
|
||||||
} // cpdr
|
}
|
||||||
|
|
||||||
case 0x40:
|
case 0x40:
|
||||||
z.in_r_c(&z.b) // in b, (c)
|
z.in_r_c(&z.b) // in b, (c)
|
||||||
|
z.mem_ptr = z.get_bc() + 1
|
||||||
case 0x48:
|
case 0x48:
|
||||||
|
z.mem_ptr = z.get_bc() + 1
|
||||||
z.in_r_c(&z.c) // in c, (c)
|
z.in_r_c(&z.c) // in c, (c)
|
||||||
|
z.updateXY(z.c)
|
||||||
|
//case 0x4e:
|
||||||
|
// ld c,(iy+dd)
|
||||||
|
|
||||||
case 0x50:
|
case 0x50:
|
||||||
z.in_r_c(&z.d) // in d, (c)
|
z.in_r_c(&z.d) // in d, (c)
|
||||||
|
z.mem_ptr = z.get_bc() + 1
|
||||||
case 0x58:
|
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:
|
case 0x60:
|
||||||
z.in_r_c(&z.h) // in h, (c)
|
z.in_r_c(&z.h) // in h, (c)
|
||||||
|
z.mem_ptr = z.get_bc() + 1
|
||||||
case 0x68:
|
case 0x68:
|
||||||
z.in_r_c(&z.l) // in l, (c)
|
z.in_r_c(&z.l) // in l, (c)
|
||||||
|
z.mem_ptr = z.get_bc() + 1
|
||||||
|
z.updateXY(z.l)
|
||||||
case 0x70:
|
case 0x70:
|
||||||
// in (c)
|
// in (c)
|
||||||
var val byte
|
var val byte
|
||||||
z.in_r_c(&val)
|
z.in_r_c(&val)
|
||||||
|
z.mem_ptr = z.get_bc() + 1
|
||||||
case 0x78:
|
case 0x78:
|
||||||
// in a, (c)
|
// in a, (c)
|
||||||
z.in_r_c(&z.a)
|
z.in_r_c(&z.a)
|
||||||
z.mem_ptr = z.get_bc() + 1
|
z.mem_ptr = z.get_bc() + 1
|
||||||
|
z.updateXY(z.a)
|
||||||
case 0xA2:
|
case 0xA2:
|
||||||
z.ini() // ini
|
z.ini() // ini
|
||||||
case 0xB2:
|
case 0xB2:
|
||||||
@ -116,7 +131,8 @@ func (z *Z80) exec_opcode_ed(opcode byte) {
|
|||||||
z.cyc += 5
|
z.cyc += 5
|
||||||
}
|
}
|
||||||
case 0xAA:
|
case 0xAA:
|
||||||
z.ind() // ind
|
// ind
|
||||||
|
z.ind()
|
||||||
case 0xBA:
|
case 0xBA:
|
||||||
// indr
|
// indr
|
||||||
z.ind()
|
z.ind()
|
||||||
@ -126,18 +142,25 @@ func (z *Z80) exec_opcode_ed(opcode byte) {
|
|||||||
}
|
}
|
||||||
case 0x41:
|
case 0x41:
|
||||||
z.core.IOWrite(z.get_bc(), z.b) // out (c), b
|
z.core.IOWrite(z.get_bc(), z.b) // out (c), b
|
||||||
|
z.mem_ptr = z.get_bc() + 1
|
||||||
case 0x49:
|
case 0x49:
|
||||||
z.core.IOWrite(z.get_bc(), z.c) // out (c), c
|
z.core.IOWrite(z.get_bc(), z.c) // out (c), c
|
||||||
|
z.mem_ptr = z.get_bc() + 1
|
||||||
case 0x51:
|
case 0x51:
|
||||||
z.core.IOWrite(z.get_bc(), z.d) // out (c), d
|
z.core.IOWrite(z.get_bc(), z.d) // out (c), d
|
||||||
|
z.mem_ptr = z.get_bc() + 1
|
||||||
case 0x59:
|
case 0x59:
|
||||||
z.core.IOWrite(z.get_bc(), z.e) // out (c), e
|
z.core.IOWrite(z.get_bc(), z.e) // out (c), e
|
||||||
|
z.mem_ptr = z.get_bc() + 1
|
||||||
case 0x61:
|
case 0x61:
|
||||||
z.core.IOWrite(z.get_bc(), z.h) // out (c), h
|
z.core.IOWrite(z.get_bc(), z.h) // out (c), h
|
||||||
|
z.mem_ptr = z.get_bc() + 1
|
||||||
case 0x69:
|
case 0x69:
|
||||||
z.core.IOWrite(z.get_bc(), z.l) // out (c), l
|
z.core.IOWrite(z.get_bc(), z.l) // out (c), l
|
||||||
|
z.mem_ptr = z.get_bc() + 1
|
||||||
case 0x71:
|
case 0x71:
|
||||||
z.core.IOWrite(z.get_bc(), 0) // out (c), 0
|
z.core.IOWrite(z.get_bc(), 0) // out (c), 0
|
||||||
|
z.mem_ptr = z.get_bc() + 1
|
||||||
case 0x79:
|
case 0x79:
|
||||||
// out (c), a
|
// out (c), a
|
||||||
z.core.IOWrite(z.get_bc(), z.a)
|
z.core.IOWrite(z.get_bc(), z.a)
|
||||||
@ -157,6 +180,7 @@ func (z *Z80) exec_opcode_ed(opcode byte) {
|
|||||||
// otdr
|
// otdr
|
||||||
z.outd()
|
z.outd()
|
||||||
if z.b > 0 {
|
if z.b > 0 {
|
||||||
|
z.cyc += 5
|
||||||
z.pc -= 2
|
z.pc -= 2
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,7 +242,7 @@ func (z *Z80) exec_opcode_ed(opcode byte) {
|
|||||||
z.mem_ptr = addr + 1
|
z.mem_ptr = addr + 1
|
||||||
case 0x44, 0x54, 0x64, 0x74, 0x4C, 0x5C, 0x6C, 0x7C:
|
case 0x44, 0x54, 0x64, 0x74, 0x4C, 0x5C, 0x6C, 0x7C:
|
||||||
z.a = z.subb(0, z.a, false) // neg
|
z.a = z.subb(0, z.a, false) // neg
|
||||||
case 0x46, 0x66:
|
case 0x46, 0x4e, 0x66, 0x6e:
|
||||||
z.interrupt_mode = 0 // im 0
|
z.interrupt_mode = 0 // im 0
|
||||||
case 0x56, 0x76:
|
case 0x56, 0x76:
|
||||||
z.interrupt_mode = 1 // im 1
|
z.interrupt_mode = 1 // im 1
|
||||||
|
|||||||
54
z80/cpu.go
54
z80/cpu.go
@ -70,5 +70,59 @@ type Z80CPU struct {
|
|||||||
DoDelayedEI bool
|
DoDelayedEI bool
|
||||||
CycleCounter byte
|
CycleCounter byte
|
||||||
InterruptOccurred bool
|
InterruptOccurred bool
|
||||||
|
MemPtr uint16
|
||||||
//core MemIoRW
|
//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"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// const SpDefault uint16 = 0xffff
|
|
||||||
type Z80 struct {
|
type Z80 struct {
|
||||||
z80.Z80CPU
|
z80.Z80CPU
|
||||||
core z80.MemIoRW
|
core z80.MemIoRW
|
||||||
@ -18,7 +17,7 @@ func (z *Z80) Reset() {
|
|||||||
z.R = 0
|
z.R = 0
|
||||||
z.SP = 0xff
|
z.SP = 0xff
|
||||||
z.PC = 0
|
z.PC = 0
|
||||||
z.setFlagsRegister(0xff)
|
z.Flags.SetFlags(0xff)
|
||||||
// Interrupts disabled
|
// Interrupts disabled
|
||||||
z.IMode = 0
|
z.IMode = 0
|
||||||
z.Iff1 = false
|
z.Iff1 = false
|
||||||
@ -116,13 +115,15 @@ func New(memIoRW z80.MemIoRW) *Z80 {
|
|||||||
HAlt: 0,
|
HAlt: 0,
|
||||||
IX: 0,
|
IX: 0,
|
||||||
IY: 0,
|
IY: 0,
|
||||||
I: 0,
|
|
||||||
|
|
||||||
R: 0,
|
I: 0,
|
||||||
SP: 0xffff,
|
R: 0,
|
||||||
PC: 0,
|
SP: 0xffff,
|
||||||
Flags: z80.FlagsType{S: true, Z: true, Y: true, H: true, X: true, P: true, N: true, C: true},
|
PC: 0,
|
||||||
FlagsAlt: z80.FlagsType{S: true, Z: true, Y: true, H: true, X: true, P: true, N: true, C: true},
|
|
||||||
|
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,
|
IMode: 0,
|
||||||
Iff1: false,
|
Iff1: false,
|
||||||
Iff2: false,
|
Iff2: false,
|
||||||
@ -137,9 +138,7 @@ func New(memIoRW z80.MemIoRW) *Z80 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (z *Z80) RunInstruction() uint64 {
|
func (z *Z80) RunInstruction() uint64 {
|
||||||
|
|
||||||
z.incR()
|
z.incR()
|
||||||
|
|
||||||
if !z.Halted {
|
if !z.Halted {
|
||||||
// If the previous instruction was a DI or an EI,
|
// If the previous instruction was a DI or an EI,
|
||||||
// we'll need to disable or enable interrupts
|
// we'll need to disable or enable interrupts
|
||||||
@ -153,16 +152,13 @@ func (z *Z80) RunInstruction() uint64 {
|
|||||||
z.DoDelayedEI = false
|
z.DoDelayedEI = false
|
||||||
doingDelayedEi = true
|
doingDelayedEi = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read the byte at the PC and run the instruction it encodes.
|
// Read the byte at the PC and run the instruction it encodes.
|
||||||
opcode := z.core.M1MemRead(z.PC)
|
opcode := z.core.M1MemRead(z.PC)
|
||||||
z.decodeInstruction(opcode)
|
z.decodeInstruction(opcode)
|
||||||
|
|
||||||
// HALT does not increase the PC
|
// HALT does not increase the PC
|
||||||
if !z.Halted {
|
if !z.Halted {
|
||||||
z.PC++
|
z.PC++
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actually do the delayed interrupt disable/enable if we have one.
|
// Actually do the delayed interrupt disable/enable if we have one.
|
||||||
if doingDelayedDi {
|
if doingDelayedDi {
|
||||||
z.Iff1 = false
|
z.Iff1 = false
|
||||||
@ -171,19 +167,16 @@ func (z *Z80) RunInstruction() uint64 {
|
|||||||
z.Iff1 = true
|
z.Iff1 = true
|
||||||
z.Iff2 = true
|
z.Iff2 = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// And finally clear out the cycle counter for the next instruction
|
// And finally clear out the cycle counter for the next instruction
|
||||||
// before returning it to the emulator core.
|
// before returning it to the emulator core.
|
||||||
cycleCounter := z.CycleCounter
|
cycleCounter := z.CycleCounter
|
||||||
z.CycleCounter = 0
|
z.CycleCounter = 0
|
||||||
return uint64(cycleCounter)
|
return uint64(cycleCounter)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HALTED
|
// HALTED
|
||||||
// During HALT, NOPs are executed which is 4T
|
// 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).
|
z.core.M1MemRead(z.PC) // HALT does a normal M1 fetch to keep the memory refresh active. The result is ignored (NOP).
|
||||||
return 4
|
return 4
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Simulates pulsing the processor's INT (or NMI) pin
|
// 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
|
// updateXYFlags Set flags X and Y based on result bits
|
||||||
func (z *Z80) updateXYFlags(result byte) {
|
func (z *Z80) updateXYFlags(result byte) {
|
||||||
z.Flags.Y = result&0x20 != 0
|
z.Flags.Y = result&0x20 != 0
|
||||||
@ -458,7 +392,6 @@ func (z *Z80) doConditionalRelativeJump(condition bool) {
|
|||||||
} else {
|
} else {
|
||||||
z.PC = z.PC + uint16(offset)
|
z.PC = z.PC + uint16(offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
z.PC++
|
z.PC++
|
||||||
}
|
}
|
||||||
@ -491,14 +424,12 @@ func (z *Z80) doReset(address uint16) {
|
|||||||
// doAdd Handle ADD A, [operand] instructions.
|
// doAdd Handle ADD A, [operand] instructions.
|
||||||
func (z *Z80) doAdd(operand byte) {
|
func (z *Z80) doAdd(operand byte) {
|
||||||
var result = uint16(z.A) + uint16(operand)
|
var result = uint16(z.A) + uint16(operand)
|
||||||
|
|
||||||
z.Flags.S = result&0x80 != 0
|
z.Flags.S = result&0x80 != 0
|
||||||
z.Flags.Z = result&0x00ff == 0
|
z.Flags.Z = result&0x00ff == 0
|
||||||
z.Flags.H = (((operand & 0x0f) + (z.A & 0x0f)) & 0x10) != 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.P = ((z.A & 0x80) == (operand & 0x80)) && (z.A&0x80 != byte(result&0x80))
|
||||||
z.Flags.N = false
|
z.Flags.N = false
|
||||||
z.Flags.C = result&0x0100 != 0
|
z.Flags.C = result&0x0100 != 0
|
||||||
|
|
||||||
z.A = byte(result & 0xff)
|
z.A = byte(result & 0xff)
|
||||||
z.updateXYFlags(z.A)
|
z.updateXYFlags(z.A)
|
||||||
}
|
}
|
||||||
@ -510,14 +441,12 @@ func (z *Z80) doAdc(operand byte) {
|
|||||||
add = 1
|
add = 1
|
||||||
}
|
}
|
||||||
var result = uint16(z.A) + uint16(operand) + uint16(add)
|
var result = uint16(z.A) + uint16(operand) + uint16(add)
|
||||||
|
|
||||||
z.Flags.S = result&0x80 != 0
|
z.Flags.S = result&0x80 != 0
|
||||||
z.Flags.Z = result&0x00ff == 0
|
z.Flags.Z = result&0x00ff == 0
|
||||||
z.Flags.H = (((operand & 0x0f) + (z.A & 0x0f) + add) & 0x10) != 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.P = ((z.A & 0x80) == (operand & 0x80)) && (z.A&0x80 != byte(result&0x80))
|
||||||
z.Flags.N = false
|
z.Flags.N = false
|
||||||
z.Flags.C = result&0x0100 != 0
|
z.Flags.C = result&0x0100 != 0
|
||||||
|
|
||||||
z.A = byte(result & 0xff)
|
z.A = byte(result & 0xff)
|
||||||
z.updateXYFlags(z.A)
|
z.updateXYFlags(z.A)
|
||||||
}
|
}
|
||||||
@ -525,14 +454,12 @@ func (z *Z80) doAdc(operand byte) {
|
|||||||
// doSub Handle SUB A, [operand] instructions.
|
// doSub Handle SUB A, [operand] instructions.
|
||||||
func (z *Z80) doSub(operand byte) {
|
func (z *Z80) doSub(operand byte) {
|
||||||
var result = uint16(z.A) - uint16(operand)
|
var result = uint16(z.A) - uint16(operand)
|
||||||
|
|
||||||
z.Flags.S = result&0x80 != 0
|
z.Flags.S = result&0x80 != 0
|
||||||
z.Flags.Z = result&0x00ff == 0
|
z.Flags.Z = result&0x00ff == 0
|
||||||
z.Flags.H = (((z.A & 0x0f) - (operand & 0x0f)) & 0x10) != 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.P = ((z.A & 0x80) != (operand & 0x80)) && ((z.A & 0x80) != byte(result&0x80))
|
||||||
z.Flags.N = true
|
z.Flags.N = true
|
||||||
z.Flags.C = result&0x0100 != 0
|
z.Flags.C = result&0x0100 != 0
|
||||||
|
|
||||||
z.A = byte(result & 0xff)
|
z.A = byte(result & 0xff)
|
||||||
z.updateXYFlags(z.A)
|
z.updateXYFlags(z.A)
|
||||||
}
|
}
|
||||||
@ -544,14 +471,12 @@ func (z *Z80) doSbc(operand byte) {
|
|||||||
sub = 1
|
sub = 1
|
||||||
}
|
}
|
||||||
var result = uint16(z.A) - uint16(operand) - uint16(sub)
|
var result = uint16(z.A) - uint16(operand) - uint16(sub)
|
||||||
|
|
||||||
z.Flags.S = result&0x80 != 0
|
z.Flags.S = result&0x80 != 0
|
||||||
z.Flags.Z = result&0x00ff == 0
|
z.Flags.Z = result&0x00ff == 0
|
||||||
z.Flags.H = (((z.A & 0x0f) - (operand & 0x0f) - sub) & 0x10) != 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.P = ((z.A & 0x80) != (operand & 0x80)) && (z.A&0x80 != byte(result&0x80))
|
||||||
z.Flags.N = true
|
z.Flags.N = true
|
||||||
z.Flags.C = result&0x0100 != 0
|
z.Flags.C = result&0x0100 != 0
|
||||||
|
|
||||||
z.A = byte(result & 0xff)
|
z.A = byte(result & 0xff)
|
||||||
z.updateXYFlags(z.A)
|
z.updateXYFlags(z.A)
|
||||||
}
|
}
|
||||||
@ -601,13 +526,11 @@ func (z *Z80) doCp(operand byte) {
|
|||||||
func (z *Z80) doInc(operand byte) byte {
|
func (z *Z80) doInc(operand byte) byte {
|
||||||
var result = uint16(operand) + 1
|
var result = uint16(operand) + 1
|
||||||
r8 := byte(result & 0xff)
|
r8 := byte(result & 0xff)
|
||||||
|
|
||||||
z.Flags.S = r8&0x80 != 0
|
z.Flags.S = r8&0x80 != 0
|
||||||
z.Flags.Z = r8 == 0
|
z.Flags.Z = r8 == 0
|
||||||
z.Flags.H = (operand & 0x0f) == 0x0f
|
z.Flags.H = (operand & 0x0f) == 0x0f
|
||||||
z.Flags.P = operand == 0x7f
|
z.Flags.P = operand == 0x7f
|
||||||
z.Flags.N = false
|
z.Flags.N = false
|
||||||
|
|
||||||
z.updateXYFlags(r8)
|
z.updateXYFlags(r8)
|
||||||
return r8
|
return r8
|
||||||
}
|
}
|
||||||
@ -616,13 +539,11 @@ func (z *Z80) doInc(operand byte) byte {
|
|||||||
func (z *Z80) doDec(operand byte) byte {
|
func (z *Z80) doDec(operand byte) byte {
|
||||||
var result = uint16(operand) - 1
|
var result = uint16(operand) - 1
|
||||||
r8 := byte(result & 0xff)
|
r8 := byte(result & 0xff)
|
||||||
|
|
||||||
z.Flags.S = r8&0x80 != 0
|
z.Flags.S = r8&0x80 != 0
|
||||||
z.Flags.Z = r8 == 0
|
z.Flags.Z = r8 == 0
|
||||||
z.Flags.H = (operand & 0x0f) == 0x00
|
z.Flags.H = (operand & 0x0f) == 0x00
|
||||||
z.Flags.P = operand == 0x80
|
z.Flags.P = operand == 0x80
|
||||||
z.Flags.N = true
|
z.Flags.N = true
|
||||||
|
|
||||||
z.updateXYFlags(r8)
|
z.updateXYFlags(r8)
|
||||||
return r8
|
return r8
|
||||||
}
|
}
|
||||||
@ -636,11 +557,7 @@ func (z *Z80) doHlAdd(operand uint16) {
|
|||||||
z.Flags.N = false
|
z.Flags.N = false
|
||||||
z.Flags.C = result > 0xffff
|
z.Flags.C = result > 0xffff
|
||||||
z.Flags.H = ((hl&0x0fff)+(operand&0x0fff))&0x1000 > 0
|
z.Flags.H = ((hl&0x0fff)+(operand&0x0fff))&0x1000 > 0
|
||||||
|
|
||||||
z.setHl(uint16(result))
|
z.setHl(uint16(result))
|
||||||
//z.L = byte(result & 0xff)
|
|
||||||
//z.H = byte((result & 0xff00) >> 8)
|
|
||||||
|
|
||||||
z.updateXYFlags(z.H)
|
z.updateXYFlags(z.H)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -649,21 +566,15 @@ func (z *Z80) doHlAdc(operand uint16) {
|
|||||||
if z.Flags.C {
|
if z.Flags.C {
|
||||||
operand++
|
operand++
|
||||||
}
|
}
|
||||||
|
|
||||||
hl := z.hl()
|
hl := z.hl()
|
||||||
result := uint32(hl) + uint32(operand)
|
result := uint32(hl) + uint32(operand)
|
||||||
|
|
||||||
z.Flags.S = (result & 0x8000) != 0
|
z.Flags.S = (result & 0x8000) != 0
|
||||||
z.Flags.Z = result&0xffff == 0
|
z.Flags.Z = result&0xffff == 0
|
||||||
z.Flags.H = (((hl & 0x0fff) + (operand & 0x0fff)) & 0x1000) != 0
|
z.Flags.H = (((hl & 0x0fff) + (operand & 0x0fff)) & 0x1000) != 0
|
||||||
z.Flags.P = ((hl & 0x8000) == (operand & 0x8000)) && (uint16(result&0x8000) != (hl & 0x8000))
|
z.Flags.P = ((hl & 0x8000) == (operand & 0x8000)) && (uint16(result&0x8000) != (hl & 0x8000))
|
||||||
z.Flags.N = false
|
z.Flags.N = false
|
||||||
z.Flags.C = result > 0xffff
|
z.Flags.C = result > 0xffff
|
||||||
|
|
||||||
z.setHl(uint16(result))
|
z.setHl(uint16(result))
|
||||||
//z.L = byte(result & 0xff)
|
|
||||||
//z.H = byte((result & 0xff00) >> 8)
|
|
||||||
|
|
||||||
z.updateXYFlags(z.H)
|
z.updateXYFlags(z.H)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -684,8 +595,6 @@ func (z *Z80) doHlSbc(operand uint16) {
|
|||||||
z.Flags.C = result > 0xffff
|
z.Flags.C = result > 0xffff
|
||||||
|
|
||||||
z.setHl(uint16(result))
|
z.setHl(uint16(result))
|
||||||
//z.L = byte(result & 0xff)
|
|
||||||
//z.H = byte((result & 0xff00) >> 8)
|
|
||||||
|
|
||||||
z.updateXYFlags(z.H)
|
z.updateXYFlags(z.H)
|
||||||
}
|
}
|
||||||
@ -983,8 +892,7 @@ func (z *Z80) setWord(address uint16, value uint16) {
|
|||||||
|
|
||||||
func (z *Z80) debugOutput() {
|
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",
|
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.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)
|
||||||
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),
|
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)
|
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 : EX AF, AF'
|
||||||
0x08: func(s *Z80) {
|
0x08: func(s *Z80) {
|
||||||
s.A, s.AAlt = s.AAlt, s.A
|
s.A, s.AAlt = s.AAlt, s.A
|
||||||
temp := s.getFlagsRegister()
|
temp := s.Flags.GetFlags()
|
||||||
s.setFlagsRegister(s.getFlagsPrimeRegister())
|
s.Flags.SetFlags(s.FlagsAlt.GetFlags()) //setFlagsRegister(s.getFlagsPrimeRegister())
|
||||||
s.setFlagsPrimeRegister(temp)
|
s.FlagsAlt.SetFlags(temp)
|
||||||
},
|
},
|
||||||
// 0x09 : ADD HL, BC
|
// 0x09 : ADD HL, BC
|
||||||
0x09: func(s *Z80) {
|
0x09: func(s *Z80) {
|
||||||
@ -583,7 +583,7 @@ var instructions = []func(s *Z80){
|
|||||||
// 0xf1 : POP AF
|
// 0xf1 : POP AF
|
||||||
0xF1: func(s *Z80) {
|
0xF1: func(s *Z80) {
|
||||||
var result = s.PopWord()
|
var result = s.PopWord()
|
||||||
s.setFlagsRegister(byte(result & 0xff))
|
s.Flags.SetFlags(byte(result & 0xff))
|
||||||
s.A = byte((result & 0xff00) >> 8)
|
s.A = byte((result & 0xff00) >> 8)
|
||||||
},
|
},
|
||||||
// 0xf2 : JP P, nn
|
// 0xf2 : JP P, nn
|
||||||
@ -601,7 +601,7 @@ var instructions = []func(s *Z80){
|
|||||||
},
|
},
|
||||||
// 0xf5 : PUSH AF
|
// 0xf5 : PUSH AF
|
||||||
0xF5: func(s *Z80) {
|
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 : OR n
|
||||||
0xF6: func(s *Z80) {
|
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