This commit is contained in:
Ivan Izaguirre 2021-05-09 20:09:54 +02:00
parent 0daf1b9fd9
commit fa7604800b
9 changed files with 33 additions and 847 deletions

View File

@ -1,305 +0,0 @@
package izapple2
import (
"fmt"
"github.com/ivanizag/izapple2/component"
"github.com/ivanizag/izapple2/storage"
)
// TODO: fast mode
/*
See:
"Understanding the Apple II, chapter 9"
Beneath Apple ProDOS, Appendix
Phases: http://yesterbits.com/media/pubs/AppleOrchard/articles/disk-ii-part-1-1983-apr.pdf
IMW Floppy Disk I/O Controller info: (https://www.brutaldeluxe.fr/documentation/iwm/apple2_IWM_INFO_19840510.pdf)
Woz https://applesaucefdc.com/woz/reference2/
Schematic: https://mirrors.apple2.org.za/ftp.apple.asimov.net/documentation/hardware/schematics/APPLE_DiskII_SCH.pdf
*/
// CardDisk2Sequencer is a DiskII interface card
type CardDisk2Sequencer struct {
cardBase
p6ROM []uint8
latch component.SN74LS259 // 8-bit latch
register component.SN74LS323 // 8-bit shift/storage register
flipFlop component.SN74LS174 // hex flip-flop
motorDelay uint8 // NE556 timer, used to delay motor off
drive [2]cardDisk2SequencerDrive
lastPulse bool
lastPulseCycles uint8 // There is a new pulse every 4ms, that's 8 cycles of 2Mhz
lastCycle uint64 // 2 Mhz cycles
}
const disk2MotorOffDelay = uint8(20) // 2 Mhz cycles, TODO: how long?
const disk2PulseCcyles = uint8(8) // 8 cycles = 4ms * 2Mhz
// NewCardDisk2Sequencer creates a new CardDisk2Sequencer
func NewCardDisk2Sequencer() *CardDisk2Sequencer {
var c CardDisk2Sequencer
c.name = "Disk II"
c.loadRomFromResource("<internal>/DISK2.rom")
data, _, err := storage.LoadResource("<internal>/DISK2P6raw.rom")
if err != nil {
// The resource should be internal and never fail
panic(err)
}
c.p6ROM = data
return &c
}
func (c *CardDisk2Sequencer) dumpState() {
fmt.Printf("Q5 %v, Q4 %v, delay %v\n", c.latch.Q(5), c.latch.Q(4), c.motorDelay)
}
// GetInfo returns card info
func (c *CardDisk2Sequencer) GetInfo() map[string]string {
info := make(map[string]string)
info["rom"] = "16 sector"
// TODO: add drives info
return info
}
func (c *CardDisk2Sequencer) reset() {
// UtA2e 9-12, all switches forced to off
c.latch.Reset()
}
func (c *CardDisk2Sequencer) assign(a *Apple2, slot int) {
c.addCardSoftSwitches(func(_ *ioC0Page, address uint8, data uint8, write bool) uint8 {
/*
Slot card pins to SN74LS259 mapping:
slot_address[3,2,1] => latch_address[2,1,0]
slot_address[0] => latch_data
slot_dev_selct => latch_write_enable // It will be true
*/
c.latch.Write(address>>1, (address&1) != 0, true)
// Advance the Disk2 state machine since the last call to softswitches
c.catchUp(data)
//c.dumpState()
/*
Slot card pins to SN74LS259 mapping:
slot_address[0] => latch_oe2_n
*/
register_output_enable_neg := (address & 1) != 0
if !register_output_enable_neg {
reg := c.register.Output()
return component.ReversePins(reg)
} else {
return 33
}
}, "DISK2SEQ")
c.cardBase.assign(a, slot)
}
func (c *CardDisk2Sequencer) catchUp(data uint8) {
currentCycle := c.a.cpu.GetCycles() << 1 // Disk2 cycles are x2 cpu cycle
motorOn := c.step(data, true)
if motorOn && c.lastCycle == 0 {
// The motor was off, now on. We start the count. We do at least a couple 2 Mhz cycles
c.lastCycle = currentCycle - 2
}
c.lastCycle++
for motorOn && c.lastCycle <= currentCycle {
motorOn = c.step(data, false)
c.lastCycle++
}
if !motorOn {
c.lastCycle = 0 // No tracking done
}
}
func (c *CardDisk2Sequencer) step(data uint8, firstStep bool) bool {
/*
Q4 and Q6 set on the sofswitches is stored on the
latch.
*/
q5 := c.latch.Q(5) // Drive selection
q4 := c.latch.Q(4) // Motor on (before delay)
/*
Motor On comes from the latched q4 via the 556 to
provide a delay. The delay is reset while q4 is on.
*/
if q4 {
c.motorDelay = disk2MotorOffDelay
}
motorOn := c.motorDelay > 0
/*
The pins for the cable drives ENBL1 and ENBL2 are
connected to q5 and motor using half of the 74LS132
NAND to combine them.
*/
c.drive[0].enable(!q5 && motorOn)
c.drive[1].enable(q5 && motorOn)
/*
Motor on AND the 2 Mhz clock (Q3 pin 37 of the slot)
are connected to the clok pulse of the shift register
if off, the sequences does not advance. The and uses
another quarter of the 74LS132 NAND.
*/
if !motorOn {
c.flipFlop.Reset()
return false
}
c.motorDelay--
/*
The register is not connected to the data bus directly.
The pin correspondence is:
/*
Head movements. We assume it's instantaneous on Q0-Q3 change. We
will place it on the first step.
Q0 to Q3 are connected directly to the drives.
*/
if firstStep {
q0 := c.latch.Q(0)
q1 := c.latch.Q(1)
q2 := c.latch.Q(3)
q3 := c.latch.Q(3)
c.drive[0].moveHead(q0, q1, q2, q3)
c.drive[1].moveHead(q0, q1, q2, q3)
}
/*
The reading from the drive is converted to a pulse detecting
changes using Q3 and Q4 of the flip flop, combines with
the last quarter of the 74LS132 NAND.
The woz format provides the pulse directly and we wont emulate
this detection.
*/
pulse := false
c.lastPulseCycles++
if c.lastPulseCycles == disk2PulseCcyles {
pulse = c.drive[0].readPulse() ||
c.drive[1].readPulse()
c.lastPulseCycles = 0
}
/* The write protected signal comes directly from the any of the
drives being enabled (motor on) and write protected.
*/
wProt := (c.drive[0].enabled && c.drive[0].writeProtected) ||
(c.drive[1].enabled && c.drive[1].writeProtected)
/*
The next instruction for the sequencer is retrieved from
the ROM P6 using the address:
A0, A5, A6, A7 <= sequence from 74LS174
A1 =< MSB of register (pin Q7)
A2 <= Q6 from 9334
A3 <= Q7 from 9334
A4 <= pulse transition
*/
romAddress := component.PinsToByte([8]bool{
c.flipFlop.Q(5), // seq1
c.register.Q7(),
c.latch.Q(6),
c.latch.Q(7),
pulse,
c.flipFlop.Q(2), // seq0
c.flipFlop.Q(1), // seq2
c.flipFlop.Q(0), //seq3
})
sequence := component.PinsToByte([8]bool{
c.flipFlop.Q(0),
c.flipFlop.Q(1),
c.flipFlop.Q(5),
c.flipFlop.Q(2),
})
fmt.Printf("For Q6(%v) Q7(%v) H(%v) P(%v) Seq(%x) => ",
c.latch.Q(6), c.latch.Q(6), c.register.Q7(), pulse, sequence,
)
romDataByte := c.p6ROM[romAddress]
romData := component.ByteToPins(romDataByte)
inst := component.PinsToByte([8]bool{
romData[0],
romData[1],
romData[2],
romData[3],
})
next := component.PinsToByte([8]bool{
romData[7],
romData[6],
romData[5],
romData[4],
})
fmt.Printf("cmd(%x) seq(%x)\n", inst, next)
/*
The next sequence is feed back to the flip-flops.
Notes: Q3 and Q4 are not used as we are not emulating
the pulse detection. MotorOn here is always true, the
code for motorOn false is above.
*/
c.flipFlop.Update(
romData[7],
romData[6],
romData[4],
false,
false,
romData[5],
motorOn,
)
/*
sequenceNew := component.PinsToByte([8]bool{
c.flipFlop.Q(2),
c.flipFlop.Q(5),
c.flipFlop.Q(1),
c.flipFlop.Q(0),
})
*/
/*
The pins for the register shifter update are:
SR(CLR) <- ROM D3
S1 <- ROM D0
S0 <- ROM D1
DS0(SR) <- WPROT pin of the selected drive
DS7(SL) <- ROM D2
IO[7.0] <-> D[0-7] slot data bus (the order is reversed)
*/
c.register.Update(
component.ReversePins(data),
romData[1], // S0
romData[0], // S1
romData[3], // SR_N
wProt, // DS0
romData[2], // DS1
)
/*
fmt.Printf("Seq %x. pulse %v, P6ROM[%02x]=%02x -> %x-%x, reg %02x\n",
sequence, pulse, romAddress, romDataByte,
sequenceNew, command,
component.ReversePins(c.register.Output()))
*/
return true
}

