package visual import ( icpu "github.com/zellyn/go6502/cpu" // Just need the interface ) type cpu struct { m icpu.Memory cycle uint64 nodes uint // number of nodes transistors uint // number of transistors vss uint vcc uint nodesPullup bitmap nodesPulldown bitmap nodesValue bitmap nodeGates [][]uint // the list of transistor indexes attached to a node nodeC1C2s [][]uint // the list of transistor c1/c2s attached to a node nodeDependents [][]uint // all C1 and C2 nodes of transistors attached to a node nodeLeftDependents [][]uint // TODO(zellyn): doc transistorsGate []uint transistorsC1 []uint transistorsC2 []uint transistorsOn bitmap listIn []uint // the nodes we are working with listOut []uint // the indirect nodes we are collecting for the next run listOutBitmap bitmap group []uint groupBitmap bitmap groupContainsValue groupContains } type groupContains uint8 const ( CONTAINS_NOTHING groupContains = iota CONTAINS_HI CONTAINS_PULLUP CONTAINS_PULLDOWN CONTAINS_VCC CONTAINS_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 { c := cpu{m: memory} c.setupNodesAndTransistors(TransDefs, SegDefs, NODE_vss, NODE_vcc) return &c } // Needed for the interface. Not really practical. I guess we could try changing the nodes directly. func (c *cpu) SetPC(uint16) { panic("Not implemented") } /************************************/ /* Interfacing and extracting state */ /************************************/ func (c *cpu) Read8(n0, n1, n2, n3, n4, n5, n6, n7 uint) byte { return (c.nodeBit(n0) | c.nodeBit(n1)<<1 | c.nodeBit(n2)<<2 | c.nodeBit(n3)<<3 | c.nodeBit(n4)<<4 | c.nodeBit(n5)<<5 | c.nodeBit(n6)<<6 | c.nodeBit(n7)<<7) } func (c *cpu) AddressBus() uint16 { abl := uint16(c.Read8(NODE_ab0, NODE_ab1, NODE_ab2, NODE_ab3, NODE_ab4, NODE_ab5, NODE_ab6, NODE_ab7)) abh := uint16(c.Read8(NODE_ab8, NODE_ab9, NODE_ab10, NODE_ab11, NODE_ab12, NODE_ab13, NODE_ab14, NODE_ab15)) return abl + abh<<8 } func (c *cpu) DataBus() byte { return c.Read8(NODE_db0, NODE_db1, NODE_db2, NODE_db3, NODE_db4, NODE_db5, NODE_db6, NODE_db7) } func (c *cpu) A() byte { return c.Read8(NODE_a0, NODE_a1, NODE_a2, NODE_a3, NODE_a4, NODE_a5, NODE_a6, NODE_a7) } func (c *cpu) X() byte { return c.Read8(NODE_x0, NODE_x1, NODE_x2, NODE_x3, NODE_x4, NODE_x5, NODE_x6, NODE_x7) } func (c *cpu) Y() byte { return c.Read8(NODE_y0, NODE_y1, NODE_y2, NODE_y3, NODE_y4, NODE_y5, NODE_y6, NODE_y7) } func (c *cpu) P() byte { return c.Read8(NODE_p0, NODE_p1, NODE_p2, NODE_p3, NODE_p4, NODE_p5, NODE_p6, NODE_p7) } func (c *cpu) SP() byte { return c.Read8(NODE_s0, NODE_s1, NODE_s2, NODE_s3, NODE_s4, NODE_s5, NODE_s6, NODE_s7) } func (c *cpu) IR() byte { return c.Read8(NODE_notir0, NODE_notir1, NODE_notir2, NODE_notir3, NODE_notir4, NODE_notir5, NODE_notir6, NODE_notir7) ^ 0xFF } func (c *cpu) PCL() byte { return c.Read8(NODE_pcl0, NODE_pcl1, NODE_pcl2, NODE_pcl3, NODE_pcl4, NODE_pcl5, NODE_pcl6, NODE_pcl7) } func (c *cpu) PCH() byte { return c.Read8(NODE_pch0, NODE_pch1, NODE_pch2, NODE_pch3, NODE_pch4, NODE_pch5, NODE_pch6, NODE_pch7) } func (c *cpu) PC() uint16 { return uint16(c.PCH())<<8 + uint16(c.PCL()) } func (c *cpu) nodeBit(n uint) byte { if c.getNodeValue(n) { return 1 } return 0 } func (c *cpu) writeDataBus(d byte) { for i := 0; i < 8; i++ { c.setNode(DataBusNodes[i], d&1 == 1) d >>= 1 } } func (c *cpu) Reset() { c.setNode(NODE_res, false) c.setNode(NODE_clk0, true) c.setNode(NODE_rdy, true) c.setNode(NODE_so, false) c.setNode(NODE_irq, true) c.setNode(NODE_nmi, true) c.stabilizeChip() // Hold RESET for 8 cycles for i := 0; i < 8; i++ { c.Step() } c.setNode(NODE_res, true) c.recalcNodeList() c.cycle = 0 } // handleMemory is called when clk0 is low, and either reads from or // writes to memory, depending on rw. func (c *cpu) handleMemory() { if c.isNodeHigh(NODE_rw) { c.writeDataBus(c.m.Read(c.AddressBus())) } else { c.m.Write(c.AddressBus(), c.DataBus()) } } // HalfStep is the main clock loop, and takes a half clock step. func (c *cpu) HalfStep() { clk := c.isNodeHigh(NODE_clk0) /* invert clock */ c.setNode(NODE_clk0, !clk) c.recalcNodeList() if !clk { c.handleMemory() } c.cycle++ } // Step takes two half steps. func (c *cpu) Step() error { c.HalfStep() c.HalfStep() return nil } /************************/ /* Algorithms for Nodes */ /************************/ /* * The "value" propertiy of VCC and GND is never evaluated in the code, * so we don't bother initializing it properly or special-casing writes. */ func (c *cpu) setNodePullup(t uint, s bool) { c.nodesPullup.set(t, s) } func (c *cpu) getNodePullup(t uint) bool { return c.nodesPullup.get(t) } func (c *cpu) setNodePulldown(t uint, s bool) { c.nodesPulldown.set(t, s) } func (c *cpu) getNodePulldown(t uint) bool { return c.nodesPulldown.get(t) } func (c *cpu) setNodeValue(t uint, s bool) { c.nodesValue.set(t, s) } func (c *cpu) getNodeValue(t uint) bool { return c.nodesValue.get(t) } /******************************/ /* Algorithms for Transistors */ /******************************/ func (c *cpu) setTransistorOn(t uint, s bool) { c.transistorsOn.set(t, s) } func (c *cpu) getTransistorOn(t uint) bool { return c.transistorsOn.get(t) } /************************/ /* Algorithms for Lists */ /************************/ func (c *cpu) switchLists() { c.listIn, c.listOut = c.listOut, c.listIn } func (c *cpu) clearListOut() { c.listOut = c.listOut[:0] c.listOutBitmap.clear() } func (c *cpu) listOutAdd(i uint) { if !c.listOutBitmap.get(i) { c.listOut = append(c.listOut, i) c.listOutBitmap.set(i, true) } } /**********************************/ /* Algorithms for Groups of Nodes */ /**********************************/ /* * a group is a set of connected nodes, which consequently * share the same value * * we use an array and a count for O(1) insert and * iteration, and a redundant bitmap for O(1) lookup */ func (c *cpu) groupClear() { c.group = c.group[:0] c.groupBitmap.clear() } func (c *cpu) groupAdd(i uint) { c.group = append(c.group, i) c.groupBitmap.set(i, true) } func (c *cpu) groupContains(el uint) bool { return c.groupBitmap.get(el) } func (c *cpu) groupCount() uint { return uint(len(c.group)) } /*********************************/ /* Node and Transistor Emulation */ /*********************************/ func (c *cpu) addNodeToGroup(n uint) { /* * We need to stop at vss and vcc, otherwise we'll revisit other groups * with the same value - just because they all derive their value from * the fact that they are connected to vcc or vss. */ if n == c.vss { c.groupContainsValue = CONTAINS_VSS return } if n == c.vcc { if c.groupContainsValue != CONTAINS_VSS { c.groupContainsValue = CONTAINS_VCC } return } if c.groupContains(n) { return } c.groupAdd(n) if c.groupContainsValue < CONTAINS_PULLDOWN && c.getNodePulldown(n) { c.groupContainsValue = CONTAINS_PULLDOWN } if c.groupContainsValue < CONTAINS_PULLUP && c.getNodePullup(n) { c.groupContainsValue = CONTAINS_PULLUP } if c.groupContainsValue < CONTAINS_HI && c.getNodeValue(n) { c.groupContainsValue = CONTAINS_HI } /* revisit all transistors that control this node */ for _, tn := range c.nodeC1C2s[n] { /* if the transistor connects c1 and c2... */ if c.getTransistorOn(uint(tn)) { /* if original node was connected to c1, continue with c2 */ if c.transistorsC1[tn] == n { c.addNodeToGroup(c.transistorsC2[tn]) } else { c.addNodeToGroup(c.transistorsC1[tn]) } } } } func (c *cpu) addAllNodesToGroup(node uint) { c.groupClear() c.groupContainsValue = CONTAINS_NOTHING c.addNodeToGroup(node) } func (c *cpu) getGroupValue() bool { switch c.groupContainsValue { case CONTAINS_VCC, CONTAINS_PULLUP, CONTAINS_HI: return true case CONTAINS_VSS, CONTAINS_PULLDOWN, CONTAINS_NOTHING: return false } panic("cannot get here") } func (c *cpu) recalcNode(node uint) { /* * get all nodes that are connected through * transistors, starting with this one */ c.addAllNodesToGroup(node) /* get the state of the group */ newv := c.getGroupValue() /* * - set all nodes to the group state * - check all transistors switched by nodes of the group * - collect all nodes behind toggled transistors * for the next run */ for _, nn := range c.group { if c.getNodeValue(nn) != newv { c.setNodeValue(nn, newv) for _, tn := range c.nodeGates[nn] { c.setTransistorOn(tn, newv) } if newv { for _, dp := range c.nodeLeftDependents[nn] { c.listOutAdd(dp) } } else { for _, dp := range c.nodeDependents[nn] { c.listOutAdd(dp) } } } } } func (c *cpu) recalcNodeList() { for j := 0; j < 100; j++ { /* loop limiter */ /* * make the secondary list our primary list, use * the data storage of the primary list as the * secondary list */ c.switchLists() if len(c.listIn) == 0 { break } c.clearListOut() /* * for all nodes, follow their paths through * turned-on transistors, find the state of the * path and assign it to all nodes, and re-evaluate * all transistors controlled by this path, collecting * all nodes that changed because of it for the next run */ for _, n := range c.listIn { c.recalcNode(n) } } c.clearListOut() } /******************/ /* Initialization */ /******************/ func (c *cpu) addNodeDependent(a uint, b uint) { for _, dp := range c.nodeDependents[a] { if dp == b { return } } c.nodeDependents[a] = append(c.nodeDependents[a], b) } func (c *cpu) addNodeLeftDependent(a uint, b uint) { for _, dp := range c.nodeLeftDependents[a] { if dp == b { return } } c.nodeLeftDependents[a] = append(c.nodeLeftDependents[a], b) } func (c *cpu) setupNodesAndTransistors(transDefs []TransDef, nodeIsPullup []bool, vss uint, vcc uint) { c.nodes = uint(len(nodeIsPullup)) c.transistors = uint(len(transDefs)) c.vss = vss c.vcc = vcc c.nodesPullup = newBitmap(c.nodes) c.nodesPulldown = newBitmap(c.nodes) c.nodesValue = newBitmap(c.nodes) c.transistorsOn = newBitmap(c.transistors) c.listOutBitmap = newBitmap(c.nodes) c.groupBitmap = newBitmap(c.nodes) c.nodeGates = make([][]uint, c.transistors) c.nodeC1C2s = make([][]uint, c.transistors) c.nodeDependents = make([][]uint, c.nodes, c.nodes) c.nodeLeftDependents = make([][]uint, c.nodes, c.nodes) /* copy nodes into r/w data structure */ for i, isPullup := range nodeIsPullup { c.setNodePullup(uint(i), isPullup) } /* copy transistors into r/w data structure */ for _, transDef := range transDefs { found := false for j2, gate := range c.transistorsGate { if gate == transDef.gate && ((c.transistorsC1[j2] == transDef.c1 && c.transistorsC2[j2] == transDef.c2) || (c.transistorsC1[j2] == transDef.c2 && c.transistorsC2[j2] == transDef.c1)) { found = true break } } if !found { c.transistorsGate = append(c.transistorsGate, transDef.gate) c.transistorsC1 = append(c.transistorsC1, transDef.c1) c.transistorsC2 = append(c.transistorsC2, transDef.c2) } } /* cross reference transistors in nodes data structures */ for i, gate := range c.transistorsGate { c1 := c.transistorsC1[i] c2 := c.transistorsC2[i] c.nodeGates[gate] = append(c.nodeGates[gate], uint(i)) c.nodeC1C2s[c1] = append(c.nodeC1C2s[c1], uint(i)) c.nodeC1C2s[c2] = append(c.nodeC1C2s[c2], uint(i)) } for i := uint(0); i < c.nodes; i++ { for _, t := range c.nodeGates[i] { c1 := c.transistorsC1[t] if c1 != vss && c1 != vcc { c.addNodeDependent(i, c1) } c2 := c.transistorsC2[t] if c2 != vss && c2 != vcc { c.addNodeDependent(i, c2) } if c1 != vss && c1 != vcc { c.addNodeLeftDependent(i, c1) } else { c.addNodeLeftDependent(i, c2) } } } } func (c *cpu) Print(bool) { panic("not implemented") } func (c *cpu) stabilizeChip() { for i := uint(0); i < c.nodes; i++ { c.listOutAdd(i) } c.recalcNodeList() } /**************/ /* Node State */ /**************/ func (c *cpu) setNode(nn uint, s bool) { c.setNodePullup(nn, s) c.setNodePulldown(nn, !s) c.listOutAdd(nn) c.recalcNodeList() } func (c *cpu) isNodeHigh(nn uint) bool { return c.getNodeValue(nn) }