mirror of
https://github.com/ariejan/i6502.git
synced 2025-04-22 03:38:38 +00:00
Because io.Reader and io.Writer already claim the functions Read and Write it was necessary to rename the Memory interface methods Read and Write to ReadByte and WriteByte.
121 lines
3.2 KiB
Go
121 lines
3.2 KiB
Go
package i6502
|
|
|
|
import (
|
|
"fmt"
|
|
)
|
|
|
|
/*
|
|
The AddressBus contains a list of all attached memory components,
|
|
like Ram, Rom and IO. It takes care of mapping the global 16-bit
|
|
address space of the Cpu to the relative memory addressing of
|
|
each component.
|
|
*/
|
|
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 (a *addressable) String() string {
|
|
return fmt.Sprintf("\t0x%04X-%04X\n", a.start, a.end)
|
|
}
|
|
|
|
// Creates a new, empty 16-bit AddressBus
|
|
func NewAddressBus() (*AddressBus, error) {
|
|
return &AddressBus{addressables: make([]addressable, 0)}, nil
|
|
}
|
|
|
|
// Returns a string with details about the AddressBus and attached memory
|
|
func (a *AddressBus) String() string {
|
|
output := "Address Bus:\n"
|
|
|
|
for _, addressable := range a.addressables {
|
|
output += addressable.String()
|
|
}
|
|
|
|
return output
|
|
}
|
|
|
|
/*
|
|
Attach the given Memory at the specified memory offset.
|
|
|
|
To attach 16kB ROM at 0xC000-FFFF, you simple attach the Rom at
|
|
address 0xC000, the Size of the Memory determines the end-address.
|
|
|
|
rom, _ := i6502.NewRom(0x4000)
|
|
bus.Attach(rom, 0xC000)
|
|
*/
|
|
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)
|
|
}
|
|
|
|
/*
|
|
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) ReadByte(address uint16) byte {
|
|
addressable, err := a.addressableForAddress(address)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
return addressable.memory.ReadByte(address - addressable.start)
|
|
}
|
|
|
|
/*
|
|
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.ReadByte(address))
|
|
hi := uint16(a.ReadByte(address + 1))
|
|
|
|
return (hi << 8) | lo
|
|
}
|
|
|
|
/*
|
|
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) WriteByte(address uint16, data byte) {
|
|
addressable, err := a.addressableForAddress(address)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
addressable.memory.WriteByte(address-addressable.start, data)
|
|
}
|
|
|
|
/*
|
|
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.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.
|
|
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)
|
|
}
|