mirror of
https://github.com/ivanizag/izapple2.git
synced 2025-01-10 12:30:17 +00:00
First run with https://github.com/TomHarte/ProcessorTests
This commit is contained in:
parent
fe4afa1de9
commit
3f916a60fa
@ -92,6 +92,14 @@ func resolveAddress(s *State, line []uint8, opcode opcode) uint16 {
|
||||
case modeIndirect:
|
||||
addressAddress := getWordInLine(line)
|
||||
address = getWord(s.mem, addressAddress)
|
||||
//address = getWordNoCrossPage(s.mem, addressAddress)
|
||||
|
||||
/*
|
||||
The tests from https://github.com/Klaus2m5/6502_65C02_functional_tests
|
||||
pass with getWord(), but the tests in https://github.com/TomHarte/ProcessorTests/tree/main/6502/v1
|
||||
need getWordNoCrossPage().
|
||||
*/
|
||||
|
||||
case modeIndirectIndexedY:
|
||||
base := getZeroPageWord(s.mem, line[1])
|
||||
address, extraCycle = addOffset(base, s.reg.getY())
|
||||
@ -162,7 +170,7 @@ func lineString(line []uint8, opcode opcode) string {
|
||||
case modeImplicitY:
|
||||
//Nothing
|
||||
case modeAccumulator:
|
||||
t += fmt.Sprintf(" A")
|
||||
t += " A"
|
||||
case modeImmediate:
|
||||
t += fmt.Sprintf(" #$%02x", line[1])
|
||||
case modeZeroPage:
|
||||
|
115
core6502/harteSuite_test.go
Normal file
115
core6502/harteSuite_test.go
Normal file
@ -0,0 +1,115 @@
|
||||
package core6502
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type scenarioState struct {
|
||||
Pc uint16
|
||||
S uint8
|
||||
A uint8
|
||||
X uint8
|
||||
Y uint8
|
||||
P uint8
|
||||
Ram [][]uint16
|
||||
}
|
||||
|
||||
type scenario struct {
|
||||
Name string
|
||||
Initial scenarioState
|
||||
Final scenarioState
|
||||
Cycles [][]interface{}
|
||||
}
|
||||
|
||||
/*
|
||||
Tests from https://github.com/TomHarte/ProcessorTests/tree/main/6502/v1
|
||||
more work needed.
|
||||
*/
|
||||
func TestHarteNMOS6502(t *testing.T) {
|
||||
t.Skip("Not ready to be used in CI")
|
||||
|
||||
s := NewNMOS6502(nil) // Use to get the opcodes names
|
||||
|
||||
path := "/home/casa/code/ProcessorTests/6502/v1/"
|
||||
for i := 0x00; i <= 0xff; /*0xff*/ i++ {
|
||||
if s.opcodes[i].name != "ADC" && // Issue with ADC crossing page boundaries
|
||||
s.opcodes[i].name != "SBC" && // Issue with ADC crossing page boundaries
|
||||
s.opcodes[i].name != "" {
|
||||
|
||||
opcode := fmt.Sprintf("%02x", i)
|
||||
t.Run(opcode+s.opcodes[i].name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
testOpcode(t, path, opcode)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testOpcode(t *testing.T, path string, opcode string) {
|
||||
m := new(FlatMemory)
|
||||
s := NewNMOS6502(m)
|
||||
|
||||
data, err := os.ReadFile(path + opcode + ".json")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var scenarios []scenario
|
||||
err = json.Unmarshal(data, &scenarios)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for _, scenario := range scenarios {
|
||||
if scenario.Name != "20 55 13" { // Skip JSR on the stack being modified
|
||||
t.Run(scenario.Name, func(t *testing.T) {
|
||||
testScenario(t, s, &scenario)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func testScenario(t *testing.T, s *State, sc *scenario) {
|
||||
// Setup CPU
|
||||
s.reg.setPC(sc.Initial.Pc)
|
||||
s.reg.setSP(sc.Initial.S)
|
||||
s.reg.setA(sc.Initial.A)
|
||||
s.reg.setX(sc.Initial.X)
|
||||
s.reg.setY(sc.Initial.Y)
|
||||
s.reg.setP(sc.Initial.P)
|
||||
for _, e := range sc.Initial.Ram {
|
||||
s.mem.Poke(uint16(e[0]), uint8(e[1]))
|
||||
}
|
||||
|
||||
// Execute instruction
|
||||
s.ExecuteInstruction()
|
||||
|
||||
// Check result
|
||||
assertReg8(t, sc, "A", s.reg.getA(), sc.Final.A)
|
||||
assertReg8(t, sc, "X", s.reg.getX(), sc.Final.X)
|
||||
assertReg8(t, sc, "Y", s.reg.getY(), sc.Final.Y)
|
||||
assertFlags(t, sc, sc.Initial.P, s.reg.getP(), sc.Final.P)
|
||||
assertReg8(t, sc, "SP", s.reg.getSP(), sc.Final.S)
|
||||
assertReg16(t, sc, "PC", s.reg.getPC(), sc.Final.Pc)
|
||||
}
|
||||
|
||||
func assertReg8(t *testing.T, sc *scenario, name string, actual uint8, wanted uint8) {
|
||||
if actual != wanted {
|
||||
t.Errorf("Register %s is %02x and should be %02x for %+v", name, actual, wanted, sc)
|
||||
}
|
||||
}
|
||||
|
||||
func assertReg16(t *testing.T, sc *scenario, name string, actual uint16, wanted uint16) {
|
||||
if actual != wanted {
|
||||
t.Errorf("Register %s is %04x and should be %04x for %+v", name, actual, wanted, sc)
|
||||
}
|
||||
}
|
||||
|
||||
func assertFlags(t *testing.T, sc *scenario, initial uint8, actual uint8, wanted uint8) {
|
||||
if actual != wanted {
|
||||
t.Errorf("%08b flag diffs, they are %08b and should be %08b, initial %08b for %+v", actual^wanted, actual, wanted, initial, sc)
|
||||
}
|
||||
}
|
@ -16,6 +16,15 @@ func getWord(m Memory, address uint16) uint16 {
|
||||
return uint16(m.Peek(address)) + 0x100*uint16(m.Peek(address+1))
|
||||
}
|
||||
|
||||
func getWordNoCrossPage(m Memory, address uint16) uint16 {
|
||||
addressMSB := address + 1
|
||||
if address&0xff == 0xff {
|
||||
// We won't cross the page bounday for the MSB byte
|
||||
addressMSB -= 0x100
|
||||
}
|
||||
return uint16(m.Peek(address)) + 0x100*uint16(m.Peek(addressMSB))
|
||||
}
|
||||
|
||||
func getZeroPageWord(m Memory, address uint8) uint16 {
|
||||
return uint16(m.Peek(uint16(address))) + 0x100*uint16(m.Peek(uint16(address+1)))
|
||||
}
|
||||
|
@ -184,6 +184,6 @@ var opcodesNMOS6502 = [256]opcode{
|
||||
|
||||
// Undocumented opcodes, see http://bbc.nvg.org/doc/6502OpList.txt
|
||||
0x1A: {"NOP", 1, 2, modeImplicit, opNOP}, // INC A in the 65c02
|
||||
0xC2: {"NOP", 1, 2, modeImplicit, opNOP}, // Should be HALT?
|
||||
0x02: {"NOP", 1, 2, modeImplicit, opNOP}, // Should be HALT?
|
||||
0xC2: {"NOP", 2, 2, modeImplicit, opNOP},
|
||||
0x02: {"NOP", 1, 1, modeImplicit, opHALT},
|
||||
}
|
||||
|
@ -255,7 +255,9 @@ func buildOpPull(regDst int) opFunc {
|
||||
return func(s *State, line []uint8, opcode opcode) {
|
||||
value := pullByte(s)
|
||||
s.reg.setRegister(regDst, value)
|
||||
if regDst != regP {
|
||||
if regDst == regP {
|
||||
s.reg.updateFlag5B()
|
||||
} else {
|
||||
s.reg.updateFlagZN(value)
|
||||
}
|
||||
}
|
||||
@ -278,6 +280,10 @@ func opJMP(s *State, line []uint8, opcode opcode) {
|
||||
|
||||
func opNOP(s *State, line []uint8, opcode opcode) {}
|
||||
|
||||
func opHALT(s *State, line []uint8, opcode opcode) {
|
||||
s.reg.setPC(s.reg.getPC() - 1)
|
||||
}
|
||||
|
||||
func opJSR(s *State, line []uint8, opcode opcode) {
|
||||
pushWord(s, s.reg.getPC()-1)
|
||||
address := resolveAddress(s, line, opcode)
|
||||
@ -286,6 +292,7 @@ func opJSR(s *State, line []uint8, opcode opcode) {
|
||||
|
||||
func opRTI(s *State, line []uint8, opcode opcode) {
|
||||
s.reg.setP(pullByte(s))
|
||||
s.reg.updateFlag5B()
|
||||
s.reg.setPC(pullWord(s))
|
||||
}
|
||||
|
||||
|
@ -86,6 +86,11 @@ func (r *registers) updateFlagZN(t uint8) {
|
||||
r.updateFlag(flagN, t >= (1<<7))
|
||||
}
|
||||
|
||||
func (r *registers) updateFlag5B() {
|
||||
r.setFlag(flag5)
|
||||
r.clearFlag(flagB)
|
||||
}
|
||||
|
||||
func (r registers) String() string {
|
||||
//ch := (r.getA() & 0x3F) + 0x40
|
||||
ch := (r.getA() & 0x7F)
|
||||
|
Loading…
x
Reference in New Issue
Block a user