From b10dec3e509cef9b21833340dab331194dfd73cf Mon Sep 17 00:00:00 2001 From: Terence Boldt Date: Wed, 25 Jan 2023 22:32:47 -0500 Subject: [PATCH] Add live drive regeneration and loading (#122) --- .github/ISSUE_TEMPLATE/bug_report.yml | 1 + RaspberryPi/apple2driver/drive/drive.go | 36 ++++++++++++++ RaspberryPi/apple2driver/driver.go | 46 +++-------------- RaspberryPi/apple2driver/go.mod | 2 +- RaspberryPi/apple2driver/go.sum | 4 +- RaspberryPi/apple2driver/handlers/exec.go | 60 ++++++++++++++++++++++- RaspberryPi/apple2driver/info/version.go | 2 +- 7 files changed, 106 insertions(+), 45 deletions(-) create mode 100644 RaspberryPi/apple2driver/drive/drive.go diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 11fae24..52b2841 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -49,6 +49,7 @@ body: label: Driver Version description: What version of the driver are you running? Check with `RPI a2version` options: + - 0028 (add live drive loading and regeneration) - 0027 (add dynamic drive support) - 0026 (add error handling) - 0025 (update RPi.command) diff --git a/RaspberryPi/apple2driver/drive/drive.go b/RaspberryPi/apple2driver/drive/drive.go new file mode 100644 index 0000000..5b79b08 --- /dev/null +++ b/RaspberryPi/apple2driver/drive/drive.go @@ -0,0 +1,36 @@ +// Copyright Terence J. Boldt (c)2023 +// Use of this source code is governed by an MIT +// license that can be found in the LICENSE file. + +// This file is used for handling ProDOS image generation + +package drive + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/tjboldt/ProDOS-Utilities/prodos" +) + +// GetDriveImageDirectory gets the default directory for driveimage +func GetDriveImageDirectory() (string, error) { + exec, err := os.Executable() + if err != nil { + fmt.Printf("ERROR: %s", err.Error()) + return "", err + } + driveImageDirectory := filepath.Join(filepath.Dir(filepath.Dir(exec)), "driveimage") + + return driveImageDirectory, nil +} + +// GenerateDriveFromDirectory regenerates a ProDOS drive from a host directory +func GenerateDriveFromDirectory(volumeName string, directory string) (prodos.ReaderWriterAt, error) { + drive := prodos.NewMemoryFile(0x2000000) + fmt.Printf("Generating Drive in memory from: %s\n", directory) + prodos.CreateVolume(drive, volumeName, 65535) + err := prodos.AddFilesFromHostDirectory(drive, directory, "/"+volumeName+"/", true) + return drive, err +} diff --git a/RaspberryPi/apple2driver/driver.go b/RaspberryPi/apple2driver/driver.go index 7d1825d..6130b3f 100644 --- a/RaspberryPi/apple2driver/driver.go +++ b/RaspberryPi/apple2driver/driver.go @@ -16,6 +16,7 @@ import ( "time" "github.com/tjboldt/Apple2-IO-RPi/RaspberryPi/apple2driver/a2io" + "github.com/tjboldt/Apple2-IO-RPi/RaspberryPi/apple2driver/drive" "github.com/tjboldt/Apple2-IO-RPi/RaspberryPi/apple2driver/handlers" "github.com/tjboldt/Apple2-IO-RPi/RaspberryPi/apple2driver/info" "github.com/tjboldt/ProDOS-Utilities/prodos" @@ -48,8 +49,6 @@ func main() { // In case Apple II is waiting, send 0 byte to start comm.WriteByte(0) - cwd, _ := os.Getwd() - for { command, err := comm.ReadByte() if err == nil { @@ -58,25 +57,13 @@ func main() { case resetCommand: handlers.ResetCommand() case readBlockCommand: - var block int - block, err = handlers.ReadBlockCommand(drive1, drive2) - if err == nil && block == 0 && len(drive1Name) == 0 { - resetCwd() - drive1, _ = generateDriveFromCwd() - } + handlers.ReadBlockCommand(drive1, drive2) case writeBlockCommand: handlers.WriteBlockCommand(drive1, drive2) case getTimeCommand: handlers.GetTimeCommand() case execCommand: - handlers.ExecCommand() - newCwd, _ := os.Getwd() - if newCwd != cwd { - cwd = newCwd - if len(drive1Name) == 0 { - drive1, _ = generateDriveFromCwd() - } - } + handlers.ExecCommand(&drive1, &drive2) case loadFileCommand: handlers.LoadFileCommand() case menuCommand: @@ -117,7 +104,9 @@ func getDriveFiles(drive1Name string, drive2Name string) (prodos.ReaderWriterAt, drive1, err = os.OpenFile(drive1Name, os.O_RDWR, 0755) logAndExitOnErr(err) } else { - drive1, err = generateDriveFromCwd() + directory, err := drive.GetDriveImageDirectory() + logAndExitOnErr(err) + drive1, err = drive.GenerateDriveFromDirectory("APPLE2.IO.RPI", directory) logAndExitOnErr(err) } @@ -136,26 +125,3 @@ func logAndExitOnErr(err error) { os.Exit(1) } } - -func resetCwd() { - exec, err := os.Executable() - if err != nil { - fmt.Printf("ERROR: %s", err.Error()) - os.Exit(1) - } - cwd := filepath.Dir(exec) - err = os.Chdir(filepath.Join(cwd, "../driveimage")) - logAndExitOnErr(err) -} - -func generateDriveFromCwd() (prodos.ReaderWriterAt, error) { - cwd, err := os.Getwd() - if err != nil { - return nil, err - } - drive := prodos.NewMemoryFile(0x2000000) - fmt.Printf("Generating Drive in memory from: %s\n", cwd) - prodos.CreateVolume(drive, "APPLE2.IO.RPI", 65535) - err = prodos.AddFilesFromHostDirectory(drive, cwd) - return drive, err -} diff --git a/RaspberryPi/apple2driver/go.mod b/RaspberryPi/apple2driver/go.mod index 323a7d1..8eddddb 100644 --- a/RaspberryPi/apple2driver/go.mod +++ b/RaspberryPi/apple2driver/go.mod @@ -5,5 +5,5 @@ go 1.16 require ( github.com/creack/pty v1.1.18 github.com/stianeikeland/go-rpio/v4 v4.6.0 - github.com/tjboldt/ProDOS-Utilities v0.4.3 + github.com/tjboldt/ProDOS-Utilities v0.4.5 ) diff --git a/RaspberryPi/apple2driver/go.sum b/RaspberryPi/apple2driver/go.sum index ed2875c..9829209 100644 --- a/RaspberryPi/apple2driver/go.sum +++ b/RaspberryPi/apple2driver/go.sum @@ -2,8 +2,8 @@ github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/stianeikeland/go-rpio/v4 v4.6.0 h1:eAJgtw3jTtvn/CqwbC82ntcS+dtzUTgo5qlZKe677EY= github.com/stianeikeland/go-rpio/v4 v4.6.0/go.mod h1:A3GvHxC1Om5zaId+HqB3HKqx4K/AqeckxB7qRjxMK7o= -github.com/tjboldt/ProDOS-Utilities v0.4.3 h1:uNC3LYO0hlForL1pxbXny1Fbk39jj6MPWjDollcApFo= -github.com/tjboldt/ProDOS-Utilities v0.4.3/go.mod h1:4D1GnCj155VTpP07052zoMN8Xa4wVpVWa+ZTBFQdtI4= +github.com/tjboldt/ProDOS-Utilities v0.4.5 h1:XT2WCUnqcaLQwBca6fn7kxcQANWxYn5YpM618ERbCUY= +github.com/tjboldt/ProDOS-Utilities v0.4.5/go.mod h1:4D1GnCj155VTpP07052zoMN8Xa4wVpVWa+ZTBFQdtI4= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= diff --git a/RaspberryPi/apple2driver/handlers/exec.go b/RaspberryPi/apple2driver/handlers/exec.go index 1ac38b5..046f62d 100644 --- a/RaspberryPi/apple2driver/handlers/exec.go +++ b/RaspberryPi/apple2driver/handlers/exec.go @@ -16,14 +16,16 @@ import ( "strings" "time" + "github.com/tjboldt/Apple2-IO-RPi/RaspberryPi/apple2driver/drive" "github.com/tjboldt/Apple2-IO-RPi/RaspberryPi/apple2driver/info" + "github.com/tjboldt/ProDOS-Utilities/prodos" ) var forceLowercase = false var execTimeoutSeconds = int(10) // ExecCommand handles requests for the Apple II executing Linux commands -func ExecCommand() { +func ExecCommand(drive1 *prodos.ReaderWriterAt, drive2 *prodos.ReaderWriterAt) { workingDirectory, err := os.Getwd() if err != nil { workingDirectory = "/home" @@ -74,6 +76,10 @@ func ExecCommand() { a2timeout(linuxCommand) return } + if strings.HasPrefix(linuxCommand, "a2drive") { + a2drive(linuxCommand, drive1, drive2) + return + } if linuxCommand == "a2prompt" { prompt := fmt.Sprintf("A2IO:%s ", workingDirectory) comm.WriteString(prompt) @@ -160,6 +166,7 @@ func a2help() { "a2timeout - seconds to timeout commands\r" + "A2LOWER - force lowercase for II+\r" + "a2lower - disable force lowercase\r" + + "a2drive - change drive images\r" + "\r") } @@ -182,6 +189,57 @@ func a2timeout(linuxCommand string) { } } +func a2drive(linuxCommand string, drive1 *prodos.ReaderWriterAt, drive2 *prodos.ReaderWriterAt) { + params := strings.Fields(linuxCommand) + + if len(params) < 3 { + showa2DriveUsage() + return + } + + driveNumber, err := strconv.ParseInt(params[1], 10, 32) + if err != nil { + comm.WriteString("\rFailed to parse drive number\r") + showa2DriveUsage() + return + } + + if params[2] == "regen" { + directory, err := drive.GetDriveImageDirectory() + if err != nil { + comm.WriteString("\rFailed to parse source directory\r") + return + } + if len(params) > 3 { + directory = params[3] + } + switch driveNumber { + case 1: + *drive1, err = drive.GenerateDriveFromDirectory("APPLE2.IO.RPI", directory) + if err != nil { + comm.WriteString("\rFailed to regenerate drive 1\r") + return + } + comm.WriteString("\rDrive 1 regenerated\r") + case 2: + *drive2, err = drive.GenerateDriveFromDirectory("APPLE2.IO.RPI2", directory) + if err != nil { + comm.WriteString("\rFailed to regenerate drive 2\r") + return + } + comm.WriteString("\rDrive 2 regenerated\r") + default: + comm.WriteString("\rOnly drives 1 or 2 are supported\r") + showa2DriveUsage() + return + } + } +} + +func showa2DriveUsage() { + comm.WriteString("\rUsage: a2drive DRIVENUMBER [regen [PATH] | load FILENAME]\rExamples: a2drive 1 regen ~/Apple2-IO-RPi/RaspberryPi/driveimage\r a2drive 2 load /home/pi/Games.hdv\r") +} + func a2lower(enable bool) { forceLowercase = enable comm.WriteString(fmt.Sprintf("All commands will be converted to lowercase: %t\r", forceLowercase)) diff --git a/RaspberryPi/apple2driver/info/version.go b/RaspberryPi/apple2driver/info/version.go index 99fbbe1..00be4f2 100644 --- a/RaspberryPi/apple2driver/info/version.go +++ b/RaspberryPi/apple2driver/info/version.go @@ -8,4 +8,4 @@ package info // Version is the hexadecimal version number that // should be incremented with each driver update -const Version = "0027" +const Version = "0028"