mirror of https://github.com/ariejan/i6502.git
209 lines
3.6 KiB
Go
209 lines
3.6 KiB
Go
package devices
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
aciaData = iota
|
|
aciaStatus
|
|
aciaCommand
|
|
aciaControl
|
|
)
|
|
|
|
var baudRateSelectors = [...]int{0, 50, 75, 110, 135, 150, 300, 600, 1200, 1800, 2400, 3600, 4800, 7200, 9600, 19200}
|
|
|
|
type Acia6551 struct {
|
|
// Registers
|
|
rx byte
|
|
tx byte
|
|
commandRegister byte
|
|
controlRegister byte
|
|
|
|
// Other required bits and pieces
|
|
lastTxWrite int64
|
|
lastRxRead int64
|
|
overrun bool
|
|
baudRate int
|
|
baudRateDelay int64
|
|
rxFull bool
|
|
txEmpty bool
|
|
|
|
rxInterruptEnabled bool
|
|
txInterruptEnabled bool
|
|
|
|
InterruptChan chan bool
|
|
|
|
TxChan chan byte
|
|
}
|
|
|
|
func NewAcia6551(interruptChan chan bool) *Acia6551 {
|
|
acia := &Acia6551{InterruptChan: interruptChan}
|
|
acia.Reset()
|
|
|
|
return acia
|
|
}
|
|
|
|
func (a *Acia6551) Reset() {
|
|
a.TxChan = make(chan byte, 4096)
|
|
|
|
a.tx = 0
|
|
a.txEmpty = true
|
|
|
|
a.rx = 0
|
|
a.rxFull = false
|
|
|
|
a.lastTxWrite = 0
|
|
a.lastRxRead = 0
|
|
a.overrun = false
|
|
|
|
a.rxInterruptEnabled = false
|
|
a.txInterruptEnabled = false
|
|
}
|
|
|
|
func (a *Acia6551) Size() int {
|
|
return 4
|
|
}
|
|
|
|
func (a *Acia6551) Read(address uint16) byte {
|
|
switch address {
|
|
case aciaData:
|
|
return a.rxRead()
|
|
case aciaStatus:
|
|
return a.statusRegister()
|
|
case aciaCommand:
|
|
return a.commandRegister
|
|
case aciaControl:
|
|
return a.controlRegister
|
|
default:
|
|
panic(fmt.Errorf("ACIA 6551 cannot handle addressing 0x%04X", address))
|
|
}
|
|
}
|
|
|
|
func (a *Acia6551) rxRead() byte {
|
|
a.lastRxRead = unixTime()
|
|
a.overrun = false
|
|
a.rxFull = false
|
|
return a.rx
|
|
}
|
|
|
|
func (a *Acia6551) RxWrite(data byte) {
|
|
// Oh noes!
|
|
if a.rxFull {
|
|
a.overrun = true
|
|
}
|
|
|
|
a.rx = data
|
|
a.rxFull = true
|
|
|
|
if a.rxInterruptEnabled {
|
|
a.InterruptChan <- true
|
|
}
|
|
}
|
|
|
|
func (a *Acia6551) statusRegister() byte {
|
|
now := unixTime()
|
|
status := byte(0)
|
|
|
|
if a.rxFull && (now >= (a.lastRxRead + a.baudRateDelay)) {
|
|
status |= 0x08
|
|
}
|
|
|
|
if a.txEmpty && (now >= (a.lastTxWrite + a.baudRateDelay)) {
|
|
status |= 0x10
|
|
}
|
|
|
|
if a.overrun {
|
|
status |= 0x04
|
|
}
|
|
|
|
return status
|
|
}
|
|
|
|
func (a *Acia6551) Write(address uint16, value byte) {
|
|
switch address {
|
|
case aciaData:
|
|
a.txWrite(value)
|
|
case aciaStatus:
|
|
a.Reset()
|
|
case aciaCommand:
|
|
a.setCommandRegister(value)
|
|
case aciaControl:
|
|
a.setControlRegister(value)
|
|
default:
|
|
panic(fmt.Errorf("ACIA 6551 cannot handle addressing 0x%04X", address))
|
|
}
|
|
}
|
|
|
|
func (a *Acia6551) txWrite(value byte) {
|
|
a.lastTxWrite = unixTime()
|
|
a.tx = value
|
|
a.txEmpty = false
|
|
|
|
// Post for others
|
|
a.debugTxOutput()
|
|
}
|
|
|
|
func (a *Acia6551) TxRead() byte {
|
|
a.txEmpty = true
|
|
|
|
if a.txInterruptEnabled {
|
|
a.InterruptChan <- true
|
|
}
|
|
|
|
return a.tx
|
|
}
|
|
|
|
func (a *Acia6551) HasTx() bool {
|
|
return !a.txEmpty
|
|
}
|
|
|
|
func (a *Acia6551) HasRx() bool {
|
|
return a.rxFull
|
|
}
|
|
|
|
func (a *Acia6551) debugTxOutput() {
|
|
if a.HasTx() {
|
|
a.TxChan <- a.TxRead()
|
|
}
|
|
}
|
|
|
|
func (a *Acia6551) setCommandRegister(data byte) {
|
|
fmt.Printf("Setting Acia6551 Command Register: %02X\n", data)
|
|
|
|
a.commandRegister = data
|
|
|
|
a.rxInterruptEnabled = ((data >> 1) & 1) == 0
|
|
a.txInterruptEnabled = ((data>>2)&1) == 1 && ((data>>3)&1) == 0
|
|
|
|
fmt.Printf("RxIRQ: %t; TxIRQ: %t\n", a.rxInterruptEnabled, a.txInterruptEnabled)
|
|
}
|
|
|
|
func (a *Acia6551) setControlRegister(data byte) {
|
|
a.controlRegister = data
|
|
|
|
if data == 0x00 {
|
|
a.Reset()
|
|
} else {
|
|
a.setBaudRate(baudRateSelectors[data&0x0f])
|
|
}
|
|
}
|
|
|
|
func (a *Acia6551) setBaudRate(baudRate int) {
|
|
fmt.Printf("Setting baudrate at %d\n", baudRate)
|
|
|
|
a.baudRate = baudRate
|
|
|
|
// Set baudRateDelay in nanoseconds. It's an approximation.
|
|
if baudRate > 0 {
|
|
a.baudRateDelay = int64((1.0 / float64(baudRate)) * 1000000000 * 8)
|
|
} else {
|
|
a.baudRateDelay = 0
|
|
}
|
|
}
|
|
|
|
func unixTime() int64 {
|
|
return time.Now().UnixNano()
|
|
}
|