2023-01-04 04:24:48 +00:00
|
|
|
// Copyright Terence J. Boldt (c)2021-2023
|
2022-01-23 21:58:34 +00:00
|
|
|
// Use of this source code is governed by an MIT
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
// This file provides access to volum bitmap on
|
|
|
|
// a ProDOS drive image
|
|
|
|
|
2021-06-07 00:15:41 +00:00
|
|
|
package prodos
|
|
|
|
|
2021-06-09 12:23:18 +00:00
|
|
|
import (
|
2022-01-23 21:58:34 +00:00
|
|
|
"io"
|
2021-06-09 12:23:18 +00:00
|
|
|
)
|
2021-06-07 00:15:41 +00:00
|
|
|
|
2022-01-23 22:30:18 +00:00
|
|
|
// ReadVolumeBitmap reads the volume bitmap from a ProDOS image
|
2022-03-04 23:08:33 +00:00
|
|
|
func ReadVolumeBitmap(reader io.ReaderAt) ([]byte, error) {
|
|
|
|
headerBlock, err := ReadBlock(reader, 2)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-06-07 00:15:41 +00:00
|
|
|
|
|
|
|
volumeHeader := parseVolumeHeader(headerBlock)
|
|
|
|
|
2021-07-02 02:35:26 +00:00
|
|
|
totalBitmapBytes := volumeHeader.TotalBlocks / 8
|
|
|
|
if volumeHeader.TotalBlocks%8 > 0 {
|
|
|
|
totalBitmapBytes++
|
|
|
|
}
|
|
|
|
|
|
|
|
bitmap := make([]byte, totalBitmapBytes)
|
2021-06-07 00:15:41 +00:00
|
|
|
|
2021-07-02 02:35:26 +00:00
|
|
|
totalBitmapBlocks := totalBitmapBytes / 512
|
2021-06-07 00:15:41 +00:00
|
|
|
|
2021-07-02 02:35:26 +00:00
|
|
|
if totalBitmapBytes%512 > 0 {
|
|
|
|
totalBitmapBlocks++
|
|
|
|
}
|
|
|
|
|
|
|
|
for i := 0; i < totalBitmapBlocks; i++ {
|
2022-03-04 23:08:33 +00:00
|
|
|
bitmapBlock, err := ReadBlock(reader, i+volumeHeader.BitmapStartBlock)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2021-06-07 00:15:41 +00:00
|
|
|
|
2021-07-02 02:35:26 +00:00
|
|
|
for j := 0; j < 512 && i*512+j < totalBitmapBytes; j++ {
|
2021-06-07 00:15:41 +00:00
|
|
|
bitmap[i*512+j] = bitmapBlock[j]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-04 23:08:33 +00:00
|
|
|
return bitmap, nil
|
2021-06-07 00:15:41 +00:00
|
|
|
}
|
|
|
|
|
2022-01-23 22:30:18 +00:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2022-03-04 23:08:33 +00:00
|
|
|
func writeVolumeBitmap(readerWriter ReaderWriterAt, bitmap []byte) error {
|
|
|
|
headerBlock, err := ReadBlock(readerWriter, 2)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2022-03-06 10:29:33 +00:00
|
|
|
|
2021-06-07 00:15:41 +00:00
|
|
|
volumeHeader := parseVolumeHeader(headerBlock)
|
2022-03-06 10:29:33 +00:00
|
|
|
totalBitmapBytes := volumeHeader.TotalBlocks / 8
|
|
|
|
if volumeHeader.TotalBlocks%8 > 0 {
|
|
|
|
totalBitmapBytes++
|
|
|
|
}
|
2021-06-07 00:15:41 +00:00
|
|
|
|
2022-03-06 10:29:33 +00:00
|
|
|
totalBitmapBlocks := totalBitmapBytes / 512
|
|
|
|
|
|
|
|
if totalBitmapBytes%512 > 0 {
|
|
|
|
totalBitmapBlocks++
|
2021-06-07 00:15:41 +00:00
|
|
|
}
|
2022-03-06 10:29:33 +00:00
|
|
|
|
|
|
|
for i := 0; i < totalBitmapBlocks; i++ {
|
|
|
|
bitmapBlock, err := ReadBlock(readerWriter, i+volumeHeader.BitmapStartBlock)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
for j := 0; j < 512 && i*512+j < totalBitmapBytes; j++ {
|
|
|
|
bitmapBlock[j] = bitmap[i*512+j]
|
|
|
|
}
|
|
|
|
|
|
|
|
err = WriteBlock(readerWriter, volumeHeader.BitmapStartBlock+i, bitmapBlock)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-04 23:08:33 +00:00
|
|
|
return nil
|
2021-06-07 00:15:41 +00:00
|
|
|
}
|
|
|
|
|
2021-06-29 02:26:20 +00:00
|
|
|
func createVolumeBitmap(numberOfBlocks int) []byte {
|
2021-06-09 12:23:18 +00:00
|
|
|
volumeBitmapBlocks := numberOfBlocks / 512 / 8
|
|
|
|
if volumeBitmapBlocks*8*512 < numberOfBlocks {
|
|
|
|
volumeBitmapBlocks++
|
|
|
|
}
|
|
|
|
|
|
|
|
// set all 1's to show blocks available...
|
|
|
|
volumeBitmap := make([]byte, volumeBitmapBlocks*512)
|
|
|
|
for i := 0; i < len(volumeBitmap); i++ {
|
|
|
|
volumeBitmap[i] = 0xFF
|
|
|
|
}
|
|
|
|
|
|
|
|
// boot blocks
|
2021-06-29 02:26:20 +00:00
|
|
|
markBlockInVolumeBitmap(volumeBitmap, 0)
|
|
|
|
markBlockInVolumeBitmap(volumeBitmap, 1)
|
2021-06-09 12:23:18 +00:00
|
|
|
|
|
|
|
// root directory
|
2021-06-29 02:26:20 +00:00
|
|
|
markBlockInVolumeBitmap(volumeBitmap, 2)
|
|
|
|
markBlockInVolumeBitmap(volumeBitmap, 3)
|
|
|
|
markBlockInVolumeBitmap(volumeBitmap, 4)
|
|
|
|
markBlockInVolumeBitmap(volumeBitmap, 5)
|
2021-06-09 12:23:18 +00:00
|
|
|
|
|
|
|
// volume bitmap blocks
|
|
|
|
for i := 0; i < volumeBitmapBlocks; i++ {
|
2021-06-29 02:26:20 +00:00
|
|
|
markBlockInVolumeBitmap(volumeBitmap, 6+i)
|
2021-06-09 12:23:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// blocks beyond the volume
|
|
|
|
totalBlocksInBitmap := volumeBitmapBlocks * 512 * 8
|
|
|
|
blocksBeyondEnd := totalBlocksInBitmap - numberOfBlocks
|
|
|
|
if blocksBeyondEnd > 0 {
|
|
|
|
for i := totalBlocksInBitmap - blocksBeyondEnd; i < totalBlocksInBitmap; i++ {
|
2021-06-29 02:26:20 +00:00
|
|
|
markBlockInVolumeBitmap(volumeBitmap, i)
|
2021-06-09 12:23:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
//DumpBlock(volumeBitmap)
|
|
|
|
|
|
|
|
return volumeBitmap
|
|
|
|
}
|
|
|
|
|
2021-06-29 02:26:20 +00:00
|
|
|
func findFreeBlocks(volumeBitmap []byte, numberOfBlocks int) []int {
|
2021-06-26 01:15:20 +00:00
|
|
|
blocks := make([]int, numberOfBlocks)
|
|
|
|
|
|
|
|
blocksFound := 0
|
|
|
|
|
|
|
|
for i := 0; i < len(volumeBitmap)*8; i++ {
|
2021-06-29 02:26:20 +00:00
|
|
|
if checkFreeBlockInVolumeBitmap(volumeBitmap, i) {
|
2021-06-26 01:15:20 +00:00
|
|
|
blocks[blocksFound] = i
|
|
|
|
blocksFound++
|
|
|
|
if blocksFound == numberOfBlocks {
|
|
|
|
return blocks
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-07 00:15:41 +00:00
|
|
|
return nil
|
|
|
|
}
|
2021-06-09 12:23:18 +00:00
|
|
|
|
2021-06-29 02:26:20 +00:00
|
|
|
func markBlockInVolumeBitmap(volumeBitmap []byte, blockNumber int) {
|
2021-06-09 12:23:18 +00:00
|
|
|
bitToChange := blockNumber % 8
|
|
|
|
byteToChange := blockNumber / 8
|
|
|
|
|
2021-06-12 02:43:35 +00:00
|
|
|
byteToAnd := 0b11111111
|
2021-06-09 12:23:18 +00:00
|
|
|
|
|
|
|
switch bitToChange {
|
|
|
|
case 0:
|
2021-06-12 02:43:35 +00:00
|
|
|
byteToAnd = 0b01111111
|
2021-06-09 12:23:18 +00:00
|
|
|
case 1:
|
2021-06-12 02:43:35 +00:00
|
|
|
byteToAnd = 0b10111111
|
2021-06-09 12:23:18 +00:00
|
|
|
case 2:
|
2021-06-12 02:43:35 +00:00
|
|
|
byteToAnd = 0b11011111
|
2021-06-09 12:23:18 +00:00
|
|
|
case 3:
|
2021-06-12 02:43:35 +00:00
|
|
|
byteToAnd = 0b11101111
|
2021-06-09 12:23:18 +00:00
|
|
|
case 4:
|
2021-06-12 02:43:35 +00:00
|
|
|
byteToAnd = 0b11110111
|
2021-06-09 12:23:18 +00:00
|
|
|
case 5:
|
2021-06-12 02:43:35 +00:00
|
|
|
byteToAnd = 0b11111011
|
2021-06-09 12:23:18 +00:00
|
|
|
case 6:
|
2021-06-12 02:43:35 +00:00
|
|
|
byteToAnd = 0b11111101
|
2021-06-09 12:23:18 +00:00
|
|
|
case 7:
|
2021-06-12 02:43:35 +00:00
|
|
|
byteToAnd = 0b11111110
|
2021-06-09 12:23:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//fmt.Printf("blockNumber: $%04X byteToWrite: 0b%08b volumeBitmap: $%02X byteToChange: $%04X\n", blockNumber, byteToWrite, volumeBitmap[byteToChange], byteToChange)
|
2021-06-12 02:43:35 +00:00
|
|
|
volumeBitmap[byteToChange] &= byte(byteToAnd)
|
|
|
|
}
|
|
|
|
|
2021-06-29 02:26:20 +00:00
|
|
|
func freeBlockInVolumeBitmap(volumeBitmap []byte, blockNumber int) {
|
2021-06-12 02:43:35 +00:00
|
|
|
bitToChange := blockNumber % 8
|
|
|
|
byteToChange := blockNumber / 8
|
|
|
|
|
|
|
|
byteToOr := 0b00000000
|
|
|
|
|
|
|
|
switch bitToChange {
|
|
|
|
case 0:
|
|
|
|
byteToOr = 0b10000000
|
|
|
|
case 1:
|
|
|
|
byteToOr = 0b01000000
|
|
|
|
case 2:
|
|
|
|
byteToOr = 0b00100000
|
|
|
|
case 3:
|
|
|
|
byteToOr = 0b00010000
|
|
|
|
case 4:
|
|
|
|
byteToOr = 0b00001000
|
|
|
|
case 5:
|
|
|
|
byteToOr = 0b00000100
|
|
|
|
case 6:
|
|
|
|
byteToOr = 0b00000010
|
|
|
|
case 7:
|
|
|
|
byteToOr = 0b00000001
|
|
|
|
}
|
|
|
|
|
|
|
|
volumeBitmap[byteToChange] |= byte(byteToOr)
|
2021-06-09 12:23:18 +00:00
|
|
|
}
|
2021-06-26 01:15:20 +00:00
|
|
|
|
2021-06-29 02:26:20 +00:00
|
|
|
func checkFreeBlockInVolumeBitmap(volumeBitmap []byte, blockNumber int) bool {
|
2021-06-26 01:15:20 +00:00
|
|
|
bitToCheck := blockNumber % 8
|
|
|
|
byteToCheck := blockNumber / 8
|
|
|
|
|
|
|
|
byteToAnd := 0b00000000
|
|
|
|
|
|
|
|
switch bitToCheck {
|
|
|
|
case 0:
|
|
|
|
byteToAnd = 0b10000000
|
|
|
|
case 1:
|
|
|
|
byteToAnd = 0b01000000
|
|
|
|
case 2:
|
|
|
|
byteToAnd = 0b00100000
|
|
|
|
case 3:
|
|
|
|
byteToAnd = 0b00010000
|
|
|
|
case 4:
|
|
|
|
byteToAnd = 0b00001000
|
|
|
|
case 5:
|
|
|
|
byteToAnd = 0b00000100
|
|
|
|
case 6:
|
|
|
|
byteToAnd = 0b00000010
|
|
|
|
case 7:
|
|
|
|
byteToAnd = 0b00000001
|
|
|
|
}
|
|
|
|
|
|
|
|
return (volumeBitmap[byteToCheck] & byte(byteToAnd)) > 0
|
|
|
|
}
|