From 3b07b77ea21c2dbfb19f3d4911bd21f5bdd5e0f0 Mon Sep 17 00:00:00 2001 From: Ivan Izaguirre Date: Fri, 24 May 2019 00:33:45 +0200 Subject: [PATCH] Support for disk writes in memory. Not persisted to host disk --- apple2/cardDisk2.go | 194 +++++++++++++++++++++---------------- apple2/diskette16sector.go | 12 ++- 2 files changed, 122 insertions(+), 84 deletions(-) diff --git a/apple2/cardDisk2.go b/apple2/cardDisk2.go index 97b079f..73a8a61 100644 --- a/apple2/cardDisk2.go +++ b/apple2/cardDisk2.go @@ -7,9 +7,14 @@ import ( /* 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 @@ -18,138 +23,167 @@ const maxHalfTrack = 68 type cardDisk2 struct { cardBase - selected int // Only 0 and 1 supported + selected int // q5, Only 0 and 1 supported drive [2]cardDisk2Drive + + dataLatch uint8 + q6 bool + q7 bool } type cardDisk2Drive struct { diskette *diskette16sector currentPhase int - power bool - writeMode bool + power bool // q4 halfTrack int position int } -func (c *cardDisk2) assign(a *Apple2, slot int) { - // Phase control soft switches - // Lazy emulation. It only checks for phases on and move the head - // up or down depending on the previous phase. - for i := 0; i < 4; i++ { - func(phase int) { - c.ssr[phase<<1] = func(_ *ioC0Page) uint8 { - //fmt.Printf("DISKII: Phase %v off\n", phase) - return 0 - } - c.ssr[(phase<<1)+1] = func(_ *ioC0Page) uint8 { - //fmt.Printf("DISKII: Phase %v on\n", phase) - halfTrack := c.drive[c.selected].halfTrack - delta := (phase - c.drive[c.selected].currentPhase + 4) % 4 - switch delta { - case 1: // Up - halfTrack++ - case 2: // Illegal, let's say up - halfTrack++ - case 3: // Down - halfTrack-- - case 0: // No chamge - } +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 - if halfTrack > maxHalfTrack { - halfTrack = maxHalfTrack - } else if halfTrack < 0 { - halfTrack = 0 - } - c.drive[c.selected].halfTrack = halfTrack - c.drive[c.selected].currentPhase = phase - //fmt.Printf("DISKII: Current halftrack is %v\n", halfTrack) - return 0 +) + +func (c *cardDisk2) assign(a *Apple2, slot int) { + // Q1, Q2, Q3 and Q4 phase control soft switches, + for i := 0; i < 4; i++ { + phase := i + c.ssr[phase<<1] = func(_ *ioC0Page) uint8 { + return c.dataLatch // All even addresses return the last dataLatch + } + c.ssr[(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 } - }(i) + + // 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 + } } - // Other soft switches + // Q4, power switch c.ssr[0x8] = func(_ *ioC0Page) uint8 { if c.drive[c.selected].power { c.drive[c.selected].power = false c.a.releaseFastMode() - //fmt.Printf("DISKII: Disk %v is off for %v\n", c.selected, x) } - return 0 + return c.dataLatch } c.ssr[0x9] = func(_ *ioC0Page) uint8 { if !c.drive[c.selected].power { c.drive[c.selected].power = true c.a.requestFastMode() - //fmt.Printf("DISKII: Disk %v is on\n", c.selected) } return 0 } + + // Q5, drive selecion c.ssr[0xA] = func(_ *ioC0Page) uint8 { c.selected = 0 - //fmt.Printf("DISKII: Disk %v selected\n", c.selected) - return 0 + return c.dataLatch } c.ssr[0xB] = func(_ *ioC0Page) uint8 { c.selected = 1 - //fmt.Printf("DISKII: Disk %v selected\n", c.selected) return 0 } - // Q6L - c.ssr[0xC] = func(_ *ioC0Page) uint8 { - //fmt.Printf("DISKII: Reading\n") - drive := &c.drive[c.selected] - if drive.diskette == nil { - return 0xff + // Q6, Q7 + for i := 0xC; i <= 0xF; i++ { + iCopy := i + c.ssr[iCopy] = func(_ *ioC0Page) uint8 { + return c.softSwitchQ6Q7(iCopy, 0) + } + c.ssw[iCopy] = func(_ *ioC0Page, value uint8) { + c.softSwitchQ6Q7(iCopy, value) } - track := drive.halfTrack / 2 - value, newPosition := drive.diskette.read(track, drive.position) - drive.position = newPosition - //fmt.Printf("DISKII: Reading value 0x%02v from track %v, position %v\n", value, track, drive.position) - return value - } - - c.ssw[0xC] = func(_ *ioC0Page, value uint8) { - //fmt.Printf("DISKII: Writing the value 0x%02x\n", value) - } - - // Q6H - c.ssr[0xD] = func(_ *ioC0Page) uint8 { - c.drive[c.selected].writeMode = false - //fmt.Printf("DISKII: Sense write protection\n") - return 0 - } - - // Q7L - c.ssr[0xE] = func(_ *ioC0Page) uint8 { - c.drive[c.selected].writeMode = false - //fmt.Printf("DISKII: Set read mode\n") - return 0 - } - - // Q7H - c.ssr[0xF] = func(_ *ioC0Page) uint8 { - c.drive[c.selected].writeMode = true - //fmt.Printf("DISKII: Set write mode\n") - return 0 } c.cardBase.assign(a, slot) } +func (c *cardDisk2) softSwitchQ6Q7(index int, 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 + } else { + 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) { binary.Write(w, binary.BigEndian, c.selected) + binary.Write(w, binary.BigEndian, c.dataLatch) + binary.Write(w, binary.BigEndian, c.q6) + binary.Write(w, binary.BigEndian, c.q7) c.drive[0].save(w) c.drive[1].save(w) } func (c *cardDisk2) load(r io.Reader) { binary.Read(r, binary.BigEndian, &c.selected) + binary.Read(r, binary.BigEndian, &c.dataLatch) + binary.Read(r, binary.BigEndian, &c.q6) + binary.Read(r, binary.BigEndian, &c.q7) c.drive[0].load(r) c.drive[1].load(r) } @@ -157,7 +191,6 @@ func (c *cardDisk2) load(r io.Reader) { func (d *cardDisk2Drive) save(w io.Writer) { binary.Write(w, binary.BigEndian, d.currentPhase) binary.Write(w, binary.BigEndian, d.power) - binary.Write(w, binary.BigEndian, d.writeMode) binary.Write(w, binary.BigEndian, d.halfTrack) binary.Write(w, binary.BigEndian, d.position) } @@ -165,7 +198,6 @@ func (d *cardDisk2Drive) save(w io.Writer) { func (d *cardDisk2Drive) load(r io.Reader) { binary.Read(r, binary.BigEndian, &d.currentPhase) binary.Read(r, binary.BigEndian, &d.power) - binary.Read(r, binary.BigEndian, &d.writeMode) binary.Read(r, binary.BigEndian, &d.halfTrack) binary.Read(r, binary.BigEndian, &d.position) } diff --git a/apple2/diskette16sector.go b/apple2/diskette16sector.go index 08e176e..d2e8fbd 100644 --- a/apple2/diskette16sector.go +++ b/apple2/diskette16sector.go @@ -5,9 +5,10 @@ import ( "os" ) -/* Info from: -"Beneath Apple DOS" https://fabiensanglard.net/fd_proxy/prince_of_persia/Beneath%20Apple%20DOS.pdf -https://github.com/TomHarte/CLK/wiki/Apple-GCR-disk-encoding +/* +See: + "Beneath Apple DOS" https://fabiensanglard.net/fd_proxy/prince_of_persia/Beneath%20Apple%20DOS.pdf + https://github.com/TomHarte/CLK/wiki/Apple-GCR-disk-encoding */ const ( @@ -31,6 +32,11 @@ func (d *diskette16sector) read(track int, position int) (value uint8, newPositi return } +func (d *diskette16sector) write(track int, position int, value uint8) int { + d.track[track][position] = value + return (position + 1) % nibBytesPerTrack +} + func loadDisquette(filename string) *diskette16sector { var d diskette16sector