diff --git a/.gitignore b/.gitignore index 59f6f21..7ab1c86 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ *-bak *.o *.DS_Store -RaspberryPi/Apple2-IO-RPi +RaspberryPi/apple2driver/apple2driver diff --git a/README.md b/README.md index d2d44cc..d57d56a 100644 --- a/README.md +++ b/README.md @@ -33,11 +33,11 @@ So far, this is a project and not a finished product. The current prototype is o 10. Update firmware with utility (not written yet) or use EPROM programmer 11. sudo apt install git golang 12. git clone https://github.com/tjboldt/Apple2-IO-RPi.git -13. cd Apple2-IO-RPi +13. cd Apple2-IO-RPi/RaspberryPi/apple2driver 14. go get 15. go build -16. ./Apple2-IO-RPi Apple2-IO-RPi.hdv -18. Setup the Driver as a service or to autostart via cronjob (crontab -e then add the line @reboot /home/pi/Apple2-IO-RPi/RaspberryPi/Apple2-IO-RPi /home/pi/Apple2-IO-RPi/RaspberryPi/Apple2-IO-RPi.hdv > /home/pi/Apple2-IO-RPi/RaspberryPi/Apple2-IO-RPi.log) +16. ./apple2driver ../Apple2-IO-RPi.hdv +18. Setup the Driver as a service or to autostart via cronjob (crontab -e then add the line @reboot /home/pi/Apple2-IO-RPi/RaspberryPi/apple2driver/apple2driver /home/pi/Apple2-IO-RPi/RaspberryPi/Apple2-IO-RPi.hdv > /home/pi/Apple2-IO-RPi/RaspberryPi/Apple2-IO-RPi.log) ## Similar Project If you prefer having Apple II peripherals control a Raspberry Pi rather than simply using the Raspberry Pi to provide storage, network access and processing to the Apple II, have a look at David Schmenk's excellent [Apple2Pi](https://github.com/dschmenk/apple2pi) project. diff --git a/RaspberryPi/Driver.go b/RaspberryPi/Driver.go deleted file mode 100644 index 48fdf2b..0000000 --- a/RaspberryPi/Driver.go +++ /dev/null @@ -1,510 +0,0 @@ -package main - -import ( - "bytes" - "errors" - "fmt" - "os" - "os/exec" - "periph.io/x/periph/conn/gpio" - "periph.io/x/periph/conn/gpio/gpioreg" - "periph.io/x/periph/host" - "strings" - "time" -) - -var edgeTimeout time.Duration - -var out_write gpio.PinIO -var out_read gpio.PinIO -var out_reserved2 gpio.PinIO -var out_reserved1 gpio.PinIO -var out_bit7 gpio.PinIO -var out_bit6 gpio.PinIO -var out_bit5 gpio.PinIO -var out_bit4 gpio.PinIO -var out_bit3 gpio.PinIO -var out_bit2 gpio.PinIO -var out_bit1 gpio.PinIO -var out_bit0 gpio.PinIO -var in_write gpio.PinIO -var in_read gpio.PinIO -var in_reserved2 gpio.PinIO -var in_reserved1 gpio.PinIO -var in_bit7 gpio.PinIO -var in_bit6 gpio.PinIO -var in_bit5 gpio.PinIO -var in_bit4 gpio.PinIO -var in_bit3 gpio.PinIO -var in_bit2 gpio.PinIO -var in_bit1 gpio.PinIO -var in_bit0 gpio.PinIO - -const ReadBlockCommand = 1 -const WriteBlockCommand = 2 -const GetTimeCommand = 3 -const ChangeDriveCommand = 4 -const ExecCommand = 5 -const LoadFileCommand = 6 -const SaveFileCommand = 7 - -var debug bool = false - -var workingDirectory string = "/home" - -func main() { - host.Init() - - initGpio() - - if len(os.Args) == 3 && os.Args[2] == "--debug" { - debug = true - } - - fmt.Printf("Starting Apple II RPi...\n") - - fileName := os.Args[1] - - file, err := os.OpenFile(fileName, os.O_RDWR, 0755) - if err != nil { - fmt.Printf("ERROR: %s", err.Error()) - os.Exit(1) - } - - for { - if debug { - fmt.Printf("Check for command\n") - } - - command, err := readByte() - if err != nil { - //fmt.Printf("Timed out waiting for command\n") - } else { - switch command { - case ReadBlockCommand: - handleReadBlockCommand(file) - case WriteBlockCommand: - handleWriteBlockCommand(file) - case GetTimeCommand: - handleGetTimeCommand() - case ExecCommand: - handleExecCommand() - case LoadFileCommand: - handleLoadFileCommand() - } - } - } -} - -func handleReadBlockCommand(file *os.File) { - blockLow, _ := readByte() - blockHigh, _ := readByte() - - buffer := make([]byte, 512) - var block int64 - block = int64(blockHigh)*256 + int64(blockLow) - - fmt.Printf("Read block %d\n", block) - - file.ReadAt(buffer, int64(block)*512) - //dumpBlock(buffer) - err := readBlock(buffer) - if err == nil { - fmt.Printf("Read block completed\n") - } else { - fmt.Printf("Failed to read block\n") - } -} - -func handleWriteBlockCommand(file *os.File) { - blockLow, _ := readByte() - blockHigh, _ := readByte() - - buffer := make([]byte, 512) - var block int64 - block = int64(blockHigh)*256 + int64(blockLow) - - fmt.Printf("Write block %d\n", block) - - writeBlock(buffer) - file.WriteAt(buffer, int64(block)*512) - file.Sync() - fmt.Printf("Write block completed\n") -} - -func handleExecCommand() { - fmt.Printf("Reading command to execute...\n") - linuxCommand, err := readString() - fmt.Printf("Command to run: %s\n", linuxCommand) - if strings.HasPrefix(linuxCommand, "cd /") { - workingDirectory = strings.Replace(linuxCommand, "cd ", "", 1) - writeString("Working directory set") - return - } - if strings.HasPrefix(linuxCommand, "cd ") { - workingDirectory = workingDirectory + "/" + strings.Replace(linuxCommand, "cd ", "", 1) - writeString("Working directory set") - return - } - if linuxCommand == "a2help" { - writeString("\r" + - "This is a pseudo shell. Each command is executed as a process. The cd command\r" + - "is intercepted and sets the working directory for the next command. Running\r" + - "commands that do not exit will hang. For example, do not use ping without a\r" + - "way to limit output like -c 1.\r\r") - return - } - cmd := exec.Command("bash", "-c", linuxCommand) - cmd.Dir = workingDirectory - cmdOut, err := cmd.Output() - if err != nil { - fmt.Printf("Failed to execute command\n") - writeString("Failed to execute command") - return - } - fmt.Printf("Command output: %s\n", cmdOut) - apple2string := strings.Replace(string(cmdOut), "\n", "\r", -1) - err = writeString(apple2string) - if err != nil { - fmt.Printf("Failed to send command output\n") - return - } -} - -func handleGetTimeCommand() { - fmt.Printf("Sending date/time...\n") - /* 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) - */ - now := time.Now() - - year := now.Year() % 100 - month := now.Month() - day := now.Day() - hour := now.Hour() - minute := now.Minute() - - bf91 := (byte(year) << 1) + (byte(month) >> 3) - bf90 := ((byte(month) & 15) << 5) + byte(day) - bf93 := byte(hour) - bf92 := byte(minute) - - writeByte(bf90) - writeByte(bf91) - writeByte(bf92) - writeByte(bf93) - fmt.Printf("Send time complete\n") -} - -func handleLoadFileCommand() { - fileName, _ := readString() - - file, err := os.OpenFile(fileName, os.O_RDWR, 0755) - if err != nil { - fmt.Printf("ERROR: %s\n", err.Error()) - writeByte(0) - writeByte(0) - return - } - - fileInfo, _ := file.Stat() - fileSize := int(fileInfo.Size()) - - fmt.Printf("FileSize: %d\n", fileSize) - - fileSizeHigh := byte(fileSize >> 8) - fileSizeLow := byte(fileSize & 255) - - writeByte(fileSizeLow) - writeByte(fileSizeHigh) - - buffer := make([]byte, fileSize) - - fmt.Printf("Read file %s SizeHigh: %d SizeLow: %d\n", fileName, fileSizeHigh, fileSizeLow) - - file.Read(buffer) - - for i := 0; i < fileSize; i++ { - err := writeByte(buffer[i]) - if err != nil { - return - } - } -} - -func readBlock(buffer []byte) error { - for i := 0; i < 512; i++ { - err := writeByte(buffer[i]) - if err != nil { - return err - } - } - - return nil -} - -func writeBlock(buffer []byte) error { - var err error - for i := 0; i < 512; i++ { - buffer[i], err = readByte() - if err != nil { - return err - } - } - - return nil -} - -func initGpio() { - out_write = gpioreg.ByName("GPIO24") - out_read = gpioreg.ByName("GPIO25") - out_reserved2 = gpioreg.ByName("GPIO7") //note GPIO7 and CPIO8 require extra effort to use - out_reserved1 = gpioreg.ByName("GPIO8") - out_bit7 = gpioreg.ByName("GPIO5") - out_bit6 = gpioreg.ByName("GPIO11") - out_bit5 = gpioreg.ByName("GPIO9") - out_bit4 = gpioreg.ByName("GPIO10") - out_bit3 = gpioreg.ByName("GPIO22") - out_bit2 = gpioreg.ByName("GPIO27") - out_bit1 = gpioreg.ByName("GPIO17") - out_bit0 = gpioreg.ByName("GPIO4") - in_write = gpioreg.ByName("GPIO23") - in_read = gpioreg.ByName("GPIO18") - in_reserved2 = gpioreg.ByName("GPIO14") - in_reserved1 = gpioreg.ByName("GPIO15") - in_bit7 = gpioreg.ByName("GPIO12") - in_bit6 = gpioreg.ByName("GPIO16") - in_bit5 = gpioreg.ByName("GPIO20") - in_bit4 = gpioreg.ByName("GPIO21") - in_bit3 = gpioreg.ByName("GPIO26") - in_bit2 = gpioreg.ByName("GPIO19") - in_bit1 = gpioreg.ByName("GPIO13") - in_bit0 = gpioreg.ByName("GPIO6") - - in_write.In(gpio.PullDown, gpio.BothEdges) - in_read.In(gpio.PullDown, gpio.BothEdges) - out_reserved1.Out(gpio.High) - out_reserved2.Out(gpio.High) - out_read.Out(gpio.High) - out_write.Out(gpio.High) - out_bit7.Out(gpio.Low) - out_bit6.Out(gpio.Low) - out_bit5.Out(gpio.Low) - out_bit4.Out(gpio.Low) - out_bit3.Out(gpio.Low) - out_bit2.Out(gpio.Low) - out_bit1.Out(gpio.Low) - out_bit0.Out(gpio.Low) - - edgeTimeout = time.Second * 5 -} - -func dumpBlock(buffer []byte) { - for i := 0; i < 512; i++ { - fmt.Printf("%02X ", buffer[i]) - } -} - -func readString() (string, error) { - var inBytes bytes.Buffer - for { - inByte, err := readByte() - if err != nil { - return "", err - } - if inByte == 0 { - break - } - inBytes.WriteByte(inByte) - } - return string(inBytes.Bytes()), nil -} - -func writeString(outString string) error { - for _, character := range outString { - err := writeByte(byte(character) | 128) - if err != nil { - fmt.Printf("Failed to write string\n") - return err - } - } - writeByte(0) - return nil -} - -func readByte() (byte, error) { - // let the Apple II know we are ready to read - if debug { - fmt.Printf("let the Apple II know we are ready to read\n") - } - out_read.Out(gpio.Low) - - // wait for the Apple II to write - if debug { - fmt.Printf("wait for the Apple II to write\n") - } - for in_write.Read() == gpio.High { - if !in_write.WaitForEdge(edgeTimeout) { - if debug { - fmt.Printf("Timed out reading byte -- write stuck high\n") - } - return 0, errors.New("Timed out reading byte -- write stuck high\n") - } - } - - // get a nibble of data - if debug { - fmt.Printf("get a byte of data\n") - } - var data byte - data = 0 - bit7 := in_bit7.Read() - bit6 := in_bit6.Read() - bit5 := in_bit5.Read() - bit4 := in_bit4.Read() - bit3 := in_bit3.Read() - bit2 := in_bit2.Read() - bit1 := in_bit1.Read() - bit0 := in_bit0.Read() - - if bit7 == gpio.High { - data += 128 - } - if bit6 == gpio.High { - data += 64 - } - if bit5 == gpio.High { - data += 32 - } - if bit4 == gpio.High { - data += 16 - } - if bit3 == gpio.High { - data += 8 - } - if bit2 == gpio.High { - data += 4 - } - if bit1 == gpio.High { - data += 2 - } - if bit0 == gpio.High { - data += 1 - } - - // let the Apple II know we are done reading - //fmt.Printf("let the Apple II know we are done reading\n") - out_read.Out(gpio.High) - - // wait for the Apple II to finish writing - //fmt.Printf("wait for the Apple II to finish writing\n") - for in_write.Read() == gpio.Low { - if !in_write.WaitForEdge(edgeTimeout) { - if debug { - fmt.Printf("Timed out reading byte -- write stuck low\n") - } - return 0, errors.New("Timed out reading byte -- write stuck low") - } - } - - return data, nil -} - -func writeByte(data byte) error { - // wait for the Apple II to be ready to read - if debug { - fmt.Printf("wait for the Apple II to be ready to read\n") - } - for in_read.Read() == gpio.High { - if !in_read.WaitForEdge(edgeTimeout) { - if debug { - fmt.Printf("Timed out writing byte -- read stuck high\n") - } - return errors.New("Timed out writing byte -- read stuck high") - } - } - - bit7 := gpio.Low - bit6 := gpio.Low - bit5 := gpio.Low - bit4 := gpio.Low - bit3 := gpio.Low - bit2 := gpio.Low - bit1 := gpio.Low - bit0 := gpio.Low - - if ((data & 128) >> 7) == 1 { - bit7 = gpio.High - } - out_bit7.Out(bit7) - - if ((data & 64) >> 6) == 1 { - bit6 = gpio.High - } - out_bit6.Out(bit6) - - if ((data & 32) >> 5) == 1 { - bit5 = gpio.High - } - out_bit5.Out(bit5) - - if ((data & 16) >> 4) == 1 { - bit4 = gpio.High - } - out_bit4.Out(bit4) - - if ((data & 8) >> 3) == 1 { - bit3 = gpio.High - } - out_bit3.Out(bit3) - - if ((data & 4) >> 2) == 1 { - bit2 = gpio.High - } - out_bit2.Out(bit2) - - if ((data & 2) >> 1) == 1 { - bit1 = gpio.High - } - out_bit1.Out(bit1) - - if (data & 1) == 1 { - bit0 = gpio.High - } - out_bit0.Out(bit0) - - // let Apple II know we're writing - if debug { - fmt.Printf("let Apple II know we're writing\n") - } - out_write.Out(gpio.Low) - - // wait for the Apple II to finsih reading - //fmt.Printf("wait for the Apple II to finsih reading\n") - for in_read.Read() == gpio.Low { - if !in_read.WaitForEdge(edgeTimeout) { - if debug { - fmt.Printf("Timed out writing byte -- read stuck low\n") - } - return errors.New("Timed out writing byte -- read stuck low") - } - } - - // let the Apple II know we are done writing - if debug { - fmt.Printf("let the Apple II know we are done writing\n") - } - out_write.Out(gpio.High) - return nil -} diff --git a/RaspberryPi/apple2driver/a2io/communication.go b/RaspberryPi/apple2driver/a2io/communication.go new file mode 100644 index 0000000..72a1b9d --- /dev/null +++ b/RaspberryPi/apple2driver/a2io/communication.go @@ -0,0 +1,283 @@ +package a2io + +import ( + "bytes" + "errors" + "fmt" + "time" + + "periph.io/x/periph/conn/gpio" + "periph.io/x/periph/conn/gpio/gpioreg" + "periph.io/x/periph/host" +) + +var edgeTimeout time.Duration + +var out_write gpio.PinIO +var out_read gpio.PinIO +var out_reserved2 gpio.PinIO +var out_reserved1 gpio.PinIO +var out_bit7 gpio.PinIO +var out_bit6 gpio.PinIO +var out_bit5 gpio.PinIO +var out_bit4 gpio.PinIO +var out_bit3 gpio.PinIO +var out_bit2 gpio.PinIO +var out_bit1 gpio.PinIO +var out_bit0 gpio.PinIO +var in_write gpio.PinIO +var in_read gpio.PinIO +var in_reserved2 gpio.PinIO +var in_reserved1 gpio.PinIO +var in_bit7 gpio.PinIO +var in_bit6 gpio.PinIO +var in_bit5 gpio.PinIO +var in_bit4 gpio.PinIO +var in_bit3 gpio.PinIO +var in_bit2 gpio.PinIO +var in_bit1 gpio.PinIO +var in_bit0 gpio.PinIO + +func InitGpio() { + host.Init() + + out_write = gpioreg.ByName("GPIO24") + out_read = gpioreg.ByName("GPIO25") + out_reserved2 = gpioreg.ByName("GPIO7") //note GPIO7 and CPIO8 require extra effort to use + out_reserved1 = gpioreg.ByName("GPIO8") + out_bit7 = gpioreg.ByName("GPIO5") + out_bit6 = gpioreg.ByName("GPIO11") + out_bit5 = gpioreg.ByName("GPIO9") + out_bit4 = gpioreg.ByName("GPIO10") + out_bit3 = gpioreg.ByName("GPIO22") + out_bit2 = gpioreg.ByName("GPIO27") + out_bit1 = gpioreg.ByName("GPIO17") + out_bit0 = gpioreg.ByName("GPIO4") + in_write = gpioreg.ByName("GPIO23") + in_read = gpioreg.ByName("GPIO18") + in_reserved2 = gpioreg.ByName("GPIO14") + in_reserved1 = gpioreg.ByName("GPIO15") + in_bit7 = gpioreg.ByName("GPIO12") + in_bit6 = gpioreg.ByName("GPIO16") + in_bit5 = gpioreg.ByName("GPIO20") + in_bit4 = gpioreg.ByName("GPIO21") + in_bit3 = gpioreg.ByName("GPIO26") + in_bit2 = gpioreg.ByName("GPIO19") + in_bit1 = gpioreg.ByName("GPIO13") + in_bit0 = gpioreg.ByName("GPIO6") + + in_write.In(gpio.PullDown, gpio.BothEdges) + in_read.In(gpio.PullDown, gpio.BothEdges) + out_reserved1.Out(gpio.High) + out_reserved2.Out(gpio.High) + out_read.Out(gpio.High) + out_write.Out(gpio.High) + out_bit7.Out(gpio.Low) + out_bit6.Out(gpio.Low) + out_bit5.Out(gpio.Low) + out_bit4.Out(gpio.Low) + out_bit3.Out(gpio.Low) + out_bit2.Out(gpio.Low) + out_bit1.Out(gpio.Low) + out_bit0.Out(gpio.Low) + + edgeTimeout = time.Second * 5 +} + +func ReadString() (string, error) { + var inBytes bytes.Buffer + for { + inByte, err := ReadByte() + if err != nil { + return "", err + } + if inByte == 0 { + break + } + inBytes.WriteByte(inByte) + } + return string(inBytes.Bytes()), nil +} + +func WriteString(outString string) error { + for _, character := range outString { + err := WriteByte(byte(character) | 128) + if err != nil { + fmt.Printf("Failed to write string\n") + return err + } + } + WriteByte(0) + return nil +} + +func ReadByte() (byte, error) { + // let the Apple II know we are ready to read + out_read.Out(gpio.Low) + + // wait for the Apple II to write + for in_write.Read() == gpio.High { + if !in_write.WaitForEdge(edgeTimeout) { + return 0, errors.New("Timed out reading byte -- write stuck high\n") + } + } + + // get a nibble of data + var data byte + data = 0 + bit7 := in_bit7.Read() + bit6 := in_bit6.Read() + bit5 := in_bit5.Read() + bit4 := in_bit4.Read() + bit3 := in_bit3.Read() + bit2 := in_bit2.Read() + bit1 := in_bit1.Read() + bit0 := in_bit0.Read() + + if bit7 == gpio.High { + data += 128 + } + if bit6 == gpio.High { + data += 64 + } + if bit5 == gpio.High { + data += 32 + } + if bit4 == gpio.High { + data += 16 + } + if bit3 == gpio.High { + data += 8 + } + if bit2 == gpio.High { + data += 4 + } + if bit1 == gpio.High { + data += 2 + } + if bit0 == gpio.High { + data += 1 + } + + // let the Apple II know we are done reading + //fmt.Printf("let the Apple II know we are done reading\n") + out_read.Out(gpio.High) + + // wait for the Apple II to finish writing + //fmt.Printf("wait for the Apple II to finish writing\n") + for in_write.Read() == gpio.Low { + if !in_write.WaitForEdge(edgeTimeout) { + return 0, errors.New("Timed out reading byte -- write stuck low") + } + } + + return data, nil +} + +func WriteByte(data byte) error { + // wait for the Apple II to be ready to read + for in_read.Read() == gpio.High { + if !in_read.WaitForEdge(edgeTimeout) { + return errors.New("Timed out writing byte -- read stuck high") + } + } + + bit7 := gpio.Low + bit6 := gpio.Low + bit5 := gpio.Low + bit4 := gpio.Low + bit3 := gpio.Low + bit2 := gpio.Low + bit1 := gpio.Low + bit0 := gpio.Low + + if ((data & 128) >> 7) == 1 { + bit7 = gpio.High + } + out_bit7.Out(bit7) + + if ((data & 64) >> 6) == 1 { + bit6 = gpio.High + } + out_bit6.Out(bit6) + + if ((data & 32) >> 5) == 1 { + bit5 = gpio.High + } + out_bit5.Out(bit5) + + if ((data & 16) >> 4) == 1 { + bit4 = gpio.High + } + out_bit4.Out(bit4) + + if ((data & 8) >> 3) == 1 { + bit3 = gpio.High + } + out_bit3.Out(bit3) + + if ((data & 4) >> 2) == 1 { + bit2 = gpio.High + } + out_bit2.Out(bit2) + + if ((data & 2) >> 1) == 1 { + bit1 = gpio.High + } + out_bit1.Out(bit1) + + if (data & 1) == 1 { + bit0 = gpio.High + } + out_bit0.Out(bit0) + + // let Apple II know we're writing + out_write.Out(gpio.Low) + + // wait for the Apple II to finsih reading + //fmt.Printf("wait for the Apple II to finsih reading\n") + for in_read.Read() == gpio.Low { + if !in_read.WaitForEdge(edgeTimeout) { + return errors.New("Timed out writing byte -- read stuck low") + } + } + + // let the Apple II know we are done writing + out_write.Out(gpio.High) + return nil +} + +func WriteBlock(buffer []byte) error { + for i := 0; i < 512; i++ { + err := WriteByte(buffer[i]) + if err != nil { + return err + } + } + + return nil +} + +func ReadBlock(buffer []byte) error { + var err error + for i := 0; i < 512; i++ { + buffer[i], err = ReadByte() + if err != nil { + return err + } + } + + return nil +} + +func WriteBuffer(buffer []byte) error { + bufferSize := len(buffer) + for i := 0; i < bufferSize; i++ { + err := WriteByte(buffer[i]) + if err != nil { + return err + } + } + + return nil +} diff --git a/RaspberryPi/apple2driver/apple2driver b/RaspberryPi/apple2driver/apple2driver new file mode 100755 index 0000000..652168f Binary files /dev/null and b/RaspberryPi/apple2driver/apple2driver differ diff --git a/RaspberryPi/apple2driver/driver.go b/RaspberryPi/apple2driver/driver.go new file mode 100644 index 0000000..fe2342f --- /dev/null +++ b/RaspberryPi/apple2driver/driver.go @@ -0,0 +1,49 @@ +package main + +import ( + "fmt" + "os" + + "github.com/tjboldt/Apple2-IO-RPi/RaspberryPi/apple2driver/a2io" + "github.com/tjboldt/Apple2-IO-RPi/RaspberryPi/apple2driver/handlers" +) + +const ReadBlockCommand = 1 +const WriteBlockCommand = 2 +const GetTimeCommand = 3 +const ChangeDriveCommand = 4 +const ExecCommand = 5 +const LoadFileCommand = 6 +const SaveFileCommand = 7 + +func main() { + a2io.InitGpio() + + fmt.Printf("Starting Apple II RPi...\n") + + fileName := os.Args[1] + + file, err := os.OpenFile(fileName, os.O_RDWR, 0755) + if err != nil { + fmt.Printf("ERROR: %s", err.Error()) + os.Exit(1) + } + + for { + command, err := a2io.ReadByte() + if err == nil { + switch command { + case ReadBlockCommand: + handlers.ReadBlockCommand(file) + case WriteBlockCommand: + handlers.WriteBlockCommand(file) + case GetTimeCommand: + handlers.GetTimeCommand() + case ExecCommand: + handlers.ExecCommand() + case LoadFileCommand: + handlers.LoadFileCommand() + } + } + } +} diff --git a/RaspberryPi/apple2driver/go.mod b/RaspberryPi/apple2driver/go.mod new file mode 100644 index 0000000..5368a16 --- /dev/null +++ b/RaspberryPi/apple2driver/go.mod @@ -0,0 +1,5 @@ +module github.com/tjboldt/Apple2-IO-RPi/RaspberryPi/apple2driver + +go 1.16 + +require periph.io/x/periph v3.6.8+incompatible diff --git a/RaspberryPi/apple2driver/go.sum b/RaspberryPi/apple2driver/go.sum new file mode 100644 index 0000000..68e7eb1 --- /dev/null +++ b/RaspberryPi/apple2driver/go.sum @@ -0,0 +1,2 @@ +periph.io/x/periph v3.6.8+incompatible h1:lki0ie6wHtvlilXhIkabdCUQMpb5QN4Fx33yNQdqnaA= +periph.io/x/periph v3.6.8+incompatible/go.mod h1:EWr+FCIU2dBWz5/wSWeiIUJTriYv9v2j2ENBmgYyy7Y= diff --git a/RaspberryPi/apple2driver/handlers/exec.go b/RaspberryPi/apple2driver/handlers/exec.go new file mode 100644 index 0000000..c84755f --- /dev/null +++ b/RaspberryPi/apple2driver/handlers/exec.go @@ -0,0 +1,60 @@ +package handlers + +import ( + "fmt" + "os" + "os/exec" + "strings" + + "github.com/tjboldt/Apple2-IO-RPi/RaspberryPi/apple2driver/a2io" +) + +func ExecCommand() { + workingDirectory, err := os.Getwd() + if err != nil { + workingDirectory = "/home" + a2io.WriteString("Failed to get current working directory, setting to /home\r") + } + + fmt.Printf("Reading command to execute...\n") + linuxCommand, err := a2io.ReadString() + fmt.Printf("Command to run: %s\n", linuxCommand) + if strings.HasPrefix(linuxCommand, "cd ") { + workingDirectory = strings.Replace(linuxCommand, "cd ", "", 1) + err = os.Chdir(workingDirectory) + if err != nil { + a2io.WriteString("Failed to set working directory\r") + return + } + a2io.WriteString("Working directory set\r") + return + } + if linuxCommand == "a2help" { + a2io.WriteString("\r" + + "This is a pseudo shell. Each command is executed as a process. The cd command\r" + + "is intercepted and sets the working directory for the next command. Running\r" + + "commands that do not exit will hang. For example, do not use ping without a\r" + + "way to limit output like -c 1.\r" + + "\r" + + "Built-in commands:\r" + + "a2help - display this message\r" + + "a2wifi - set up wifi\r" + + "\r") + return + } + cmd := exec.Command("bash", "-c", linuxCommand) + cmd.Dir = workingDirectory + cmdOut, err := cmd.Output() + if err != nil { + fmt.Printf("Failed to execute command\n") + a2io.WriteString("Failed to execute command\r") + return + } + fmt.Printf("Command output: %s\n", cmdOut) + apple2string := strings.Replace(string(cmdOut), "\n", "\r", -1) + err = a2io.WriteString(apple2string) + if err != nil { + fmt.Printf("Failed to send command output\n") + return + } +} diff --git a/RaspberryPi/apple2driver/handlers/getTime.go b/RaspberryPi/apple2driver/handlers/getTime.go new file mode 100644 index 0000000..7950a8e --- /dev/null +++ b/RaspberryPi/apple2driver/handlers/getTime.go @@ -0,0 +1,44 @@ +package handlers + +import ( + "fmt" + "time" + + "github.com/tjboldt/Apple2-IO-RPi/RaspberryPi/apple2driver/a2io" +) + +func GetTimeCommand() { + fmt.Printf("Sending date/time...\n") + /* 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) + */ + now := time.Now() + + year := now.Year() % 100 + month := now.Month() + day := now.Day() + hour := now.Hour() + minute := now.Minute() + + bf91 := (byte(year) << 1) + (byte(month) >> 3) + bf90 := ((byte(month) & 15) << 5) + byte(day) + bf93 := byte(hour) + bf92 := byte(minute) + + a2io.WriteByte(bf90) + a2io.WriteByte(bf91) + a2io.WriteByte(bf92) + a2io.WriteByte(bf93) + fmt.Printf("Send time complete\n") +} diff --git a/RaspberryPi/apple2driver/handlers/loadFile.go b/RaspberryPi/apple2driver/handlers/loadFile.go new file mode 100644 index 0000000..7923e64 --- /dev/null +++ b/RaspberryPi/apple2driver/handlers/loadFile.go @@ -0,0 +1,39 @@ +package handlers + +import ( + "fmt" + "os" + + "github.com/tjboldt/Apple2-IO-RPi/RaspberryPi/apple2driver/a2io" +) + +func LoadFileCommand() { + fileName, _ := a2io.ReadString() + + file, err := os.OpenFile(fileName, os.O_RDWR, 0755) + if err != nil { + fmt.Printf("ERROR: %s\n", err.Error()) + a2io.WriteByte(0) + a2io.WriteByte(0) + return + } + + fileInfo, _ := file.Stat() + fileSize := int(fileInfo.Size()) + + fmt.Printf("FileSize: %d\n", fileSize) + + fileSizeHigh := byte(fileSize >> 8) + fileSizeLow := byte(fileSize & 255) + + a2io.WriteByte(fileSizeLow) + a2io.WriteByte(fileSizeHigh) + + buffer := make([]byte, fileSize) + + fmt.Printf("Read file %s SizeHigh: %d SizeLow: %d\n", fileName, fileSizeHigh, fileSizeLow) + + file.Read(buffer) + + a2io.WriteBuffer(buffer) +} diff --git a/RaspberryPi/apple2driver/handlers/readBlock.go b/RaspberryPi/apple2driver/handlers/readBlock.go new file mode 100644 index 0000000..5a8c71e --- /dev/null +++ b/RaspberryPi/apple2driver/handlers/readBlock.go @@ -0,0 +1,28 @@ +package handlers + +import ( + "fmt" + "os" + + "github.com/tjboldt/Apple2-IO-RPi/RaspberryPi/apple2driver/a2io" +) + +func ReadBlockCommand(file *os.File) { + blockLow, _ := a2io.ReadByte() + blockHigh, _ := a2io.ReadByte() + + buffer := make([]byte, 512) + var block int64 + block = int64(blockHigh)*256 + int64(blockLow) + + fmt.Printf("Read block %d\n", block) + + file.ReadAt(buffer, int64(block)*512) + + err := a2io.WriteBlock(buffer) + if err == nil { + fmt.Printf("Read block completed\n") + } else { + fmt.Printf("Failed to read block\n") + } +} diff --git a/RaspberryPi/apple2driver/handlers/writeBlock.go b/RaspberryPi/apple2driver/handlers/writeBlock.go new file mode 100644 index 0000000..33344d5 --- /dev/null +++ b/RaspberryPi/apple2driver/handlers/writeBlock.go @@ -0,0 +1,24 @@ +package handlers + +import ( + "fmt" + "os" + + "github.com/tjboldt/Apple2-IO-RPi/RaspberryPi/apple2driver/a2io" +) + +func WriteBlockCommand(file *os.File) { + blockLow, _ := a2io.ReadByte() + blockHigh, _ := a2io.ReadByte() + + buffer := make([]byte, 512) + var block int64 + block = int64(blockHigh)*256 + int64(blockLow) + + fmt.Printf("Write block %d\n", block) + + a2io.ReadBlock(buffer) + file.WriteAt(buffer, int64(block)*512) + file.Sync() + fmt.Printf("Write block completed\n") +} diff --git a/RaspberryPi/go.mod b/RaspberryPi/go.mod deleted file mode 100644 index 5191bc0..0000000 --- a/RaspberryPi/go.mod +++ /dev/null @@ -1,5 +0,0 @@ -module github.com/tjboldt/Apple2-IO-RPi - -go 1.16 - -require periph.io/x/periph v3.6.7+incompatible diff --git a/RaspberryPi/go.sum b/RaspberryPi/go.sum deleted file mode 100644 index eb10138..0000000 --- a/RaspberryPi/go.sum +++ /dev/null @@ -1,2 +0,0 @@ -periph.io/x/periph v3.6.7+incompatible h1:ZfRdHbcxVekgSJZmxp3873YpxNdWs6wg7waDCF7GB18= -periph.io/x/periph v3.6.7+incompatible/go.mod h1:EWr+FCIU2dBWz5/wSWeiIUJTriYv9v2j2ENBmgYyy7Y=