This commit is contained in:
Ivan Izaguirre 2021-09-25 20:33:45 +02:00
parent fe4afa1de9
commit 3f916a60fa
6 changed files with 148 additions and 4 deletions

View File

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

View File

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

View File

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

View File

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

View File

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