View File

@ -1,8 +1,6 @@
package izapple2
import (
"fmt"
"github.com/ivanizag/izapple2/component"
"github.com/ivanizag/izapple2/storage"
)
@ -26,10 +24,10 @@ type CardDisk2Sequencer struct {
cardBase
p6ROM []uint8
latch component.SN74LS259 // 8-bit latch
register uint8 // 8-bit shift/storage register SN74LS323
sequence uint8 // 4 bits stored in an hex flip-flop SN74LS174
motorDelay uint8 // NE556 timer, used to delay motor off
q [8]bool // 8-bit latch SN74LS259
register uint8 // 8-bit shift/storage register SN74LS323
sequence uint8 // 4 bits stored in an hex flip-flop SN74LS174
motorDelay uint8 // NE556 timer, used to delay motor off
drive [2]cardDisk2SequencerDrive
lastPulse bool
@ -38,8 +36,17 @@ type CardDisk2Sequencer struct {
lastCycle uint64 // 2 Mhz cycles
}
const disk2MotorOffDelay = uint8(20) // 2 Mhz cycles, TODO: how long?
const disk2PulseCcyles = uint8(8) // 8 cycles = 4ms * 2Mhz
const (
disk2MotorOffDelay = uint8(20) // 2 Mhz cycles, TODO: how long?
disk2PulseCcyles = uint8(8) // 8 cycles = 4ms * 2Mhz
/*
We skip register calculations for long periods with the motor
on but not reading bytes. It's an optimizations, 10000 is too
short for cross track sync copy protections.
*/
disk2CyclestoLoseSsync = 100000
)
// NewCardDisk2Sequencer creates a new CardDisk2Sequencer
func NewCardDisk2Sequencer() *CardDisk2Sequencer {
@ -57,10 +64,6 @@ func NewCardDisk2Sequencer() *CardDisk2Sequencer {
return &c
}
func (c *CardDisk2Sequencer) dumpState() {
fmt.Printf("Q5 %v, Q4 %v, delay %v\n", c.latch.Q(5), c.latch.Q(4), c.motorDelay)
}
// GetInfo returns card info
func (c *CardDisk2Sequencer) GetInfo() map[string]string {
info := make(map[string]string)
@ -71,34 +74,30 @@ func (c *CardDisk2Sequencer) GetInfo() map[string]string {
func (c *CardDisk2Sequencer) reset() {
// UtA2e 9-12, all switches forced to off
c.latch.Reset()
c.q = [8]bool{}
}
func (c *CardDisk2Sequencer) assign(a *Apple2, slot int) {
c.addCardSoftSwitches(func(_ *ioC0Page, address uint8, data uint8, write bool) uint8 {
/*
Slot card pins to SN74LS259 mapping:
Slot card pins to SN74LS259 latch mapping:
slot_address[3,2,1] => latch_address[2,1,0]
slot_address[0] => latch_data
slot_dev_selct => latch_write_enable // It will be true
slot_dev_selct => latch_write_enable ;It will be true
*/
c.latch.Write(address>>1, (address&1) != 0, true)
c.q[address>>1] = (address & 1) != 0
// Advance the Disk2 state machine since the last call to softswitches
c.catchUp(data)
//c.dumpState()
/*
Slot card pins to SN74LS259 mapping:
slot_address[0] => latch_oe2_n
*/
register_output_enable_neg := (address & 1) != 0
if !register_output_enable_neg {
//if c.register >= 0x80 && address == 0xc {
// fmt.Printf("Byte %x\n", c.register)
//}
return c.register
} else {
return 33
return 33 // Floating
}
}, "DISK2SEQ")
@ -109,9 +108,9 @@ func (c *CardDisk2Sequencer) catchUp(data uint8) {
currentCycle := c.a.cpu.GetCycles() << 1 // Disk2 cycles are x2 cpu cycle
motorOn := c.step(data, true)
//if motorOn && c.lastCycle == 0 {
if motorOn && currentCycle > c.lastCycle+100000 { // With 10000, cross track snc not working
// The motor was off, now on. We start the count. We do at least a couple 2 Mhz cycles
if motorOn && currentCycle > c.lastCycle+disk2CyclestoLoseSsync {
// We have losy sync. We start the count.
//We do at least a couple 2 Mhz cycles
c.lastCycle = currentCycle - 2
}
c.lastCycle++
@ -122,7 +121,7 @@ func (c *CardDisk2Sequencer) catchUp(data uint8) {
}
if !motorOn {
c.lastCycle = 0 // No tracking done
c.lastCycle = 0 // Sync lost
}
}
@ -131,8 +130,8 @@ func (c *CardDisk2Sequencer) step(data uint8, firstStep bool) bool {
Q4 and Q6 set on the sofswitches is stored on the
latch.
*/
q5 := c.latch.Q(5) // Drive selection
q4 := c.latch.Q(4) // Motor on (before delay)
q5 := c.q[5] // Drive selection
q4 := c.q[4] // Motor on (before delay)
/*
Motor On comes from the latched q4 via the 556 to
@ -169,10 +168,10 @@ func (c *CardDisk2Sequencer) step(data uint8, firstStep bool) bool {
Q0 to Q3 are connected directly to the drives.
*/
if firstStep {
q0 := c.latch.Q(0)
q1 := c.latch.Q(1)
q2 := c.latch.Q(2)
q3 := c.latch.Q(3)
q0 := c.q[0]
q1 := c.q[1]
q2 := c.q[2]
q3 := c.q[3]
c.drive[0].moveHead(q0, q1, q2, q3)
c.drive[1].moveHead(q0, q1, q2, q3)
}
@ -214,21 +213,17 @@ func (c *CardDisk2Sequencer) step(data uint8, firstStep bool) bool {
romAddress := component.PinsToByte([8]bool{
seqBits[1], // seq1
high,
c.latch.Q(6),
c.latch.Q(7),
c.q[6],
c.q[7],
!pulse,
seqBits[0], // seq0
seqBits[2], // seq2
seqBits[3], //seq3
seqBits[3], // seq3
})
//fmt.Printf("For Q6(%v) Q7(%v) H(%v) P(%v) Seq(%x) => ",
// c.latch.Q(6), c.latch.Q(6), high, pulse, c.sequence)
romData := c.p6ROM[romAddress]
inst := romData & 0xf
next := romData >> 4
//fmt.Printf("cmd(%x) seq(%x) ", inst, next)
/*
The pins for the register shifter update are:
@ -262,6 +257,5 @@ func (c *CardDisk2Sequencer) step(data uint8, firstStep bool) bool {
}
c.sequence = next
//fmt.Printf("reg %02x\n", c.register)
return true
}

View File

@ -1,38 +0,0 @@
package component
/*
SN74LS174: hex D flip-flop
http://www.skot9000.com/ttl/datasheets/174.pdf
Pins:
A0, A1, A2: Address inputs. 'address' parameter of Write()
D: Data input. 'value' parameter of Write()
E: Enable input. 'enable' parameter of Write()
C: Clear input. Method Reset()
Q0, Q7: Parallel latch outputs. Return of Q(n)
*/
type SN74LS174 struct {
q [6]bool
}
func (o *SN74LS174) Update(d0, d1, d2, d3, d4, d5, mr bool) {
o.q[0] = d0 && mr
o.q[1] = d1 && mr
o.q[2] = d2 && mr
o.q[3] = d3 && mr
o.q[4] = d4 && mr
o.q[5] = d5 && mr
}
func (o *SN74LS174) Reset() {
o.Update(false, false, false, false, false, false, false)
}
func (o *SN74LS174) Q(index uint8) bool {
if index > 5 {
panic("There are only 6 flip flops")
}
return o.q[index]
}

View File

@ -1,45 +0,0 @@
package component
/*
SN74LS259 or 9344: 8-bit addressable latch
http://www.skot9000.com/ttl/datasheets/259.pdf
Pins:
A0, A1, A2: Address inputs. 'address' parameter of Write()
D: Data input. 'value' parameter of Write()
E: Enable input. 'enable' parameter of Write()
C: Clear input. Method Reset()
Q0, Q7: Parallel latch outputs. Return of Q(n)
*/
type SN74LS259 struct {
value uint8
}
func (o *SN74LS259) Write(address uint8, value bool, enable bool) {
if address > 7 {
panic("The address can have only three bits")
}
if !enable {
return
}
o.value = o.value &^ (uint8(1) << address)
if value {
o.value |= 1 << address
}
}
func (o *SN74LS259) Q(address uint8) bool {
if address > 7 {
panic("The address can have only three bits")
}
return ((o.value >> address) & 1) == 1
}
func (o *SN74LS259) Reset() {
o.value = 0
}

View File

@ -1,30 +0,0 @@
package component
import (
"testing"
)
func TestSN74LS259WriteTrue(t *testing.T) {
var o SN74LS259
o.Write(2, true, true)
if !o.Q(2) {
t.Error("Wrote true but got false")
}
}
func TestSN74LS259WriteFalse(t *testing.T) {
var o SN74LS259
o.Write(1, false, true)
if o.Q(1) {
t.Error("Wrote false but got true")
}
}
func TestSN74LS259WriteTrueThenFalse(t *testing.T) {
var o SN74LS259
o.Write(5, true, true)
o.Write(5, false, true)
if o.Q(5) {
t.Error("Wrote true the false but got true")
}
}

View File

@ -1,70 +0,0 @@
package component
/*
SN74LS323: 8-bit shift storage register
http://www.skot9000.com/ttl/datasheets/323.pdf
Pins:
CP: Clock pulse. Call to Update()
DS0: Serial data input for shift right. 'ds0' parameter of Update()
DS7: Serial data input for shift left. 'ds1' parameter of Update()
I0-7: Parallel data input. 'data' parameter of Update()
O0-7: Parallel data output. Return of Output()
OE1-2: Not supported
Q0: Serial outputs LSB. Return of Q0()
Q7: Serial outputs MSB. Return of Q7()
S0: Mode select, 's0' parameter of Update()
S1: Mode select, 's1' parameter of Update()
SR: Sync reset (active low), 'sr_n' parameter of Update()
Note: left for datasheet (I0-I1-I2..I7) is right for uint8 (b7-b6...b0)
*/
type SN74LS323 struct {
value uint8
}
func (o *SN74LS323) Update(data uint8, s0 bool, s1 bool, sr_n bool, ds0 bool, ds7 bool) {
if !sr_n {
// Reset on SR low
o.value = 0
return
}
if s1 {
if s0 {
// high, high: Parallel load
o.value = data
} else {
// high, low: Shift left (it's shift right for uint8)
o.value >>= 1
if ds7 {
o.value += 0x80
}
}
} else {
if s0 {
// low, high: Shift right (it's shift left for uint8)
o.value <<= 1
if ds0 {
o.value += 0x01
}
} else {
// low, low: Hold
// do nothing
}
}
}
func (o *SN74LS323) Q0() bool {
return (o.value & 1) != 0
}
func (o *SN74LS323) Q7() bool {
return (o.value & 0x80) != 0
}
func (o *SN74LS323) Output() uint8 {
return o.value
}

View File

@ -1,41 +0,0 @@
package component
import (
"testing"
)
func TestSN74LS323Reset(t *testing.T) {
var o SN74LS323
o.value = 0x89
o.Update(0x12, false, false, false, false, false)
if o.Output() != 0 {
t.Error("Value should reset to 0")
}
}
func TestSN74LS323ShiftLeft(t *testing.T) {
var o SN74LS323
o.value = 0x11
o.Update(0x12, false, true, true, false, true)
if o.Output() != 0x88 {
t.Error("Bad shift left")
}
}
func TestSN74LS323ShiftRight(t *testing.T) {
var o SN74LS323
o.value = 0x11
o.Update(0x12, true, false, true, true, false)
if o.Output() != 0x23 {
t.Error("Bad shift right")
}
}
func TestSN74LS323Load(t *testing.T) {
var o SN74LS323
o.value = 0x11
o.Update(0x12, true, true, true, true, false)
if o.Output() != 0x12 {
t.Error("Bad load")
}
}

View File

@ -1,65 +0,0 @@
<18>
99;;8(
99;;-<2D>8H
(H(H(H(H-H8H
(H(H(H(H<><48><EFBFBD><EFBFBD>
XxXxXxXxXxXx
XxXxXxXx<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
hh<>hh<>h<EFBFBD>h<EFBFBD>
hh<>hh<><68><EFBFBD><EFBFBD><EFBFBD>
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
<EFBFBD>ȨȨȨ<EFBFBD>)Y<><59>
<EFBFBD>ȨȨȨ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
<EFBFBD><EFBFBD><08><>M<><4D>
<EFBFBD><EFBFBD><08><>

View File

@ -1,214 +0,0 @@
package main
import (
"fmt"
"github.com/ivanizag/izapple2/component"
"github.com/ivanizag/izapple2/storage"
)
// https://embeddedmicro.weebly.com/apple-ii-p6-prom-dump.html
func getInstruction(data []uint8, Q6 bool, Q7 bool, high bool, pulse bool, sequence uint8) uint8 {
seqBits := component.ByteToPins(sequence)
address := component.PinsToByte([8]bool{
seqBits[1],
high,
Q6,
Q7,
!pulse,
seqBits[0],
seqBits[2],
seqBits[3],
})
v := data[address]
//fmt.Printf("Seq %x. pulse %v, P6ROM[%02x]=%02x", sequence, pulse, i, v)
fmt.Printf("For Q6(%v) Q7(%v) H(%v) P(%v) Seq(%x) => ",
Q6, Q7, high, pulse, sequence,
)
return v
}
func printTable(data []uint8, q6 bool, q7 bool) {
for i := uint8(0); i < 16; i++ {
fmt.Printf("%x %02x %02x %02x %02x\n",
i,
getInstruction(data, q6, q7, false, true, i),
getInstruction(data, q6, q7, false, false, i),
getInstruction(data, q6, q7, true, true, i),
getInstruction(data, q6, q7, true, false, i),
)
}
}
func mainXX() {
fmt.Println("SM Analysis:")
data, _, _ := storage.LoadResource("<internal>/DISK2P6.rom")
fmt.Println("Q6 On, Q7 Off")
printTable(data, true, false)
fmt.Println("Q6 Off, Q7 Off")
printTable(data, false, false)
fmt.Println("Q6 On, Q7 On")
printTable(data, true, true)
fmt.Println("Q6 Off, Q7 On")
printTable(data, false, true)
}
type lsq struct {
sequence uint8
register uint8 // 74LS323, https://components101.com/asset/sites/default/files/component_datasheet/SN74LS323-Datasheet.pdf
}
func main() {
/*
$C08E -> q7 false
Every time $C08C high byte is set -> q6 false
*/
data, _, _ := storage.LoadResource("<internal>/DISK2P6.rom")
wozData, _, err := storage.LoadResource("/home/casa/applerelease/disks/woz test images/WOZ 2.0/DOS 3.3 System Master.woz")
if err != nil {
panic("Error creando woz")
}
woz, err := storage.NewFileWoz(wozData)
var lsq lsq
nibs := woz.DumpTrackAsNib(0)
for i := 0; i < 20; i++ {
fmt.Printf("\n%v: ", i)
for j := 0; j < 16; j++ {
fmt.Printf("%02x ", nibs[i*16+j])
}
}
fmt.Println("")
/*
bits := woz.DumpTrackAsWoz(0)
for i := 0; i < 20; i++ {
fmt.Printf("%v: %02x\n", i, bits[i])
}
*/
position := uint32(1)
maxPosition := uint32(50304)
hcycles := 0
lastReadCycle := 0
bit := uint8(0)
for position != 0 {
hcycles++
pulse := false
if (hcycles % 8) == 0 {
bit, position, maxPosition = woz.GetNextBitAndPosition(position, maxPosition, 0)
pulse = bit != 0
fmt.Printf("==Read bit %v @ %v \n", bit, position)
}
high := (lsq.register & 0x80) != 0
command := getInstruction(data, false, false, high, pulse, lsq.sequence)
//fmt.Printf(" reg %02x\n", lsq.register)
inst := command & 0xf
next := command >> 4
fmt.Printf("cmd(%x) seq(%x)\n", inst, next)
/*
https://www.youtube.com/watch?v=r1VlrJboDMw 21:30
74LS323 pins: (register)
SR = inst[3]
S1 = inst[0]
S0 = inst[1]
DS0 = write protect
DS7 = inst[2]
IO[7.0] = D[7-0] slot data bus
OE1 = AD[0], register copied to bus depending on this
OE2 = (DEV pin 41 slot)
Q7 = high
Q0 = not used
CP = clock pulse (Q3 pin37 slot, 2Mhz clock) AND motor (using 74LS132)
ROM P6 pins:
A0, A5, A6, A7 <= sequence from 74LS174
D4, D5, D6, D7 => sequence to 74LS174
A4 <= pulse transition
A2 <= Q6 from 9334
A3 <= Q7 from 9334
A1 =< MSB of register (pin Q7)
D0-S3 => S1, S0, SR and DS7 of register
E2: motor
74LS174 pins (hex flip flop) (sequence 0,1,2,5)
CP = clock pulse (Q3 pin37 slot, 2Mhz clock) AND motor (using 74LS132)
MR = motor
seq:
D0 <- ROM D7
Q0 -> ROM A7
D1 <- ROM D6
Q1 -> ROM A6
D2 <- ROM D4
Q2 -> ROM A5
D5 <- ROM D5
Q5 -> ROM A0
pulse transition:
D3 <- from Q4
Q3 -> and not Q4 -> to ROM A4 (detects change in pulse)
D4 <- pulse (from disk)
Q4 -> to D3
9334 (or 74LS259) 8 bit latch
Q0-Q3: phases
Q4: motor control to 556
Q5: drive select
Q6: to ROM P6
Q7: to ROM P7
R: to slot reset
A0: slot AD[1]
A1: slot AD[2]
A3: slot AD[3]
D: slot AD[0]
E: (DEV 41 pin slot)
Write on Q7 from the 9334 and Q0 (seq[3]) transition on 74ls174
556, for the motor signal, to continue a bit longer?
*/
//fmt.Printf("cmd %02x, reg %02x, inst %x, seq %x, next %x, pulse %v ", command, lsq.register, inst, lsq.sequence, next, pulse)
switch inst {
case 0: // CLR, 74LS323 pin 9, Sync Reset
lsq.register = 0
//fmt.Printf("CLR")
case 8: // NOP
// Nothing
//fmt.Printf("NOP")
case 9: // SL0 -> S1=1, S0=0
lsq.register = lsq.register << 1
//fmt.Printf("SL0")
case 0xa: // SR
lsq.register = lsq.register >> 1 // + write protect MSB on pin 11 DS0
//fmt.Printf("SR")
case 0xb: // LD -> parallel load S1=1, S0=1
panic("not in read mode")
// lsq.register = bus
case 0xd: // SL1
lsq.register = 1 + (lsq.register << 1)
//fmt.Printf("SL1")
default:
panic("missing instruction")
}
lsq.sequence = next
//fmt.Println("")
if lsq.register > 0x7f && (hcycles-lastReadCycle) > 60 {
fmt.Printf("Byte %02x at %v\n", lsq.register, position)
lastReadCycle = hcycles
}
}
}