ProDOS-Utilities/prodos/bitmap.go

241 lines
5.2 KiB
Go
Raw Normal View History

// Copyright Terence J. Boldt (c)2021-2023
// 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 (
"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
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)
totalBitmapBytes := volumeHeader.TotalBlocks / 8
if volumeHeader.TotalBlocks%8 > 0 {
totalBitmapBytes++
}
bitmap := make([]byte, totalBitmapBytes)
2021-06-07 00:15:41 +00:00
totalBitmapBlocks := totalBitmapBytes / 512
2021-06-07 00:15:41 +00:00
if totalBitmapBytes%512 > 0 {
totalBitmapBlocks++
}
for i := 0; i < totalBitmapBlocks; i++ {
bitmapBlock, err := ReadBlock(reader, i+volumeHeader.BitmapStartBlock)
if err != nil {
return nil, err
}
2021-06-07 00:15:41 +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]
}
}
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
}
func writeVolumeBitmap(readerWriter ReaderWriterAt, bitmap []byte) error {
headerBlock, err := ReadBlock(readerWriter, 2)
if err != nil {
return err
}
2021-06-07 00:15:41 +00:00
volumeHeader := parseVolumeHeader(headerBlock)
totalBitmapBytes := volumeHeader.TotalBlocks / 8
if volumeHeader.TotalBlocks%8 > 0 {
totalBitmapBytes++
}
2021-06-07 00:15:41 +00:00
totalBitmapBlocks := totalBitmapBytes / 512
if totalBitmapBytes%512 > 0 {
totalBitmapBlocks++
2021-06-07 00:15:41 +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
}
}
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
}