mirror of
https://github.com/ariejan/i6502.git
synced 2025-01-15 16:29:45 +00:00
Implement acia 6551
This commit is contained in:
parent
432119f507
commit
099d6c11bd
118
acia6551.go
118
acia6551.go
@ -1,7 +1,5 @@
|
|||||||
package i6502
|
package i6502
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
aciaData = iota
|
aciaData = iota
|
||||||
aciaStatus
|
aciaStatus
|
||||||
@ -21,11 +19,8 @@ The supplied Rx and Tx channels can be used to read and wirte
|
|||||||
data to the ACIA 6551.
|
data to the ACIA 6551.
|
||||||
*/
|
*/
|
||||||
type Acia6551 struct {
|
type Acia6551 struct {
|
||||||
Rx chan byte // Reading (Acia Input) line
|
rx byte
|
||||||
Tx chan byte // Transmitting (Acia Output) line
|
tx byte
|
||||||
|
|
||||||
rxData byte
|
|
||||||
txData byte
|
|
||||||
|
|
||||||
commandData byte
|
commandData byte
|
||||||
controlData byte
|
controlData byte
|
||||||
@ -39,25 +34,10 @@ type Acia6551 struct {
|
|||||||
overrun bool
|
overrun bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAcia6551(rx chan byte, tx chan byte) (*Acia6551, error) {
|
func NewAcia6551() (*Acia6551, error) {
|
||||||
acia := &Acia6551{Tx: tx, Rx: rx}
|
acia := &Acia6551{}
|
||||||
acia.Reset()
|
acia.Reset()
|
||||||
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case data := <-acia.Rx:
|
|
||||||
acia.rxData = data
|
|
||||||
acia.rxFull = true
|
|
||||||
fmt.Printf("Rx: 0x%02X\n", data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
// Handle tx data channel
|
|
||||||
}()
|
|
||||||
|
|
||||||
return acia, nil
|
return acia, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,10 +48,10 @@ func (a *Acia6551) Size() uint16 {
|
|||||||
|
|
||||||
// Emulates a hardware reset
|
// Emulates a hardware reset
|
||||||
func (a *Acia6551) Reset() {
|
func (a *Acia6551) Reset() {
|
||||||
a.rxData = 0
|
a.rx = 0
|
||||||
a.rxFull = false
|
a.rxFull = false
|
||||||
|
|
||||||
a.txData = 0
|
a.tx = 0
|
||||||
a.txEmpty = true
|
a.txEmpty = true
|
||||||
|
|
||||||
a.rxIrqEnabled = false
|
a.rxIrqEnabled = false
|
||||||
@ -84,36 +64,102 @@ func (a *Acia6551) Reset() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *Acia6551) setControl(data byte) {
|
func (a *Acia6551) setControl(data byte) {
|
||||||
|
a.controlData = data
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Acia6551) setCommand(data byte) {
|
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) {
|
||||||
|
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
|
// Used by the AddressBus to read data from the ACIA 6551
|
||||||
func (a *Acia6551) Read(address uint16) byte {
|
func (a *Acia6551) ReadByte(address uint16) byte {
|
||||||
switch address {
|
switch address {
|
||||||
case aciaData:
|
case aciaData:
|
||||||
// Read Rx
|
return a.rxRead()
|
||||||
case aciaStatus:
|
case aciaStatus:
|
||||||
// Read Status reg.
|
return a.statusRegister()
|
||||||
case aciaCommand:
|
case aciaCommand:
|
||||||
// Read command
|
return a.commandData
|
||||||
case aciaControl:
|
case aciaControl:
|
||||||
// Read control
|
return a.controlData
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0x00
|
return 0x00
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Acia6551) Write(address uint16, data byte) {
|
// Used by the AddressBus to write data to the ACIA 6551
|
||||||
|
func (a *Acia6551) WriteByte(address uint16, data byte) {
|
||||||
switch address {
|
switch address {
|
||||||
case aciaData:
|
case aciaData:
|
||||||
// Write Tx
|
a.txWrite(data)
|
||||||
case aciaStatus:
|
case aciaStatus:
|
||||||
// Reset
|
a.Reset()
|
||||||
case aciaCommand:
|
case aciaCommand:
|
||||||
// Write command
|
a.setCommand(data)
|
||||||
case aciaControl:
|
case aciaControl:
|
||||||
// Write control
|
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.tx = data
|
||||||
|
a.txEmpty = false
|
||||||
|
}
|
||||||
|
110
acia6551_test.go
110
acia6551_test.go
@ -1,37 +1,32 @@
|
|||||||
package i6502
|
package i6502
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func AciaSubject() (*Acia6551, chan byte, chan byte) {
|
func AciaSubject() *Acia6551 {
|
||||||
tx := make(chan byte)
|
acia, _ := NewAcia6551()
|
||||||
rx := make(chan byte)
|
return acia
|
||||||
acia, _ := NewAcia6551(rx, tx)
|
|
||||||
|
|
||||||
return acia, rx, tx
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewAcia6551(t *testing.T) {
|
func TestNewAcia6551(t *testing.T) {
|
||||||
tx := make(chan byte)
|
acia, err := NewAcia6551()
|
||||||
rx := make(chan byte)
|
|
||||||
acia, err := NewAcia6551(rx, tx)
|
|
||||||
|
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 0x4, acia.Size())
|
assert.Equal(t, 0x4, acia.Size())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAciaReset(t *testing.T) {
|
func TestAciaReset(t *testing.T) {
|
||||||
a, _, _ := AciaSubject()
|
a := AciaSubject()
|
||||||
|
|
||||||
a.Reset()
|
a.Reset()
|
||||||
|
|
||||||
assert.Equal(t, a.txData, 0)
|
assert.Equal(t, a.tx, 0)
|
||||||
assert.True(t, a.txEmpty)
|
assert.True(t, a.txEmpty)
|
||||||
|
|
||||||
assert.Equal(t, a.rxData, 0)
|
assert.Equal(t, a.rx, 0)
|
||||||
assert.False(t, a.rxFull)
|
assert.False(t, a.rxFull)
|
||||||
|
|
||||||
assert.False(t, a.txIrqEnabled)
|
assert.False(t, a.txIrqEnabled)
|
||||||
@ -41,15 +36,86 @@ func TestAciaReset(t *testing.T) {
|
|||||||
assert.Equal(t, 0, a.controlData)
|
assert.Equal(t, 0, a.controlData)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAciaReadData(t *testing.T) {
|
func TestAciaWriteByteAndReader(t *testing.T) {
|
||||||
a, _, _ := AciaSubject()
|
a := AciaSubject()
|
||||||
|
|
||||||
a.Rx <- 0x42
|
// CPU writes data
|
||||||
|
a.WriteByte(aciaData, 0x42)
|
||||||
|
|
||||||
assert.True(t, a.rxFull)
|
// System reads from Tx
|
||||||
|
value := make([]byte, 1)
|
||||||
|
bytesRead, _ := a.Read(value)
|
||||||
|
|
||||||
fmt.Printf("Reading...\n")
|
if assert.Equal(t, 1, bytesRead) {
|
||||||
value := a.Read(aciaData)
|
assert.Equal(t, 0x42, value[0])
|
||||||
assert.Equal(t, 0x42, value)
|
}
|
||||||
assert.False(t, a.rxFull)
|
}
|
||||||
|
|
||||||
|
func TestAciaWriterAndReadByte(t *testing.T) {
|
||||||
|
a := AciaSubject()
|
||||||
|
|
||||||
|
// System writes a single byte
|
||||||
|
bytesWritten, _ := a.Write([]byte{0x42})
|
||||||
|
|
||||||
|
if assert.Equal(t, 1, bytesWritten) {
|
||||||
|
assert.Equal(t, 0x42, a.ReadByte(aciaData))
|
||||||
|
}
|
||||||
|
|
||||||
|
// System writes multiple bytes
|
||||||
|
bytesWritten, _ = a.Write([]byte{0x42, 0x32, 0xAB})
|
||||||
|
|
||||||
|
if assert.Equal(t, 3, bytesWritten) {
|
||||||
|
assert.Equal(t, 0xAB, a.ReadByte(aciaData))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAciaCommandRegister(t *testing.T) {
|
||||||
|
a := AciaSubject()
|
||||||
|
assert.False(t, a.rxIrqEnabled)
|
||||||
|
assert.False(t, a.txIrqEnabled)
|
||||||
|
|
||||||
|
a.WriteByte(aciaCommand, 0x02) // b0000 0010 RX Irq enabled
|
||||||
|
assert.True(t, a.rxIrqEnabled)
|
||||||
|
assert.False(t, a.txIrqEnabled)
|
||||||
|
|
||||||
|
a.WriteByte(aciaCommand, 0x04) // b0000 0100 TX Irq enabled
|
||||||
|
assert.False(t, a.rxIrqEnabled)
|
||||||
|
assert.True(t, a.txIrqEnabled)
|
||||||
|
|
||||||
|
a.WriteByte(aciaCommand, 0x06) // b0000 0110 RX + TX Irq enabled
|
||||||
|
assert.True(t, a.rxIrqEnabled)
|
||||||
|
assert.True(t, a.txIrqEnabled)
|
||||||
|
|
||||||
|
assert.Equal(t, 0x06, a.ReadByte(aciaCommand))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAciaControlRegister(t *testing.T) {
|
||||||
|
a := AciaSubject()
|
||||||
|
|
||||||
|
a.WriteByte(aciaControl, 0xB8)
|
||||||
|
assert.Equal(t, 0xB8, a.ReadByte(aciaControl))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAciaStatusRegister(t *testing.T) {
|
||||||
|
a := AciaSubject()
|
||||||
|
|
||||||
|
a.rxFull = false
|
||||||
|
a.txEmpty = false
|
||||||
|
a.overrun = false
|
||||||
|
assert.Equal(t, 0x00, a.ReadByte(aciaStatus))
|
||||||
|
|
||||||
|
a.rxFull = true
|
||||||
|
a.txEmpty = false
|
||||||
|
a.overrun = false
|
||||||
|
assert.Equal(t, 0x08, a.ReadByte(aciaStatus))
|
||||||
|
|
||||||
|
a.rxFull = false
|
||||||
|
a.txEmpty = true
|
||||||
|
a.overrun = false
|
||||||
|
assert.Equal(t, 0x10, a.ReadByte(aciaStatus))
|
||||||
|
|
||||||
|
a.rxFull = false
|
||||||
|
a.txEmpty = false
|
||||||
|
a.overrun = true
|
||||||
|
assert.Equal(t, 0x04, a.ReadByte(aciaStatus))
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user