From 099d6c11bdca1dd0dac4b76d9b9cdf6d9bff57fa Mon Sep 17 00:00:00 2001 From: Ariejan de Vroom Date: Tue, 19 Aug 2014 16:40:31 +0200 Subject: [PATCH] Implement acia 6551 --- acia6551.go | 118 ++++++++++++++++++++++++++++++++--------------- acia6551_test.go | 110 ++++++++++++++++++++++++++++++++++--------- 2 files changed, 170 insertions(+), 58 deletions(-) diff --git a/acia6551.go b/acia6551.go index 3cd4a64..a264a9a 100644 --- a/acia6551.go +++ b/acia6551.go @@ -1,7 +1,5 @@ package i6502 -import "fmt" - const ( aciaData = iota aciaStatus @@ -21,11 +19,8 @@ The supplied Rx and Tx channels can be used to read and wirte data to the ACIA 6551. */ type Acia6551 struct { - Rx chan byte // Reading (Acia Input) line - Tx chan byte // Transmitting (Acia Output) line - - rxData byte - txData byte + rx byte + tx byte commandData byte controlData byte @@ -39,25 +34,10 @@ type Acia6551 struct { overrun bool } -func NewAcia6551(rx chan byte, tx chan byte) (*Acia6551, error) { - acia := &Acia6551{Tx: tx, Rx: rx} +func NewAcia6551() (*Acia6551, error) { + acia := &Acia6551{} 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 } @@ -68,10 +48,10 @@ func (a *Acia6551) Size() uint16 { // Emulates a hardware reset func (a *Acia6551) Reset() { - a.rxData = 0 + a.rx = 0 a.rxFull = false - a.txData = 0 + a.tx = 0 a.txEmpty = true a.rxIrqEnabled = false @@ -84,36 +64,102 @@ func (a *Acia6551) Reset() { } 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) { + 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) Read(address uint16) byte { +func (a *Acia6551) ReadByte(address uint16) byte { switch address { case aciaData: - // Read Rx + return a.rxRead() case aciaStatus: - // Read Status reg. + return a.statusRegister() case aciaCommand: - // Read command + return a.commandData case aciaControl: - // Read control + return a.controlData } 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 { case aciaData: - // Write Tx + a.txWrite(data) case aciaStatus: - // Reset + a.Reset() case aciaCommand: - // Write command + a.setCommand(data) 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 +} diff --git a/acia6551_test.go b/acia6551_test.go index c0f0226..f540e8e 100644 --- a/acia6551_test.go +++ b/acia6551_test.go @@ -1,37 +1,32 @@ package i6502 import ( - "fmt" - "github.com/stretchr/testify/assert" "testing" + + "github.com/stretchr/testify/assert" ) -func AciaSubject() (*Acia6551, chan byte, chan byte) { - tx := make(chan byte) - rx := make(chan byte) - acia, _ := NewAcia6551(rx, tx) - - return acia, rx, tx +func AciaSubject() *Acia6551 { + acia, _ := NewAcia6551() + return acia } func TestNewAcia6551(t *testing.T) { - tx := make(chan byte) - rx := make(chan byte) - acia, err := NewAcia6551(rx, tx) + acia, err := NewAcia6551() assert.Nil(t, err) assert.Equal(t, 0x4, acia.Size()) } func TestAciaReset(t *testing.T) { - a, _, _ := AciaSubject() + a := AciaSubject() a.Reset() - assert.Equal(t, a.txData, 0) + assert.Equal(t, a.tx, 0) 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.txIrqEnabled) @@ -41,15 +36,86 @@ func TestAciaReset(t *testing.T) { assert.Equal(t, 0, a.controlData) } -func TestAciaReadData(t *testing.T) { - a, _, _ := AciaSubject() +func TestAciaWriteByteAndReader(t *testing.T) { + 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") - value := a.Read(aciaData) - assert.Equal(t, 0x42, value) - assert.False(t, a.rxFull) + if assert.Equal(t, 1, bytesRead) { + assert.Equal(t, 0x42, value[0]) + } +} + +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)) }