mirror of
https://github.com/tjboldt/Apple2-IO-RPi.git
synced 2024-06-05 00:29:31 +00:00
Added another comminucation interface implementation.
Use ACM CDC (or another proprietary) USB serial device for communication. Currently VID / PID of a Raspberry Pi Pico SDK CDC UART are hardcoded - see https://github.com/raspberrypi/usb-pid
This commit is contained in:
parent
0ef9d567aa
commit
4c2e938474
147
RaspberryPi/apple2driver/a2io/cdcio.go
Normal file
147
RaspberryPi/apple2driver/a2io/cdcio.go
Normal file
|
@ -0,0 +1,147 @@
|
|||
// Copyright Terence J. Boldt (c)2020-2022
|
||||
// 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"
|
||||
|
||||
"go.bug.st/serial"
|
||||
"go.bug.st/serial/enumerator"
|
||||
)
|
||||
|
||||
var port serial.Port
|
||||
|
||||
// CDCio is a live implementation of A2Io interface
|
||||
type CDCio struct {
|
||||
}
|
||||
|
||||
// Init initializes the CDC driver on the Raspberry Pi
|
||||
func (a2 CDCio) Init() {
|
||||
name := ""
|
||||
for {
|
||||
portInfos, err := enumerator.GetDetailedPortsList()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for _, portInfo := range portInfos {
|
||||
if portInfo.IsUSB && portInfo.VID == "2E8A" && portInfo.PID == "000A" {
|
||||
name = portInfo.Name
|
||||
fmt.Printf("Found CDC port %s\n", name)
|
||||
break
|
||||
}
|
||||
}
|
||||
if name != "" {
|
||||
break
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
|
||||
for {
|
||||
var err error
|
||||
port, err = serial.Open(name, &serial.Mode{})
|
||||
if err == nil {
|
||||
break;
|
||||
}
|
||||
var portErr *serial.PortError
|
||||
if !errors.As(err, &portErr) || portErr.Code() != serial.PortNotFound {
|
||||
panic(err)
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
|
||||
err := port.SetReadTimeout(time.Second)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
// ReadByte reads a byte from the Apple II via Raspberry Pi's CDC driver
|
||||
func (a2 CDCio) ReadByte() (byte, error) {
|
||||
var data [1]byte
|
||||
n, err := port.Read(data[:]);
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if n == 0 {
|
||||
return 0, errors.New("timed out reading byte")
|
||||
}
|
||||
return data[0], nil
|
||||
}
|
||||
|
||||
// WriteByte writes a byte to the Apple II via Raspberry Pi's CDC driver
|
||||
func (a2 CDCio) WriteByte(data byte) error {
|
||||
_, err := port.Write([]byte{data});
|
||||
return err
|
||||
}
|
||||
|
||||
// ReadString reads a string from the Apple II via Raspberry Pi's CDC driver
|
||||
func (a2 CDCio) 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 CDC driver
|
||||
func (a2 CDCio) 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
|
||||
}
|
||||
|
||||
// WriteBlock writes 512 bytes to the Apple II via Raspberry Pi's CDC driver
|
||||
func (a2 CDCio) WriteBlock(buffer []byte) error {
|
||||
_, err := port.Write(buffer);
|
||||
return err
|
||||
}
|
||||
|
||||
// ReadBlock reads 512 bytes from the Apple II via Raspberry Pi's CDC driver
|
||||
func (a2 CDCio) 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 CDC driver
|
||||
func (a2 CDCio) WriteBuffer(buffer []byte) error {
|
||||
_, err := port.Write(buffer);
|
||||
return err
|
||||
}
|
||||
|
||||
// SendCharacter is a pass-through to vt100 implementation
|
||||
func (a2 CDCio) SendCharacter(character byte) {
|
||||
sendCharacter(a2, character)
|
||||
}
|
||||
|
||||
// ReadCharacter is a pass-through to vt100 implementation
|
||||
func (a2 CDCio) ReadCharacter() (string, error) {
|
||||
return readCharacter(a2)
|
||||
}
|
|
@ -9,12 +9,15 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"go.bug.st/serial"
|
||||
|
||||
"github.com/tjboldt/Apple2-IO-RPi/RaspberryPi/apple2driver/a2io"
|
||||
"github.com/tjboldt/Apple2-IO-RPi/RaspberryPi/apple2driver/handlers"
|
||||
"github.com/tjboldt/Apple2-IO-RPi/RaspberryPi/apple2driver/info"
|
||||
|
@ -32,11 +35,16 @@ const menuCommand = 8
|
|||
const shellCommand = 9
|
||||
|
||||
func main() {
|
||||
drive1, drive2 := getDriveFiles()
|
||||
drive1, drive2, cdc := getFlags()
|
||||
|
||||
fmt.Printf("Starting Apple II RPi v%s...\n", info.Version)
|
||||
|
||||
comm := a2io.A2Gpio{}
|
||||
var comm a2io.A2Io
|
||||
if cdc {
|
||||
comm = a2io.CDCio{}
|
||||
} else {
|
||||
comm = a2io.A2Gpio{}
|
||||
}
|
||||
|
||||
handlers.SetCommunication(comm)
|
||||
comm.Init()
|
||||
|
@ -68,6 +76,13 @@ func main() {
|
|||
case shellCommand:
|
||||
handlers.ShellCommand()
|
||||
}
|
||||
// the A2Io interface should be extended in one way or another
|
||||
// to encapsulate this, e.g. by a ReadByte variant / parameter
|
||||
} else if cdc {
|
||||
var portErr *serial.PortError
|
||||
if errors.As(err, &portErr) && portErr.Code() == serial.PortClosed {
|
||||
comm.Init()
|
||||
}
|
||||
// temporary workaround for busy wait loop heating up the RPi
|
||||
} else if time.Since(lastCommandTime) > time.Millisecond*100 {
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
|
@ -75,9 +90,10 @@ func main() {
|
|||
}
|
||||
}
|
||||
|
||||
func getDriveFiles() (*os.File, *os.File) {
|
||||
func getFlags() (*os.File, *os.File, bool) {
|
||||
var drive1Name string
|
||||
var drive2Name string
|
||||
var cdc bool
|
||||
|
||||
execName, _ := os.Executable()
|
||||
path := filepath.Dir(execName)
|
||||
|
@ -87,6 +103,7 @@ func getDriveFiles() (*os.File, *os.File) {
|
|||
|
||||
flag.StringVar(&drive1Name, "d1", "", "A ProDOS format drive image for drive 1")
|
||||
flag.StringVar(&drive2Name, "d2", defaultFileName, "A ProDOS format drive image for drive 2 and will be used for drive 1 if drive 1 empty")
|
||||
flag.BoolVar(&cdc, "cdc", false, "Use ACM CDC serial device")
|
||||
flag.Parse()
|
||||
|
||||
var drive1 *os.File
|
||||
|
@ -125,5 +142,5 @@ func getDriveFiles() (*os.File, *os.File) {
|
|||
os.Exit(1)
|
||||
}
|
||||
|
||||
return drive1, drive2
|
||||
return drive1, drive2, cdc
|
||||
}
|
||||
|
|
|
@ -3,7 +3,8 @@ module github.com/tjboldt/Apple2-IO-RPi/RaspberryPi/apple2driver
|
|||
go 1.16
|
||||
|
||||
require (
|
||||
github.com/creack/pty v1.1.17
|
||||
github.com/creack/pty v1.1.18
|
||||
github.com/stianeikeland/go-rpio/v4 v4.6.0
|
||||
github.com/tjboldt/ProDOS-Utilities v0.3.0
|
||||
go.bug.st/serial v1.3.5
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue
Block a user