Apple2-IO-RPi/RaspberryPi/apple2driver/a2io/gpio.go

385 lines
7.8 KiB
Go
Raw Normal View History

// Copyright Terence J. Boldt (c)2020-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 communicating with the Apple II data bus via the
// GPIO ports on the Raspberry Pi
2021-11-03 09:33:09 +00:00
2021-10-30 11:03:18 +00:00
package a2io
import (
"bytes"
"errors"
"fmt"
"time"
2022-02-15 02:02:55 +00:00
"github.com/stianeikeland/go-rpio/v4"
2021-10-30 11:03:18 +00:00
)
var edgeTimeout time.Duration
2022-02-15 02:02:55 +00:00
var outWrite rpio.Pin
var outRead rpio.Pin
var outReserved2 rpio.Pin
var outReserved1 rpio.Pin
var outBit7 rpio.Pin
var outBit6 rpio.Pin
var outBit5 rpio.Pin
var outBit4 rpio.Pin
var outBit3 rpio.Pin
var outBit2 rpio.Pin
var outBit1 rpio.Pin
var outBit0 rpio.Pin
var inWrite rpio.Pin
var inRead rpio.Pin
var inReserved2 rpio.Pin
var inReserved1 rpio.Pin
var inBit7 rpio.Pin
var inBit6 rpio.Pin
var inBit5 rpio.Pin
var inBit4 rpio.Pin
var inBit3 rpio.Pin
var inBit2 rpio.Pin
var inBit1 rpio.Pin
var inBit0 rpio.Pin
2021-11-03 09:33:09 +00:00
// A2Gpio is the live implementation of A2Io interface
2021-10-30 11:03:18 +00:00
type A2Gpio struct {
}
2021-11-03 09:33:09 +00:00
// Init initializes the GPIO ports on the Raspberry Pi
2021-10-30 11:03:18 +00:00
func (a2 A2Gpio) Init() {
2022-02-15 02:02:55 +00:00
err := rpio.Open()
if err != nil {
panic("failed to open gpio")
}
outWrite = rpio.Pin(24)
outRead = rpio.Pin(25)
outReserved2 = rpio.Pin(7) //note GPIO7 and CPIO8 require extra effort to use
outReserved1 = rpio.Pin(8)
outBit7 = rpio.Pin(5)
outBit6 = rpio.Pin(11)
outBit5 = rpio.Pin(9)
outBit4 = rpio.Pin(10)
outBit3 = rpio.Pin(22)
outBit2 = rpio.Pin(27)
outBit1 = rpio.Pin(17)
outBit0 = rpio.Pin(4)
inWrite = rpio.Pin(23)
inRead = rpio.Pin(18)
inReserved2 = rpio.Pin(14)
inReserved1 = rpio.Pin(15)
inBit7 = rpio.Pin(12)
inBit6 = rpio.Pin(16)
inBit5 = rpio.Pin(20)
inBit4 = rpio.Pin(21)
inBit3 = rpio.Pin(26)
inBit2 = rpio.Pin(19)
inBit1 = rpio.Pin(13)
inBit0 = rpio.Pin(6)
inWrite.PullDown()
inRead.PullDown()
2022-02-15 23:56:02 +00:00
outWrite.Output()
outRead.Output()
outReserved2.Output()
outReserved1.Output()
outBit7.Output()
outBit6.Output()
outBit5.Output()
outBit4.Output()
outBit3.Output()
outBit2.Output()
outBit1.Output()
outBit0.Output()
inWrite.Input()
inRead.Input()
inReserved2.Input()
inReserved1.Input()
inBit7.Input()
inBit6.Input()
inBit5.Input()
inBit4.Input()
inBit3.Input()
inBit2.Input()
inBit1.Input()
inBit0.Input()
2022-02-15 02:02:55 +00:00
outReserved1.High()
outReserved2.High()
outRead.High()
outWrite.High()
outBit7.Low()
outBit6.Low()
outBit5.Low()
outBit4.Low()
outBit3.Low()
outBit2.Low()
outBit1.Low()
outBit0.Low()
2021-10-30 11:03:18 +00:00
edgeTimeout = time.Second
}
2021-11-03 09:33:09 +00:00
// ReadString reads a string from the Apple II via Raspberry Pi's GPIO ports
2021-10-30 11:03:18 +00:00
func (a2 A2Gpio) ReadString() (string, error) {
var inBytes bytes.Buffer
for {
inByte, err := a2.ReadByte()
if err != nil {
return "", err
}
if inByte == 0 {
break
}
inBytes.WriteByte(inByte)
}
2021-11-03 09:33:09 +00:00
return inBytes.String(), nil
2021-10-30 11:03:18 +00:00
}
2021-11-03 09:33:09 +00:00
// WriteString writes a string to the Apple II via Raspberry Pi's GPIO ports
2021-10-30 11:03:18 +00:00
func (a2 A2Gpio) WriteString(outString string) error {
for _, character := range outString {
err := a2.WriteByte(byte(character) | 128)
if err != nil {
fmt.Printf("Failed to write string\n")
return err
}
}
a2.WriteByte(0)
return nil
}
2021-11-03 09:33:09 +00:00
// ReadByte reads a byte from the Apple II via Raspberry Pi's GPIO ports
2023-11-02 00:00:21 +00:00
func (a2 A2Gpio) ReadByte(noDelay ...bool) (byte, error) {
2021-10-30 11:03:18 +00:00
// let the Apple II know we are ready to read
2022-02-15 02:02:55 +00:00
outRead.Low()
2021-10-30 11:03:18 +00:00
// wait for the Apple II to write
startTime := time.Now()
lastSleepTime := time.Now()
sleepDuration := 10
2022-02-15 02:02:55 +00:00
for inWrite.Read() == 1 {
if time.Since(startTime) > edgeTimeout {
2022-02-15 02:02:55 +00:00
outRead.High()
2021-11-03 09:33:09 +00:00
return 0, errors.New("timed out reading byte -- write stuck high")
2021-10-30 11:03:18 +00:00
}
if time.Since(lastSleepTime) > edgeTimeout/10 {
2023-11-02 00:00:21 +00:00
if len(noDelay) == 0 || !noDelay[0] {
sleepDuration *= 3;
}
time.Sleep(time.Millisecond * time.Duration(sleepDuration));
lastSleepTime = time.Now()
}
2021-10-30 11:03:18 +00:00
}
// get a nibble of data
var data byte
data = 0
2021-11-03 09:33:09 +00:00
bit7 := inBit7.Read()
bit6 := inBit6.Read()
bit5 := inBit5.Read()
bit4 := inBit4.Read()
bit3 := inBit3.Read()
bit2 := inBit2.Read()
bit1 := inBit1.Read()
bit0 := inBit0.Read()
2021-10-30 11:03:18 +00:00
2022-02-15 02:02:55 +00:00
if bit7 == 1 {
2021-10-30 11:03:18 +00:00
data += 128
}
2022-02-15 02:02:55 +00:00
if bit6 == 1 {
2021-10-30 11:03:18 +00:00
data += 64
}
2022-02-15 02:02:55 +00:00
if bit5 == 1 {
2021-10-30 11:03:18 +00:00
data += 32
}
2022-02-15 02:02:55 +00:00
if bit4 == 1 {
2021-10-30 11:03:18 +00:00
data += 16
}
2022-02-15 02:02:55 +00:00
if bit3 == 1 {
2021-10-30 11:03:18 +00:00
data += 8
}
2022-02-15 02:02:55 +00:00
if bit2 == 1 {
2021-10-30 11:03:18 +00:00
data += 4
}
2022-02-15 02:02:55 +00:00
if bit1 == 1 {
2021-10-30 11:03:18 +00:00
data += 2
}
2022-02-15 02:02:55 +00:00
if bit0 == 1 {
2021-11-03 09:33:09 +00:00
data++
2021-10-30 11:03:18 +00:00
}
// let the Apple II know we are done reading
//fmt.Printf("let the Apple II know we are done reading\n")
2022-02-15 02:02:55 +00:00
outRead.High()
2021-10-30 11:03:18 +00:00
// wait for the Apple II to finish writing
//fmt.Printf("wait for the Apple II to finish writing\n")
startTime = time.Now()
lastSleepTime = time.Now()
sleepDuration = 10
2022-02-15 02:02:55 +00:00
for inWrite.Read() == 0 {
if time.Since(startTime) > edgeTimeout {
2021-11-03 09:33:09 +00:00
return 0, errors.New("timed out reading byte -- write stuck low")
2021-10-30 11:03:18 +00:00
}
if time.Since(lastSleepTime) > edgeTimeout/10 {
2023-11-02 00:00:21 +00:00
if len(noDelay) == 0 || !noDelay[0] {
sleepDuration *= 3;
}
time.Sleep(time.Millisecond * time.Duration(sleepDuration));
lastSleepTime = time.Now()
}
2021-10-30 11:03:18 +00:00
}
return data, nil
}
2021-11-03 09:33:09 +00:00
// WriteByte writes a byte to the Apple II via Raspberry Pi's GPIO ports
2021-10-30 11:03:18 +00:00
func (a2 A2Gpio) WriteByte(data byte) error {
// check if the Apple II wants to send a byte to us first
2022-02-15 02:02:55 +00:00
if inWrite.Read() == 0 {
outWrite.High()
2021-11-03 09:33:09 +00:00
return errors.New("can't write byte while byte is incoming")
2021-10-30 11:03:18 +00:00
}
// wait for the Apple II to be ready to read
startTime := time.Now()
lastSleepTime := time.Now()
sleepDuration := 10
2022-02-15 02:02:55 +00:00
for inRead.Read() == 1 {
if time.Since(startTime) > edgeTimeout {
2022-02-15 02:02:55 +00:00
outWrite.High()
2021-11-03 09:33:09 +00:00
return errors.New("timed out writing byte -- read stuck high")
2021-10-30 11:03:18 +00:00
}
if time.Since(lastSleepTime) > edgeTimeout/10 {
sleepDuration *= 3;
time.Sleep(time.Millisecond * time.Duration(sleepDuration));
lastSleepTime = time.Now()
}
2021-10-30 11:03:18 +00:00
}
if ((data & 128) >> 7) == 1 {
2022-02-15 02:02:55 +00:00
outBit7.High()
} else {
outBit7.Low()
2021-10-30 11:03:18 +00:00
}
if ((data & 64) >> 6) == 1 {
2022-02-15 02:02:55 +00:00
outBit6.High()
} else {
outBit6.Low()
2021-10-30 11:03:18 +00:00
}
if ((data & 32) >> 5) == 1 {
2022-02-15 02:02:55 +00:00
outBit5.High()
} else {
outBit5.Low()
2021-10-30 11:03:18 +00:00
}
if ((data & 16) >> 4) == 1 {
2022-02-15 02:02:55 +00:00
outBit4.High()
} else {
outBit4.Low()
2021-10-30 11:03:18 +00:00
}
if ((data & 8) >> 3) == 1 {
2022-02-15 02:02:55 +00:00
outBit3.High()
} else {
outBit3.Low()
2021-10-30 11:03:18 +00:00
}
if ((data & 4) >> 2) == 1 {
2022-02-15 02:02:55 +00:00
outBit2.High()
} else {
outBit2.Low()
2021-10-30 11:03:18 +00:00
}
if ((data & 2) >> 1) == 1 {
2022-02-15 02:02:55 +00:00
outBit1.High()
} else {
outBit1.Low()
2021-10-30 11:03:18 +00:00
}
if (data & 1) == 1 {
2022-02-15 02:02:55 +00:00
outBit0.High()
} else {
outBit0.Low()
2021-10-30 11:03:18 +00:00
}
// let Apple II know we're writing
2022-02-15 02:02:55 +00:00
outWrite.Low()
2021-10-30 11:03:18 +00:00
// wait for the Apple II to finsih reading
//fmt.Printf("wait for the Apple II to finsih reading\n")
startTime = time.Now()
lastSleepTime = time.Now()
sleepDuration = 10
2022-02-15 02:02:55 +00:00
for inRead.Read() == 0 {
if time.Since(startTime) > edgeTimeout {
2022-02-15 02:02:55 +00:00
outWrite.High()
2021-11-03 09:33:09 +00:00
return errors.New("timed out writing byte -- read stuck low")
2021-10-30 11:03:18 +00:00
}
if time.Since(lastSleepTime) > edgeTimeout/10 {
sleepDuration *= 3;
time.Sleep(time.Millisecond * time.Duration(sleepDuration));
lastSleepTime = time.Now()
}
2021-10-30 11:03:18 +00:00
}
// let the Apple II know we are done writing
2022-02-15 02:02:55 +00:00
outWrite.High()
2021-10-30 11:03:18 +00:00
return nil
}
2021-11-03 09:33:09 +00:00
// WriteBlock writes 512 bytes to the Apple II via Raspberry Pi's GPIO ports
2021-10-30 11:03:18 +00:00
func (a2 A2Gpio) WriteBlock(buffer []byte) error {
for i := 0; i < 512; i++ {
err := a2.WriteByte(buffer[i])
if err != nil {
return err
}
}
return nil
}
2021-11-03 09:33:09 +00:00
// ReadBlock reads 512 bytes from the Apple II via Raspberry Pi's GPIO ports
2021-10-30 11:03:18 +00:00
func (a2 A2Gpio) ReadBlock(buffer []byte) error {
var err error
for i := 0; i < 512; i++ {
buffer[i], err = a2.ReadByte()
if err != nil {
return err
}
}
return nil
}
2021-11-03 09:33:09 +00:00
// WriteBuffer writes a buffer of bytes to the Apple II via Raspberry Pi's GPIO ports
2021-10-30 11:03:18 +00:00
func (a2 A2Gpio) WriteBuffer(buffer []byte) error {
bufferSize := len(buffer)
for i := 0; i < bufferSize; i++ {
err := a2.WriteByte(buffer[i])
if err != nil {
return err
}
}
return nil
}
// SendCharacter is a pass-through to vt100 implementation
func (a2 A2Gpio) SendCharacter(character byte) {
sendCharacter(a2, character)
}
// ReadCharacter is a pass-through to vt100 implementation
func (a2 A2Gpio) ReadCharacter() (string, error) {
return readCharacter(a2)
}