izapple2/cardDisk2.go

336 lines
7.5 KiB
Go
Raw Normal View History

package apple2
import (
"encoding/binary"
2019-10-20 22:00:42 +00:00
"fmt"
"io"
)
2019-03-02 19:41:25 +00:00
/*
https://applesaucefdc.com/woz/reference2/
2019-03-02 19:41:25 +00:00
Good explanation of the softswitches and the phases:
http://yesterbits.com/media/pubs/AppleOrchard/articles/disk-ii-part-1-1983-apr.pdf
2019-03-04 23:00:12 +00:00
"IMW Floppy Disk I/O Controller info" (https://www.brutaldeluxe.fr/documentation/iwm/apple2_IWM_INFO_19840510.pdf)
"Understanfing the Apple II, chapter 9"
2019-03-04 23:00:12 +00:00
35 tracks, 16 sectors, 256 bytes
NIB: 35 tracks 6656 bytes, 232960 bytes
2019-03-02 19:41:25 +00:00
*/
2019-03-04 23:00:12 +00:00
const maxHalfTrack = 68
2019-03-02 19:41:25 +00:00
type cardDisk2 struct {
cardBase
selected int // q5, Only 0 and 1 supported
2019-03-04 23:00:12 +00:00
drive [2]cardDisk2Drive
dataLatch uint8
q6 bool
q7 bool
2019-03-04 23:00:12 +00:00
}
type cardDisk2Drive struct {
diskette *diskette16sector
power bool // q4, not realy used for anything
position int
magnets uint8 // q3, q2, q1 and q0 with q0 on the LSB
tracksStep int // Stepmotor for tracks position. 4 steps per track
}
type diskette interface {
powerOn(cycle uint64)
powerOff(cycle uint64)
read(halfTrack int, cycle uint64) uint8
write(halfTrack int, value uint8, cycle uint64)
}
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
)
2019-05-18 14:43:51 +00:00
func (c *cardDisk2) assign(a *Apple2, slot int) {
// Q1, Q2, Q3 and Q4 phase control soft switches,
2019-10-20 22:00:42 +00:00
for i := uint8(0); i < 4; i++ {
phase := i
2019-10-20 22:00:42 +00:00
c.addCardSoftSwitchR(phase<<1, func(_ *ioC0Page) uint8 {
// Update magnets and position
drive := &c.drive[c.selected]
drive.magnets &^= (1 << phase)
drive.tracksStep = moveStep(drive.magnets, drive.tracksStep)
return c.dataLatch // All even addresses return the last dataLatch
2019-10-20 22:00:42 +00:00
}, fmt.Sprintf("PHASE%vOFF", phase))
c.addCardSoftSwitchR((phase<<1)+1, func(_ *ioC0Page) uint8 {
// Update magnets and position
drive := &c.drive[c.selected]
drive.magnets |= (1 << phase)
drive.tracksStep = moveStep(drive.magnets, drive.tracksStep)
return 0
2019-10-20 22:00:42 +00:00
}, fmt.Sprintf("PHASE%vOFF", phase))
2019-03-02 19:41:25 +00:00
}
// Q4, power switch
2019-10-20 22:00:42 +00:00
c.addCardSoftSwitchR(0x8, func(_ *ioC0Page) uint8 {
if c.drive[c.selected].power {
c.drive[c.selected].power = false
c.a.releaseFastMode()
}
return c.dataLatch
2019-10-20 22:00:42 +00:00
}, "Q4DRIVEOFF")
c.addCardSoftSwitchR(0x9, func(_ *ioC0Page) uint8 {
if !c.drive[c.selected].power {
c.drive[c.selected].power = true
c.a.requestFastMode()
}
2019-03-02 19:41:25 +00:00
return 0
2019-10-20 22:00:42 +00:00
}, "")
// Q5, drive selecion
2019-10-20 22:00:42 +00:00
c.addCardSoftSwitchR(0xA, func(_ *ioC0Page) uint8 {
2019-03-02 19:41:25 +00:00
c.selected = 0
return c.dataLatch
2019-10-20 22:00:42 +00:00
}, "Q5SELECT1")
c.addCardSoftSwitchR(0xB, func(_ *ioC0Page) uint8 {
2019-03-02 19:41:25 +00:00
c.selected = 1
return 0
2019-10-20 22:00:42 +00:00
}, "Q5SELECT2")
2019-03-02 19:41:25 +00:00
// Q6, Q7
2019-10-20 22:00:42 +00:00
for i := uint8(0xC); i <= 0xF; i++ {
iCopy := i
2019-10-20 22:00:42 +00:00
c.addCardSoftSwitchR(iCopy, func(_ *ioC0Page) uint8 {
return c.softSwitchQ6Q7(iCopy, 0)
2019-10-20 22:00:42 +00:00
}, "Q6Q7")
c.addCardSoftSwitchW(iCopy, func(_ *ioC0Page, value uint8) {
c.softSwitchQ6Q7(iCopy, value)
2019-10-20 22:00:42 +00:00
}, "Q6Q7")
2019-03-03 22:54:43 +00:00
}
c.cardBase.assign(a, slot)
}
2019-03-03 22:54:43 +00:00
const (
maxStep = 68 * 2 // What is the maximum quarter tracks a DiskII can go?
stepsPerTurn = 8
)
const (
dirN = 0
dirNW = 1
dirW = 2
dirSW = 3
dirS = 4
dirSE = 5
dirE = 6
dirNE = 7
dirUndefined = 8
)
var magnetsDirections = []int{
// Magnets bits, ESWN: east, south, west, north
dirUndefined, // 0000
dirN, // 0001
dirW, // 0010
dirNW, // 0011
dirS, // 0100
dirUndefined, // 0101
dirSW, // 0110
dirW, // 0111
dirE, // 1000
dirNE, // 1001
dirUndefined, // 1010
dirN, // 1011
dirSE, // 1100
dirE, // 1101
dirS, // 1110
dirUndefined, // 1111
}
func moveStep(magnets uint8, prevStep int) int {
//fmt.Printf("magnets: 0x%x\n", magnets)
magnetsDirection := magnetsDirections[magnets]
if magnetsDirection == dirUndefined {
// Don't move if magnets don't push on a defined direction.
return prevStep
}
prevDirection := prevStep % stepsPerTurn // Direction, removing full revolutions.
delta := magnetsDirection - prevDirection
if delta < 0 {
delta = delta + stepsPerTurn
}
var nextStep int
if delta < 4 {
// Steps up
nextStep = prevStep + delta
if nextStep > maxStep {
nextStep = maxStep
}
} else if delta == 4 {
// Don't move if magnets push on the oposite direction
nextStep = prevStep
} else { // delta > 4
// Steps down
nextStep = prevStep + delta - stepsPerTurn
if nextStep < 0 {
nextStep = 0
}
}
return nextStep
}
2019-10-20 22:00:42 +00:00
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
2019-03-03 22:54:43 +00:00
}
c.processQ6Q7(in)
if index&1 == 0 {
// All even addresses return the last dataLatch
return c.dataLatch
2019-03-03 22:54:43 +00:00
}
2019-06-07 15:42:18 +00:00
return 0
}
2019-03-03 22:54:43 +00:00
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.tracksStep / 4
c.dataLatch, d.position = d.diskette.read(track, d.position)
} else { // Q6L-Q7H: Write the dataLatch value to disk. Shift data out
track := d.tracksStep / 4
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
}
2019-03-03 22:54:43 +00:00
}
}
2019-03-04 23:00:12 +00:00
func (d *cardDisk2Drive) insertDiskette(dt *diskette16sector) {
d.diskette = dt
2019-03-04 23:00:12 +00:00
}
2019-10-05 23:26:00 +00:00
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)
}
2019-10-05 23:26:00 +00:00
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)
}
2019-10-05 23:26:00 +00:00
func (d *cardDisk2Drive) save(w io.Writer) error {
err := binary.Write(w, binary.BigEndian, d.power)
2019-10-05 23:26:00 +00:00
if err != nil {
return err
}
err = binary.Write(w, binary.BigEndian, d.magnets)
2019-10-05 23:26:00 +00:00
if err != nil {
return err
}
err = binary.Write(w, binary.BigEndian, d.tracksStep)
2019-10-05 23:26:00 +00:00
if err != nil {
return err
}
err = binary.Write(w, binary.BigEndian, d.position)
if err != nil {
return err
}
return nil
}
2019-10-05 23:26:00 +00:00
func (d *cardDisk2Drive) load(r io.Reader) error {
err := binary.Read(r, binary.BigEndian, &d.power)
2019-10-05 23:26:00 +00:00
if err != nil {
return err
}
err = binary.Read(r, binary.BigEndian, &d.magnets)
2019-10-05 23:26:00 +00:00
if err != nil {
return err
}
err = binary.Read(r, binary.BigEndian, &d.tracksStep)
2019-10-05 23:26:00 +00:00
if err != nil {
return err
}
err = binary.Read(r, binary.BigEndian, &d.position)
if err != nil {
return err
}
return nil
}