Merge pull request #3 from ariejan/issue_3_acia6551

Add support for 6551 Asynchronous Communications Interface Adapter (ACIA)
This commit is contained in:
Ariejan de Vroom 2014-08-19 22:09:46 +02:00
commit 4764cb24ca
9 changed files with 551 additions and 220 deletions

165
acia6551.go Normal file
View File

@ -0,0 +1,165 @@
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
}
func NewAcia6551() (*Acia6551, error) {
acia := &Acia6551{}
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) {
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.tx = data
a.txEmpty = false
}

157
acia6551_test.go Normal file
View File

@ -0,0 +1,157 @@
package i6502
import (
"testing"
"github.com/stretchr/testify/assert"
)
func AciaSubject() *Acia6551 {
acia, _ := NewAcia6551()
return acia
}
func TestNewAcia6551(t *testing.T) {
acia, err := NewAcia6551()
assert.Nil(t, err)
assert.Equal(t, 0x4, acia.Size())
}
func TestAciaReset(t *testing.T) {
a := AciaSubject()
a.Reset()
assert.Equal(t, a.tx, 0)
assert.True(t, a.txEmpty)
assert.Equal(t, a.rx, 0)
assert.False(t, a.rxFull)
assert.False(t, a.txIrqEnabled)
assert.False(t, a.rxIrqEnabled)
assert.False(t, a.overrun)
assert.Equal(t, 0, a.controlData)
}
func TestAciaWriteByteAndReader(t *testing.T) {
a := AciaSubject()
// CPU writes data
a.WriteByte(aciaData, 0x42)
// System reads from Tx
value := make([]byte, 1)
bytesRead, _ := a.Read(value)
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))
}
func TestAciaIntegration(t *testing.T) {
// Create a system
// * 32kB RAM at 0x0000-7FFFF
// * ACIA at 0x8800-8803
ram, _ := NewRam(0x8000)
acia, _ := NewAcia6551()
bus, _ := NewAddressBus()
bus.Attach(ram, 0x0000)
bus.Attach(acia, 0x8800)
cpu, _ := NewCpu(bus)
program := []byte{
0xA9, 0x00, // LDA #$00
0x8D, 0x01, 0x88, // STA AciaStatus (Reset)
0xA9, 0x42, // LDA #$42
0x8D, 0x00, 0x88, // STA AciaData (Write)
0xAD, 0x00, 0x88, // LDA AciaData (Read)
}
cpu.LoadProgram(program, 0x0200)
cpu.Steps(2)
acia.Write([]byte{0xAB})
cpu.Steps(3)
value := make([]byte, 1)
bytesRead, _ := acia.Read(value)
if assert.Equal(t, 1, bytesRead) {
assert.Equal(t, 0x42, value[0])
}
assert.Equal(t, 0xAB, cpu.A)
}

View File

