2019-03-02 17:33:50 +00:00
|
|
|
package apple2
|
|
|
|
|
2019-05-17 21:28:20 +00:00
|
|
|
import (
|
|
|
|
"encoding/binary"
|
|
|
|
"io"
|
|
|
|
"io/ioutil"
|
|
|
|
)
|
2019-03-02 19:41:25 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
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
|
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
|
2019-03-04 23:00:12 +00:00
|
|
|
selected int // Only 0 and 1 supported
|
|
|
|
drive [2]cardDisk2Drive
|
|
|
|
}
|
|
|
|
|
|
|
|
type cardDisk2Drive struct {
|
2019-03-10 23:12:34 +00:00
|
|
|
diskette *diskette16sector
|
2019-03-04 23:00:12 +00:00
|
|
|
currentPhase int
|
|
|
|
power bool
|
|
|
|
writeMode bool
|
|
|
|
halfTrack int
|
|
|
|
position int
|
2019-03-02 17:33:50 +00:00
|
|
|
}
|
|
|
|
|
2019-03-02 19:41:25 +00:00
|
|
|
func newCardDisk2(filename string) *cardDisk2 {
|
|
|
|
var c cardDisk2
|
2019-05-16 20:51:04 +00:00
|
|
|
c.rom = newMemoryRange(0, loadCardRom(filename))
|
2019-03-02 19:41:25 +00:00
|
|
|
|
|
|
|
// Phase control soft switches
|
2019-03-04 23:00:12 +00:00
|
|
|
// Lazy emulation. It only checks for phases on and move the head
|
|
|
|
// up or down depending on the previous phase.
|
2019-03-02 19:41:25 +00:00
|
|
|
for i := 0; i < 4; i++ {
|
2019-03-04 23:00:12 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
}(i)
|
2019-03-02 19:41:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Other soft switches
|
|
|
|
c.ssr[0x8] = func(_ *ioC0Page) uint8 {
|
2019-05-10 16:07:36 +00:00
|
|
|
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)
|
|
|
|
}
|
2019-03-02 19:41:25 +00:00
|
|
|
return 0
|
|
|
|
}
|
|
|
|
c.ssr[0x9] = func(_ *ioC0Page) uint8 {
|
2019-05-10 16:07:36 +00:00
|
|
|
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)
|
|
|
|
}
|
2019-03-02 19:41:25 +00:00
|
|
|
return 0
|
|
|
|
}
|
|
|
|
c.ssr[0xA] = func(_ *ioC0Page) uint8 {
|
|
|
|
c.selected = 0
|
2019-03-04 23:00:12 +00:00
|
|
|
//fmt.Printf("DISKII: Disk %v selected\n", c.selected)
|
2019-03-02 19:41:25 +00:00
|
|
|
return 0
|
|
|
|
}
|
|
|
|
c.ssr[0xB] = func(_ *ioC0Page) uint8 {
|
|
|
|
c.selected = 1
|
2019-03-04 23:00:12 +00:00
|
|
|
//fmt.Printf("DISKII: Disk %v selected\n", c.selected)
|
2019-03-02 19:41:25 +00:00
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
2019-03-03 22:54:43 +00:00
|
|
|
// Q6L
|
|
|
|
c.ssr[0xC] = func(_ *ioC0Page) uint8 {
|
2019-03-04 23:00:12 +00:00
|
|
|
//fmt.Printf("DISKII: Reading\n")
|
|
|
|
drive := &c.drive[c.selected]
|
2019-03-10 23:12:34 +00:00
|
|
|
if drive.diskette == nil {
|
|
|
|
return 0xff
|
2019-03-04 23:00:12 +00:00
|
|
|
}
|
2019-03-10 23:12:34 +00:00
|
|
|
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
|
2019-03-03 22:54:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
c.ssw[0xC] = func(_ *ioC0Page, value uint8) {
|
2019-03-04 23:00:12 +00:00
|
|
|
//fmt.Printf("DISKII: Writing the value 0x%02x\n", value)
|
2019-03-03 22:54:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Q6H
|
|
|
|
c.ssr[0xD] = func(_ *ioC0Page) uint8 {
|
2019-03-04 23:00:12 +00:00
|
|
|
c.drive[c.selected].writeMode = false
|
|
|
|
//fmt.Printf("DISKII: Sense write protection\n")
|
2019-03-03 22:54:43 +00:00
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
// Q7L
|
|
|
|
c.ssr[0xE] = func(_ *ioC0Page) uint8 {
|
2019-03-04 23:00:12 +00:00
|
|
|
c.drive[c.selected].writeMode = false
|
|
|
|
//fmt.Printf("DISKII: Set read mode\n")
|
2019-03-03 22:54:43 +00:00
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
// Q7H
|
|
|
|
c.ssr[0xF] = func(_ *ioC0Page) uint8 {
|
2019-03-04 23:00:12 +00:00
|
|
|
c.drive[c.selected].writeMode = true
|
|
|
|
//fmt.Printf("DISKII: Set write mode\n")
|
2019-03-03 22:54:43 +00:00
|
|
|
return 0
|
|
|
|
}
|
2019-03-02 19:41:25 +00:00
|
|
|
return &c
|
|
|
|
}
|
|
|
|
|
2019-05-16 20:51:04 +00:00
|
|
|
func loadCardRom(filename string) []uint8 {
|
|
|
|
data, err := ioutil.ReadFile(filename)
|
2019-03-02 19:41:25 +00:00
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2019-05-16 20:51:04 +00:00
|
|
|
return data
|
2019-03-02 17:33:50 +00:00
|
|
|
}
|
2019-03-04 23:00:12 +00:00
|
|
|
|
2019-03-10 23:12:34 +00:00
|
|
|
func (d *cardDisk2Drive) insertDiskette(dt *diskette16sector) {
|
|
|
|
d.diskette = dt
|
2019-03-04 23:00:12 +00:00
|
|
|
}
|
2019-05-17 21:28:20 +00:00
|
|
|
|
|
|
|
func (c *cardDisk2) save(w io.Writer) {
|
|
|
|
binary.Write(w, binary.BigEndian, c.selected)
|
|
|
|
c.drive[0].save(w)
|
|
|
|
c.drive[1].save(w)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *cardDisk2) load(r io.Reader) {
|
|
|
|
binary.Read(r, binary.BigEndian, &c.selected)
|
|
|
|
c.drive[0].load(r)
|
|
|
|
c.drive[1].load(r)
|
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
}
|