diff --git a/README.md b/README.md index acbd2fe..f5ecfd3 100644 --- a/README.md +++ b/README.md @@ -151,4 +151,4 @@ ProDOS-Utilities -d ../Apple2-IO-RPi/RaspberryPi/Apple2-IO-RPi.hdv -c get -o Upd 1060 NEXT PG 1900 PRINT 2000 PRINT "Firmware Update Complete" -``` \ No newline at end of file +``` diff --git a/prodos/basic.go b/prodos/basic.go index fa5f1d9..803256c 100644 --- a/prodos/basic.go +++ b/prodos/basic.go @@ -121,6 +121,7 @@ var tokens = map[byte]string{ 0xEA: "MID$", } +// ConvertBasicToText converts AppleSoft BASIC to text func ConvertBasicToText(basic []byte) string { var builder strings.Builder diff --git a/prodos/bitmap.go b/prodos/bitmap.go index fd46d08..54ad262 100644 --- a/prodos/bitmap.go +++ b/prodos/bitmap.go @@ -11,6 +11,7 @@ import ( "io" ) +// ReadVolumeBitmap reads the volume bitmap from a ProDOS image func ReadVolumeBitmap(reader io.ReaderAt) []byte { headerBlock := ReadBlock(reader, 2) @@ -40,6 +41,18 @@ func ReadVolumeBitmap(reader io.ReaderAt) []byte { return bitmap } +// GetFreeBlockCount gets the number of free blocks on a ProDOS image +func GetFreeBlockCount(volumeBitmap []byte, totalBlocks int) int { + freeBlockCount := 0 + + for i := 0; i < totalBlocks; i++ { + if checkFreeBlockInVolumeBitmap(volumeBitmap, i) { + freeBlockCount++ + } + } + return freeBlockCount +} + func writeVolumeBitmap(writer io.WriterAt, reader io.ReaderAt, bitmap []byte) { headerBlock := ReadBlock(reader, 2) @@ -108,17 +121,6 @@ func findFreeBlocks(volumeBitmap []byte, numberOfBlocks int) []int { return nil } -func GetFreeBlockCount(volumeBitmap []byte, totalBlocks int) int { - freeBlockCount := 0 - - for i := 0; i < totalBlocks; i++ { - if checkFreeBlockInVolumeBitmap(volumeBitmap, i) { - freeBlockCount++ - } - } - return freeBlockCount -} - func markBlockInVolumeBitmap(volumeBitmap []byte, blockNumber int) { bitToChange := blockNumber % 8 byteToChange := blockNumber / 8 diff --git a/prodos/block.go b/prodos/block.go index 10fe2ea..7707bd0 100644 --- a/prodos/block.go +++ b/prodos/block.go @@ -11,6 +11,7 @@ import ( "io" ) +// ReadBlock reads a block from a ProDOS volume into a byte array func ReadBlock(reader io.ReaderAt, block int) []byte { buffer := make([]byte, 512) @@ -19,6 +20,7 @@ func ReadBlock(reader io.ReaderAt, block int) []byte { return buffer } +// WriteBlock writes a block to a ProDOS volume from a byte array func WriteBlock(writer io.WriterAt, block int, buffer []byte) { writer.WriteAt(buffer, int64(block)*512) } diff --git a/prodos/directory.go b/prodos/directory.go index 7b250bd..422a62b 100644 --- a/prodos/directory.go +++ b/prodos/directory.go @@ -15,6 +15,7 @@ import ( "time" ) +// VolumeHeader from ProDOS type VolumeHeader struct { VolumeName string CreationTime time.Time @@ -28,6 +29,7 @@ type VolumeHeader struct { Version int } +// DirectoryHeader from ProDOS type DirectoryHeader struct { Name string ActiveFileCount int @@ -45,6 +47,7 @@ const ( StorageDirectory = 13 ) +// FileEntry from ProDOS type FileEntry struct { StorageType int FileName string @@ -63,6 +66,8 @@ type FileEntry struct { DirectoryOffset int } +// ReadDirectory reads the directory information from a specified path +// on a ProDOS image func ReadDirectory(reader io.ReaderAt, path string) (VolumeHeader, DirectoryHeader, []FileEntry) { buffer := ReadBlock(reader, 2) diff --git a/prodos/file.go b/prodos/file.go index 940e001..c9ef880 100644 --- a/prodos/file.go +++ b/prodos/file.go @@ -38,6 +38,7 @@ func LoadFile(reader io.ReaderAt, path string) ([]byte, error) { return buffer, nil } +// WriteFile writes a file to a ProDOS volume from a byte array func WriteFile(writer io.WriterAt, reader io.ReaderAt, path string, fileType int, auxType int, buffer []byte) error { directory, fileName := GetDirectoryAndFileNameFromPath(path) @@ -61,7 +62,7 @@ func WriteFile(writer io.WriterAt, reader io.ReaderAt, path string, fileType int // TODO: add tree file if len(buffer) > 0x20000 { - return errors.New("Files > 128KB not supported yet.") + return errors.New("files > 128KB not supported yet") } updateVolumeBitmap(writer, reader, blockList) @@ -99,6 +100,7 @@ func WriteFile(writer io.WriterAt, reader io.ReaderAt, path string, fileType int return nil } +// DeleteFile deletes a file from a ProDOS volume func DeleteFile(writer io.WriterAt, reader io.ReaderAt, path string) error { fileEntry, err := getFileEntry(reader, path) if err != nil { @@ -137,6 +139,7 @@ func DeleteFile(writer io.WriterAt, reader io.ReaderAt, path string) error { return nil } +// GetDirectoryAndFileNameFromPath gets the directory and filename from a path func GetDirectoryAndFileNameFromPath(path string) (string, string) { path = strings.ToUpper(path) paths := strings.Split(path, "/") diff --git a/prodos/text.go b/prodos/text.go index 1adbef1..bc214eb 100644 --- a/prodos/text.go +++ b/prodos/text.go @@ -13,6 +13,7 @@ import ( "time" ) +// TimeToString displays the date and time in ProDOS format func TimeToString(printTime time.Time) string { return fmt.Sprintf("%04d-%s-%02d %02d:%02d", printTime.Year(), @@ -23,6 +24,7 @@ func TimeToString(printTime time.Time) string { ) } +// FileTypeToString display the file type as a string func FileTypeToString(fileType int) string { switch fileType { case 1: @@ -85,6 +87,7 @@ func FileTypeToString(fileType int) string { */ } +// DumpFileEntry dumps the file entry values as text func DumpFileEntry(fileEntry FileEntry) { fmt.Printf("FileName: %s\n", fileEntry.FileName) fmt.Printf("Creation time: %d-%s-%d %02d:%02d\n", fileEntry.CreationTime.Year(), fileEntry.CreationTime.Month(), fileEntry.CreationTime.Day(), fileEntry.CreationTime.Hour(), fileEntry.CreationTime.Minute()) @@ -99,6 +102,7 @@ func DumpFileEntry(fileEntry FileEntry) { fmt.Printf("\n") } +// DumpVolumeHeader dumps the volume header values as text func DumpVolumeHeader(volumeHeader VolumeHeader) { fmt.Printf("Next block: %d\n", volumeHeader.NextBlock) fmt.Printf("Volume name: %s\n", volumeHeader.VolumeName) @@ -112,6 +116,7 @@ func DumpVolumeHeader(volumeHeader VolumeHeader) { fmt.Printf("Total blocks: %d\n", volumeHeader.TotalBlocks) } +// DumpDirectoryHeader dumps the directory header as text func DumpDirectoryHeader(directoryHeader DirectoryHeader) { fmt.Printf("Name: %s\n", directoryHeader.Name) fmt.Printf("File count: %d\n", directoryHeader.ActiveFileCount) @@ -120,6 +125,7 @@ func DumpDirectoryHeader(directoryHeader DirectoryHeader) { fmt.Printf("Next block: %04X\n", directoryHeader.NextBlock) } +// DumpBlock dumps the block as hexadecimal and text func DumpBlock(buffer []byte) { for i := 0; i < len(buffer); i += 16 { fmt.Printf("%04X: ", i) @@ -138,6 +144,7 @@ func DumpBlock(buffer []byte) { } } +// DumpDirectory displays the directory similar to ProDOS catalog func DumpDirectory(blocksFree int, totalBlocks int, path string, fileEntries []FileEntry) { fmt.Printf("%s\n\n", path) fmt.Printf("NAME TYPE BLOCKS MODIFIED CREATED ENDFILE SUBTYPE\n\n") diff --git a/prodos/time.go b/prodos/time.go index cc34af8..83c6c04 100644 --- a/prodos/time.go +++ b/prodos/time.go @@ -10,21 +10,20 @@ import ( "time" ) -/* 49041 ($BF91) 49040 ($BF90) - - 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 - +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ -DATE: | year | month | day | - +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ - - 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 - +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ -TIME: | hour | | minute | - +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ - - 49043 ($BF93) 49042 ($BF92) -*/ - +// DateTimeToProDOS converts Time to ProDOS date time +// 49041 ($BF91) 49040 ($BF90) +// +// 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +// +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +// DATE: | year | month | day | +// +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +// +// 49043 ($BF93) 49042 ($BF92) +// +// 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0 +// +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ +// TIME: | hour | | minute | +// +-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+ func DateTimeToProDOS(dateTime time.Time) []byte { year := dateTime.Year() % 100 month := dateTime.Month() @@ -41,6 +40,7 @@ func DateTimeToProDOS(dateTime time.Time) []byte { return buffer } +// DateTimeToProDOS converts Time from ProDOS date time func DateTimeFromProDOS(buffer []byte) time.Time { if buffer[0] == 0 && buffer[1] == 0 &&