mirror of
https://github.com/tjboldt/ProDOS-Utilities.git
synced 2025-01-15 05:31:37 +00:00
Add putall command (#14)
This commit is contained in:
parent
eb438ee9fe
commit
4ed23e0e6f
34
README.md
34
README.md
@ -77,6 +77,11 @@ BLOCKS FREE: 15 BLOCKS USED: 65520 TOTAL BLOCKS: 65535
|
|||||||
ProDOS-Utilities -d new.hdv -c create -b 65535
|
ProDOS-Utilities -d new.hdv -c create -b 65535
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Add all files from a host directory
|
||||||
|
```
|
||||||
|
ProDOS-Utilities -d new.hdv -c putall -i .
|
||||||
|
```
|
||||||
|
|
||||||
### Hex dump a block with command readblock and block number (both decimal and hexadecimal input work)
|
### Hex dump a block with command readblock and block number (both decimal and hexadecimal input work)
|
||||||
```
|
```
|
||||||
ProDOS-Utilities -d new.hdv -c readblock -b 0
|
ProDOS-Utilities -d new.hdv -c readblock -b 0
|
||||||
@ -118,31 +123,6 @@ Block 0x0000 (0):
|
|||||||
|
|
||||||
### Export files (using .bas file extension coverts Applesoft to text file)
|
### Export files (using .bas file extension coverts Applesoft to text file)
|
||||||
```
|
```
|
||||||
ProDOS-Utilities -d ../Apple2-IO-RPi/RaspberryPi/Apple2-IO-RPi.hdv -c get -o Update.Firmware.bas -p /APPLE2.IO.RPI/UPDATE.FIRMWARE; cat Update.Firmware.bas
|
ProDOS-Utilities -d example.hdv -c get -o Startup.bas -p /EXAMPLE/STARTUP; cat Startup.bas
|
||||||
10 HOME
|
10 PRINT "HELLO WORLD"
|
||||||
100 PRINT CHR$ (4)"BLOAD AT28C64B.BIN,A$2000"
|
|
||||||
200 PRINT "Program the firmare in slot #"
|
|
||||||
300 INPUT SL
|
|
||||||
400 FW = 8192 + 256 * SL: REM Firmware source
|
|
||||||
500 PS = 49287 + SL * 16: REM Firmware page selection
|
|
||||||
600 EP = 49152 + SL * 256: REM EPROM location
|
|
||||||
900 HOME
|
|
||||||
1000 FOR PG = 0 TO 3
|
|
||||||
1004 PRINT : PRINT
|
|
||||||
1005 PRINT "Writing page "PG" to slot "SL"
|
|
||||||
1006 PRINT "_______________________________________"
|
|
||||||
1007 PRINT "_______________________________________";
|
|
||||||
1008 HTAB 1
|
|
||||||
1010 POKE PS,PG * 16 + 15: REM Set firmware page
|
|
||||||
1020 FOR X = 0 TO 255
|
|
||||||
1030 A = PEEK (FW + PG * 2048 + X)
|
|
||||||
1040 POKE EP + X,A
|
|
||||||
1041 B = PEEK (EP + X)
|
|
||||||
1042 IF (B < > A) THEN GOTO 1041
|
|
||||||
1045 HTAB (X / 256) * 39 + 1
|
|
||||||
1046 INVERSE : PRINT " ";: NORMAL
|
|
||||||
1050 NEXT X
|
|
||||||
1060 NEXT PG
|
|
||||||
1900 PRINT
|
|
||||||
2000 PRINT "Firmware Update Complete"
|
|
||||||
```
|
```
|
||||||
|
64
main.go
64
main.go
@ -9,7 +9,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
@ -18,7 +17,7 @@ import (
|
|||||||
"github.com/tjboldt/ProDOS-Utilities/prodos"
|
"github.com/tjboldt/ProDOS-Utilities/prodos"
|
||||||
)
|
)
|
||||||
|
|
||||||
const version = "0.3.2"
|
const version = "0.4.0"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var fileName string
|
var fileName string
|
||||||
@ -33,14 +32,14 @@ func main() {
|
|||||||
var auxType int
|
var auxType int
|
||||||
flag.StringVar(&fileName, "d", "", "A ProDOS format drive image")
|
flag.StringVar(&fileName, "d", "", "A ProDOS format drive image")
|
||||||
flag.StringVar(&pathName, "p", "", "Path name in ProDOS drive image (default is root of volume)")
|
flag.StringVar(&pathName, "p", "", "Path name in ProDOS drive image (default is root of volume)")
|
||||||
flag.StringVar(&command, "c", "ls", "Command to execute: ls, get, put, rm, mkdir, readblock, writeblock, create")
|
flag.StringVar(&command, "c", "ls", "Command to execute: ls, get, put, rm, mkdir, readblock, writeblock, create, putall")
|
||||||
flag.StringVar(&outFileName, "o", "", "Name of file to write")
|
flag.StringVar(&outFileName, "o", "", "Name of file to write")
|
||||||
flag.StringVar(&inFileName, "i", "", "Name of file to read")
|
flag.StringVar(&inFileName, "i", "", "Name of file to read")
|
||||||
flag.IntVar(&volumeSize, "s", 65535, "Number of blocks to create the volume with (default 65535, 64 to 65535, 0x0040 to 0xFFFF hex input accepted)")
|
flag.IntVar(&volumeSize, "s", 65535, "Number of blocks to create the volume with (default 65535, 64 to 65535, 0x0040 to 0xFFFF hex input accepted)")
|
||||||
flag.StringVar(&volumeName, "v", "NO.NAME", "Specifiy a name for the volume from 1 to 15 characters")
|
flag.StringVar(&volumeName, "v", "NO.NAME", "Specifiy a name for the volume from 1 to 15 characters")
|
||||||
flag.IntVar(&blockNumber, "b", 0, "A block number to read/write from 0 to 65535 (0x0000 to 0xFFFF hex input accepted)")
|
flag.IntVar(&blockNumber, "b", 0, "A block number to read/write from 0 to 65535 (0x0000 to 0xFFFF hex input accepted)")
|
||||||
flag.IntVar(&fileType, "t", 6, "ProDOS FileType: 0x04 for TXT, 0x06 for BIN, 0xFC for BAS, 0xFF for SYS etc.")
|
flag.IntVar(&fileType, "t", 0, "ProDOS FileType: 0x04 for TXT, 0x06 for BIN, 0xFC for BAS, 0xFF for SYS etc., omit to autodetect")
|
||||||
flag.IntVar(&auxType, "a", 0x2000, "ProDOS AuxType from 0 to 65535 (0x0000 to 0xFFFF hex input accepted)")
|
flag.IntVar(&auxType, "a", 0, "ProDOS AuxType from 0 to 65535 (0x0000 to 0xFFFF hex input accepted), omit to autodetect")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
if len(fileName) == 0 {
|
if len(fileName) == 0 {
|
||||||
@ -108,49 +107,10 @@ func main() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
if len(pathName) == 0 {
|
|
||||||
fmt.Println("Missing pathname")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
inFile, err := os.ReadFile(inFileName)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("Failed to open input file %s: %s", inFileName, err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
if strings.HasSuffix(strings.ToLower(inFileName), ".bas") {
|
|
||||||
inFile, err = prodos.ConvertTextToBasic(string(inFile))
|
|
||||||
fileType = 0xFC
|
|
||||||
auxType = 0x0801
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for an AppleSingle file as produced by cc65
|
err = prodos.WriteFileFromFile(file, pathName, fileType, auxType, inFileName)
|
||||||
if // Magic number
|
|
||||||
binary.BigEndian.Uint32(inFile[0x00:]) == 0x00051600 &&
|
|
||||||
// Version number
|
|
||||||
binary.BigEndian.Uint32(inFile[0x04:]) == 0x00020000 &&
|
|
||||||
// Number of entries
|
|
||||||
binary.BigEndian.Uint16(inFile[0x18:]) == 0x0002 &&
|
|
||||||
// Data Fork ID
|
|
||||||
binary.BigEndian.Uint32(inFile[0x1A:]) == 0x00000001 &&
|
|
||||||
// Offset
|
|
||||||
binary.BigEndian.Uint32(inFile[0x1E:]) == 0x0000003A &&
|
|
||||||
// Length
|
|
||||||
binary.BigEndian.Uint32(inFile[0x22:]) == uint32(len(inFile))-0x3A &&
|
|
||||||
// ProDOS File Info ID
|
|
||||||
binary.BigEndian.Uint32(inFile[0x26:]) == 0x0000000B &&
|
|
||||||
// Offset
|
|
||||||
binary.BigEndian.Uint32(inFile[0x2A:]) == 0x00000032 &&
|
|
||||||
// Length
|
|
||||||
binary.BigEndian.Uint32(inFile[0x2E:]) == 0x00000008 {
|
|
||||||
|
|
||||||
fileType = int(binary.BigEndian.Uint16(inFile[0x34:]))
|
|
||||||
auxType = int(binary.BigEndian.Uint32(inFile[0x36:]))
|
|
||||||
inFile = inFile[0x3A:]
|
|
||||||
fmt.Printf("AppleSingle (File type: %02X, AuxType: %04X) detected\n", fileType, auxType)
|
|
||||||
}
|
|
||||||
err = prodos.WriteFile(file, pathName, fileType, auxType, inFile)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Failed to write file %s: %s", pathName, err)
|
fmt.Printf("Failed to write file %s", err)
|
||||||
}
|
}
|
||||||
case "readblock":
|
case "readblock":
|
||||||
fmt.Printf("Reading block 0x%04X (%d):\n\n", blockNumber, blockNumber)
|
fmt.Printf("Reading block 0x%04X (%d):\n\n", blockNumber, blockNumber)
|
||||||
@ -188,6 +148,18 @@ func main() {
|
|||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
prodos.CreateVolume(file, volumeName, volumeSize)
|
prodos.CreateVolume(file, volumeName, volumeSize)
|
||||||
|
case "putall":
|
||||||
|
file, err := os.Create(fileName)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("failed to create file: %s\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
err = prodos.AddFilesFromHostDirectory(file, inFileName, volumeName, volumeSize)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("failed to add host files: %s\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
case "rm":
|
case "rm":
|
||||||
file, err := os.OpenFile(fileName, os.O_RDWR, 0755)
|
file, err := os.OpenFile(fileName, os.O_RDWR, 0755)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -8,7 +8,6 @@
|
|||||||
package prodos
|
package prodos
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -147,10 +146,6 @@ func findFreeBlocks(volumeBitmap []byte, numberOfBlocks int) []int {
|
|||||||
blocks[blocksFound] = i
|
blocks[blocksFound] = i
|
||||||
blocksFound++
|
blocksFound++
|
||||||
if blocksFound == numberOfBlocks {
|
if blocksFound == numberOfBlocks {
|
||||||
for i := 0; i < len(blocks); i++ {
|
|
||||||
fmt.Printf("%04X ", blocks[i])
|
|
||||||
}
|
|
||||||
fmt.Printf("\n")
|
|
||||||
return blocks
|
return blocks
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
101
prodos/file.go
101
prodos/file.go
@ -8,9 +8,12 @@
|
|||||||
package prodos
|
package prodos
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@ -42,6 +45,93 @@ func LoadFile(reader io.ReaderAt, path string) ([]byte, error) {
|
|||||||
return buffer, nil
|
return buffer, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriteFileFromFile writes a file to a ProDOS volume from a host file
|
||||||
|
func WriteFileFromFile(readerWriter ReaderWriterAt, pathName string, fileType int, auxType int, inFileName string) error {
|
||||||
|
inFile, err := os.ReadFile(inFileName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if auxType == 0 && fileType == 0 {
|
||||||
|
auxType, fileType, inFile, err = convertFileByType(inFileName, inFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(pathName) == 0 {
|
||||||
|
_, pathName = filepath.Split(inFileName)
|
||||||
|
pathName = strings.ToUpper(pathName)
|
||||||
|
ext := filepath.Ext(pathName)
|
||||||
|
if len(ext) > 0 {
|
||||||
|
switch ext {
|
||||||
|
case ".SYS", ".TXT", ".BAS", ".BIN":
|
||||||
|
pathName = strings.TrimSuffix(pathName, ext)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return WriteFile(readerWriter, pathName, fileType, auxType, inFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertFileByType(inFileName string, inFile []byte) (int, int, []byte, error) {
|
||||||
|
fileType := 0x06 // default to BIN
|
||||||
|
auxType := 0x2000 // default to $2000
|
||||||
|
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// Check for an AppleSingle file as produced by cc65
|
||||||
|
if // Magic number
|
||||||
|
binary.BigEndian.Uint32(inFile[0x00:]) == 0x00051600 &&
|
||||||
|
// Version number
|
||||||
|
binary.BigEndian.Uint32(inFile[0x04:]) == 0x00020000 &&
|
||||||
|
// Number of entries
|
||||||
|
binary.BigEndian.Uint16(inFile[0x18:]) == 0x0002 &&
|
||||||
|
// Data Fork ID
|
||||||
|
binary.BigEndian.Uint32(inFile[0x1A:]) == 0x00000001 &&
|
||||||
|
// Offset
|
||||||
|
binary.BigEndian.Uint32(inFile[0x1E:]) == 0x0000003A &&
|
||||||
|
// Length
|
||||||
|
binary.BigEndian.Uint32(inFile[0x22:]) == uint32(len(inFile))-0x3A &&
|
||||||
|
// ProDOS File Info ID
|
||||||
|
binary.BigEndian.Uint32(inFile[0x26:]) == 0x0000000B &&
|
||||||
|
// Offset
|
||||||
|
binary.BigEndian.Uint32(inFile[0x2A:]) == 0x00000032 &&
|
||||||
|
// Length
|
||||||
|
binary.BigEndian.Uint32(inFile[0x2E:]) == 0x00000008 {
|
||||||
|
|
||||||
|
fileType = int(binary.BigEndian.Uint16(inFile[0x34:]))
|
||||||
|
auxType = int(binary.BigEndian.Uint32(inFile[0x36:]))
|
||||||
|
inFile = inFile[0x3A:]
|
||||||
|
} else {
|
||||||
|
// use extension to determine file type
|
||||||
|
ext := strings.ToUpper(filepath.Ext(inFileName))
|
||||||
|
|
||||||
|
switch ext {
|
||||||
|
case ".BAS":
|
||||||
|
inFile, err = ConvertTextToBasic(string(inFile))
|
||||||
|
fileType = 0xFC
|
||||||
|
auxType = 0x0801
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return 0, 0, nil, err
|
||||||
|
}
|
||||||
|
case ".SYS":
|
||||||
|
fileType = 0xFF
|
||||||
|
auxType = 0x2000
|
||||||
|
case ".BIN":
|
||||||
|
fileType = 0x06
|
||||||
|
auxType = 0x2000
|
||||||
|
case ".TXT":
|
||||||
|
inFile = []byte(strings.ReplaceAll(strings.ReplaceAll(string(inFile), "\r\n", "r"), "\n", "\r"))
|
||||||
|
fileType = 0x04
|
||||||
|
auxType = 0x0000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return auxType, fileType, inFile, err
|
||||||
|
}
|
||||||
|
|
||||||
// WriteFile writes a file to a ProDOS volume from a byte array
|
// WriteFile writes a file to a ProDOS volume from a byte array
|
||||||
func WriteFile(readerWriter ReaderWriterAt, path string, fileType int, auxType int, buffer []byte) error {
|
func WriteFile(readerWriter ReaderWriterAt, path string, fileType int, auxType int, buffer []byte) error {
|
||||||
directory, fileName := GetDirectoryAndFileNameFromPath(path)
|
directory, fileName := GetDirectoryAndFileNameFromPath(path)
|
||||||
@ -57,11 +147,6 @@ func WriteFile(readerWriter ReaderWriterAt, path string, fileType int, auxType i
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < len(blockList); i++ {
|
|
||||||
fmt.Printf("%04X ", blockList[i])
|
|
||||||
}
|
|
||||||
fmt.Printf("\n")
|
|
||||||
|
|
||||||
// seedling file
|
// seedling file
|
||||||
if len(buffer) <= 0x200 {
|
if len(buffer) <= 0x200 {
|
||||||
WriteBlock(readerWriter, blockList[0], buffer)
|
WriteBlock(readerWriter, blockList[0], buffer)
|
||||||
@ -183,10 +268,6 @@ func GetDirectoryAndFileNameFromPath(path string) (string, string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func updateVolumeBitmap(readerWriter ReaderWriterAt, blockList []int) error {
|
func updateVolumeBitmap(readerWriter ReaderWriterAt, blockList []int) error {
|
||||||
for i := 0; i < len(blockList); i++ {
|
|
||||||
fmt.Printf("%04X ", blockList[i])
|
|
||||||
}
|
|
||||||
fmt.Printf("\n")
|
|
||||||
|
|
||||||
volumeBitmap, err := ReadVolumeBitmap(readerWriter)
|
volumeBitmap, err := ReadVolumeBitmap(readerWriter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -319,7 +400,7 @@ func createBlockList(reader io.ReaderAt, fileSize int) ([]int, error) {
|
|||||||
numberOfBlocks++ // add index block
|
numberOfBlocks++ // add index block
|
||||||
}
|
}
|
||||||
|
|
||||||
if fileSize > 0x20000 && fileSize < 0x1000000 {
|
if fileSize > 0x20000 && fileSize <= 0x1000000 {
|
||||||
//fmt.Printf("Tree file\n")
|
//fmt.Printf("Tree file\n")
|
||||||
// add index blocks for each 256 blocks
|
// add index blocks for each 256 blocks
|
||||||
numberOfBlocks += numberOfBlocks / 256
|
numberOfBlocks += numberOfBlocks / 256
|
||||||
|
43
prodos/host.go
Normal file
43
prodos/host.go
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
// Copyright Terence J. Boldt (c)2022
|
||||||
|
// Use of this source code is governed by an MIT
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// This file provides access to generate a ProDOS drive image from a host directory
|
||||||
|
|
||||||
|
package prodos
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AddFilesFromHostDirectory fills the root volume with files
|
||||||
|
// from the specified host directory
|
||||||
|
func AddFilesFromHostDirectory(
|
||||||
|
readerWriter ReaderWriterAt,
|
||||||
|
directory string,
|
||||||
|
volumeName string,
|
||||||
|
numberOfBlocks int) error {
|
||||||
|
CreateVolume(readerWriter, volumeName, numberOfBlocks)
|
||||||
|
|
||||||
|
files, err := os.ReadDir(directory)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, file := range files {
|
||||||
|
info, err := file.Info()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !file.IsDir() && info.Size() > 0 && info.Size() <= 0x20000 {
|
||||||
|
err = WriteFileFromFile(readerWriter, "", 0, 0, filepath.Join(directory, file.Name()))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user