Added disk writes
This commit is contained in:
parent
1ea1b10ab0
commit
bdcf3e0db7
|
@ -84,14 +84,14 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
cpu.Init()
|
cpu.Init()
|
||||||
|
|
||||||
keyboard.Init()
|
keyboard.Init()
|
||||||
video.Init()
|
video.Init()
|
||||||
audio.InitEbiten()
|
audio.InitEbiten()
|
||||||
audio.Mute = *mute
|
audio.Mute = *mute
|
||||||
system.Init()
|
system.Init()
|
||||||
|
|
||||||
cpu.Reset()
|
cpu.Reset()
|
||||||
|
|
||||||
ebiten.Run(update, 280*video.ScreenSizeFactor, 192*video.ScreenSizeFactor, 2, "Apple //e")
|
ebiten.Run(update, 280*video.ScreenSizeFactor, 192*video.ScreenSizeFactor, 2, "Apple //e")
|
||||||
|
|
||||||
|
mmu.FlushImage()
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,40 +6,30 @@ import (
|
||||||
"mos6502go/keyboard"
|
"mos6502go/keyboard"
|
||||||
"mos6502go/mmu"
|
"mos6502go/mmu"
|
||||||
"mos6502go/system"
|
"mos6502go/system"
|
||||||
|
"mos6502go/utils"
|
||||||
"mos6502go/video"
|
"mos6502go/video"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const DiskImage = "dos33_disk.dsk"
|
const diskImage = "dos33_disk.dsk"
|
||||||
|
|
||||||
func TestDOS33Boot(t *testing.T) {
|
func TestDOS33Boot(t *testing.T) {
|
||||||
cpu.InitInstructionDecoder()
|
cpu.InitInstructionDecoder()
|
||||||
mmu.InitRAM()
|
mmu.InitRAM()
|
||||||
mmu.InitApple2eROM()
|
mmu.InitApple2eROM()
|
||||||
mmu.InitIO()
|
mmu.InitIO()
|
||||||
mmu.ReadDiskImage(DiskImage)
|
mmu.ReadDiskImage(diskImage)
|
||||||
cpu.Init()
|
cpu.Init()
|
||||||
keyboard.Init()
|
keyboard.Init()
|
||||||
video.Init()
|
video.Init()
|
||||||
system.Init()
|
system.Init()
|
||||||
cpu.Reset()
|
cpu.Reset()
|
||||||
|
|
||||||
system.FrameCycles = 0
|
|
||||||
system.LastAudioCycles = 0
|
|
||||||
showInstructions := false
|
|
||||||
disableFirmwareWait := false
|
|
||||||
|
|
||||||
// Break at the BASIC interpreter to ensure DOS has started
|
|
||||||
breakAddress := uint16(0xa503)
|
|
||||||
exitAtBreak := false
|
|
||||||
|
|
||||||
t0 := time.Now()
|
t0 := time.Now()
|
||||||
cpu.Run(showInstructions, &breakAddress, exitAtBreak, disableFirmwareWait, system.CpuFrequency*7)
|
|
||||||
|
|
||||||
if cpu.State.PC != 0xa503 {
|
// Run until BASIC would execute the program.
|
||||||
t.Fatal("Did not reach BASIC entrypoint")
|
utils.RunUntilBreakPoint(t, 0xd7d2, 5, false, "BASIC NEWSTT")
|
||||||
}
|
|
||||||
|
|
||||||
elapsed := float64(time.Since(t0) / time.Millisecond)
|
elapsed := float64(time.Since(t0) / time.Millisecond)
|
||||||
fmt.Printf("CPU Cycles: %d\n", system.FrameCycles)
|
fmt.Printf("CPU Cycles: %d\n", system.FrameCycles)
|
||||||
|
|
300
mmu/disk.go
300
mmu/disk.go
|
@ -22,7 +22,8 @@ const diskSectorBytes = 3 + 8 + 3 + 3 + 0x56 + 0x100 + 1 + 3
|
||||||
const trackDataBytes = sectorsPerTrack * diskSectorBytes
|
const trackDataBytes = sectorsPerTrack * diskSectorBytes
|
||||||
|
|
||||||
var dos33SectorInterleaving [16]uint8
|
var dos33SectorInterleaving [16]uint8
|
||||||
var translateTable62 [0x40]uint8 // Conversion of a 6 bit byte to a 8 bit "disk" byte
|
var sixTwoEncoding [0x40]uint8 // Conversion of a 6 bit byte to a 8 bit "disk" byte
|
||||||
|
var sixTwoDecoding [0x100]uint8 // Conversion of a 8 bit "disk" byte to a 6 bit byte
|
||||||
|
|
||||||
type sector struct {
|
type sector struct {
|
||||||
data [0x100]uint8
|
data [0x100]uint8
|
||||||
|
@ -36,9 +37,40 @@ type disk struct {
|
||||||
tracks [tracksPerDisk]track
|
tracks [tracksPerDisk]track
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var imagePath string // Loaded disk image path
|
||||||
var image disk // A loaded disk image
|
var image disk // A loaded disk image
|
||||||
|
var imageIsDirty bool // If an image has been written to and needs a flush
|
||||||
var TrackData [trackDataBytes]uint8 // Converted image data as it it returned by the disk controller for a single track
|
var TrackData [trackDataBytes]uint8 // Converted image data as it it returned by the disk controller for a single track
|
||||||
|
|
||||||
|
// vars to keep track of writes
|
||||||
|
const (
|
||||||
|
WaitingForDataPrologue byte = 1 + iota
|
||||||
|
ReceivingData
|
||||||
|
)
|
||||||
|
|
||||||
|
const rawDataBufferSize = diskSectorBytes + 16
|
||||||
|
|
||||||
|
type AddressField struct {
|
||||||
|
volume uint8
|
||||||
|
track uint8
|
||||||
|
sector uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
var lastReadAddress AddressField
|
||||||
|
var lastReadSectorDataPosition int
|
||||||
|
|
||||||
|
var SectorWriteState struct {
|
||||||
|
State byte
|
||||||
|
RawData [rawDataBufferSize]uint8
|
||||||
|
RawDataPosition uint16
|
||||||
|
Address AddressField
|
||||||
|
}
|
||||||
|
|
||||||
|
func ResetSectorWriteState() {
|
||||||
|
SectorWriteState.State = WaitingForDataPrologue
|
||||||
|
SectorWriteState.RawDataPosition = 0
|
||||||
|
}
|
||||||
|
|
||||||
func InitDiskImage() {
|
func InitDiskImage() {
|
||||||
// Map DOS 3.3 sector interleaving
|
// Map DOS 3.3 sector interleaving
|
||||||
// Physical sector -> Logical sector
|
// Physical sector -> Logical sector
|
||||||
|
@ -69,7 +101,7 @@ func InitDiskImage() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert a 6 bit "byte" to a 8 bit "disk" byte
|
// Convert a 6 bit "byte" to a 8 bit "disk" byte
|
||||||
translateTable62 = [0x40]uint8{
|
sixTwoEncoding = [0x40]uint8{
|
||||||
0x96, 0x97, 0x9a, 0x9b, 0x9d, 0x9e, 0x9f, 0xa6,
|
0x96, 0x97, 0x9a, 0x9b, 0x9d, 0x9e, 0x9f, 0xa6,
|
||||||
0xa7, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb2, 0xb3,
|
0xa7, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb2, 0xb3,
|
||||||
0xb4, 0xb5, 0xb6, 0xb7, 0xb9, 0xba, 0xbb, 0xbc,
|
0xb4, 0xb5, 0xb6, 0xb7, 0xb9, 0xba, 0xbb, 0xbc,
|
||||||
|
@ -79,12 +111,20 @@ func InitDiskImage() {
|
||||||
0xed, 0xee, 0xef, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6,
|
0xed, 0xee, 0xef, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6,
|
||||||
0xf7, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
|
0xf7, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for i := uint8(0); i < 0x40; i++ {
|
||||||
|
sixTwoDecoding[sixTwoEncoding[i]] = i
|
||||||
|
}
|
||||||
|
|
||||||
|
ResetSectorWriteState()
|
||||||
}
|
}
|
||||||
|
|
||||||
func ReadDiskImage(path string) {
|
func ReadDiskImage(path string) {
|
||||||
|
imagePath = path
|
||||||
|
|
||||||
bytes, err := ioutil.ReadFile(path)
|
bytes, err := ioutil.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Sprintf("Unable to read ROM: %s", err))
|
panic(fmt.Sprintf("Unable to read disk image: %s", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(bytes) != imageLength {
|
if len(bytes) != imageLength {
|
||||||
|
@ -100,6 +140,27 @@ func ReadDiskImage(path string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
imageIsDirty = false
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeDiskImage() {
|
||||||
|
bytes := make([]byte, tracksPerDisk*sectorsPerTrack*0x100)
|
||||||
|
|
||||||
|
pos := 0
|
||||||
|
for t := 0; t < tracksPerDisk; t++ {
|
||||||
|
for s := 0; s < sectorsPerTrack; s++ {
|
||||||
|
for i := 0; i < 0x100; i++ {
|
||||||
|
bytes[pos] = byte(image.tracks[t].sectors[s].data[i])
|
||||||
|
pos++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err := ioutil.WriteFile(imagePath, bytes, 0644)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("Unable to write disk image: %s", err))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encode a byte into two 4-bit bytes with odd-even encoding. This is used
|
// Encode a byte into two 4-bit bytes with odd-even encoding. This is used
|
||||||
|
@ -119,8 +180,13 @@ func oddEvenEncode(data uint8) (uint8, uint8) {
|
||||||
return l, h
|
return l, h
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Merge the two bytes together produce by oddEvenEncode
|
||||||
|
func oddEvenDecode(data0 byte, data1 byte) uint8 {
|
||||||
|
return ((data0 << 1) | 1) & data1
|
||||||
|
}
|
||||||
|
|
||||||
// Convert 8 bit bytes to 0x56 2-bit sections and 0x100 6 bit sections
|
// Convert 8 bit bytes to 0x56 2-bit sections and 0x100 6 bit sections
|
||||||
func makeSectorData(s sector) (data [0x56 + 0x100]uint8) {
|
func sectorDataEncode(s sector) (data [0x56 + 0x100]uint8) {
|
||||||
twoBitPos := 0x0
|
twoBitPos := 0x0
|
||||||
for i := 0; i < 0x100; i++ {
|
for i := 0; i < 0x100; i++ {
|
||||||
b := s.data[i]
|
b := s.data[i]
|
||||||
|
@ -142,15 +208,92 @@ func makeSectorData(s sector) (data [0x56 + 0x100]uint8) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func sectorDataDecode(data []uint8) (sector [0x100]uint8) {
|
||||||
|
for i := 0; i < 0x100; i++ {
|
||||||
|
sector[i] = data[i+0x56]
|
||||||
|
}
|
||||||
|
|
||||||
|
twoBitPos := 0x00
|
||||||
|
for i := 0; i < 0x100; i++ {
|
||||||
|
twoBit := data[twoBitPos]
|
||||||
|
sector[i] = (sector[i] << 2) + ((twoBit & 1) << 1) + ((twoBit & 2) >> 1)
|
||||||
|
data[twoBitPos] >>= 2
|
||||||
|
|
||||||
|
twoBitPos++
|
||||||
|
if twoBitPos == 0x56 {
|
||||||
|
twoBitPos = 0x0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func clearTrackData() {
|
func clearTrackData() {
|
||||||
for i := 0; i < trackDataBytes; i++ {
|
for i := 0; i < trackDataBytes; i++ {
|
||||||
TrackData[i] = 0
|
TrackData[i] = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func makeSectorData(track uint8, physicalSector uint8) {
|
||||||
|
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 := sectorDataEncode(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 := sixTwoEncoding[a]
|
||||||
|
TrackData[offset+17+i] = b
|
||||||
|
a = sectorData[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the checksum byte
|
||||||
|
TrackData[offset+17+0x56+0x100] = sixTwoEncoding[a]
|
||||||
|
|
||||||
|
// Data epilogue
|
||||||
|
TrackData[offset+17+0x56+0x100+1] = 0xde
|
||||||
|
TrackData[offset+17+0x56+0x100+2] = 0xaa
|
||||||
|
TrackData[offset+17+0x56+0x100+3] = 0xeb
|
||||||
|
}
|
||||||
|
|
||||||
func MakeTrackData(armPosition uint8) {
|
func MakeTrackData(armPosition uint8) {
|
||||||
// Tracks are present on even arm positions.
|
// Tracks are present on even arm positions.
|
||||||
track := armPosition / 2
|
track := uint8(armPosition / 2)
|
||||||
|
|
||||||
// If it's an odd arm position or a track beyond the image, zero the data
|
// If it's an odd arm position or a track beyond the image, zero the data
|
||||||
if (armPosition >= (tracksPerDisk * 2)) || ((armPosition % 2) == 1) {
|
if (armPosition >= (tracksPerDisk * 2)) || ((armPosition % 2) == 1) {
|
||||||
|
@ -161,69 +304,34 @@ func MakeTrackData(armPosition uint8) {
|
||||||
DriveState.BytePosition = 0 // Point the head at the first sector
|
DriveState.BytePosition = 0 // Point the head at the first sector
|
||||||
|
|
||||||
// For each sector, encode the data and add it to TrackData
|
// For each sector, encode the data and add it to TrackData
|
||||||
for physicalSector := 0; physicalSector < sectorsPerTrack; physicalSector++ {
|
for physicalSector := uint8(0); physicalSector < sectorsPerTrack; physicalSector++ {
|
||||||
logicalSector := dos33SectorInterleaving[physicalSector]
|
makeSectorData(track, 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
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func DecodeAddressField(data []uint8) AddressField {
|
||||||
|
var af AddressField
|
||||||
|
af.volume = oddEvenDecode(data[0], data[1])
|
||||||
|
af.track = oddEvenDecode(data[2], data[3])
|
||||||
|
af.sector = oddEvenDecode(data[4], data[5])
|
||||||
|
return af
|
||||||
|
}
|
||||||
|
|
||||||
// Read a byte from the disk head and spin the disk along
|
// Read a byte from the disk head and spin the disk along
|
||||||
func ReadTrackData() (result uint8) {
|
func ReadTrackData() (result uint8) {
|
||||||
result = TrackData[DriveState.BytePosition]
|
result = TrackData[DriveState.BytePosition]
|
||||||
|
|
||||||
|
if DriveState.BytePosition >= 9 {
|
||||||
|
if TrackData[DriveState.BytePosition-9] == 0xd5 &&
|
||||||
|
TrackData[DriveState.BytePosition-8] == 0xaa &&
|
||||||
|
TrackData[DriveState.BytePosition-7] == 0x96 {
|
||||||
|
var addressData []uint8
|
||||||
|
addressData = TrackData[DriveState.BytePosition-6 : DriveState.BytePosition]
|
||||||
|
lastReadAddress = DecodeAddressField(addressData)
|
||||||
|
lastReadSectorDataPosition = DriveState.BytePosition + 8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
DriveState.BytePosition++
|
DriveState.BytePosition++
|
||||||
if DriveState.BytePosition == trackDataBytes {
|
if DriveState.BytePosition == trackDataBytes {
|
||||||
DriveState.BytePosition = 0
|
DriveState.BytePosition = 0
|
||||||
|
@ -231,3 +339,73 @@ func ReadTrackData() (result uint8) {
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriteTrackData gets called whenever a byte is written to the write address.
|
||||||
|
// Reads are done at the same time by the OS to await the drive to be in the
|
||||||
|
// right position. The last read address determines the track and sector. The expeted sequence of writes are:
|
||||||
|
// - up to 5 bytes of 0xff padding (ignored)
|
||||||
|
// - data prologue d5 aa ad
|
||||||
|
// - 0x56 bytes of 2-bit data
|
||||||
|
// - 0x56 bytes of 6-bit data
|
||||||
|
// - checksum byte (ignored)
|
||||||
|
// - data epilogue (ignored)
|
||||||
|
//
|
||||||
|
// The sector is decoded and updated in memory once the 0x156 data bytes have
|
||||||
|
// been read. The image is flagged as dirty and flushed on exit.
|
||||||
|
func WriteTrackData(value uint8) {
|
||||||
|
if SectorWriteState.State == WaitingForDataPrologue {
|
||||||
|
if SectorWriteState.RawDataPosition >= 16 {
|
||||||
|
ResetSectorWriteState()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
SectorWriteState.RawData[SectorWriteState.RawDataPosition] = value
|
||||||
|
SectorWriteState.RawDataPosition += 1
|
||||||
|
|
||||||
|
// Check for address prologue
|
||||||
|
if SectorWriteState.RawDataPosition > 2 && SectorWriteState.RawData[SectorWriteState.RawDataPosition-3] == 0xd5 &&
|
||||||
|
SectorWriteState.RawData[SectorWriteState.RawDataPosition-2] == 0xaa &&
|
||||||
|
SectorWriteState.RawData[SectorWriteState.RawDataPosition-1] == 0xad {
|
||||||
|
|
||||||
|
// We got it, record the last read address field and reset RawDataPosition
|
||||||
|
SectorWriteState.State = ReceivingData
|
||||||
|
SectorWriteState.Address = lastReadAddress
|
||||||
|
SectorWriteState.RawDataPosition = 0
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if SectorWriteState.State == ReceivingData {
|
||||||
|
SectorWriteState.RawData[SectorWriteState.RawDataPosition] = value
|
||||||
|
SectorWriteState.RawDataPosition += 1
|
||||||
|
|
||||||
|
if SectorWriteState.RawDataPosition == 0x56+0x100 {
|
||||||
|
// We have the full sector data
|
||||||
|
physicalSector := lastReadAddress.sector
|
||||||
|
logicalSector := dos33SectorInterleaving[physicalSector]
|
||||||
|
|
||||||
|
// transform the data from disk bytes to 6-bytes and EOR it
|
||||||
|
a := uint8(0)
|
||||||
|
for i := 0; i < 0x56+0x100; i++ {
|
||||||
|
b := sixTwoDecoding[SectorWriteState.RawData[i]]
|
||||||
|
a ^= b
|
||||||
|
SectorWriteState.RawData[i] = a
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transform the 0x156 bytes into the final 0x100 bytes
|
||||||
|
sectorData := sectorDataDecode(SectorWriteState.RawData[0:0x156])
|
||||||
|
|
||||||
|
// Save the data to memory & recreate the raw sector data
|
||||||
|
image.tracks[lastReadAddress.track].sectors[logicalSector].data = sectorData
|
||||||
|
makeSectorData(lastReadAddress.track, physicalSector)
|
||||||
|
|
||||||
|
ResetSectorWriteState()
|
||||||
|
imageIsDirty = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func FlushImage() {
|
||||||
|
if imageIsDirty {
|
||||||
|
writeDiskImage()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -208,8 +208,11 @@ func readWrite(address uint16, isRead bool) bool {
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
case S6Q6H:
|
case S6Q6H:
|
||||||
DriveState.Q6 = true
|
if isRead {
|
||||||
return true
|
DriveState.Q6 = true
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
case S6Q7L:
|
case S6Q7L:
|
||||||
DriveState.Q7 = false
|
DriveState.Q7 = false
|
||||||
return true
|
return true
|
||||||
|
@ -299,6 +302,8 @@ func WriteIO(address uint16, value uint8) {
|
||||||
// CLRC3ROM not implemented
|
// CLRC3ROM not implemented
|
||||||
case SETC3ROM:
|
case SETC3ROM:
|
||||||
// SETC3ROM not implemented
|
// SETC3ROM not implemented
|
||||||
|
case S6Q6H:
|
||||||
|
WriteTrackData(value)
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("TODO write %04x\n", address))
|
panic(fmt.Sprintf("TODO write %04x\n", address))
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"mos6502go/cpu"
|
||||||
|
"mos6502go/keyboard"
|
||||||
|
"mos6502go/mmu"
|
||||||
|
"mos6502go/system"
|
||||||
|
"mos6502go/utils"
|
||||||
|
"mos6502go/video"
|
||||||
|
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
const diskImage = "dos33_disk.dsk"
|
||||||
|
|
||||||
|
func writeBytes(address int, data []uint8) {
|
||||||
|
for i := 0; i < len(data); i++ {
|
||||||
|
mmu.WriteMemory(uint16(address)+uint16(i), data[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDos33RtsWriteRead(t *testing.T) {
|
||||||
|
// Test writing and reading a sector using DOS 3.3's RWTS
|
||||||
|
cpu.InitInstructionDecoder()
|
||||||
|
mmu.InitRAM()
|
||||||
|
mmu.InitApple2eROM()
|
||||||
|
mmu.InitIO()
|
||||||
|
mmu.ReadDiskImage(diskImage)
|
||||||
|
cpu.Init()
|
||||||
|
keyboard.Init()
|
||||||
|
video.Init()
|
||||||
|
system.Init()
|
||||||
|
cpu.Reset()
|
||||||
|
|
||||||
|
// Boot up DOS3.3
|
||||||
|
utils.RunUntilBreakPoint(t, 0xd7d2, 5, false, "BASIC NEWSTT")
|
||||||
|
|
||||||
|
// Write a sector from 0x2000 to track 35, sector 14
|
||||||
|
start := 0x800
|
||||||
|
writeBuffer := 0x2000
|
||||||
|
readBuffer := 0x2100
|
||||||
|
|
||||||
|
// Put some test data in
|
||||||
|
for i := uint16(0); i < 0x100; i++ {
|
||||||
|
mmu.WriteMemory(uint16(writeBuffer)+i, uint8(i)^0xaa)
|
||||||
|
}
|
||||||
|
|
||||||
|
writeBytes(start+0x00, []uint8{0x20, 0xe3, 0x03}) // JSR $03E3 LOCRPL = LOCATE RWTS PARAM LIST
|
||||||
|
writeBytes(start+0x03, []uint8{0x84, 0x00}) // STY $00
|
||||||
|
writeBytes(start+0x05, []uint8{0x85, 0x01}) // STA $01
|
||||||
|
writeBytes(start+0x07, []uint8{0xa9, 0x22}) // LDA #$22 track 34
|
||||||
|
writeBytes(start+0x09, []uint8{0xa0, 0x04}) // LDY #$04
|
||||||
|
writeBytes(start+0x0b, []uint8{0x91, 0x00}) // STA ($00),Y
|
||||||
|
writeBytes(start+0x0d, []uint8{0xa9, 0x0e}) // LDA #$0e sector 14
|
||||||
|
writeBytes(start+0x0f, []uint8{0xa0, 0x05}) // LDY #$05
|
||||||
|
writeBytes(start+0x11, []uint8{0x91, 0x00}) // STA ($00),Y
|
||||||
|
writeBytes(start+0x13, []uint8{0xa9, uint8(writeBuffer & 0xff)}) // LDA writeBuffer lsb
|
||||||
|
writeBytes(start+0x15, []uint8{0xa0, 0x08}) // LDY #$08
|
||||||
|
writeBytes(start+0x17, []uint8{0x91, 0x00}) // STA ($00),Y
|
||||||
|
writeBytes(start+0x19, []uint8{0xa9, uint8(writeBuffer >> 8)}) // LDA writeBuffer msb
|
||||||
|
writeBytes(start+0x1b, []uint8{0xa0, 0x09}) // LDY #$09
|
||||||
|
writeBytes(start+0x1d, []uint8{0x91, 0x00}) // STA ($00),Y
|
||||||
|
writeBytes(start+0x1f, []uint8{0xa9, 0x02}) // LDA #$02 command=2 (write)
|
||||||
|
writeBytes(start+0x21, []uint8{0xa0, 0x0c}) // LDY #$0c
|
||||||
|
writeBytes(start+0x23, []uint8{0x91, 0x00}) // STA ($00),Y
|
||||||
|
writeBytes(start+0x25, []uint8{0xa9, 0x00}) // LDA #$00 any volume will do
|
||||||
|
writeBytes(start+0x27, []uint8{0xa0, 0x03}) // LDY #$03
|
||||||
|
writeBytes(start+0x29, []uint8{0x91, 0x00}) // STA ($00),Y
|
||||||
|
writeBytes(start+0x2b, []uint8{0x20, 0xe3, 0x03}) // JSR $03E3 Relocate pointer to parms
|
||||||
|
writeBytes(start+0x2f, []uint8{0x20, 0xd9, 0x03}) // JSR $03D9 RWTS
|
||||||
|
writeBytes(start+0x32, []uint8{0x00}) // BRK
|
||||||
|
|
||||||
|
cpu.State.PC = uint16(start)
|
||||||
|
utils.RunUntilBreakPoint(t, 0xb944, 128, false, "RWTS RDADDR")
|
||||||
|
utils.RunUntilBreakPoint(t, 0xb82a, 8, false, "RWTS WRITESEC")
|
||||||
|
utils.RunUntilBreakPoint(t, 0xb7ba, 8, false, "RWTS ENTERWTS")
|
||||||
|
utils.RunUntilBreakPoint(t, uint16(start+0x32), 1, false, "Write routine break")
|
||||||
|
|
||||||
|
// Now run some modified code to read the same track/sector
|
||||||
|
writeBytes(start+0x13, []uint8{0xa9, uint8(readBuffer & 0xff)}) // LDA readBuffer lsb
|
||||||
|
writeBytes(start+0x15, []uint8{0xa0, 0x08}) // LDY #$08
|
||||||
|
writeBytes(start+0x17, []uint8{0x91, 0x00}) // STA ($00),Y
|
||||||
|
writeBytes(start+0x19, []uint8{0xa9, uint8(readBuffer >> 8)}) // LDA readBuffer msb
|
||||||
|
writeBytes(start+0x1b, []uint8{0xa0, 0x09}) // LDY #$09
|
||||||
|
writeBytes(start+0x1d, []uint8{0x91, 0x00}) // STA ($00),Y
|
||||||
|
writeBytes(start+0x1f, []uint8{0xa9, 0x01}) // LDA #$01 command=1 (read)
|
||||||
|
writeBytes(start+0x1b, []uint8{0xa0, 0x09}) // LDY #$09
|
||||||
|
writeBytes(start+0x1d, []uint8{0x91, 0x00}) // STA ($00),Y
|
||||||
|
|
||||||
|
cpu.State.PC = uint16(start)
|
||||||
|
utils.RunUntilBreakPoint(t, uint16(start+0x32), 1, false, "Read routine break")
|
||||||
|
|
||||||
|
// Check the read bytes match the witten ones
|
||||||
|
for i := 0; i < 0x100; i++ {
|
||||||
|
b1 := mmu.ReadMemory(uint16(readBuffer + i))
|
||||||
|
b2 := mmu.ReadMemory(uint16(writeBuffer + i))
|
||||||
|
if b1 != b2 {
|
||||||
|
t.Fatalf("Mismatch at %02x: %02x vs %02x", readBuffer+i, b1, b2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,8 +3,12 @@ package utils
|
||||||
import (
|
import (
|
||||||
"compress/gzip"
|
"compress/gzip"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"mos6502go/cpu"
|
||||||
|
"mos6502go/system"
|
||||||
"os"
|
"os"
|
||||||
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ReadMemoryFromGzipFile(filename string) (data []byte, err error) {
|
func ReadMemoryFromGzipFile(filename string) (data []byte, err error) {
|
||||||
|
@ -44,3 +48,14 @@ func DecodeCmdLineAddress(s *string) (result *uint16) {
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func RunUntilBreakPoint(t *testing.T, breakAddress uint16, seconds int, showInstructions bool, message string) {
|
||||||
|
fmt.Printf("Running until %#04x: %s \n", breakAddress, message)
|
||||||
|
system.FrameCycles = 0
|
||||||
|
exitAtBreak := false
|
||||||
|
disableFirmwareWait := false
|
||||||
|
cpu.Run(showInstructions, &breakAddress, exitAtBreak, disableFirmwareWait, uint64(system.CpuFrequency*seconds))
|
||||||
|
if cpu.State.PC != breakAddress {
|
||||||
|
t.Fatalf("Did not reach breakpoint at %04x. Got to %04x", breakAddress, cpu.State.PC)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue