mirror of
https://github.com/ariejan/i6502.git
synced 2024-11-18 17:13:01 +00:00
177 lines
2.8 KiB
Go
177 lines
2.8 KiB
Go
package i6502
|
|
|
|
const (
|
|
aciaData = iota
|
|
aciaStatus
|
|
aciaCommand
|
|
aciaControl
|
|
)
|
|
|
|
/*
|
|
ACIA 6551 Serial IO
|
|
|
|
This Asynchronous Communications Interface Adapater can be
|
|
directly attached to the 6502's address and data busses.
|
|
|
|
It provides serial IO.
|
|
|
|
The supplied Rx and Tx channels can be used to read and wirte
|
|
data to the ACIA 6551.
|
|
*/
|
|
type Acia6551 struct {
|
|
rx byte
|
|
tx byte
|
|
|
|
commandData byte
|
|
controlData byte
|
|
|
|
rxFull bool
|
|
txEmpty bool
|
|
|
|
rxIrqEnabled bool
|
|
txIrqEnabled bool
|
|
|
|
overrun bool
|
|
|
|
output chan []byte
|
|
}
|
|
|
|
func NewAcia6551(output chan []byte) (*Acia6551, error) {
|
|
acia := &Acia6551{output: output}
|
|
acia.Reset()
|
|
|
|
return acia, nil
|
|
}
|
|
|
|
func (a *Acia6551) Size() uint16 {
|
|
// We have a only 4 addresses, Data, Status, Command and Control
|
|
return 0x04
|
|
}
|
|
|
|
// Emulates a hardware reset
|
|
func (a *Acia6551) Reset() {
|
|
a.rx = 0
|
|
a.rxFull = false
|
|
|
|
a.tx = 0
|
|
a.txEmpty = true
|
|
|
|
a.rxIrqEnabled = false
|
|
a.txIrqEnabled = false
|
|
|
|
a.overrun = false
|
|
|
|
a.setControl(0)
|
|
a.setCommand(0)
|
|
}
|
|
|
|
func (a *Acia6551) setControl(data byte) {
|
|
a.controlData = data
|
|
}
|
|
|
|
func (a *Acia6551) setCommand(data byte) {
|
|
a.commandData = data
|
|
|
|
a.rxIrqEnabled = (data & 0x02) != 0
|
|
a.txIrqEnabled = ((data & 0x04) != 0) && ((data & 0x08) != 1)
|
|
}
|
|
|
|
func (a *Acia6551) statusRegister() byte {
|
|
status := byte(0)
|
|
|
|
if a.rxFull {
|
|
status |= 0x08
|
|
}
|
|
|
|
if a.txEmpty {
|
|
status |= 0x10
|
|
}
|
|
|
|
if a.overrun {
|
|
status |= 0x04
|
|
}
|
|
|
|
return status
|
|
}
|
|
|
|
// Implements io.Reader, for external programs to read TX'ed data from
|
|
// the serial output.
|
|
func (a *Acia6551) Read(p []byte) (n int, err error) {
|
|
if len(p) == 0 {
|
|
return 0, nil
|
|
}
|
|
|
|
if a.txEmpty {
|
|
return 0, nil
|
|
} else {
|
|
a.txEmpty = true
|
|
copy(p, []byte{a.tx})
|
|
// TODO: Handle txInterrupt
|
|
return 1, nil
|
|
}
|
|
}
|
|
|
|
// Implements io.Writer, for external programs to write to the
|
|
// ACIA's RX
|
|
func (a *Acia6551) Write(p []byte) (n int, err error) {
|
|
for _, b := range p {
|
|
a.rxWrite(b)
|
|
}
|
|
|
|
return len(p), nil
|
|
}
|
|
|
|
// Used by the AddressBus to read data from the ACIA 6551
|
|
func (a *Acia6551) ReadByte(address uint16) byte {
|
|
switch address {
|
|
case aciaData:
|
|
return a.rxRead()
|
|
case aciaStatus:
|
|
return a.statusRegister()
|
|
case aciaCommand:
|
|
return a.commandData
|
|
case aciaControl:
|
|
return a.controlData
|
|
}
|
|
|
|
return 0x00
|
|
}
|
|
|
|
// Used by the AddressBus to write data to the ACIA 6551
|
|
func (a *Acia6551) WriteByte(address uint16, data byte) {
|
|
switch address {
|
|
case aciaData:
|
|
a.txWrite(data)
|
|
case aciaStatus:
|
|
a.Reset()
|
|
case aciaCommand:
|
|
a.setCommand(data)
|
|
case aciaControl:
|
|
a.setControl(data)
|
|
}
|
|
}
|
|
|
|
func (a *Acia6551) rxRead() byte {
|
|
a.overrun = false
|
|
a.rxFull = false
|
|
return a.rx
|
|
}
|
|
|
|
func (a *Acia6551) rxWrite(data byte) {
|
|
// Oh no, overrun. Set the appropriate status
|
|
if a.rxFull {
|
|
a.overrun = true
|
|
}
|
|
|
|
a.rx = data
|
|
a.rxFull = true
|
|
|
|
// TODO: Interrupts
|
|
}
|
|
|
|
func (a *Acia6551) txWrite(data byte) {
|
|
a.output <- []byte{data}
|
|
a.tx = data
|
|
// a.txEmpty = false
|
|
}
|