apple2-go/mmu/disk.go
Will Angenent 100c44cb11 Added disk controller and disk images ...
With this, DOS3.3 is booting
2018-05-12 13:49:30 +01:00

234 lines
6.3 KiB
Go

package mmu
import (
"fmt"
"io/ioutil"
)
const tracksPerDisk = 35
const sectorsPerTrack = 16
const imageLength = tracksPerDisk * sectorsPerTrack * 0x100
// Each sector has
// Address field prologue 0x003 bytes
// Volume, Track, Sector, Checksum 0x008 bytes
// Address field epilogue 0x003 bytes
// Data prologue 0x003 bytes
// 2-bits 0x056 bytes
// 6-bits 0x100 bytes
// checksum 0x001 byte
// Data epilogue 0x003 bytes
const diskSectorBytes = 3 + 8 + 3 + 3 + 0x56 + 0x100 + 1 + 3
const trackDataBytes = sectorsPerTrack * diskSectorBytes
var dos33SectorInterleaving [16]uint8
var translateTable62 [0x40]uint8 // Conversion of a 6 bit byte to a 8 bit "disk" byte
type sector struct {
data [0x100]uint8
}
type track struct {
sectors [sectorsPerTrack]sector
}
type disk struct {
tracks [tracksPerDisk]track
}
var image disk // A loaded disk image
var TrackData [trackDataBytes]uint8 // Converted image data as it it returned by the disk controller for a single track
func InitDiskImage() {
// Map DOS 3.3 sector interleaving
// Physical sector -> Logical sector
dos33SectorInterleaving[0x0] = 0x0
dos33SectorInterleaving[0x1] = 0x7
dos33SectorInterleaving[0x2] = 0xe
dos33SectorInterleaving[0x3] = 0x6
dos33SectorInterleaving[0x4] = 0xd
dos33SectorInterleaving[0x5] = 0x5
dos33SectorInterleaving[0x6] = 0xc
dos33SectorInterleaving[0x7] = 0x4
dos33SectorInterleaving[0x8] = 0xb
dos33SectorInterleaving[0x9] = 0x3
dos33SectorInterleaving[0xa] = 0xa
dos33SectorInterleaving[0xb] = 0x2
dos33SectorInterleaving[0xc] = 0x9
dos33SectorInterleaving[0xd] = 0x1
dos33SectorInterleaving[0xe] = 0x8
dos33SectorInterleaving[0xf] = 0xf
// Zero disk image data
for t := 0; t < tracksPerDisk; t++ {
for s := 0; s < sectorsPerTrack; s++ {
for i := 0; i < 0x100; i++ {
image.tracks[t].sectors[s].data[i] = 0
}
}
}
// Convert a 6 bit "byte" to a 8 bit "disk" byte
translateTable62 = [0x40]uint8{
0x96, 0x97, 0x9a, 0x9b, 0x9d, 0x9e, 0x9f, 0xa6,
0xa7, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb2, 0xb3,
0xb4, 0xb5, 0xb6, 0xb7, 0xb9, 0xba, 0xbb, 0xbc,
0xbd, 0xbe, 0xbf, 0xcb, 0xcd, 0xce, 0xcf, 0xd3,
0xd6, 0xd7, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde,
0xdf, 0xe5, 0xe6, 0xe7, 0xe9, 0xea, 0xeb, 0xec,
0xed, 0xee, 0xef, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6,
0xf7, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
}
}
func ReadDiskImage(path string) {
bytes, err := ioutil.ReadFile(path)
if err != nil {
panic(fmt.Sprintf("Unable to read ROM: %s", err))
}
if len(bytes) != imageLength {
panic(fmt.Sprintf("Disk image has invalid length %d, expected %d", len(bytes), imageLength))
}
pos := 0
for t := 0; t < tracksPerDisk; t++ {
for s := 0; s < sectorsPerTrack; s++ {
for i := 0; i < 0x100; i++ {
image.tracks[t].sectors[s].data[i] = bytes[pos]
pos++
}
}
}
}
// Encode a byte into two 4-bit bytes with odd-even encoding. This is used
// for the sector and data headers
func oddEvenEncode(data uint8) (uint8, uint8) {
bit0 := (data & 0x01) >> 0
bit1 := (data & 0x02) >> 1
bit2 := (data & 0x04) >> 2
bit3 := (data & 0x08) >> 3
bit4 := (data & 0x10) >> 4
bit5 := (data & 0x20) >> 5
bit6 := (data & 0x40) >> 6
bit7 := (data & 0x80) >> 7
l := 0xaa | (bit7 << 6) | (bit5 << 4) | (bit3 << 2) | (bit1)
h := 0xaa | (bit6 << 6) | (bit4 << 4) | (bit2 << 2) | (bit0)
return l, h
}
// Convert 8 bit bytes to 0x56 2-bit sections and 0x100 6 bit sections
func makeSectorData(s sector) (data [0x56 + 0x100]uint8) {
twoBitPos := 0x0
for i := 0; i < 0x100; i++ {
b := s.data[i]
bit0 := b & 0x1
bit1 := (b & 0x2) >> 1
data[twoBitPos] = (data[twoBitPos] >> 2) | (bit0 << 5) | (bit1 << 4)
data[i+0x56] = b >> 2
twoBitPos++
if twoBitPos == 0x56 {
twoBitPos = 0x0
}
}
// Make sure the bits for 2 remainders from the 256 divide by 3 are in the right place.
data[0x54] = (data[0x54] >> 2)
data[0x55] = (data[0x55] >> 2)
return
}
func clearTrackData() {
for i := 0; i < trackDataBytes; i++ {
TrackData[i] = 0
}
}
func MakeTrackData(armPosition uint8) {
// Tracks are present on even arm positions.
track := armPosition / 2
// If it's an odd arm position or a track beyond the image, zero the data
if (armPosition >= (tracksPerDisk * 2)) || ((armPosition % 2) == 1) {
clearTrackData()
return
}
DriveState.BytePosition = 0 // Point the head at the first sector
// For each sector, encode the data and add it to TrackData
for physicalSector := 0; physicalSector < sectorsPerTrack; physicalSector++ {
logicalSector := dos33SectorInterleaving[physicalSector]
offset := int(physicalSector) * diskSectorBytes
volume := uint8(254) // Volume numbers aren't implemented
checksum := volume ^ track ^ uint8(physicalSector)
volL, volH := oddEvenEncode(volume)
trL, trH := oddEvenEncode(track)
seL, seH := oddEvenEncode(uint8(physicalSector))
csL, csH := oddEvenEncode(checksum)
// Address field prologue
TrackData[offset+0] = 0xd5
TrackData[offset+1] = 0xaa
TrackData[offset+2] = 0x96
// Volume, track, sector and checksum
TrackData[offset+3] = volL
TrackData[offset+4] = volH
TrackData[offset+5] = trL
TrackData[offset+6] = trH
TrackData[offset+7] = seL
TrackData[offset+8] = seH
TrackData[offset+9] = csL
TrackData[offset+10] = csH
// Address epilogue
TrackData[offset+11] = 0xde
TrackData[offset+12] = 0xaa
TrackData[offset+13] = 0xeb
// Data field prologue
TrackData[offset+14] = 0xd5
TrackData[offset+15] = 0xaa
TrackData[offset+16] = 0xad
sectorData := makeSectorData(image.tracks[track].sectors[logicalSector])
// a is the previous byte's value
a := uint8(0)
for i := 0; i < 0x56+0x100; i++ {
a ^= sectorData[i]
b := translateTable62[a]
TrackData[offset+17+i] = b
a = sectorData[i]
}
// Set the checksum byte
TrackData[offset+17+0x56+0x100] = translateTable62[a]
// Data epilogue
TrackData[offset+17+0x56+0x100+1] = 0xde
TrackData[offset+17+0x56+0x100+2] = 0xaa
TrackData[offset+17+0x56+0x100+3] = 0xeb
}
}
// Read a byte from the disk head and spin the disk along
func ReadTrackData() (result uint8) {
result = TrackData[DriveState.BytePosition]
DriveState.BytePosition++
if DriveState.BytePosition == trackDataBytes {
DriveState.BytePosition = 0
}
return
}