mirror of
https://github.com/ariejan/i6502.git
synced 2025-04-08 21:43:30 +00:00
Add basic Cpu/Bus/Ram architecture.
This commit is contained in:
parent
f88946a622
commit
e96ce1e84a
85
address_bus.go
Normal file
85
address_bus.go
Normal file
@ -0,0 +1,85 @@
|
||||
package i6502
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type AddressBus struct {
|
||||
addressables []addressable // Different components
|
||||
}
|
||||
|
||||
type addressable struct {
|
||||
memory Memory // Actual memory
|
||||
start uint16 // First address in address space
|
||||
end uint16 // Last address in address space
|
||||
}
|
||||
|
||||
func NewAddressBus() (*AddressBus, error) {
|
||||
return &AddressBus{addressables: make([]addressable, 0)}, nil
|
||||
}
|
||||
|
||||
func (a *addressable) String() string {
|
||||
return fmt.Sprintf("\t0x%04X-%04X\n", a.start, a.end)
|
||||
}
|
||||
|
||||
func (a *AddressBus) String() string {
|
||||
output := "Address Bus:\n"
|
||||
|
||||
for _, addressable := range a.addressables {
|
||||
output += addressable.String()
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
func (a *AddressBus) AddressablesCount() int {
|
||||
return len(a.addressables)
|
||||
}
|
||||
|
||||
func (a *AddressBus) Attach(memory Memory, offset uint16) {
|
||||
start := offset
|
||||
end := offset + memory.Size() - 1
|
||||
addressable := addressable{memory: memory, start: start, end: end}
|
||||
|
||||
a.addressables = append(a.addressables, addressable)
|
||||
}
|
||||
|
||||
func (a *AddressBus) addressableForAddress(address uint16) (*addressable, error) {
|
||||
for _, addressable := range a.addressables {
|
||||
if addressable.start <= address && addressable.end >= address {
|
||||
return &addressable, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("No addressable memory found at 0x%04X", address)
|
||||
}
|
||||
|
||||
func (a *AddressBus) Read(address uint16) byte {
|
||||
addressable, err := a.addressableForAddress(address)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return addressable.memory.Read(address - addressable.start)
|
||||
}
|
||||
|
||||
func (a *AddressBus) Read16(address uint16) uint16 {
|
||||
lo := uint16(a.Read(address))
|
||||
hi := uint16(a.Read(address + 1))
|
||||
|
||||
return (hi << 8) | lo
|
||||
}
|
||||
|
||||
func (a *AddressBus) Write(address uint16, data byte) {
|
||||
addressable, err := a.addressableForAddress(address)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
addressable.memory.Write(address-addressable.start, data)
|
||||
}
|
||||
|
||||
func (a *AddressBus) Write16(address uint16, data uint16) {
|
||||
a.Write(address, byte(data))
|
||||
a.Write(address+1, byte(data>>8))
|
||||
}
|
65
address_bus_test.go
Normal file
65
address_bus_test.go
Normal file
@ -0,0 +1,65 @@
|
||||
package i6502
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestEmptyAddressBus(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
bus, err := NewAddressBus()
|
||||
|
||||
assert.Nil(err)
|
||||
|
||||
if assert.NotNil(bus) {
|
||||
assert.Equal(0, bus.AddressablesCount())
|
||||
}
|
||||
}
|
||||
|
||||
func TestAttachToAddressBus(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
bus, _ := NewAddressBus()
|
||||
ram, _ := NewRam(0x10000)
|
||||
|
||||
bus.Attach(ram, 0x0000)
|
||||
assert.Equal(1, bus.AddressablesCount())
|
||||
}
|
||||
|
||||
func TestBusReadWrite(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
bus, _ := NewAddressBus()
|
||||
ram, _ := NewRam(0x8000)
|
||||
ram2, _ := NewRam(0x8000)
|
||||
bus.Attach(ram, 0x0000)
|
||||
bus.Attach(ram2, 0x8000)
|
||||
|
||||
// 8-bit Writing
|
||||
bus.Write(0x1234, 0xFA)
|
||||
assert.Equal(0xFA, ram.Read(0x1234))
|
||||
|
||||
// 16-bit Writing
|
||||
bus.Write16(0x1000, 0xAB42)
|
||||
assert.Equal(0x42, ram.Read(0x1000))
|
||||
assert.Equal(0xAB, ram.Read(0x1001))
|
||||
|
||||
// 8-bit Reading
|
||||
ram.Write(0x5522, 0xDA)
|
||||
assert.Equal(0xDA, bus.Read(0x5522))
|
||||
|
||||
// 16-bit Reading
|
||||
ram.Write(0x4440, 0x7F)
|
||||
ram.Write(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))
|
||||
|
||||
bus.Write(0x8001, 0x12)
|
||||
assert.Equal(0x12, ram2.Read(0x0001))
|
||||
}
|
21
cpu.go
21
cpu.go
@ -1,8 +1,25 @@
|
||||
package i6502
|
||||
|
||||
type Cpu struct {
|
||||
bus *AddressBus // The address bus
|
||||
|
||||
PC uint16 // 16-bit program counter
|
||||
P byte // Status Register
|
||||
}
|
||||
|
||||
func NewCpu() (*Cpu, error) {
|
||||
return &Cpu{}, nil
|
||||
const (
|
||||
ResetVector = 0xFFFC
|
||||
)
|
||||
|
||||
func NewCpu(bus *AddressBus) (*Cpu, error) {
|
||||
return &Cpu{bus: bus}, nil
|
||||
}
|
||||
|
||||
func (c *Cpu) HasAddressBus() bool {
|
||||
return c.bus != nil
|
||||
}
|
||||
|
||||
func (c *Cpu) Reset() {
|
||||
c.PC = c.bus.Read16(ResetVector)
|
||||
c.P = 0x34
|
||||
}
|
||||
|
37
cpu_test.go
37
cpu_test.go
@ -5,9 +5,44 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Creates a new machine, returning the different parts
|
||||
func NewRamMachine() (*Cpu, *AddressBus, *Ram) {
|
||||
ram, _ := NewRam(0x100000) // Full 64kB
|
||||
bus, _ := NewAddressBus()
|
||||
bus.Attach(ram, 0x0000)
|
||||
cpu, _ := NewCpu(bus)
|
||||
|
||||
return cpu, bus, ram
|
||||
}
|
||||
|
||||
func TestNewCpu(t *testing.T) {
|
||||
cpu, err := NewCpu()
|
||||
cpu, err := NewCpu(nil)
|
||||
|
||||
assert.NotNil(t, cpu)
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestCpuAddressBus(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
cpu, _, _ := NewRamMachine()
|
||||
assert.True(cpu.HasAddressBus())
|
||||
}
|
||||
|
||||
func TestCpuState(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
cpu, _, _ := NewRamMachine()
|
||||
|
||||
cpu.bus.Write(0xFFFC, 0x34)
|
||||
cpu.bus.Write(0xFFFD, 0x12)
|
||||
|
||||
cpu.Reset()
|
||||
|
||||
// **1101** is specified, but we are satisfied with
|
||||
// 00110100 here.
|
||||
assert.Equal(0x34, cpu.P)
|
||||
|
||||
// Read PC from $FFFC-FFFD
|
||||
assert.Equal(0x1234, cpu.PC)
|
||||
}
|
||||
|
7
memory.go
Normal file
7
memory.go
Normal file
@ -0,0 +1,7 @@
|
||||
package i6502
|
||||
|
||||
type Memory interface {
|
||||
Size() uint16
|
||||
Read(address uint16) byte
|
||||
Write(address uint16, data byte)
|
||||
}
|
21
ram.go
Normal file
21
ram.go
Normal file
@ -0,0 +1,21 @@
|
||||
package i6502
|
||||
|
||||
type Ram struct {
|
||||
data []byte
|
||||
}
|
||||
|
||||
func NewRam(size int) (*Ram, error) {
|
||||
return &Ram{data: make([]byte, size)}, nil
|
||||
}
|
||||
|
||||
func (r *Ram) Size() uint16 {
|
||||
return uint16(len(r.data))
|
||||
}
|
||||
|
||||
func (r *Ram) Read(address uint16) byte {
|
||||
return r.data[address]
|
||||
}
|
||||
|
||||
func (r *Ram) Write(address uint16, data byte) {
|
||||
r.data[address] = data
|
||||
}
|
26
ram_test.go
Normal file
26
ram_test.go
Normal file
@ -0,0 +1,26 @@
|
||||
package i6502
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRamSize(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
ram, _ := NewRam(0x8000) // 32 kB
|
||||
assert.Equal(0x8000, ram.Size())
|
||||
}
|
||||
|
||||
func TestRamReadWrite(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
ram, _ := NewRam(0x8000) // 32 kB
|
||||
|
||||
// Ram zeroed out initially
|
||||
for i := 0; i < 0x8000; i++ {
|
||||
assert.Equal(0x00, ram.data[i])
|
||||
}
|
||||
|
||||
ram.Write(0x1000, 0x42)
|
||||
assert.Equal(0x42, ram.Read(0x1000))
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user