mirror of
https://github.com/zellyn/go6502.git
synced 2025-01-14 15:30:09 +00:00
Translate perfect6502 to go.
This commit is contained in:
parent
b268e242f8
commit
9605e2d69f
@ -11,6 +11,7 @@ import (
|
|||||||
|
|
||||||
"github.com/zellyn/go6502/asm"
|
"github.com/zellyn/go6502/asm"
|
||||||
"github.com/zellyn/go6502/cpu"
|
"github.com/zellyn/go6502/cpu"
|
||||||
|
"github.com/zellyn/go6502/visual"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Memory for the tests. Satisfies the cpu.Memory interface.
|
// Memory for the tests. Satisfies the cpu.Memory interface.
|
||||||
@ -37,14 +38,15 @@ func randomize(k *K64) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// printStatus prints out the current CPU instruction and register status.
|
// printStatus prints out the current CPU instruction and register status.
|
||||||
func printStatus(c cpu.Cpu, m K64, cc CycleCount) {
|
func printStatus(c cpu.Cpu, m K64) {
|
||||||
bytes, text, _ := asm.Disasm(c.PC(), m[c.PC()], m[c.PC()+1], m[c.PC()+2])
|
bytes, text, _ := asm.Disasm(c.PC(), m[c.PC()], m[c.PC()+1], m[c.PC()+2])
|
||||||
fmt.Printf("$%04X: %s %s A=$%02X X=$%02X Y=$%02X SP=$%02X P=$%08b - %d\n",
|
fmt.Printf("$%04X: %s %s A=$%02X X=$%02X Y=$%02X SP=$%02X P=$%08b\n",
|
||||||
c.PC(), bytes, text, c.A(), c.X(), c.Y(), c.SP(), c.P(), cc)
|
c.PC(), bytes, text, c.A(), c.X(), c.Y(), c.SP(), c.P())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run Klaus Dormann's amazing comprehensive test.
|
// Run Klaus Dormann's amazing comprehensive test against the
|
||||||
func TestFunctionalTest(t *testing.T) {
|
// instruction-level CPU emulation.
|
||||||
|
func TestFunctionalTestInstructions(t *testing.T) {
|
||||||
unused := map[byte]bool{}
|
unused := map[byte]bool{}
|
||||||
for k, _ := range cpu.Opcodes {
|
for k, _ := range cpu.Opcodes {
|
||||||
unused[k] = true
|
unused[k] = true
|
||||||
@ -74,6 +76,7 @@ func TestFunctionalTest(t *testing.T) {
|
|||||||
if c.PC() != 0x3CC5 {
|
if c.PC() != 0x3CC5 {
|
||||||
t.Errorf("Stuck at $%04X: 0x%02X", oldPC, m[oldPC])
|
t.Errorf("Stuck at $%04X: 0x%02X", oldPC, m[oldPC])
|
||||||
}
|
}
|
||||||
|
fmt.Println(cc, "ticks")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -84,6 +87,58 @@ func TestFunctionalTest(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Run the first few thousand steps of Klaus Dormann's amazing
|
||||||
|
// comprehensive test against the gate-level CPU emulation.
|
||||||
|
func TestFunctionalTestGates(t *testing.T) {
|
||||||
|
expected := []uint16{
|
||||||
|
0,
|
||||||
|
4327,
|
||||||
|
4288,
|
||||||
|
4253,
|
||||||
|
4221,
|
||||||
|
4194,
|
||||||
|
4171,
|
||||||
|
4152,
|
||||||
|
4137,
|
||||||
|
4105,
|
||||||
|
4374,
|
||||||
|
4368,
|
||||||
|
}
|
||||||
|
bytes, err := ioutil.ReadFile("6502_functional_test.bin")
|
||||||
|
if err != nil {
|
||||||
|
panic("Cannot read file")
|
||||||
|
}
|
||||||
|
var m K64
|
||||||
|
randomize(&m)
|
||||||
|
OFFSET := 0xa
|
||||||
|
copy(m[OFFSET:len(bytes)+OFFSET], bytes)
|
||||||
|
// Set the RESET vector to jump to the tests
|
||||||
|
m[0xFFFC] = 0x00
|
||||||
|
m[0xFFFD] = 0x10
|
||||||
|
c := visual.NewCPU(&m)
|
||||||
|
c.Reset()
|
||||||
|
count := uint64(0)
|
||||||
|
for {
|
||||||
|
count++
|
||||||
|
if count%1000 == 0 {
|
||||||
|
pc := c.PC()
|
||||||
|
if pc == 0x3CC5 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if count/1000 < uint64(len(expected)) {
|
||||||
|
expectedPC := expected[count/1000]
|
||||||
|
if pc != expectedPC {
|
||||||
|
t.Errorf("Expected PC=%d at step %d, got %d", expectedPC, count, pc)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// printStatus(c, m)
|
||||||
|
c.Step()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Run Bruce Clark's decimal test in 6502 mode.
|
// Run Bruce Clark's decimal test in 6502 mode.
|
||||||
func TestDecimalMode6502(t *testing.T) {
|
func TestDecimalMode6502(t *testing.T) {
|
||||||
bytes, err := ioutil.ReadFile("decimal_mode.bin")
|
bytes, err := ioutil.ReadFile("decimal_mode.bin")
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
/*
|
/*
|
||||||
Package visual provides routines for emulating a 6502 at the
|
Package visual provides routines for emulating a 6502 at the
|
||||||
transistor level, using data from visual6502.org by way of
|
transistor level, using data from visual6502.org.
|
||||||
https://github.com/mist64/perfect6502.
|
|
||||||
|
It started out as an almost token-for-token translation of
|
||||||
|
https://github.com/mist64/perfect6502, and is slowly changing to be a
|
||||||
|
little more go-like, although keeping it fast seems to mean keeping
|
||||||
|
the c-nature.
|
||||||
*/
|
*/
|
||||||
package visual
|
package visual
|
||||||
|
168
visual/visual.go
168
visual/visual.go
@ -1,26 +1,21 @@
|
|||||||
package visual
|
package visual
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"github.com/willf/bitset"
|
||||||
|
|
||||||
"github.com/zellyn/bitset"
|
icpu "github.com/zellyn/go6502/cpu" // Just need the interface
|
||||||
|
|
||||||
icpu "github.com/zellyn/go6502/cpu"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type cpu struct {
|
type cpu struct {
|
||||||
m icpu.Memory
|
m icpu.Memory
|
||||||
cycle uint64
|
cycle uint64
|
||||||
nodeValues *bitset.BitSet
|
nodeValues []byte // Bitmask of node values (see const VAL_* below)
|
||||||
nodePullups *bitset.BitSet
|
|
||||||
nodePulldowns *bitset.BitSet
|
|
||||||
|
|
||||||
nodeGateCounts [NODES]uint // the number of transistor gates attached to a node
|
nodeGateCounts [NODES]uint // the number of transistor gates attached to a node
|
||||||
nodeGates [NODES][NODES]uint // the list of transistor indexes attached to a node
|
nodeGates [NODES][NODES]uint // the list of transistor indexes attached to a node
|
||||||
nodeC1C2Counts [NODES]uint // the number of transistor c1/c2s attached to a node
|
nodeC1C2Counts [NODES]uint // the number of transistor c1/c2s attached to a node
|
||||||
nodeC1C2s [NODES][2 * NODES]uint // the list of transistor c1/c2s attached to a node
|
nodeC1C2s [NODES][2 * NODES]uint // the list of transistor c1/c2s attached to a node
|
||||||
|
|
||||||
transistorValues *bitset.BitSet
|
transistorValues []bool
|
||||||
transistorGates [TRANSISTORS]uint
|
transistorGates [TRANSISTORS]uint
|
||||||
transistorC1s [TRANSISTORS]uint
|
transistorC1s [TRANSISTORS]uint
|
||||||
transistorC2s [TRANSISTORS]uint
|
transistorC2s [TRANSISTORS]uint
|
||||||
@ -31,11 +26,55 @@ type cpu struct {
|
|||||||
listIn []uint
|
listIn []uint
|
||||||
listOut []uint
|
listOut []uint
|
||||||
|
|
||||||
groupList []uint
|
groupList []uint
|
||||||
groupSet *bitset.BitSet
|
groupSet *bitset.BitSet
|
||||||
groupContainsPullup bool
|
groupValue byte
|
||||||
groupContainsPulldown bool
|
}
|
||||||
groupContainsHi bool
|
|
||||||
|
// Bitfield for node values.
|
||||||
|
const (
|
||||||
|
VAL_HI = 1 << iota // We count on this being bit 0, so we can mask it out for 0 or 1.
|
||||||
|
VAL_PULLUP
|
||||||
|
VAL_PULLDOWN
|
||||||
|
VAL_VCC
|
||||||
|
VAL_VSS
|
||||||
|
)
|
||||||
|
|
||||||
|
// The lookup table for the group value. If vss is in the group, it's 0, vcc makes it 1, etc.
|
||||||
|
// vss, vcc, pulldown, pullup, hi
|
||||||
|
var GroupValues = [32]byte{
|
||||||
|
0, // 00000 - nothing
|
||||||
|
1, // 00001 - contains at least one hi node
|
||||||
|
1, // 00010 - contains at least one pullup
|
||||||
|
1, // 00011 - contains at least one pullup
|
||||||
|
0, // 00100 - contains at least one pulldown
|
||||||
|
0, // 00101 - contains at least one pulldown
|
||||||
|
0, // 00110 - contains at least one pulldown
|
||||||
|
0, // 00111 - contains at least one pulldown
|
||||||
|
1, // 01000 - contains vcc
|
||||||
|
1, // 01001 - contains vcc
|
||||||
|
1, // 01010 - contains vcc
|
||||||
|
1, // 01011 - contains vcc
|
||||||
|
1, // 01100 - contains vcc
|
||||||
|
1, // 01101 - contains vcc
|
||||||
|
1, // 01110 - contains vcc
|
||||||
|
1, // 01111 - contains vcc
|
||||||
|
0, // 10000- contains vss
|
||||||
|
0, // 10001- contains vss
|
||||||
|
0, // 10010- contains vss
|
||||||
|
0, // 10011- contains vss
|
||||||
|
0, // 10100- contains vss
|
||||||
|
0, // 10101- contains vss
|
||||||
|
0, // 10110- contains vss
|
||||||
|
0, // 10111- contains vss
|
||||||
|
0, // 11000- contains vss
|
||||||
|
0, // 11001- contains vss
|
||||||
|
0, // 11010- contains vss
|
||||||
|
0, // 11011- contains vss
|
||||||
|
0, // 11100- contains vss
|
||||||
|
0, // 11101- contains vss
|
||||||
|
0, // 11110- contains vss
|
||||||
|
0, // 11111- contains vss
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCPU(memory icpu.Memory) icpu.Cpu {
|
func NewCPU(memory icpu.Memory) icpu.Cpu {
|
||||||
@ -44,6 +83,7 @@ func NewCPU(memory icpu.Memory) icpu.Cpu {
|
|||||||
return &c
|
return &c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Needed for the interface. Not really practical. I guess we could try changing the nodes directly.
|
||||||
func (c *cpu) SetPC(uint16) {
|
func (c *cpu) SetPC(uint16) {
|
||||||
panic("Not implemented")
|
panic("Not implemented")
|
||||||
}
|
}
|
||||||
@ -105,10 +145,7 @@ func (c *cpu) PC() uint16 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *cpu) nodeBit(n uint) byte {
|
func (c *cpu) nodeBit(n uint) byte {
|
||||||
if c.nodeValues.Test(n) {
|
return c.nodeValues[n] & VAL_HI // 1
|
||||||
return 1
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *cpu) writeDataBus(d byte) {
|
func (c *cpu) writeDataBus(d byte) {
|
||||||
@ -119,12 +156,16 @@ func (c *cpu) writeDataBus(d byte) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *cpu) Reset() {
|
func (c *cpu) Reset() {
|
||||||
fmt.Println("Reset called")
|
|
||||||
// All nodes down
|
// All nodes down
|
||||||
c.nodeValues.ClearAll()
|
for i := range c.nodeValues {
|
||||||
|
c.nodeValues[i] &^= VAL_HI
|
||||||
|
}
|
||||||
|
|
||||||
// All transistors off
|
// All transistors off
|
||||||
c.transistorValues.ClearAll()
|
|
||||||
|
for i := range c.transistorValues {
|
||||||
|
c.transistorValues[i] = false
|
||||||
|
}
|
||||||
|
|
||||||
c.setNode(NODE_res, false)
|
c.setNode(NODE_res, false)
|
||||||
c.setNode(NODE_clk0, true)
|
c.setNode(NODE_clk0, true)
|
||||||
@ -137,7 +178,6 @@ func (c *cpu) Reset() {
|
|||||||
|
|
||||||
// Hold RESET for 8 cycles
|
// Hold RESET for 8 cycles
|
||||||
for i := 0; i < 8; i++ {
|
for i := 0; i < 8; i++ {
|
||||||
fmt.Println("Reset step ", i)
|
|
||||||
c.Step()
|
c.Step()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,16 +198,7 @@ func (c *cpu) addNodeToGroup(n uint) {
|
|||||||
c.groupSet.Set(n)
|
c.groupSet.Set(n)
|
||||||
c.groupList = append(c.groupList, n)
|
c.groupList = append(c.groupList, n)
|
||||||
|
|
||||||
if c.nodePullups.Test(n) {
|
c.groupValue |= c.nodeValues[n]
|
||||||
c.groupContainsPullup = true
|
|
||||||
}
|
|
||||||
if c.nodePulldowns.Test(n) {
|
|
||||||
c.groupContainsPulldown = true
|
|
||||||
}
|
|
||||||
if c.nodeValues.Test(n) {
|
|
||||||
c.groupContainsHi = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if n == NODE_vss || n == NODE_vcc {
|
if n == NODE_vss || n == NODE_vcc {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -175,7 +206,7 @@ func (c *cpu) addNodeToGroup(n uint) {
|
|||||||
/* revisit all transistors that are controlled by this node */
|
/* revisit all transistors that are controlled by this node */
|
||||||
for t := uint(0); t < c.nodeC1C2Counts[n]; t++ {
|
for t := uint(0); t < c.nodeC1C2Counts[n]; t++ {
|
||||||
tn := c.nodeC1C2s[n][t]
|
tn := c.nodeC1C2s[n][t]
|
||||||
if c.transistorValues.Test(tn) {
|
if c.transistorValues[tn] {
|
||||||
if c.transistorC1s[tn] == n {
|
if c.transistorC1s[tn] == n {
|
||||||
c.addNodeToGroup(c.transistorC2s[tn])
|
c.addNodeToGroup(c.transistorC2s[tn])
|
||||||
} else {
|
} else {
|
||||||
@ -187,30 +218,12 @@ func (c *cpu) addNodeToGroup(n uint) {
|
|||||||
|
|
||||||
func (c *cpu) addAllNodesToGroup(node uint) {
|
func (c *cpu) addAllNodesToGroup(node uint) {
|
||||||
c.groupList = c.groupList[0:0]
|
c.groupList = c.groupList[0:0]
|
||||||
|
c.groupValue = 0
|
||||||
c.groupSet.ClearAll()
|
c.groupSet.ClearAll()
|
||||||
c.groupContainsPullup = false
|
|
||||||
c.groupContainsPulldown = false
|
|
||||||
c.groupContainsHi = false
|
|
||||||
|
|
||||||
c.addNodeToGroup(node)
|
c.addNodeToGroup(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *cpu) getGroupValue() bool {
|
|
||||||
if c.groupSet.Test(NODE_vss) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if c.groupSet.Test(NODE_vcc) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if c.groupContainsPulldown {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if c.groupContainsPullup {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return c.groupContainsHi
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *cpu) recalcNode(node uint) {
|
func (c *cpu) recalcNode(node uint) {
|
||||||
/*
|
/*
|
||||||
* get all nodes that are connected through
|
* get all nodes that are connected through
|
||||||
@ -219,7 +232,7 @@ func (c *cpu) recalcNode(node uint) {
|
|||||||
c.addAllNodesToGroup(node)
|
c.addAllNodesToGroup(node)
|
||||||
|
|
||||||
/* get the state of the group */
|
/* get the state of the group */
|
||||||
newv := c.getGroupValue()
|
newv := GroupValues[c.groupValue]
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* - set all nodes to the group state
|
* - set all nodes to the group state
|
||||||
@ -228,11 +241,11 @@ func (c *cpu) recalcNode(node uint) {
|
|||||||
* for the next run
|
* for the next run
|
||||||
*/
|
*/
|
||||||
for _, nn := range c.groupList {
|
for _, nn := range c.groupList {
|
||||||
if c.nodeValues.Test(nn) != newv {
|
if c.nodeValues[nn]&VAL_HI != newv {
|
||||||
c.nodeValues.SetTo(nn, newv)
|
c.nodeValues[nn] ^= VAL_HI
|
||||||
for t := uint(0); t < c.nodeGateCounts[nn]; t++ {
|
for t := uint(0); t < c.nodeGateCounts[nn]; t++ {
|
||||||
tn := c.nodeGates[nn][t]
|
tn := c.nodeGates[nn][t]
|
||||||
c.transistorValues.Flip(tn)
|
c.transistorValues[tn] = !c.transistorValues[tn]
|
||||||
}
|
}
|
||||||
c.listOut = append(c.listOut, nn)
|
c.listOut = append(c.listOut, nn)
|
||||||
}
|
}
|
||||||
@ -287,14 +300,28 @@ func (c *cpu) recalcAllNodes() {
|
|||||||
/* Node State */
|
/* Node State */
|
||||||
/**************/
|
/**************/
|
||||||
|
|
||||||
|
// So we don't have to keep re-allocating
|
||||||
|
var oneNode = []uint{0}
|
||||||
|
|
||||||
func (c *cpu) setNode(nn uint, state bool) {
|
func (c *cpu) setNode(nn uint, state bool) {
|
||||||
c.nodePullups.SetTo(nn, state)
|
oldState := c.nodeValues[nn]
|
||||||
c.nodePulldowns.SetTo(nn, !state)
|
newState := oldState
|
||||||
c.recalcNodeList([]uint{nn})
|
if state {
|
||||||
|
newState &^= VAL_PULLDOWN
|
||||||
|
newState |= VAL_PULLUP
|
||||||
|
} else {
|
||||||
|
newState &^= VAL_PULLUP
|
||||||
|
newState |= VAL_PULLDOWN
|
||||||
|
}
|
||||||
|
if newState != oldState {
|
||||||
|
c.nodeValues[nn] = newState
|
||||||
|
oneNode[0] = nn
|
||||||
|
c.recalcNodeList(oneNode)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *cpu) isNodeHigh(n uint) bool {
|
func (c *cpu) isNodeHigh(n uint) bool {
|
||||||
return c.nodeValues.Test(n)
|
return c.nodeValues[n]&VAL_HI > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleMemory is called when clk0 is low, and either reads from or
|
// handleMemory is called when clk0 is low, and either reads from or
|
||||||
@ -342,18 +369,25 @@ func (c *cpu) addNodeDependant(a, b uint) {
|
|||||||
func (c *cpu) setupNodesAndTransistors() {
|
func (c *cpu) setupNodesAndTransistors() {
|
||||||
|
|
||||||
// Zero out bitsets
|
// Zero out bitsets
|
||||||
c.nodeValues = bitset.New(NODES)
|
c.transistorValues = make([]bool, TRANSISTORS)
|
||||||
c.nodePullups = bitset.New(NODES)
|
|
||||||
c.nodePulldowns = bitset.New(NODES)
|
|
||||||
c.transistorValues = bitset.New(TRANSISTORS)
|
|
||||||
c.groupSet = bitset.New(NODES)
|
c.groupSet = bitset.New(NODES)
|
||||||
c.groupList = make([]uint, 0, NODES)
|
c.groupList = make([]uint, 0, NODES)
|
||||||
|
c.nodeValues = make([]byte, NODES)
|
||||||
|
|
||||||
// Copy node data from SegDefs into r/w data structures
|
// Copy node data from SegDefs into r/w data structures
|
||||||
for i := uint(0); i < NODES; i++ {
|
for i := uint(0); i < NODES; i++ {
|
||||||
c.nodePullups.SetTo(i, SegDefs[i])
|
|
||||||
c.nodeGateCounts[i] = 0
|
c.nodeGateCounts[i] = 0
|
||||||
c.nodeC1C2Counts[i] = 0
|
c.nodeC1C2Counts[i] = 0
|
||||||
|
|
||||||
|
if SegDefs[i] {
|
||||||
|
c.nodeValues[i] = VAL_PULLUP
|
||||||
|
}
|
||||||
|
if i == NODE_vss {
|
||||||
|
c.nodeValues[i] |= VAL_VSS
|
||||||
|
}
|
||||||
|
if i == NODE_vcc {
|
||||||
|
c.nodeValues[i] |= VAL_VCC
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy transistor data from TransDefs into r/w data structures
|
// Copy transistor data from TransDefs into r/w data structures
|
||||||
|
Loading…
x
Reference in New Issue
Block a user