mirror of
https://github.com/tjboldt/Apple2-IO-RPi.git
synced 2024-06-11 08: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
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"go.bug.st/serial"
|
||||||
|
|
||||||
"github.com/tjboldt/Apple2-IO-RPi/RaspberryPi/apple2driver/a2io"
|
"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/handlers"
|
||||||
"github.com/tjboldt/Apple2-IO-RPi/RaspberryPi/apple2driver/info"
|
"github.com/tjboldt/Apple2-IO-RPi/RaspberryPi/apple2driver/info"
|
||||||
|
@ -32,11 +35,16 @@ const menuCommand = 8
|
||||||
const shellCommand = 9
|
const shellCommand = 9
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
drive1, drive2 := getDriveFiles()
|
drive1, drive2, cdc := getFlags()
|
||||||
|
|
||||||
fmt.Printf("Starting Apple II RPi v%s...\n", info.Version)
|
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)
|
handlers.SetCommunication(comm)
|
||||||
comm.Init()
|
comm.Init()
|
||||||
|
@ -68,6 +76,13 @@ func main() {
|
||||||
case shellCommand:
|
case shellCommand:
|
||||||
handlers.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
|
// temporary workaround for busy wait loop heating up the RPi
|
||||||
} else if time.Since(lastCommandTime) > time.Millisecond*100 {
|
} else if time.Since(lastCommandTime) > time.Millisecond*100 {
|
||||||
time.Sleep(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 drive1Name string
|
||||||
var drive2Name string
|
var drive2Name string
|
||||||
|
var cdc bool
|
||||||
|
|
||||||
execName, _ := os.Executable()
|
execName, _ := os.Executable()
|
||||||
path := filepath.Dir(execName)
|
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(&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.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()
|
flag.Parse()
|
||||||
|
|
||||||
var drive1 *os.File
|
var drive1 *os.File
|
||||||
|
@ -125,5 +142,5 @@ func getDriveFiles() (*os.File, *os.File) {
|
||||||
os.Exit(1)
|
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
|
go 1.16
|
||||||
|
|
||||||
require (
|
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/stianeikeland/go-rpio/v4 v4.6.0
|
||||||
github.com/tjboldt/ProDOS-Utilities v0.3.0
|
github.com/tjboldt/ProDOS-Utilities v0.3.0
|
||||||
|
go.bug.st/serial v1.3.5
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user