From 612b5770a28c939708a26659c8cacbf8f115a9ce Mon Sep 17 00:00:00 2001 From: Terence Boldt Date: Wed, 23 Dec 2020 17:19:42 -0500 Subject: [PATCH 1/5] Add Linux command support --- RaspberryPi/Driver.go | 444 +++++++++++++++++++++++++----------------- 1 file changed, 264 insertions(+), 180 deletions(-) diff --git a/RaspberryPi/Driver.go b/RaspberryPi/Driver.go index 31b38f4..32c1eb5 100644 --- a/RaspberryPi/Driver.go +++ b/RaspberryPi/Driver.go @@ -1,14 +1,17 @@ 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" - "fmt" - "os" "time" - "errors" ) + var edgeTimeout time.Duration var out_write gpio.PinIO @@ -31,23 +34,259 @@ var in_bit0 gpio.PinIO const ReadBlockCommand = 1 const WriteBlockCommand = 2 const GetTimeCommand = 3 +const ChangeDriveCommand = 4 +const ExecCommand = 5 + +var debug bool = false + +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() + } + } + } +} + +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) + readBlock(buffer) +} + +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() +} + +func handleExecCommand() { + linuxCommand,err := readString() + cmd := exec.Command("bash", "-c", linuxCommand) + cmdOut, err := cmd.Output() + if err != nil { + fmt.Printf("Failed to execute command\n") + } + writeString(cmdOut) +} + +func handleGetTimeCommand() { + /* 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) +} + +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("GPIO5") + out_read = gpioreg.ByName("GPIO11") + out_commandWrite = gpioreg.ByName("GPIO9") + out_commandRead = 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("GPIO12") + in_read = gpioreg.ByName("GPIO16") + in_commandWrite = gpioreg.ByName("GPIO20") + in_commandRead = 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) + + edgeTimeout = time.Second * 5 +} + +func dumpBlock(buffer []byte) { + for i := 0; i < 512; i++ { + fmt.Printf("%02X ", buffer[i]) + } +} + +func readString() (string, error) { + inByte := byte(0) + var inBytes bytes.Buffer + var err error + for inByte == 0 { + inByte,err = readByte() + if err != nil { + return "", err + } + inBytes.WriteByte(inByte) + } + return string(inBytes.Bytes()), nil +} + +func writeString(outBytes []byte) error { + for outByte := range outBytes { + err := writeByte(byte(outByte)) + if err != nil { + return err + } + } + writeByte(0) + return nil +} + +func readByte() (byte, error) { + data, err := readNibble() + data = data << byte(4) + if err != nil { + return 0, err + } + highNibble, err := readNibble() + if err != nil { + return 0, err + } + data += highNibble + //fmt.Printf("R%02X ", data) + return data, nil +} + +func writeByte(data byte) error { + //fmt.Printf("W%02X ", data) + err := writeNibble(data >> 4) + if err != nil { + return err + } + err = writeNibble(data & 15) + if err != nil { + return err + } + + return nil +} func readNibble() (byte, error) { // let the Apple II know we are ready to read - //fmt.Printf("let the Apple II know we are ready to read\n") + 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 - //fmt.Printf("wait for the Apple II to write\n") + if debug { + fmt.Printf("wait for the Apple II to write\n") + } for in_write.Read() == gpio.High { if !in_write.WaitForEdge(edgeTimeout) { - //fmt.Printf("Timed out reading nibble -- write stuck high\n") + if debug { + fmt.Printf("Timed out reading nibble -- write stuck high\n") + } return 0, errors.New("Timed out reading nibble -- write stuck high\n") } } // get a nibble of data - //fmt.Printf("get a nibble of data\n") + if debug { + fmt.Printf("get a nibble of data\n") + } var nibble byte nibble = 0 bit3 := in_bit3.Read() @@ -76,7 +315,9 @@ func readNibble() (byte, error) { //fmt.Printf("wait for the Apple II to finish writing\n") for in_write.Read() == gpio.Low { if !in_write.WaitForEdge(edgeTimeout) { - //fmt.Printf("Timed out reading nibble -- write stuck low\n") + if debug { + fmt.Printf("Timed out reading nibble -- write stuck low\n") + } return 0, errors.New("Timed out reading nibble -- write stuck low") } } @@ -84,23 +325,16 @@ func readNibble() (byte, error) { return nibble, nil } -func readByte() (byte, error) { - data, err := readNibble() - data = data << byte(4) - if err != nil { return 0, err } - highNibble, err := readNibble() - if err != nil { return 0, err } - data += highNibble - //fmt.Printf("R%02X ", data) - return data, nil -} - func writeNibble(data byte) error { // wait for the Apple II to be ready to read - //fmt.Printf("wait for the Apple II to be ready to read\n") + 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) { - //fmt.Printf("Timed out writing nibble -- read stuck high\n") + if debug { + fmt.Printf("Timed out writing nibble -- read stuck high\n") + } return errors.New("Timed out writing nibble -- read stuck high") } } @@ -130,176 +364,26 @@ func writeNibble(data byte) error { out_bit0.Out(bit0) // let Apple II know we're writing - //fmt.Printf("let Apple II know we're writing\n") + 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) { - //fmt.Printf("Timed out writing nibble -- read stuck low\n") + if debug { + fmt.Printf("Timed out writing nibble -- read stuck low\n") + } return errors.New("Timed out writing nibble -- read stuck low") } } // let the Apple II know we are done writing - //fmt.Printf("let the Apple II know we are done writing\n") + if debug { + fmt.Printf("let the Apple II know we are done writing\n") + } out_write.Out(gpio.High) return nil } - -func writeByte(data byte) error { - //fmt.Printf("W%02X ", data) - err := writeNibble(data >> 4) - if err != nil { return err } - err = writeNibble(data & 15) - if err != nil { return err } - - return nil -} - -func readBlock(buffer []byte) error { - for i := 0; i < 512; i++ { - err := writeByte(buffer[i]) - if err != nil { return err } - } - - return nil -} - -func dumpBlock(buffer []byte) { - for i := 0; i < 512; i++ { - fmt.Printf("%02X ", buffer[i]) - } -} - -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 main() { - host.Init() - - out_write = gpioreg.ByName("GPIO5") - out_read = gpioreg.ByName("GPIO11") - out_commandWrite = gpioreg.ByName("GPIO9") - out_commandRead = 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("GPIO12") - in_read = gpioreg.ByName("GPIO16") - in_commandWrite = gpioreg.ByName("GPIO20") - in_commandRead = 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) - - edgeTimeout = time.Second * 5 - - 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) - } - //if err := f.Close(); err != nil { - //log.Fatal(err) - //} - - //for in_write.Read() == gpio.Low { - // in_write.WaitForEdge(-1) - //} - buffer := make([]byte, 512) - //file.ReadAt(buffer, int64(0) * 512) - //dumpBlock(buffer) - - for { - //fmt.Printf("Check for command") - - command,err := readByte(); - if err != nil { - //fmt.Printf("Timed out waiting for command\n") - } else { - switch command { - case ReadBlockCommand: - blockLow,_ := readByte(); - blockHigh,_ := readByte(); - - - var block int64 - block = int64(blockHigh) * 256 + int64(blockLow) - - fmt.Printf("Read block %d\n", block) - - - file.ReadAt(buffer, int64(block) * 512) - //dumpBlock(buffer) - readBlock(buffer) - break - case WriteBlockCommand: - blockLow,_ := readByte(); - blockHigh,_ := readByte(); - - - 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() - break - case GetTimeCommand: - /* - 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) - break - } - } - } -} - From 3176e6112ea1dfc730a2daa117c0bdf937e677ab Mon Sep 17 00:00:00 2001 From: Terence Boldt Date: Wed, 23 Dec 2020 17:20:52 -0500 Subject: [PATCH 2/5] Rename Firmware to Apple2 --- {Firmware => Apple2}/Driver.asm | 0 {Firmware => Apple2}/Driver.lst | 0 {Firmware => Apple2}/Firmware.asm | 0 {Firmware => Apple2}/Firmware.bin | Bin {Firmware => Apple2}/driver_assemble.sh | 0 {Firmware => Apple2}/test.asm | 0 {Firmware => Apple2}/test_assemble.sh | 0 7 files changed, 0 insertions(+), 0 deletions(-) rename {Firmware => Apple2}/Driver.asm (100%) rename {Firmware => Apple2}/Driver.lst (100%) rename {Firmware => Apple2}/Firmware.asm (100%) rename {Firmware => Apple2}/Firmware.bin (100%) rename {Firmware => Apple2}/driver_assemble.sh (100%) rename {Firmware => Apple2}/test.asm (100%) rename {Firmware => Apple2}/test_assemble.sh (100%) diff --git a/Firmware/Driver.asm b/Apple2/Driver.asm similarity index 100% rename from Firmware/Driver.asm rename to Apple2/Driver.asm diff --git a/Firmware/Driver.lst b/Apple2/Driver.lst similarity index 100% rename from Firmware/Driver.lst rename to Apple2/Driver.lst diff --git a/Firmware/Firmware.asm b/Apple2/Firmware.asm similarity index 100% rename from Firmware/Firmware.asm rename to Apple2/Firmware.asm diff --git a/Firmware/Firmware.bin b/Apple2/Firmware.bin similarity index 100% rename from Firmware/Firmware.bin rename to Apple2/Firmware.bin diff --git a/Firmware/driver_assemble.sh b/Apple2/driver_assemble.sh similarity index 100% rename from Firmware/driver_assemble.sh rename to Apple2/driver_assemble.sh diff --git a/Firmware/test.asm b/Apple2/test.asm similarity index 100% rename from Firmware/test.asm rename to Apple2/test.asm diff --git a/Firmware/test_assemble.sh b/Apple2/test_assemble.sh similarity index 100% rename from Firmware/test_assemble.sh rename to Apple2/test_assemble.sh From 8d62b0c57ede4d84296f1b564b8eb16a8aa41ca0 Mon Sep 17 00:00:00 2001 From: Terence Boldt Date: Wed, 23 Dec 2020 22:53:20 -0500 Subject: [PATCH 3/5] Partial fix for command execution --- Apple2/driver_assemble.sh | 16 ++++++++-------- RaspberryPi/Driver.go | 23 +++++++++++++++++------ 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/Apple2/driver_assemble.sh b/Apple2/driver_assemble.sh index d0ca23f..d2c46b2 100755 --- a/Apple2/driver_assemble.sh +++ b/Apple2/driver_assemble.sh @@ -1,12 +1,12 @@ #!/bin/sh ca65 Driver.asm --listing Driver.lst ld65 Driver.o -o Driver.bin -t none -ca65 Firmware.asm -D STARTSLOT=\$c000 -o Slot0.o -ca65 Firmware.asm -D STARTSLOT=\$c100 -o Slot1.o -ca65 Firmware.asm -D STARTSLOT=\$c200 -o Slot2.o -ca65 Firmware.asm -D STARTSLOT=\$c300 -o Slot3.o -ca65 Firmware.asm -D STARTSLOT=\$c400 -o Slot4.o -ca65 Firmware.asm -D STARTSLOT=\$c500 -o Slot5.o -ca65 Firmware.asm -D STARTSLOT=\$c600 -o Slot6.o -ca65 Firmware.asm -D STARTSLOT=\$c700 -o Slot7.o +ca65 Firmware.asm -D STARTSLOT=\$c000 -o Slot0.o +ca65 Firmware.asm -D STARTSLOT=\$c100 -o Slot1.o --listing Firmware1.lst +ca65 Firmware.asm -D STARTSLOT=\$c200 -o Slot2.o --listing Firmware2.lst +ca65 Firmware.asm -D STARTSLOT=\$c300 -o Slot3.o --listing Firmware3.lst +ca65 Firmware.asm -D STARTSLOT=\$c400 -o Slot4.o --listing Firmware4.lst +ca65 Firmware.asm -D STARTSLOT=\$c500 -o Slot5.o --listing Firmware5.lst +ca65 Firmware.asm -D STARTSLOT=\$c600 -o Slot6.o --listing Firmware6.lst +ca65 Firmware.asm -D STARTSLOT=\$c700 -o Slot7.o --listing Firmware7.lst ld65 Slot0.o Slot1.o Slot2.o Slot3.o Slot4.o Slot5.o Slot6.o Slot7.o -o Firmware.bin -t none diff --git a/RaspberryPi/Driver.go b/RaspberryPi/Driver.go index 32c1eb5..e351877 100644 --- a/RaspberryPi/Driver.go +++ b/RaspberryPi/Driver.go @@ -113,13 +113,22 @@ func handleWriteBlockCommand(file *os.File) { } func handleExecCommand() { + fmt.Printf("Reading command to execute...\n") linuxCommand,err := readString() + fmt.Printf("Command to run: %s\n", linuxCommand) cmd := exec.Command("bash", "-c", linuxCommand) 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) + err = writeString(string(cmdOut)) + if err != nil { + fmt.Printf("Failed to send command output\n") + return } - writeString(cmdOut) } func handleGetTimeCommand() { @@ -210,10 +219,10 @@ func dumpBlock(buffer []byte) { } func readString() (string, error) { - inByte := byte(0) + inByte := byte(255) var inBytes bytes.Buffer var err error - for inByte == 0 { + for inByte != 0 { inByte,err = readByte() if err != nil { return "", err @@ -223,10 +232,12 @@ func readString() (string, error) { return string(inBytes.Bytes()), nil } -func writeString(outBytes []byte) error { - for outByte := range outBytes { - err := writeByte(byte(outByte)) +func writeString(outString string) error { + for _, character := range outString { + fmt.Printf("Out: %s\n", character); + err := writeByte(byte(character)|128) if err != nil { + fmt.Printf("Failed to write string\n") return err } } From 52a86cc8a1706378138acb95ab5ec3d530ce706c Mon Sep 17 00:00:00 2001 From: Terence Boldt Date: Thu, 24 Dec 2020 09:01:46 -0500 Subject: [PATCH 4/5] Fix command execution (this is being committed from my Apple //e) --- RaspberryPi/Driver.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/RaspberryPi/Driver.go b/RaspberryPi/Driver.go index e351877..ea2123c 100644 --- a/RaspberryPi/Driver.go +++ b/RaspberryPi/Driver.go @@ -9,6 +9,7 @@ import ( "periph.io/x/periph/conn/gpio" "periph.io/x/periph/conn/gpio/gpioreg" "periph.io/x/periph/host" + "strings" "time" ) @@ -124,7 +125,8 @@ func handleExecCommand() { return } fmt.Printf("Command output: %s\n", cmdOut) - err = writeString(string(cmdOut)) + apple2string := strings.Replace(string(cmdOut), "\n", "\r", -1) + err = writeString(apple2string) if err != nil { fmt.Printf("Failed to send command output\n") return @@ -219,14 +221,15 @@ func dumpBlock(buffer []byte) { } func readString() (string, error) { - inByte := byte(255) var inBytes bytes.Buffer - var err error - for inByte != 0 { - inByte,err = readByte() + for { + inByte,err := readByte() if err != nil { return "", err } + if inByte == 0 { + break + } inBytes.WriteByte(inByte) } return string(inBytes.Bytes()), nil @@ -234,7 +237,6 @@ func readString() (string, error) { func writeString(outString string) error { for _, character := range outString { - fmt.Printf("Out: %s\n", character); err := writeByte(byte(character)|128) if err != nil { fmt.Printf("Failed to write string\n") From f6d1607a33e8b4dd43b78d6159f34e0874e1dfe4 Mon Sep 17 00:00:00 2001 From: Terence Boldt Date: Mon, 28 Dec 2020 10:54:57 -0500 Subject: [PATCH 5/5] Update project status --- Apple2/test.asm | 11 ----------- Apple2/test_assemble.sh | 3 --- README.md | 4 ++-- 3 files changed, 2 insertions(+), 16 deletions(-) delete mode 100644 Apple2/test.asm delete mode 100755 Apple2/test_assemble.sh diff --git a/Apple2/test.asm b/Apple2/test.asm deleted file mode 100644 index f90a23e..0000000 --- a/Apple2/test.asm +++ /dev/null @@ -1,11 +0,0 @@ - ldx #$50 ;slot 5 for this test -start: lda #$80 ;set read flag low (ready to read) - sta $c08d,x ;bit 1 low for writing values -waitwl: lda $c08e,x ;bit 0 low for reading values - bmi waitwl ;wait for write flag low - jsr $fde3 ;print nibble of data - lda #$c0 ;set read flag high (done reading) - sta $c08d,x -waitwh: lda $c08e,x - bpl waitwh ;wait for write flag high - bmi start ;go around againg for next nibble diff --git a/Apple2/test_assemble.sh b/Apple2/test_assemble.sh deleted file mode 100755 index 8b1c025..0000000 --- a/Apple2/test_assemble.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -ca65 test.asm -ld65 test.o -o test.bin -t none diff --git a/README.md b/README.md index 02ad737..0b93c07 100644 --- a/README.md +++ b/README.md @@ -13,8 +13,8 @@ This is an early stage project. Currently one board has been assembled and teste 1. DONE - Build initial prototype that reads/writes virtual hard drive 2. DONE - Create firmware to make the card a bootable device 3. DONE - Fix board with updated second prototype PCB -4. DONE - Add ProDOS clock driver -5. Add RPi terminal access +4. WORKING - Add ProDOS clock driver (real driver later, for now just directly sets values) +5. WORKING - Add RPi terminal access (does not keep context between commands yet) 6. Add web service call support 7. Proxy VNC connection, rendering as Apple II compatible graphics 8. Create new schematic/PCB with faster data transfer