Cleanup
This commit is contained in:
parent
0daf1b9fd9
commit
fa7604800b
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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]
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
}
|
|
@ -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><>
|
214
disk/main.go
214
disk/main.go
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue