o hardware/memory

- moved cartridge code into new package
    - moved bus definitions into new package

o recorder
    - changed file-format
This commit is contained in:
steve 2019-12-12 18:30:32 +00:00
parent 9e71a20847
commit 095efecd32
32 changed files with 299 additions and 303 deletions

View file

@ -83,7 +83,7 @@ var commandTemplate = []string{
cmdGrep + " %S",
cmdHexLoad + " %N %N {%N}",
cmdInsert + " %F",
cmdLast + " (DEFN)",
cmdLast + " (DEFN|BYTECODE)",
cmdList + " [BREAKS|TRAPS|WATCHES|ALL]",
cmdMemMap,
cmdReflect + " (ON|OFF)",
@ -587,20 +587,35 @@ func (dbg *Debugger) enactCommand(tokens *commandline.Tokens, interactive bool)
return doNothing, nil
case cmdLast:
option, ok := tokens.Get()
if ok {
switch strings.ToUpper(option) {
case "DEFN":
dbg.print(terminal.StyleFeedback, "%s", dbg.vcs.CPU.LastResult.Defn)
}
if dbg.vcs.CPU.LastResult.Defn == nil {
// special condition for when LAST is called before any execution
// has taken place
dbg.print(terminal.StyleFeedback, "no instruction decoded yet")
} else {
var printTag terminal.Style
if dbg.vcs.CPU.LastResult.Final {
printTag = terminal.StyleCPUStep
} else {
printTag = terminal.StyleVideoStep
done := false
resultStyle := result.StyleExecution
option, ok := tokens.Get()
if ok {
switch strings.ToUpper(option) {
case "DEFN":
dbg.print(terminal.StyleFeedback, "%s", dbg.vcs.CPU.LastResult.Defn)
done = true
case "BYTECODE":
resultStyle = result.StyleDisasm
}
}
if !done {
var printTag terminal.Style
if dbg.vcs.CPU.LastResult.Final {
printTag = terminal.StyleCPUStep
} else {
printTag = terminal.StyleVideoStep
}
dbg.print(printTag, "%s", dbg.vcs.CPU.LastResult.GetString(dbg.disasm.Symtable, resultStyle))
}
dbg.print(printTag, "%s", dbg.vcs.CPU.LastResult.GetString(dbg.disasm.Symtable, result.StyleExecution))
}
case cmdMemMap:

View file

@ -37,7 +37,7 @@
//
// dbg.Start(initScript, cartloader)
//
// The initscript is a script previously created either by the script.Scribe or
// by hand. The cartloader argument must be an instance of cartloader. The
// emulation proper handles the loading of the cartridge data.
// The initscript is a script previously created either by the script.Scribe
// package or by hand. The cartloader argument must be an instance of
// cartloader.
package debugger

View file

@ -5,8 +5,8 @@ import (
"gopher2600/cartridgeloader"
"gopher2600/errors"
"gopher2600/hardware/cpu"
"gopher2600/hardware/memory"
"gopher2600/hardware/memory/addresses"
"gopher2600/hardware/memory/cartridge"
"gopher2600/symbols"
"io"
"strings"
@ -18,7 +18,7 @@ type bank [disasmMask + 1]Entry
// Disassembly represents the annotated disassembly of a 6507 binary
type Disassembly struct {
cart *memory.Cartridge
cart *cartridge.Cartridge
// discovered/inferred cartridge attributes
nonCartJmps bool
@ -76,7 +76,7 @@ func FromCartridge(cartload cartridgeloader.Loader) (*Disassembly, error) {
// standard symbols table even in the event of an error
symtable, _ := symbols.ReadSymbolsFile(cartload.Filename)
cart := memory.NewCartridge()
cart := cartridge.NewCartridge()
err := cart.Attach(cartload)
if err != nil {
@ -93,7 +93,7 @@ func FromCartridge(cartload cartridgeloader.Loader) (*Disassembly, error) {
// FromMemory disassembles an existing instance of cartridge memory using a
// cpu with no flow control.
func FromMemory(cart *memory.Cartridge, symtable *symbols.Table) (*Disassembly, error) {
func FromMemory(cart *cartridge.Cartridge, symtable *symbols.Table) (*Disassembly, error) {
dsm := &Disassembly{}
dsm.cart = cart

View file

@ -1,14 +1,14 @@
package disassembly
import (
"gopher2600/hardware/memory"
"gopher2600/hardware/memory/cartridge"
"gopher2600/hardware/memory/memorymap"
)
// disasmMemory is a simplified memory model that allows the emulated CPU to
// read cartridge memory.
type disasmMemory struct {
cart *memory.Cartridge
cart *cartridge.Cartridge
}
func (dismem *disasmMemory) Read(address uint16) (uint8, error) {

View file

@ -6,8 +6,8 @@ import (
"gopher2600/hardware/cpu/definitions"
"gopher2600/hardware/cpu/registers"
"gopher2600/hardware/cpu/result"
"gopher2600/hardware/memory"
"gopher2600/hardware/memory/addresses"
"gopher2600/hardware/memory/bus"
"log"
)
@ -27,7 +27,7 @@ type CPU struct {
acc8 *registers.Register
acc16 *registers.ProgramCounter
mem memory.CPUBus
mem bus.CPUBus
opCodes []*definitions.InstructionDefinition
// isExecuting is used for sanity checks - to make sure we're not calling CPU
@ -59,7 +59,7 @@ type CPU struct {
}
// NewCPU is the preferred method of initialisation for the CPU structure
func NewCPU(mem memory.CPUBus) (*CPU, error) {
func NewCPU(mem bus.CPUBus) (*CPU, error) {
mc := &CPU{mem: mem}
mc.PC = registers.NewProgramCounter(0)

View file

@ -126,14 +126,14 @@ func init() {
}
}
// Named TIA registers
// TIA registers
//
// These value are used by the emulator to specifiy known addresses. For
// example, when writing collision information we know we need the CXM0P
// register. these named values make the code more readable
//
// For simplicity values are enumerated from 0; value is added to the origin
// address of the TIA in ChipBus.ChipWrite implementation
// Values are enumerated from 0; value is added to the origin address of the
// TIA in ChipBus.ChipWrite implementation
const (
CXM0P uint16 = iota
CXM1P
@ -151,14 +151,14 @@ const (
INPT5
)
// Named RIOT registers
// RIOT registers
//
// These value are used by the emulator to specifiy known addresses. For
// example, the timer updates itself every cycle and stores time remaining
// value in the INTIM register.
//
// For simplicity values are enumerated from 0; value is added to the origin
// address of the TIA in ChipBus.ChipWrite implementation
// Values are enumerated from 0; value is added to the origin address of the
// TIA in ChipBus.ChipWrite implementation
const (
SWCHA uint16 = iota
SWACNT

View file

@ -10,9 +10,11 @@
// is noticeably slower than accessing a sparse array. There is probably no
// need to use this arrays outside of the emulation code.
//
// The Named TIA and RIOT registers probably don't need referring to outside
// the emulation code.
// "TIA Registers" and "RIOT Registers" are so named because to those areas,
// those addresses look like registers. They probably don't need referring to
// outside the emulation code.
//
// DataMasks help implement VCS data/address bus artefacts and probably don't
// need to be referred to outside the emulation code.
// DataMasks help implement VCS data/address bus artefacts (fully explained
// beloew) and probably don't need to be referred to outside the emulation
// code.
package addresses

View file

@ -1,4 +1,6 @@
package memory
// Package bus defines the memory bus concept. For an explanation see the
// memory package documentation.
package bus
// CPUBus defines the operations for the memory system when accessed from the CPU
// All memory areas implement this interface because they are all accessible

View file

@ -0,0 +1,28 @@
package cartridge
// cartMapper implementations hold the actual data from the loaded ROM and
// keeps track of which banks are mapped to individual addresses. for
// convenience, functions with an address argument recieve that address
// normalised to a range of 0x0000 to 0x0fff
type cartMapper interface {
initialise()
read(addr uint16) (data uint8, err error)
write(addr uint16, data uint8) error
numBanks() int
getBank(addr uint16) (bank int)
setBank(addr uint16, bank int) error
saveState() interface{}
restoreState(interface{}) error
ram() []uint8
// tigervision cartridges have a very wierd bank-switching method that
// require a way of notifying the cartridge of writes to addresses outside
// of cartridge space
listen(addr uint16, data uint8)
}
// optionalSuperchip are implemented by cartMappers that have an optional
// superchip
type optionalSuperchip interface {
addSuperchip() bool
}

View file

@ -1,74 +1,33 @@
package memory
package cartridge
import (
"crypto/sha1"
"fmt"
"gopher2600/cartridgeloader"
"gopher2600/errors"
"gopher2600/hardware/memory/bus"
"gopher2600/hardware/memory/memorymap"
"strings"
)
// cartMapper implementations hold the actual data from the loaded ROM and
// keeps track of which banks are mapped to individual addresses. for
// convenience, functions with an address argument recieve that address
// normalised to a range of 0x0000 to 0x0fff
type cartMapper interface {
initialise()
read(addr uint16) (data uint8, err error)
write(addr uint16, data uint8) error
numBanks() int
getBank(addr uint16) (bank int)
setBank(addr uint16, bank int) error
saveState() interface{}
restoreState(interface{}) error
ram() []uint8
// listen differs from write in that the address is the unmapped address on
// the address bus. for convenience, memory functions deal with addresses
// that have been mapped and normalised so they count from zero.
// cartMapper.listen() is the exception.
listen(addr uint16, data uint8) error
}
// optionalSuperchip are implemented by cartMappers that have an optional
// superchip
type optionalSuperchip interface {
addSuperchip() bool
}
// Cartridge defines the information and operations for a VCS cartridge
type Cartridge struct {
DebuggerBus
CPUBus
bus.DebuggerBus
bus.CPUBus
origin uint16
memtop uint16
// full path to the cartridge as stored on disk
Filename string
// the format requested by the CartridgeLoader
RequestedFormat string
// hash of binary loaded from disk. any subsequent pokes to cartridge
// memory will not be reflected in the value
Hash string
Hash string
// the specific cartridge data, mapped appropriately to the memory
// interfaces
mapper cartMapper
}
// NewCartridge is the preferred method of initialisation for the cartridges
// NewCartridge is the preferred method of initialisation for the cartridge
// type
func NewCartridge() *Cartridge {
cart := &Cartridge{
origin: memorymap.OriginCart,
memtop: memorymap.MemtopCart,
}
cart := &Cartridge{}
cart.Eject()
return cart
}
@ -78,7 +37,7 @@ func (cart Cartridge) String() string {
// Peek is an implementation of memory.DebuggerBus
func (cart Cartridge) Peek(addr uint16) (uint8, error) {
addr &= cart.origin - 1
addr &= memorymap.OriginCart - 1
return cart.mapper.read(addr)
}
@ -90,13 +49,13 @@ func (cart Cartridge) Poke(addr uint16, data uint8) error {
// Read is an implementation of memory.CPUBus
// * optimisation: called a lot. pointer to Cartridge to prevent duffcopy
func (cart *Cartridge) Read(addr uint16) (uint8, error) {
addr &= cart.origin - 1
addr &= memorymap.OriginCart - 1
return cart.mapper.read(addr)
}
// Write is an implementation of memory.CPUBus
func (cart *Cartridge) Write(addr uint16, data uint8) error {
addr &= cart.origin - 1
addr &= memorymap.OriginCart - 1
return cart.mapper.write(addr, data)
}
@ -108,86 +67,6 @@ func (cart *Cartridge) Eject() {
cart.mapper = newEjected()
}
// fingerprint8k attempts a divination of 8k cartridge data and decide on a
// suitable cartMapper implementation
func (cart Cartridge) fingerprint8k(data []byte) func([]byte) (cartMapper, error) {
if fingerprintTigervision(data) {
return newTigervision
}
if fingerprintParkerBros(data) {
return newparkerBros
}
return newAtari8k
}
// fingerprint16k attempts a divination of 16k cartridge data and decide on a
// suitable cartMapper implementation
func (cart Cartridge) fingerprint16k(data []byte) func([]byte) (cartMapper, error) {
if fingerprintMnetwork(data) {
return newMnetwork
}
return newAtari16k
}
func (cart *Cartridge) fingerprint(data []byte) error {
var err error
switch len(data) {
case 2048:
cart.mapper, err = newAtari2k(data)
if err != nil {
return err
}
case 4096:
cart.mapper, err = newAtari4k(data)
if err != nil {
return err
}
case 8192:
cart.mapper, err = cart.fingerprint8k(data)(data)
if err != nil {
return err
}
case 12288:
cart.mapper, err = newCBS(data)
if err != nil {
return err
}
case 16384:
cart.mapper, err = cart.fingerprint16k(data)(data)
if err != nil {
return err
}
case 32768:
cart.mapper, err = newAtari32k(data)
if err != nil {
return err
}
case 65536:
return errors.New(errors.CartridgeError, "65536 bytes not yet supported")
default:
return errors.New(errors.CartridgeError, fmt.Sprintf("unrecognised cartridge size (%d bytes)", len(data)))
}
// if cartridge mapper implements the optionalSuperChip interface then try
// to add the additional RAM
if superchip, ok := cart.mapper.(optionalSuperchip); ok {
superchip.addSuperchip()
}
return nil
}
// IsEjected returns true if no cartridge is attached
func (cart *Cartridge) IsEjected() bool {
return cart.Hash == ejectedHash
@ -203,7 +82,6 @@ func (cart *Cartridge) Attach(cartload cartridgeloader.Loader) error {
// note name of cartridge
cart.Filename = cartload.Filename
cart.RequestedFormat = cartload.Format
cart.mapper = newEjected()
// generate hash
@ -293,7 +171,7 @@ func (cart Cartridge) NumBanks() int {
// GetBank returns the current bank number for the specified address
func (cart Cartridge) GetBank(addr uint16) int {
addr &= cart.origin - 1
addr &= memorymap.OriginCart - 1
return cart.mapper.getBank(addr)
}
@ -301,7 +179,7 @@ func (cart Cartridge) GetBank(addr uint16) int {
// bank. For many cart mappers this just means switching banks for the entire
// cartridge
func (cart *Cartridge) SetBank(addr uint16, bank int) error {
addr &= cart.origin - 1
addr &= memorymap.OriginCart - 1
return cart.mapper.setBank(addr, bank)
}
@ -321,9 +199,9 @@ func (cart Cartridge) RAM() []uint8 {
return cart.mapper.ram()
}
// Listen for data at the specified address. return CartridgeListen error if
// nothing was done with the information. Callers to Listen() will probably
// want to filter out that error.
func (cart Cartridge) Listen(addr uint16, data uint8) error {
return cart.mapper.listen(addr, data)
// Listen for data at the specified address. very wierd requirement of the
// tigervision cartridge format. If there was a better way of implementing the
// tigervision format, there'd be no need for this function.
func (cart Cartridge) Listen(addr uint16, data uint8) {
cart.mapper.listen(addr, data)
}

View file

@ -1,4 +1,4 @@
package memory
package cartridge
import (
"fmt"
@ -146,8 +146,7 @@ func (cart atari) ram() []uint8 {
return cart.superchip
}
func (cart atari) listen(addr uint16, data uint8) error {
return nil
func (cart atari) listen(addr uint16, data uint8) {
}
// atari4k is the original and most straightforward format

View file

@ -1,4 +1,4 @@
package memory
package cartridge
import (
"fmt"
@ -126,6 +126,5 @@ func (cart cbs) ram() []uint8 {
return cart.superchip
}
func (cart cbs) listen(addr uint16, data uint8) error {
return nil
func (cart cbs) listen(addr uint16, data uint8) {
}

View file

@ -1,4 +1,4 @@
package memory
package cartridge
import (
"fmt"
@ -60,6 +60,5 @@ func (cart ejected) ram() []uint8 {
return []uint8{}
}
func (cart ejected) listen(addr uint16, data uint8) error {
return nil
func (cart ejected) listen(addr uint16, data uint8) {
}

View file

@ -1,4 +1,4 @@
package memory
package cartridge
import (
"fmt"
@ -188,7 +188,7 @@ func (cart *mnetwork) bankSwitchOnAccess(addr uint16) bool {
case 0x0fe7:
cart.lowerSegment = 7
// from bankswitch_size.txt: "You select which 256 byte block appears
// from bankswitch_sizes.txt: "You select which 256 byte block appears
// here by accessing 1FF8 to 1FFB."
//
// "here" refers to the read range 0x0900 to 0x09ff and the write range
@ -272,6 +272,5 @@ func (cart *mnetwork) ram() []uint8 {
return mem
}
func (cart *mnetwork) listen(addr uint16, data uint8) error {
return nil
func (cart *mnetwork) listen(addr uint16, data uint8) {
}

View file

@ -1,4 +1,4 @@
package memory
package cartridge
import (
"fmt"
@ -219,6 +219,5 @@ func (cart parkerBros) ram() []uint8 {
return []uint8{}
}
func (cart parkerBros) listen(addr uint16, data uint8) error {
return nil
func (cart parkerBros) listen(addr uint16, data uint8) {
}

View file

@ -1,4 +1,4 @@
package memory
package cartridge
import (
"fmt"
@ -136,17 +136,22 @@ func (cart *tigervision) ram() []uint8 {
return []uint8{}
}
func (cart *tigervision) listen(addr uint16, data uint8) error {
// tigervision is seemingly unique in that in bank-switches when an address
func (cart *tigervision) listen(addr uint16, data uint8) {
// tigervision is seemingly unique in that it bank switches when an address
// outside of cartridge space is written to. for this to work, we need the
// listen() function.
// althought address 3F is used primarily for bank switching in actual
// fact writing anywhere in TIA space is okay
// although address 3F is used primarily, in actual fact writing anywhere
// in TIA space is okay. from the description from Kevin Horton's document
// (quoted above) whenever an address in TIA space is written to, the lower
// 3 bits of the value being written is used to set the segment.
if addr < 0x40 {
// only the lowest three bits of the data value are used
cart.segment[0] = int(data & 0x03)
return nil
}
return nil
// this bank switching method causes a problem when the CPU wants to write
// to TIA space for real and not cause a bankswitch. for this reason,
// tigervision cartridges use mirror addresses to write to the TIA.
}

View file

@ -0,0 +1,22 @@
// Package cartridge fully implements loading of mapping of cartridge memory.
//
// There are many different types of cartridge most of which are supported by
// the package. Some cartridge types contain additional RAM but the main
// difference is how they map additional ROM to the relatively small address
// space available for cartridges in the VCS. This is called bank-switching.
// All of these differences are handled transparently by the package.
//
// Currently supported cartridge types are:
//
// - Atari 2k / 4k / 8k / 16k and 32k
//
// - the above with additional Superchip (additional RAM in other words)
//
// - Parker Bros.
//
// - MNetwork
//
// - Tigervision
//
// - CBS
package cartridge

View file

@ -0,0 +1,82 @@
package cartridge
import (
"fmt"
"gopher2600/errors"
)
func (cart Cartridge) fingerprint8k(data []byte) func([]byte) (cartMapper, error) {
if fingerprintTigervision(data) {
return newTigervision
}
if fingerprintParkerBros(data) {
return newparkerBros
}
return newAtari8k
}
func (cart Cartridge) fingerprint16k(data []byte) func([]byte) (cartMapper, error) {
if fingerprintMnetwork(data) {
return newMnetwork
}
return newAtari16k
}
func (cart *Cartridge) fingerprint(data []byte) error {
var err error
switch len(data) {
case 2048:
cart.mapper, err = newAtari2k(data)
if err != nil {
return err
}
case 4096:
cart.mapper, err = newAtari4k(data)
if err != nil {
return err
}
case 8192:
cart.mapper, err = cart.fingerprint8k(data)(data)
if err != nil {
return err
}
case 12288:
cart.mapper, err = newCBS(data)
if err != nil {
return err
}
case 16384:
cart.mapper, err = cart.fingerprint16k(data)(data)
if err != nil {
return err
}
case 32768:
cart.mapper, err = newAtari32k(data)
if err != nil {
return err
}
case 65536:
return errors.New(errors.CartridgeError, "65536 bytes not yet supported")
default:
return errors.New(errors.CartridgeError, fmt.Sprintf("unrecognised cartridge size (%d bytes)", len(data)))
}
// if cartridge mapper implements the optionalSuperChip interface then try
// to add the additional RAM
if superchip, ok := cart.mapper.(optionalSuperchip); ok {
superchip.addSuperchip()
}
return nil
}

View file

@ -4,18 +4,23 @@ import (
"fmt"
"gopher2600/errors"
"gopher2600/hardware/memory/addresses"
"gopher2600/hardware/memory/bus"
)
// ChipMemory defines the information for and operations allowed for those
// memory areas accessed by the VCS chips as well as the CPU
type ChipMemory struct {
DebuggerBus
ChipBus
CPUBus
PeriphBus
bus.DebuggerBus
bus.ChipBus
bus.CPUBus
bus.PeriphBus
// because we're servicing two different memory areas with this type, we
// need to store the origin and memtop values here, rather than using the
// constants from the memorymap package directly
origin uint16
memtop uint16
memory []uint8
// additional mask to further reduce address space when read from the CPU
@ -49,13 +54,13 @@ func (area ChipMemory) Poke(address uint16, value uint8) error {
}
// ChipRead is an implementation of memory.ChipBus
func (area *ChipMemory) ChipRead() (bool, ChipData) {
func (area *ChipMemory) ChipRead() (bool, bus.ChipData) {
if area.writeSignal {
area.writeSignal = false
return true, ChipData{Name: addresses.Write[area.lastWriteAddress], Value: area.writeData}
return true, bus.ChipData{Name: addresses.Write[area.lastWriteAddress], Value: area.writeData}
}
return false, ChipData{}
return false, bus.ChipData{}
}
// ChipWrite is an implementation of memory.ChipBus

View file

@ -57,32 +57,6 @@
//
// The arrow pointing away from the Cartridge area indicates that the CPU can
// only read from the cartridge, it cannot write to it. Unless that is, the
// cartridge has internal RAM. The differences in cartridge abilities in this
// regard is handled by the cartMapper interface.
//
// The cartMapper interface allows the transparent implementation of the
// different cartridge formats that have been used by the VCS. We've already
// mentioned cartridge RAM but the major difference betwen cartridge types is
// how they handle so-called bank-switching.
//
// The differences between the cartridge types is too much to go into here but
// a good reference for this can be found here:
//
// http://blog.kevtris.org/blogfiles/Atari%202600%20Mappers.txt
//
// Currently supported cartridge types are:
//
// - Atari 2k / 4k / 8k / 16k and 32k
//
// - the above with additional Superchip (additional RAM in other words)
//
// - Parker Bros.
//
// - MNetwork
//
// - Tigervision
//
// - CBS
//
// Other cartridge types can easily be added using the cartMapper system.
// cartridge has internal RAM. See the cartridge package documentation for the
// discussion on this.
package memory

View file

@ -3,22 +3,24 @@ package memory
import (
"gopher2600/errors"
"gopher2600/hardware/memory/addresses"
"gopher2600/hardware/memory/bus"
"gopher2600/hardware/memory/cartridge"
"gopher2600/hardware/memory/memorymap"
)
// VCSMemory is the monolithic representation of the memory in 2600.
type VCSMemory struct {
CPUBus
bus.CPUBus
// memmap is a hash for every address in the VCS address space, returning
// one of the four memory areas
Memmap []DebuggerBus
Memmap []bus.DebuggerBus
// the four memory areas
RIOT *ChipMemory
TIA *ChipMemory
PIA *PIA
Cart *Cartridge
Cart *cartridge.Cartridge
// the following are only used by the debugging interface. it would be
// lovely to remove these for non-debugging emulation but there's not much
@ -46,12 +48,12 @@ type VCSMemory struct {
func NewVCSMemory() (*VCSMemory, error) {
mem := &VCSMemory{}
mem.Memmap = make([]DebuggerBus, memorymap.Memtop+1)
mem.Memmap = make([]bus.DebuggerBus, memorymap.Memtop+1)
mem.RIOT = newRIOT()
mem.TIA = newTIA()
mem.PIA = newPIA()
mem.Cart = NewCartridge()
mem.Cart = cartridge.NewCartridge()
if mem.RIOT == nil || mem.TIA == nil || mem.PIA == nil || mem.Cart == nil {
return nil, errors.New(errors.MemoryError, "cannot create memory areas")
@ -59,19 +61,19 @@ func NewVCSMemory() (*VCSMemory, error) {
// create the memory map by associating all addresses in each memory area
// with that area
for i := mem.TIA.origin; i <= mem.TIA.memtop; i++ {
for i := memorymap.OriginTIA; i <= memorymap.MemtopTIA; i++ {
mem.Memmap[i] = mem.TIA
}
for i := mem.PIA.origin; i <= mem.PIA.memtop; i++ {
for i := memorymap.OriginPIA; i <= memorymap.MemtopPIA; i++ {
mem.Memmap[i] = mem.PIA
}
for i := mem.RIOT.origin; i <= mem.RIOT.memtop; i++ {
for i := memorymap.OriginRIOT; i <= memorymap.MemtopRIOT; i++ {
mem.Memmap[i] = mem.RIOT
}
for i := mem.Cart.origin; i <= mem.Cart.memtop; i++ {
for i := memorymap.OriginCart; i <= memorymap.MemtopCart; i++ {
mem.Memmap[i] = mem.Cart
}
@ -79,7 +81,7 @@ func NewVCSMemory() (*VCSMemory, error) {
}
// GetArea returns the actual memory of the specified area type
func (mem *VCSMemory) GetArea(area memorymap.Area) (DebuggerBus, error) {
func (mem *VCSMemory) GetArea(area memorymap.Area) (bus.DebuggerBus, error) {
switch area {
case memorymap.TIA:
return mem.TIA, nil
@ -104,7 +106,7 @@ func (mem *VCSMemory) Read(address uint16) (uint8, error) {
return 0, err
}
data, err := area.(CPUBus).Read(ma)
data, err := area.(bus.CPUBus).Read(ma)
// some memory areas do not change all the bits on the data bus, leaving
// some bits of the address in the result
@ -147,19 +149,11 @@ func (mem *VCSMemory) Write(address uint16, data uint8) error {
mem.LastAccessID = mem.accessCount
mem.accessCount++
// as incredible as it may seem some cartridges react to memory writes to
// addresses not in the cartridge space. for example, tigervision
// cartridges switch banks whenever any (non-mapped) address in the range
// 0x00 to 0x3f is written to.
err = mem.Cart.Listen(address, data)
// as incredible as it may seem tigervision cartridges react to memory
// writes to (unmapped) addresses in the range 0x00 to 0x3f. the Listen()
// function is a horrible solution to this but I can't see how else to
// handle it.
mem.Cart.Listen(address, data)
// the only error we expect from the cartMapper is and UnwritableAddress
// error, which most cartridge types will respond with in all circumstances
if err != nil {
if _, ok := err.(errors.AtariError); !ok {
return err
}
}
return area.(CPUBus).Write(ma, data)
return area.(bus.CPUBus).Write(ma, data)
}

View file

@ -2,14 +2,15 @@ package memory
import (
"fmt"
"gopher2600/hardware/memory/bus"
"gopher2600/hardware/memory/memorymap"
"strings"
)
// PIA defines the information for and operation allowed for PIA PIA
type PIA struct {
DebuggerBus
CPUBus
bus.DebuggerBus
bus.CPUBus
origin uint16
memtop uint16

View file

@ -1,11 +1,9 @@
// Panel uses the concurrent chip bus interface
package peripherals
import (
"gopher2600/errors"
"gopher2600/hardware/memory"
"gopher2600/hardware/memory/addresses"
"gopher2600/hardware/memory/bus"
"strings"
)
@ -15,7 +13,7 @@ type Panel struct {
id PeriphID
riot memory.PeriphBus
riot bus.PeriphBus
p0pro bool
p1pro bool
color bool
@ -24,7 +22,7 @@ type Panel struct {
}
// NewPanel is the preferred method of initialisation for the Panel type
func NewPanel(riot memory.PeriphBus) *Panel {
func NewPanel(riot bus.PeriphBus) *Panel {
pan := &Panel{
id: PanelID,
riot: riot,

View file

@ -2,8 +2,8 @@ package peripherals
import (
"gopher2600/errors"
"gopher2600/hardware/memory"
"gopher2600/hardware/memory/addresses"
"gopher2600/hardware/memory/bus"
)
// Ports is the containing structure for the two player ports
@ -13,7 +13,7 @@ type Ports struct {
}
// NewPorts is the preferred method of initialisation for the Ports type
func NewPorts(riot memory.PeriphBus, tia memory.PeriphBus, panel *Panel) *Ports {
func NewPorts(riot bus.PeriphBus, tia bus.PeriphBus, panel *Panel) *Ports {
return &Ports{
Player0: newPlayer0(riot, tia, panel),
Player1: newPlayer1(riot, tia, panel),
@ -26,8 +26,8 @@ type player struct {
id PeriphID
riot memory.PeriphBus
tia memory.PeriphBus
riot bus.PeriphBus
tia bus.PeriphBus
panel *Panel
// joystick
@ -48,7 +48,7 @@ type player struct {
buttonMask uint8
}
func newPlayer0(riot memory.PeriphBus, tia memory.PeriphBus, panel *Panel) *player {
func newPlayer0(riot bus.PeriphBus, tia bus.PeriphBus, panel *Panel) *player {
pl := &player{
id: PlayerZeroID,
riot: riot,
@ -71,7 +71,7 @@ func newPlayer0(riot memory.PeriphBus, tia memory.PeriphBus, panel *Panel) *play
return pl
}
func newPlayer1(riot memory.PeriphBus, tia memory.PeriphBus, panel *Panel) *player {
func newPlayer1(riot bus.PeriphBus, tia bus.PeriphBus, panel *Panel) *player {
pl := &player{
id: PlayerOneID,
riot: riot,

View file

@ -1,19 +1,19 @@
package riot
import (
"gopher2600/hardware/memory"
"gopher2600/hardware/memory/bus"
"strings"
)
// RIOT contains all the sub-components of the VCS RIOT sub-system
type RIOT struct {
mem memory.ChipBus
mem bus.ChipBus
Timer *timer
}
// NewRIOT creates a RIOT, to be used in a VCS emulation
func NewRIOT(mem memory.ChipBus) *RIOT {
func NewRIOT(mem bus.ChipBus) *RIOT {
riot := &RIOT{mem: mem}
riot.Timer = newTimer(mem)

View file

@ -2,12 +2,12 @@ package riot
import (
"fmt"
"gopher2600/hardware/memory"
"gopher2600/hardware/memory/addresses"
"gopher2600/hardware/memory/bus"
)
type timer struct {
mem memory.ChipBus
mem bus.ChipBus
// register is the name of the currently selected RIOT timer
register string
@ -45,7 +45,7 @@ type timer struct {
cyclesElapsed int
}
func newTimer(mem memory.ChipBus) *timer {
func newTimer(mem bus.ChipBus) *timer {
tmr := &timer{
mem: mem,
register: "TIM1024",
@ -71,7 +71,7 @@ func (tmr timer) String() string {
)
}
func (tmr *timer) serviceMemory(data memory.ChipData) bool {
func (tmr *timer) serviceMemory(data bus.ChipData) bool {
switch data.Name {
case "TIM1T":
tmr.register = data.Name

View file

@ -1,14 +1,12 @@
package audio
import (
"gopher2600/hardware/memory"
)
import "gopher2600/hardware/memory/bus"
// UpdateRegisters checks the TIA memory for changes to registers that are
// interesting to the audio sub-system
//
// Returns true if memory.ChipData has not been serviced.
func (au *Audio) UpdateRegisters(data memory.ChipData) bool {
func (au *Audio) UpdateRegisters(data bus.ChipData) bool {
switch data.Name {
case "AUDC0":
au.channel0.regControl = data.Value & 0x0f

View file

@ -2,7 +2,7 @@ package tia
import (
"gopher2600/errors"
"gopher2600/hardware/memory"
"gopher2600/hardware/memory/bus"
"gopher2600/television"
)
@ -34,7 +34,7 @@ func (tia *TIA) Step(serviceMemory bool) (bool, error) {
// update debugging information
tia.videoCycles++
var memoryData memory.ChipData
var memoryData bus.ChipData
// update memory if required
if serviceMemory {

View file

@ -2,7 +2,7 @@ package tia
import (
"fmt"
"gopher2600/hardware/memory"
"gopher2600/hardware/memory/bus"
"gopher2600/hardware/tia/audio"
"gopher2600/hardware/tia/future"
"gopher2600/hardware/tia/phaseclock"
@ -15,7 +15,7 @@ import (
// TIA contains all the sub-components of the VCS TIA sub-system
type TIA struct {
tv television.Television
mem memory.ChipBus
mem bus.ChipBus
// number of video cycles since the last WSYNC. also cycles back to 0 on
// RSYNC and when polycounter reaches count 56
@ -92,7 +92,7 @@ func (tia TIA) String() string {
}
// NewTIA creates a TIA, to be used in a VCS emulation
func NewTIA(tv television.Television, mem memory.ChipBus) *TIA {
func NewTIA(tv television.Television, mem bus.ChipBus) *TIA {
tia := TIA{tv: tv, mem: mem, hblank: true}
var err error
@ -124,7 +124,7 @@ func NewTIA(tv television.Television, mem memory.ChipBus) *TIA {
// UpdateTIA checks for side effects in the TIA sub-system.
//
// Returns true if ChipData has *not* been serviced.
func (tia *TIA) UpdateTIA(data memory.ChipData) bool {
func (tia *TIA) UpdateTIA(data bus.ChipData) bool {
switch data.Name {
case "VSYNC":
tia.sig.VSync = data.Value&0x02 == 0x02

View file

@ -2,12 +2,12 @@ package video
import (
"fmt"
"gopher2600/hardware/memory"
"gopher2600/hardware/memory/addresses"
"gopher2600/hardware/memory/bus"
)
type collisions struct {
mem memory.ChipBus
mem bus.ChipBus
cxm0p uint8
cxm1p uint8
@ -19,7 +19,7 @@ type collisions struct {
cxppmm uint8
}
func newCollisions(mem memory.ChipBus) *collisions {
func newCollisions(mem bus.ChipBus) *collisions {
col := &collisions{mem: mem}
col.clear()
return col

View file

@ -1,8 +1,8 @@
package video
import (
"gopher2600/hardware/memory"
"gopher2600/hardware/memory/addresses"
"gopher2600/hardware/memory/bus"
"gopher2600/hardware/tia/future"
"gopher2600/hardware/tia/phaseclock"
"gopher2600/hardware/tia/polycounter"
@ -34,7 +34,7 @@ type Video struct {
// pisel locatoin of the sprites in relation to the hsync counter (or
// screen)
func NewVideo(pclk *phaseclock.PhaseClock, hsync *polycounter.Polycounter,
mem memory.ChipBus, tv television.Television,
mem bus.ChipBus, tv television.Television,
hblank, hmoveLatch *bool) *Video {
vd := &Video{
@ -301,7 +301,7 @@ func (vd *Video) Pixel() (uint8, television.DebugColorSignal) {
// CTRLPF is serviced in UpdateSpriteVariations()
//
// Returns true if ChipData has *not* been serviced.
func (vd *Video) UpdatePlayfield(tiaDelay future.Scheduler, data memory.ChipData) bool {
func (vd *Video) UpdatePlayfield(tiaDelay future.Scheduler, data bus.ChipData) bool {
// homebrew Donkey Kong shows the need for a delay of at least two cycles
// to write new playfield data
switch data.Name {
@ -322,7 +322,7 @@ func (vd *Video) UpdatePlayfield(tiaDelay future.Scheduler, data memory.ChipData
// require a short pause, using the TIA scheduler
//
// Returns true if ChipData has *not* been serviced.
func (vd *Video) UpdateSpriteHMOVE(tiaDelay future.Scheduler, data memory.ChipData) bool {
func (vd *Video) UpdateSpriteHMOVE(tiaDelay future.Scheduler, data bus.ChipData) bool {
switch data.Name {
// horizontal movement values range from -8 to +7 for convenience we
// convert this to the range 0 to 15. from TIA_HW_Notes.txt:
@ -375,7 +375,7 @@ func (vd *Video) UpdateSpriteHMOVE(tiaDelay future.Scheduler, data memory.ChipDa
// registers
//
// Returns true if memory.ChipData has not been serviced.
func (vd *Video) UpdateSpritePositioning(data memory.ChipData) bool {
func (vd *Video) UpdateSpritePositioning(data bus.ChipData) bool {
switch data.Name {
// the reset registers *must* be serviced after HSYNC has been ticked.
// resets are resolved after a short delay, governed by the sprite itself
@ -399,7 +399,7 @@ func (vd *Video) UpdateSpritePositioning(data memory.ChipData) bool {
// UpdateColor checks the TIA memory for changes to color registers
//
// Returns true if memory.ChipData has not been serviced.
func (vd *Video) UpdateColor(data memory.ChipData) bool {
func (vd *Video) UpdateColor(data bus.ChipData) bool {
switch data.Name {
case "COLUP0":
vd.Player0.setColor(data.Value & 0xfe)
@ -423,7 +423,7 @@ func (vd *Video) UpdateColor(data memory.ChipData) bool {
// occur after a call to Pixel()
//
// Returns true if memory.ChipData has not been serviced.
func (vd *Video) UpdateSpritePixels(data memory.ChipData) bool {
func (vd *Video) UpdateSpritePixels(data bus.ChipData) bool {
// the barnstormer ROM demonstrate perfectly how GRP0 is affected if we
// alter its state before a call to Pixel(). if we write do alter state
// before Pixel(), then an unwanted artefact can be seen on scanline 61.
@ -450,7 +450,7 @@ func (vd *Video) UpdateSpritePixels(data memory.ChipData) bool {
// because it affects the ball sprite.
//
// Returns true if memory.ChipData has not been serviced.
func (vd *Video) UpdateSpriteVariations(data memory.ChipData) bool {
func (vd *Video) UpdateSpriteVariations(data bus.ChipData) bool {
switch data.Name {
case "CTRLPF":
vd.Ball.setSize((data.Value & 0x30) >> 4)

View file

@ -31,7 +31,6 @@ const fieldSep = ", "
const (
lineMagicString int = iota
lineCartName
lineCartFormat
lineCartHash
lineTVtype
numHeaderLines
@ -45,7 +44,6 @@ func (rec *Recorder) writeHeader() error {
// add header information
lines[lineMagicString] = magicString
lines[lineCartName] = rec.vcs.Mem.Cart.Filename
lines[lineCartFormat] = rec.vcs.Mem.Cart.RequestedFormat
lines[lineCartHash] = rec.vcs.Mem.Cart.Hash
lines[lineTVtype] = fmt.Sprintf("%v\n", rec.vcs.TV.GetSpec().ID)
@ -73,7 +71,6 @@ func (plb *Playback) readHeader(lines []string) error {
// read header
plb.CartLoad.Filename = lines[lineCartName]
plb.CartLoad.Format = lines[lineCartFormat]
plb.CartLoad.Hash = lines[lineCartHash]
plb.TVtype = lines[lineTVtype]