Test FUSE implemented. c99.z80 passed this test now

This commit is contained in:
Роман Бойков 2026-03-11 22:44:50 +03:00
parent db6ca6e0f8
commit fc91bab922
10 changed files with 28993 additions and 208 deletions

View File

@ -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,6 +105,52 @@ 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,
@ -145,12 +167,14 @@ func (z *Z80) GetState() *z80.Z80CPU {
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,
}
}

View File

@ -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

View File

@ -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:

View File

@ -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:
{
// 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 += 1
//z.mem_ptr++
}
} // cpir
//z.cpir()
case 0xB9:
{
// 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 += 1
//z.mem_ptr++
}
} // cpdr
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

View File

@ -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
View 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++
}
}
}

View File

@ -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,
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)

View File

@ -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

File diff suppressed because it is too large Load Diff

9153
z80/tests.in Normal file

File diff suppressed because it is too large Load Diff