mirror of
https://github.com/tjboldt/Apple2-IO-RPi.git
synced 2024-11-22 02:30:46 +00:00
385 lines
7.8 KiB
Go
385 lines
7.8 KiB
Go
// Copyright Terence J. Boldt (c)2020-2024
|
|
// 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
|
|
|
|
package a2io
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/stianeikeland/go-rpio/v4"
|
|
)
|
|
|
|
var edgeTimeout time.Duration
|
|
|
|
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
|
|
|
|
// A2Gpio is the live implementation of A2Io interface
|
|
type A2Gpio struct {
|
|
}
|
|
|
|
// Init initializes the GPIO ports on the Raspberry Pi
|
|
func (a2 A2Gpio) Init() {
|
|
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()
|
|
|
|
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()
|
|
|
|
outReserved1.High()
|
|
outReserved2.High()
|
|
outRead.High()
|
|
outWrite.High()
|
|
outBit7.Low()
|
|
outBit6.Low()
|
|
outBit5.Low()
|
|
outBit4.Low()
|
|
outBit3.Low()
|
|
outBit2.Low()
|
|
outBit1.Low()
|
|
outBit0.Low()
|
|
|
|
edgeTimeout = time.Second
|
|
}
|
|
|
|
// ReadString reads a string from the Apple II via Raspberry Pi's GPIO ports
|
|
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)
|
|
}
|
|
return inBytes.String(), nil
|
|
}
|
|
|
|
// WriteString writes a string to the Apple II via Raspberry Pi's GPIO ports
|
|
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
|
|
}
|
|
|
|
// ReadByte reads a byte from the Apple II via Raspberry Pi's GPIO ports
|
|
func (a2 A2Gpio) ReadByte(noDelay ...bool) (byte, error) {
|
|
// let the Apple II know we are ready to read
|
|
outRead.Low()
|
|
|
|
// wait for the Apple II to write
|
|
startTime := time.Now()
|
|
lastSleepTime := time.Now()
|
|
sleepDuration := 10
|
|
for inWrite.Read() == 1 {
|
|
if time.Since(startTime) > edgeTimeout {
|
|
outRead.High()
|
|
return 0, errors.New("timed out reading byte -- write stuck high")
|
|
}
|
|
if time.Since(lastSleepTime) > edgeTimeout/10 {
|
|
if len(noDelay) == 0 || !noDelay[0] {
|
|
sleepDuration *= 3;
|
|
}
|
|
time.Sleep(time.Millisecond * time.Duration(sleepDuration));
|
|
lastSleepTime = time.Now()
|
|
}
|
|
}
|
|
|
|
// get a nibble of data
|
|
var data byte
|
|
data = 0
|
|
bit7 := inBit7.Read()
|
|
bit6 := inBit6.Read()
|
|
bit5 := inBit5.Read()
|
|
bit4 := inBit4.Read()
|
|
bit3 := inBit3.Read()
|
|
bit2 := inBit2.Read()
|
|
bit1 := inBit1.Read()
|
|
bit0 := inBit0.Read()
|
|
|
|
if bit7 == 1 {
|
|
data += 128
|
|
}
|
|
if bit6 == 1 {
|
|
data += 64
|
|
}
|
|
if bit5 == 1 {
|
|
data += 32
|
|
}
|
|
if bit4 == 1 {
|
|
data += 16
|
|
}
|
|
if bit3 == 1 {
|
|
data += 8
|
|
}
|
|
if bit2 == 1 {
|
|
data += 4
|
|
}
|
|
if bit1 == 1 {
|
|
data += 2
|
|
}
|
|
if bit0 == 1 {
|
|
data++
|
|
}
|
|
|
|
// let the Apple II know we are done reading
|
|
//fmt.Printf("let the Apple II know we are done reading\n")
|
|
outRead.High()
|
|
|
|
// 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
|
|
for inWrite.Read() == 0 {
|
|
if time.Since(startTime) > edgeTimeout {
|
|
return 0, errors.New("timed out reading byte -- write stuck low")
|
|
}
|
|
if time.Since(lastSleepTime) > edgeTimeout/10 {
|
|
if len(noDelay) == 0 || !noDelay[0] {
|
|
sleepDuration *= 3;
|
|
}
|
|
time.Sleep(time.Millisecond * time.Duration(sleepDuration));
|
|
lastSleepTime = time.Now()
|
|
}
|
|
}
|
|
|
|
return data, nil
|
|
}
|
|
|
|
// WriteByte writes a byte to the Apple II via Raspberry Pi's GPIO ports
|
|
func (a2 A2Gpio) WriteByte(data byte) error {
|
|
// check if the Apple II wants to send a byte to us first
|
|
if inWrite.Read() == 0 {
|
|
outWrite.High()
|
|
return errors.New("can't write byte while byte is incoming")
|
|
}
|
|
|
|
// wait for the Apple II to be ready to read
|
|
startTime := time.Now()
|
|
lastSleepTime := time.Now()
|
|
sleepDuration := 10
|
|
for inRead.Read() == 1 {
|
|
if time.Since(startTime) > edgeTimeout {
|
|
outWrite.High()
|
|
return errors.New("timed out writing byte -- read stuck high")
|
|
}
|
|
if time.Since(lastSleepTime) > edgeTimeout/10 {
|
|
sleepDuration *= 3;
|
|
time.Sleep(time.Millisecond * time.Duration(sleepDuration));
|
|
lastSleepTime = time.Now()
|
|
}
|
|
}
|
|
|
|
if ((data & 128) >> 7) == 1 {
|
|
outBit7.High()
|
|
} else {
|
|
outBit7.Low()
|
|
}
|
|
|
|
if ((data & 64) >> 6) == 1 {
|
|
outBit6.High()
|
|
} else {
|
|
outBit6.Low()
|
|
}
|
|
|
|
if ((data & 32) >> 5) == 1 {
|
|
outBit5.High()
|
|
} else {
|
|
outBit5.Low()
|
|
}
|
|
|
|
if ((data & 16) >> 4) == 1 {
|
|
outBit4.High()
|
|
} else {
|
|
outBit4.Low()
|
|
}
|
|
|
|
if ((data & 8) >> 3) == 1 {
|
|
outBit3.High()
|
|
} else {
|
|
outBit3.Low()
|
|
}
|
|
|
|
if ((data & 4) >> 2) == 1 {
|
|
outBit2.High()
|
|
} else {
|
|
outBit2.Low()
|
|
}
|
|
|
|
if ((data & 2) >> 1) == 1 {
|
|
outBit1.High()
|
|
} else {
|
|
outBit1.Low()
|
|
}
|
|
|
|
if (data & 1) == 1 {
|
|
outBit0.High()
|
|
} else {
|
|
outBit0.Low()
|
|
}
|
|
|
|
// let Apple II know we're writing
|
|
outWrite.Low()
|
|
|
|
// 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
|
|
for inRead.Read() == 0 {
|
|
if time.Since(startTime) > edgeTimeout {
|
|
outWrite.High()
|
|
return errors.New("timed out writing byte -- read stuck low")
|
|
}
|
|
if time.Since(lastSleepTime) > edgeTimeout/10 {
|
|
sleepDuration *= 3;
|
|
time.Sleep(time.Millisecond * time.Duration(sleepDuration));
|
|
lastSleepTime = time.Now()
|
|
}
|
|
}
|
|
|
|
// let the Apple II know we are done writing
|
|
outWrite.High()
|
|
return nil
|
|
}
|
|
|
|
// WriteBlock writes 512 bytes to the Apple II via Raspberry Pi's GPIO ports
|
|
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
|
|
}
|
|
|
|
// ReadBlock reads 512 bytes from the Apple II via Raspberry Pi's GPIO ports
|
|
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
|
|
}
|
|
|
|
// WriteBuffer writes a buffer of bytes to the Apple II via Raspberry Pi's GPIO ports
|
|
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)
|
|
}
|