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/Apple2/driver_assemble.sh b/Apple2/driver_assemble.sh new file mode 100755 index 0000000..d2c46b2 --- /dev/null +++ b/Apple2/driver_assemble.sh @@ -0,0 +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 --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/Firmware/driver_assemble.sh b/Firmware/driver_assemble.sh deleted file mode 100755 index d0ca23f..0000000 --- a/Firmware/driver_assemble.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/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 -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/Firmware/test.asm b/Firmware/test.asm deleted file mode 100644 index f90a23e..0000000 --- a/Firmware/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/Firmware/test_assemble.sh b/Firmware/test_assemble.sh deleted file mode 100755 index 8b1c025..0000000 --- a/Firmware/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 diff --git a/RaspberryPi/Driver.go b/RaspberryPi/Driver.go index 31b38f4..ea2123c 100644 --- a/RaspberryPi/Driver.go +++ b/RaspberryPi/Driver.go @@ -1,14 +1,18 @@ 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" + "strings" "time" - "errors" ) + var edgeTimeout time.Duration var out_write gpio.PinIO @@ -31,23 +35,271 @@ 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() { + 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) + 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() { + /* 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) { + 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) { + 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 +328,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 +338,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 +377,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 - } - } - } -} -