1
0
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:
Ariejan de Vroom 2014-08-19 16:40:31 +02:00
parent 432119f507
commit 099d6c11bd
2 changed files with 170 additions and 58 deletions

View File

@ -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
}

View File

@ -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))
} }