@ -62,13 +62,13 @@ Read an 8-bit value from Memory attached at the 16-bit address.
This will panic if you try to read from an address that has no Memory attached.
*/
func (a *AddressBus) Read(address uint16) byte {
func (a *AddressBus) ReadByte(address uint16) byte {
addressable, err := a.addressableForAddress(address)
if err != nil {
panic(err)
}
return addressable.memory.Read(address - addressable.start)
return addressable.memory.ReadByte(address - addressable.start)
}
/*
@ -77,8 +77,8 @@ Convenience method to quickly read a 16-bit value from address and address + 1.
Note that we first read the LOW byte from address and then the HIGH byte from address + 1.
*/
func (a *AddressBus) Read16(address uint16) uint16 {
lo := uint16(a.Read(address))
hi := uint16(a.Read(address + 1))
lo := uint16(a.ReadByte(address))
hi := uint16(a.ReadByte(address + 1))
return (hi << 8) | lo
}
@ -89,13 +89,13 @@ Write an 8-bit value to the Memory at the 16-bit address.
This will panic if you try to write to an address that has no Memory attached or
Memory that is read-only, like Rom.
*/
func (a *AddressBus) Write(address uint16, data byte) {
func (a *AddressBus) WriteByte(address uint16, data byte) {
addressable, err := a.addressableForAddress(address)
if err != nil {
panic(err)
}
addressable.memory.Write(address-addressable.start, data)
addressable.memory.WriteByte(address-addressable.start, data)
}
/*
@ -104,8 +104,8 @@ Convenience method to quickly write a 16-bit value to address and address + 1.
Note that the LOW byte will be stored in address and the high byte in address + 1.
*/
func (a *AddressBus) Write16(address uint16, data uint16) {
a.Write(address, byte(data))
a.Write(address+1, byte(data>>8))
a.WriteByte(address, byte(data))
a.WriteByte(address+1, byte(data>>8))
}
// Returns the addressable for the specified address, or an error if no addressable exists.

View File

@ -1,8 +1,9 @@
package i6502
import (
"github.com/stretchr/testify/assert"
"testing"
"github.com/stretchr/testify/assert"
)
func TestEmptyAddressBus(t *testing.T) {
@ -37,29 +38,29 @@ func TestBusReadWrite(t *testing.T) {
bus.Attach(ram2, 0x8000)
// 8-bit Writing
bus.Write(0x1234, 0xFA)
assert.Equal(0xFA, ram.Read(0x1234))
bus.WriteByte(0x1234, 0xFA)
assert.Equal(0xFA, ram.ReadByte(0x1234))
// 16-bit Writing
bus.Write16(0x1000, 0xAB42)
assert.Equal(0x42, ram.Read(0x1000))
assert.Equal(0xAB, ram.Read(0x1001))
assert.Equal(0x42, ram.ReadByte(0x1000))
assert.Equal(0xAB, ram.ReadByte(0x1001))
// 8-bit Reading
ram.Write(0x5522, 0xDA)
assert.Equal(0xDA, bus.Read(0x5522))
ram.WriteByte(0x5522, 0xDA)
assert.Equal(0xDA, bus.ReadByte(0x5522))
// 16-bit Reading
ram.Write(0x4440, 0x7F)
ram.Write(0x4441, 0x56)
ram.WriteByte(0x4440, 0x7F)
ram.WriteByte(0x4441, 0x56)
assert.Equal(0x567F, bus.Read16(0x4440))
//// Test addressing memory not mounted at 0x0000
// Read from relative addressable Ram2: $C123
ram2.Write(0x4123, 0xEF)
assert.Equal(0xEF, bus.Read(0xC123))
ram2.WriteByte(0x4123, 0xEF)
assert.Equal(0xEF, bus.ReadByte(0xC123))
bus.Write(0x8001, 0x12)
assert.Equal(0x12, ram2.Read(0x0001))
bus.WriteByte(0x8001, 0x12)
assert.Equal(0x12, ram2.ReadByte(0x0001))
}

50
cpu.go
View File

@ -90,12 +90,18 @@ func (c *Cpu) handleIrq(PC uint16) {
// and point the Program Counter to the beginning of the program.
func (c *Cpu) LoadProgram(data []byte, location uint16) {
for i, b := range data {
c.Bus.Write(location+uint16(i), b)
c.Bus.WriteByte(location+uint16(i), b)
}
c.PC = location
}
func (c *Cpu) Steps(steps int) {
for i := 0; i < steps; i++ {
c.Step()
}
}
// Read and execute the instruction pointed to by the Program Counter (PC)
func (c *Cpu) Step() {
instruction := c.readNextInstruction()
@ -158,13 +164,13 @@ func (c *Cpu) execute(instruction Instruction) {
c.setA(c.A ^ value)
case sta:
address := c.memoryAddress(instruction)
c.Bus.Write(address, c.A)
c.Bus.WriteByte(address, c.A)
case stx:
address := c.memoryAddress(instruction)
c.Bus.Write(address, c.X)
c.Bus.WriteByte(address, c.X)
case sty:
address := c.memoryAddress(instruction)
c.Bus.Write(address, c.Y)
c.Bus.WriteByte(address, c.Y)
case tax:
c.setX(c.A)
case tay:
@ -264,7 +270,7 @@ func (c *Cpu) execute(instruction Instruction) {
func (c *Cpu) readNextInstruction() Instruction {
// Read the opcode
opcode := c.Bus.Read(c.PC)
opcode := c.Bus.ReadByte(c.PC)
optype, ok := opTypes[opcode]
if !ok {
@ -275,7 +281,7 @@ func (c *Cpu) readNextInstruction() Instruction {
switch instruction.Size {
case 1: // Zero operand instruction
case 2: // 8-bit operand
instruction.Op8 = c.Bus.Read(c.PC + 1)
instruction.Op8 = c.Bus.ReadByte(c.PC + 1)
case 3: // 16-bit operand
instruction.Op16 = c.Bus.Read16(c.PC + 1)
}
@ -297,7 +303,7 @@ func (c *Cpu) resolveOperand(in Instruction) uint8 {
case immediate:
return in.Op8
default:
return c.Bus.Read(c.memoryAddress(in))
return c.Bus.ReadByte(c.memoryAddress(in))
}
}
@ -356,17 +362,17 @@ func (c *Cpu) sbc(in Instruction) {
func (c *Cpu) inc(in Instruction) {
address := c.memoryAddress(in)
value := c.Bus.Read(address) + 1
value := c.Bus.ReadByte(address) + 1
c.Bus.Write(address, value)
c.Bus.WriteByte(address, value)
c.setArithmeticFlags(value)
}
func (c *Cpu) dec(in Instruction) {
address := c.memoryAddress(in)
value := c.Bus.Read(address) - 1
value := c.Bus.ReadByte(address) - 1
c.Bus.Write(address, value)
c.Bus.WriteByte(address, value)
c.setArithmeticFlags(value)
}
@ -378,10 +384,10 @@ func (c *Cpu) asl(in Instruction) {
c.setArithmeticFlags(c.A)
default:
address := c.memoryAddress(in)
value := c.Bus.Read(address)
value := c.Bus.ReadByte(address)
c.setCarry((value >> 7) == 1)
value <<= 1
c.Bus.Write(address, value)
c.Bus.WriteByte(address, value)
c.setArithmeticFlags(value)
}
}
@ -394,10 +400,10 @@ func (c *Cpu) lsr(in Instruction) {
c.setArithmeticFlags(c.A)
default:
address := c.memoryAddress(in)
value := c.Bus.Read(address)
value := c.Bus.ReadByte(address)
c.setCarry((value & 0x01) == 1)
value >>= 1
c.Bus.Write(address, value)
c.Bus.WriteByte(address, value)
c.setArithmeticFlags(value)
}
}
@ -412,10 +418,10 @@ func (c *Cpu) rol(in Instruction) {
c.setArithmeticFlags(c.A)
default:
address := c.memoryAddress(in)
value := c.Bus.Read(address)
value := c.Bus.ReadByte(address)
c.setCarry((value & 0x80) != 0)
value = value<<1 | carry
c.Bus.Write(address, value)
c.Bus.WriteByte(address, value)
c.setArithmeticFlags(value)
}
}
@ -430,10 +436,10 @@ func (c *Cpu) ror(in Instruction) {
c.setArithmeticFlags(c.A)
default:
address := c.memoryAddress(in)
value := c.Bus.Read(address)
value := c.Bus.ReadByte(address)
c.setCarry(value&0x01 == 1)
value = value>>1 | carry<<7
c.Bus.Write(address, value)
c.Bus.WriteByte(address, value)
c.setArithmeticFlags(value)
}
}
@ -513,15 +519,15 @@ func (c *Cpu) sbcDecimal(a uint8, b uint8, carryIn uint8) {
}
func (c *Cpu) stackPush(data byte) {
c.Bus.Write(StackBase+uint16(c.SP), data)
c.Bus.WriteByte(StackBase+uint16(c.SP), data)
c.SP -= 1
}
func (c *Cpu) stackPeek() byte {
return c.Bus.Read(StackBase + uint16(c.SP+1))
return c.Bus.ReadByte(StackBase + uint16(c.SP+1))
}
func (c *Cpu) stackPop() byte {
c.SP += 1
return c.Bus.Read(StackBase + uint16(c.SP))
return c.Bus.ReadByte(StackBase + uint16(c.SP))
}

File diff suppressed because it is too large Load Diff

View File

@ -6,6 +6,6 @@ and become accessible by the Cpu.
*/
type Memory interface {
Size() uint16
Read(address uint16) byte
Write(address uint16, data byte)
ReadByte(address uint16) byte
WriteByte(address uint16, data byte)
}

4
ram.go
View File

@ -16,10 +16,10 @@ func (r *Ram) Size() uint16 {
return uint16(len(r.data))
}
func (r *Ram) Read(address uint16) byte {
func (r *Ram) ReadByte(address uint16) byte {
return r.data[address]
}
func (r *Ram) Write(address uint16, data byte) {
func (r *Ram) WriteByte(address uint16, data byte) {
r.data[address] = data
}

View File

@ -1,8 +1,9 @@
package i6502
import (
"github.com/stretchr/testify/assert"
"testing"
"github.com/stretchr/testify/assert"
)
func TestRamSize(t *testing.T) {
@ -18,6 +19,6 @@ func TestRamReadWrite(t *testing.T) {
assert.Equal(t, 0x00, ram.data[i])
}
ram.Write(0x1000, 0x42)
assert.Equal(t, 0x42, ram.Read(0x1000))
ram.WriteByte(0x1000, 0x42)
assert.Equal(t, 0x42, ram.ReadByte(0x1000))
}