diff --git a/diskette.go b/diskette.go index 9822de6..7a73192 100644 --- a/diskette.go +++ b/diskette.go @@ -2,7 +2,6 @@ package apple2 import ( "errors" - "strings" ) type diskette interface { @@ -25,9 +24,8 @@ func loadDiskette(filename string) (diskette, error) { } if isFileDsk(data) { - isPO := strings.HasSuffix(strings.ToLower(filename), "po") - var d diskette16sector - d.nib = newFileDsk(data, isPO) + var d diskette16sectorWritable + d.nib = newFileDsk(data, filename) return &d, nil } diff --git a/diskette16sectorWritable.go b/diskette16sectorWritable.go new file mode 100644 index 0000000..4b40690 --- /dev/null +++ b/diskette16sectorWritable.go @@ -0,0 +1,51 @@ +package apple2 + +/* +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 +*/ + +type diskette16sectorWritable struct { + nib *fileNib + position int + + // Needed to write back + hasDirtyTrack bool + dirtyTrack int +} + +func (d *diskette16sectorWritable) powerOn(cycle uint64) { + // Not used +} +func (d *diskette16sectorWritable) powerOff(_ uint64) { + d.commit() +} + +func (d *diskette16sectorWritable) read(quarterTrack int, cycle uint64) uint8 { + track := d.nib.track[quarterTrack/stepsPerTrack] + value := track[d.position] + d.position = (d.position + 1) % nibBytesPerTrack + return value +} + +func (d *diskette16sectorWritable) write(quarterTrack int, value uint8, _ uint64) { + track := quarterTrack / stepsPerTrack + + if d.hasDirtyTrack && track != d.dirtyTrack { + d.commit() + } + + d.nib.track[track][d.position] = value + d.position = (d.position + 1) % nibBytesPerTrack + + d.hasDirtyTrack = true + d.dirtyTrack = track +} + +func (d *diskette16sectorWritable) commit() { + if d.hasDirtyTrack { + d.nib.saveTrack(d.dirtyTrack) + d.hasDirtyTrack = false + } +} diff --git a/fileNib.go b/fileNib.go index 039735f..7516591 100644 --- a/fileNib.go +++ b/fileNib.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "os" + "strings" ) /* @@ -26,6 +27,11 @@ const ( type fileNib struct { track [numberOfTracks][]byte + + // Needed to write back + supportsWrite bool + filename string + logicalOrder *[16]int } func isFileNib(data []uint8) bool { @@ -46,22 +52,50 @@ func isFileDsk(data []uint8) bool { return len(data) == dskImageSize } -func newFileDsk(data []uint8, isPO bool) *fileNib { +func newFileDsk(data []uint8, filename string) *fileNib { var f fileNib - logicalOrder := dos33SectorsLogicalOrder + isPO := strings.HasSuffix(strings.ToLower(filename), "po") + f.logicalOrder = &dos33SectorsLogicalOrder if isPO { - logicalOrder = prodosSectorsLogicalOrder + f.logicalOrder = &prodosSectorsLogicalOrder } + f.filename = filename + f.supportsWrite = true + for i := 0; i < numberOfTracks; i++ { trackData := data[i*bytesPerTrack : (i+1)*bytesPerTrack] - f.track[i] = nibEncodeTrack(trackData, defaultVolumeTag, byte(i), &logicalOrder) + f.track[i] = nibEncodeTrack(trackData, defaultVolumeTag, byte(i), f.logicalOrder) } return &f } +func (f *fileNib) saveTrack(track int) { + if f.supportsWrite { + file, err := os.OpenFile(f.filename, os.O_RDWR, 0) + if err != nil { + // We can't open the file for writing" + f.supportsWrite = false + fmt.Printf("Data can't be written for %v\n", f.filename) + } + + data, err := nibDecodeTrack(f.track[track], f.logicalOrder) + if err != nil { + f.supportsWrite = false + fmt.Printf("Data written can't be decoded from nibbles\n") + } + + offset := int64(track * bytesPerTrack) + _, err = file.WriteAt(data, offset) + if err != nil { + f.supportsWrite = false + fmt.Printf("Data can't be written\n") + } + } +} + func (f *fileNib) saveNib(filename string) error { file, err := os.Create(filename) if err != nil { @@ -235,7 +269,7 @@ func findProlog(diskPrologByte3 uint8, data []byte, position int) int { return -1 } -func nibDecodeTrack(data []byte, track byte, logicalOrder *[16]int) ([]byte, error) { +func nibDecodeTrack(data []byte, logicalOrder *[16]int) ([]byte, error) { b := make([]byte, bytesPerTrack) // Buffer slice with enough capacity i := int(0) @@ -247,15 +281,14 @@ func nibDecodeTrack(data []byte, track byte, logicalOrder *[16]int) ([]byte, err if i == -1 { break } + // We just want the sector from the address field, we ignore the rest, no error detection sector := oddEvenDecodeByte(data[(i+4)%l], data[(i+5)%l]) logicalSector := logicalOrder[sector] dst := int(logicalSector) * bytesPerSector - fmt.Printf("Processing sector %v (%v), destination: %v\n", sector, logicalSector, dst) - - i = (i + 8 + 3) % l // We skip the four two byte fields and the epilog // Find data prolog + i = (i + 8 + 3) % l // We skip the four two byte fields and the epilog i = findProlog(diskPrologByte3Data, data, i) // Read secondary buffer