package apple2 import ( "encoding/binary" "fmt" "io" ) /* https://applesaucefdc.com/woz/reference2/ Good explanation of the softswitches and the 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) "Understanfing the Apple II, chapter 9" 35 tracks, 16 sectors, 256 bytes NIB: 35 tracks 6656 bytes, 232960 bytes */ const maxHalfTrack = 68 type cardDisk2 struct { cardBase selected int // q5, Only 0 and 1 supported drive [2]cardDisk2Drive dataLatch uint8 q6 bool q7 bool } type cardDisk2Drive struct { diskette *diskette16sector currentPhase uint8 power bool // q4 halfTrack int position int } const ( diskBitCycle = 4 // There is a dataLatch bit transferred every 4 cycles diskLatchReadCycles = 7 // Loaded data is available for a little more than 7ns diskWriteByteCycle = 32 // Load data to write every 32 cycles diskWriteSelfSyncCycle = 40 // Save $FF every 40 cycles. Self sync is 10 bits: 1111 1111 00 diskMotorStartMs = 150 // Time with the disk spinning to get full speed ) func (c *cardDisk2) assign(a *Apple2, slot int) { // Q1, Q2, Q3 and Q4 phase control soft switches, for i := uint8(0); i < 4; i++ { phase := i c.addCardSoftSwitchR(phase<<1, func(_ *ioC0Page) uint8 { return c.dataLatch // All even addresses return the last dataLatch }, fmt.Sprintf("PHASE%vOFF", phase)) c.addCardSoftSwitchR((phase<<1)+1, func(_ *ioC0Page) uint8 { // Move the head up or down depending on the previous phase. drive := &c.drive[c.selected] delta := (phase - drive.currentPhase + 4) % 4 switch delta { case 1: // Up drive.halfTrack++ case 2: // Illegal, let's say up drive.halfTrack++ case 3: // Down drive.halfTrack-- case 0: // No chamge } // Don't go over the limits if drive.halfTrack > maxHalfTrack { drive.halfTrack = maxHalfTrack } else if drive.halfTrack < 0 { drive.halfTrack = 0 } drive.currentPhase = phase //fmt.Printf("DISKII: Current halftrack is %v\n", drive.halfTrack) return 0 }, fmt.Sprintf("PHASE%vOFF", phase)) } // Q4, power switch c.addCardSoftSwitchR(0x8, func(_ *ioC0Page) uint8 { if c.drive[c.selected].power { c.drive[c.selected].power = false c.a.releaseFastMode() } return c.dataLatch }, "Q4DRIVEOFF") c.addCardSoftSwitchR(0x9, func(_ *ioC0Page) uint8 { if !c.drive[c.selected].power { c.drive[c.selected].power = true c.a.requestFastMode() } return 0 }, "") // Q5, drive selecion c.addCardSoftSwitchR(0xA, func(_ *ioC0Page) uint8 { c.selected = 0 return c.dataLatch }, "Q5SELECT1") c.addCardSoftSwitchR(0xB, func(_ *ioC0Page) uint8 { c.selected = 1 return 0 }, "Q5SELECT2") // Q6, Q7 for i := uint8(0xC); i <= 0xF; i++ { iCopy := i c.addCardSoftSwitchR(iCopy, func(_ *ioC0Page) uint8 { return c.softSwitchQ6Q7(iCopy, 0) }, "Q6Q7") c.addCardSoftSwitchW(iCopy, func(_ *ioC0Page, value uint8) { c.softSwitchQ6Q7(iCopy, value) }, "Q6Q7") } c.cardBase.assign(a, slot) } func (c *cardDisk2) softSwitchQ6Q7(index uint8, in uint8) uint8 { switch index { case 0xC: // Q6L c.q6 = false case 0xD: // Q6H c.q6 = true case 0xE: // Q/L c.q7 = false case 0xF: // Q7H c.q7 = true } c.processQ6Q7(in) if index&1 == 0 { // All even addresses return the last dataLatch return c.dataLatch } return 0 } func (c *cardDisk2) processQ6Q7(in uint8) { d := &c.drive[c.selected] if d.diskette == nil { return } if !c.q6 { if !c.q7 { // Q6L-Q7L: Read track := d.halfTrack / 2 c.dataLatch, d.position = d.diskette.read(track, d.position) } else { // Q6L-Q7H: Write the dataLatch value to disk. Shift data out track := d.halfTrack / 2 d.position = d.diskette.write(track, d.position, c.dataLatch) } } else { if !c.q7 { // Q6H-Q7L: Sense write protect / prewrite state // Bit 7 of the control status register means write protected c.dataLatch = 0 // Never write protected } else { // Q6H-Q7H: Load data into the controller c.dataLatch = in } } } func (d *cardDisk2Drive) insertDiskette(dt *diskette16sector) { d.diskette = dt } func (c *cardDisk2) save(w io.Writer) error { err := binary.Write(w, binary.BigEndian, c.selected) if err != nil { return err } err = binary.Write(w, binary.BigEndian, c.dataLatch) if err != nil { return err } err = binary.Write(w, binary.BigEndian, c.q6) if err != nil { return err } err = binary.Write(w, binary.BigEndian, c.q7) if err != nil { return err } err = c.drive[0].save(w) if err != nil { return err } err = c.drive[1].save(w) if err != nil { return err } return c.cardBase.save(w) } func (c *cardDisk2) load(r io.Reader) error { err := binary.Read(r, binary.BigEndian, &c.selected) if err != nil { return err } err = binary.Read(r, binary.BigEndian, &c.dataLatch) if err != nil { return err } err = binary.Read(r, binary.BigEndian, &c.q6) if err != nil { return err } err = binary.Read(r, binary.BigEndian, &c.q7) if err != nil { return err } err = c.drive[0].load(r) if err != nil { return err } err = c.drive[1].load(r) if err != nil { return err } return c.cardBase.load(r) } func (d *cardDisk2Drive) save(w io.Writer) error { err := binary.Write(w, binary.BigEndian, d.currentPhase) if err != nil { return err } err = binary.Write(w, binary.BigEndian, d.power) if err != nil { return err } err = binary.Write(w, binary.BigEndian, d.halfTrack) if err != nil { return err } err = binary.Write(w, binary.BigEndian, d.position) if err != nil { return err } return nil } func (d *cardDisk2Drive) load(r io.Reader) error { err := binary.Read(r, binary.BigEndian, &d.currentPhase) if err != nil { return err } err = binary.Read(r, binary.BigEndian, &d.power) if err != nil { return err } err = binary.Read(r, binary.BigEndian, &d.halfTrack) if err != nil { return err } err = binary.Read(r, binary.BigEndian, &d.position) if err != nil { return err } return nil }