mirror of
https://github.com/JetSetIlly/Gopher2600.git
synced 2024-06-15 19:17:34 -04:00
remove Poke() from cartMapper interface. replaced with an argument to
Write() all cartridge types are now poke-able all cartridge types are no patch-able reworked error types/messages. replaced some errors with panics
This commit is contained in:
parent
4422f195e8
commit
39fd8381dd
|
@ -298,12 +298,14 @@ func (dbg *Debugger) processTokens(tokens *commandline.Tokens) (bool, error) {
|
|||
}
|
||||
}
|
||||
case "STATIC":
|
||||
// !TODO: poke/peek static cartridge static data areas
|
||||
if db := dbg.VCS.Mem.Cart.GetDebugBus(); db != nil {
|
||||
dbg.printInstrument(db.GetStatic())
|
||||
} else {
|
||||
dbg.printLine(terminal.StyleFeedback, "cartridge has no static data areas")
|
||||
}
|
||||
case "REGISTERS":
|
||||
// !TODO: poke/peek cartridge registers
|
||||
bus := dbg.VCS.Mem.Cart.GetDebugBus()
|
||||
if bus != nil {
|
||||
dbg.printInstrument(bus.GetRegisters())
|
||||
|
@ -312,6 +314,8 @@ func (dbg *Debugger) processTokens(tokens *commandline.Tokens) (bool, error) {
|
|||
}
|
||||
|
||||
case "RAM":
|
||||
// cartridge RAM is accessible through the normal VCS buses so
|
||||
// the normal peek/poke commands will work
|
||||
bus := dbg.VCS.Mem.Cart.GetRAMbus()
|
||||
if bus != nil {
|
||||
s := &strings.Builder{}
|
||||
|
@ -725,10 +729,14 @@ func (dbg *Debugger) processTokens(tokens *commandline.Tokens) (bool, error) {
|
|||
// get address token
|
||||
a, _ := tokens.Get()
|
||||
|
||||
// convert address
|
||||
// convert address. note that the calls to dbgmem.poke() also call
|
||||
// mapAddress(). the reason we map the address here is because we want
|
||||
// a numeric address that we can iterate with in the for loop below.
|
||||
// simply converting to a number is no good because we want the user to
|
||||
// be able to specify an address by name, so we may as well just call
|
||||
// mapAddress, even if it does seem redundant.
|
||||
ai := dbg.dbgmem.mapAddress(a, false)
|
||||
if ai == nil {
|
||||
// using poke error because hexload is basically the same as poking
|
||||
dbg.printLine(terminal.StyleError, errors.New(errors.UnpokeableAddress, a).Error())
|
||||
return false, nil
|
||||
}
|
||||
|
@ -745,7 +753,6 @@ func (dbg *Debugger) processTokens(tokens *commandline.Tokens) (bool, error) {
|
|||
continue // for loop (without advancing address)
|
||||
}
|
||||
|
||||
// perform individual poke
|
||||
ai, err := dbg.dbgmem.poke(addr, uint8(val))
|
||||
if err != nil {
|
||||
dbg.printLine(terminal.StyleError, "%s", err)
|
||||
|
|
|
@ -148,12 +148,10 @@ func (dbgmem memoryDebug) peek(address interface{}) (*addressInfo, error) {
|
|||
return nil, errors.New(errors.DebuggerError, errors.New(errors.UnpeekableAddress, address))
|
||||
}
|
||||
|
||||
ar, err := dbgmem.mem.GetArea(ai.area)
|
||||
if err != nil {
|
||||
return nil, errors.New(errors.DebuggerError, err)
|
||||
}
|
||||
area := dbgmem.mem.GetArea(ai.area)
|
||||
|
||||
ai.data, err = ar.Peek(ai.mappedAddress)
|
||||
var err error
|
||||
ai.data, err = area.Peek(ai.mappedAddress)
|
||||
if err != nil {
|
||||
return nil, errors.New(errors.DebuggerError, err)
|
||||
}
|
||||
|
@ -171,12 +169,9 @@ func (dbgmem memoryDebug) poke(address interface{}, data uint8) (*addressInfo, e
|
|||
return nil, errors.New(errors.DebuggerError, errors.New(errors.UnpokeableAddress, address))
|
||||
}
|
||||
|
||||
ar, err := dbgmem.mem.GetArea(ai.area)
|
||||
if err != nil {
|
||||
return nil, errors.New(errors.DebuggerError, err)
|
||||
}
|
||||
area := dbgmem.mem.GetArea(ai.area)
|
||||
|
||||
err = ar.Poke(ai.mappedAddress, data)
|
||||
err := area.Poke(ai.mappedAddress, data)
|
||||
if err != nil {
|
||||
return nil, errors.New(errors.DebuggerError, err)
|
||||
}
|
||||
|
|
|
@ -215,18 +215,6 @@ func parseTarget(dbg *Debugger, tokens *commandline.Tokens) (*target, error) {
|
|||
},
|
||||
}
|
||||
|
||||
case "BUS":
|
||||
trg = &target{
|
||||
label: "Bus Error",
|
||||
currentValue: func() interface{} {
|
||||
s := dbg.VCS.CPU.LastResult.BusError
|
||||
if s == "" {
|
||||
return "ok"
|
||||
}
|
||||
return s
|
||||
},
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, errors.New(errors.InvalidTarget, fmt.Sprintf("%s %s", keyword, subkey))
|
||||
}
|
||||
|
|
|
@ -105,16 +105,15 @@ const (
|
|||
CPUBug = "cpu bug: %v"
|
||||
|
||||
// memory
|
||||
MemoryError = "memory error: %v"
|
||||
UnpokeableAddress = "memory error: cannot poke address (%v)"
|
||||
UnpeekableAddress = "memory error: cannot peek address (%v)"
|
||||
BusError = "bus error: address %#04x"
|
||||
MemoryBusError = "memory error: inaccessible address (%v)"
|
||||
|
||||
// cartridges
|
||||
CartridgeError = "cartridge error: %v"
|
||||
CartridgeEjected = "cartridge error: no cartridge attached"
|
||||
UnpatchableCartType = "cartridge error: cannot patch this cartridge type (%v)"
|
||||
CartridgeStaticOOB = "cartridge error: static data address to high (%#04x)"
|
||||
CartridgeError = "cartridge error: %v"
|
||||
CartridgeEjected = "cartridge error: no cartridge attached"
|
||||
CartridgePatchOOB = "cartrdige error: patch offset too high (%#04x)"
|
||||
CartridgeStaticOOB = "cartridge error: static data address too high (%#04x)"
|
||||
|
||||
// input
|
||||
UnknownInputEvent = "input error: %v: unsupported event (%v)"
|
||||
|
|
|
@ -189,10 +189,10 @@ func (mc *CPU) read8Bit(address uint16) (uint8, error) {
|
|||
val, err := mc.mem.Read(address)
|
||||
|
||||
if err != nil {
|
||||
if !errors.Is(err, errors.BusError) {
|
||||
if !errors.Is(err, errors.MemoryBusError) {
|
||||
return 0, err
|
||||
}
|
||||
mc.LastResult.BusError = err.Error()
|
||||
mc.LastResult.Error = err.Error()
|
||||
}
|
||||
|
||||
// +1 cycle
|
||||
|
@ -211,10 +211,10 @@ func (mc *CPU) read8BitZeroPage(address uint8) (uint8, error) {
|
|||
val, err := mc.mem.ReadZeroPage(address)
|
||||
|
||||
if err != nil {
|
||||
if !errors.Is(err, errors.BusError) {
|
||||
if !errors.Is(err, errors.MemoryBusError) {
|
||||
return 0, err
|
||||
}
|
||||
mc.LastResult.BusError = err.Error()
|
||||
mc.LastResult.Error = err.Error()
|
||||
}
|
||||
|
||||
// +1 cycle
|
||||
|
@ -237,10 +237,10 @@ func (mc *CPU) write8Bit(address uint16, value uint8) error {
|
|||
if err != nil {
|
||||
// don't worry about unwritable addresses (unless strict addressing
|
||||
// is on)
|
||||
if !errors.Is(err, errors.BusError) {
|
||||
if !errors.Is(err, errors.MemoryBusError) {
|
||||
return err
|
||||
}
|
||||
mc.LastResult.BusError = err.Error()
|
||||
mc.LastResult.Error = err.Error()
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -252,10 +252,10 @@ func (mc *CPU) write8Bit(address uint16, value uint8) error {
|
|||
func (mc *CPU) read16Bit(address uint16) (uint16, error) {
|
||||
lo, err := mc.mem.Read(address)
|
||||
if err != nil {
|
||||
if !errors.Is(err, errors.BusError) {
|
||||
if !errors.Is(err, errors.MemoryBusError) {
|
||||
return 0, err
|
||||
}
|
||||
mc.LastResult.BusError = err.Error()
|
||||
mc.LastResult.Error = err.Error()
|
||||
}
|
||||
|
||||
// +1 cycle
|
||||
|
@ -266,10 +266,10 @@ func (mc *CPU) read16Bit(address uint16) (uint16, error) {
|
|||
|
||||
hi, err := mc.mem.Read(address + 1)
|
||||
if err != nil {
|
||||
if !errors.Is(err, errors.BusError) {
|
||||
if !errors.Is(err, errors.MemoryBusError) {
|
||||
return 0, err
|
||||
}
|
||||
mc.LastResult.BusError = err.Error()
|
||||
mc.LastResult.Error = err.Error()
|
||||
}
|
||||
|
||||
// +1 cycle
|
||||
|
@ -291,10 +291,10 @@ func (mc *CPU) read8BitPC(val *uint8, f func() error) error {
|
|||
v, err := mc.mem.Read(mc.PC.Address())
|
||||
|
||||
if err != nil {
|
||||
if !errors.Is(err, errors.BusError) {
|
||||
if !errors.Is(err, errors.MemoryBusError) {
|
||||
return err
|
||||
}
|
||||
mc.LastResult.BusError = err.Error()
|
||||
mc.LastResult.Error = err.Error()
|
||||
}
|
||||
|
||||
carry, _ := mc.PC.Add(1)
|
||||
|
@ -601,19 +601,19 @@ func (mc *CPU) ExecuteInstruction(cycleCallback func() error) error {
|
|||
|
||||
lo, err := mc.mem.Read(indirectAddress)
|
||||
if err != nil {
|
||||
if !errors.Is(err, errors.BusError) {
|
||||
if !errors.Is(err, errors.MemoryBusError) {
|
||||
return err
|
||||
}
|
||||
mc.LastResult.BusError = err.Error()
|
||||
mc.LastResult.Error = err.Error()
|
||||
}
|
||||
|
||||
// +1 cycle
|
||||
err = mc.endCycle()
|
||||
if err != nil {
|
||||
if !errors.Is(err, errors.BusError) {
|
||||
if !errors.Is(err, errors.MemoryBusError) {
|
||||
return err
|
||||
}
|
||||
mc.LastResult.BusError = err.Error()
|
||||
mc.LastResult.Error = err.Error()
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -66,7 +66,7 @@ func (mem *mockMem) Clear() {
|
|||
|
||||
func (mem mockMem) Read(address uint16) (uint8, error) {
|
||||
if address&0xff00 == 0xff00 {
|
||||
return 0, errors.New(errors.BusError, address)
|
||||
return 0, errors.New(errors.MemoryBusError, address)
|
||||
}
|
||||
return mem.internal[address], nil
|
||||
}
|
||||
|
@ -77,7 +77,7 @@ func (mem mockMem) ReadZeroPage(address uint8) (uint8, error) {
|
|||
|
||||
func (mem *mockMem) Write(address uint16, data uint8) error {
|
||||
if address&0xff00 == 0xff00 {
|
||||
return errors.New(errors.BusError, address)
|
||||
return errors.New(errors.MemoryBusError, address)
|
||||
}
|
||||
mem.internal[address] = data
|
||||
return nil
|
||||
|
|
|
@ -66,8 +66,8 @@ type Result struct {
|
|||
// whether a known buggy code path (in the emulated CPU) was triggered
|
||||
CPUBug string
|
||||
|
||||
// whether the last memory access resulted in a bus error
|
||||
BusError string
|
||||
// error string. will be a memory access error
|
||||
Error string
|
||||
|
||||
// whether this data has been finalised - some fields in this struct will
|
||||
// be undefined if Final is false
|
||||
|
@ -83,6 +83,6 @@ func (r *Result) Reset() {
|
|||
r.ActualCycles = 0
|
||||
r.PageFault = false
|
||||
r.CPUBug = ""
|
||||
r.BusError = ""
|
||||
r.Error = ""
|
||||
r.Final = false
|
||||
}
|
||||
|
|
|
@ -33,27 +33,28 @@ type DebuggerBus interface {
|
|||
Poke(address uint16, value uint8) error
|
||||
}
|
||||
|
||||
// CartDebugBus defines the operations required for a debugger to access
|
||||
// non-addressable areas of a cartridge. You have to know the precise cartridge
|
||||
// mapper for PutRegister() to work effectively.
|
||||
// CartDebugBus defines the operations required for a debugger to access the
|
||||
// static and special function areas of a cartrudge.
|
||||
//
|
||||
// The mapper is allowed to panic if it is not interfaced with correctly.
|
||||
//
|
||||
// So what's the point of the interface if you need to know the details of the
|
||||
// underlying type? Well, it goes some way to helping us understand what parts
|
||||
// of the cartridge are beyond the scope of the regular buses.
|
||||
// You should know the precise cartridge mapper for the CartRegister and
|
||||
// CartStatic type to be usable.
|
||||
//
|
||||
// Primarily though, it is useful when used in conjunction with the lazy
|
||||
// evaluation system used by GUI systems running in a different goroutine. The
|
||||
// point of the lazy system is to prevent race conditions, the way we do that
|
||||
// is to make a copy of the system variable before using it in the GUI. Now,
|
||||
// because we must know the internals of the cartridge format, could we not
|
||||
// just make those copies manually? Well we could, but it would mean another
|
||||
// place where the cartridge internal knowledge needs to be coded (we need to
|
||||
// use that knowledge in the GUI code but it would be nice to avoid it in the
|
||||
// lazy system). The GetRegisters() allows us to conceptualise the copying
|
||||
// process and to keep the details inside the cartridge implementation as much
|
||||
// as possible.
|
||||
// So what's the point of the interface if you need to know the details of the
|
||||
// underlying type? Couldn't we just use a type assertion?
|
||||
//
|
||||
// Yes, but doing it this way helps with the lazy evaluation system used by
|
||||
// debugging GUIs. The point of the lazy system is to prevent race conditions
|
||||
// and the way we do that is to make copies of system variables before using it
|
||||
// in the GUI. Now, because we must know the internals of the cartridge format,
|
||||
// could we not just make those copies manually? Again, yes. But that would
|
||||
// mean another place where the cartridge's internal knowledge needs to be
|
||||
// coded (we need to use that knowledge in the GUI code but it would be nice to
|
||||
// avoid it in the lazy system).
|
||||
//
|
||||
// The GetRegisters() allows us to conceptualise the copying process and to
|
||||
// keep the details inside the cartridge implementation as much as possible.
|
||||
type CartDebugBus interface {
|
||||
// GetRegisters returns a copy of the cartridge's registers
|
||||
GetRegisters() CartRegisters
|
||||
|
|
|
@ -65,16 +65,14 @@ func (cart *Cartridge) Peek(addr uint16) (uint8, error) {
|
|||
return cart.Read(addr)
|
||||
}
|
||||
|
||||
// Poke is an implementation of memory.DebuggerBus. This poke pokes the current
|
||||
// cartridge bank. See Patch for a different method. Address must be
|
||||
// normalised.
|
||||
// Poke is an implementation of memory.DebuggerBus. Address must be normalised.
|
||||
func (cart *Cartridge) Poke(addr uint16, data uint8) error {
|
||||
return cart.mapper.Poke(addr^memorymap.OriginCart, data)
|
||||
return cart.mapper.Write(addr^memorymap.OriginCart, data, true)
|
||||
}
|
||||
|
||||
// Patch writes to cartridge memory. Offset is measured from the start of
|
||||
// cartridge memory. It differs from Poke in that respect
|
||||
func (cart *Cartridge) Patch(offset uint16, data uint8) error {
|
||||
func (cart *Cartridge) Patch(offset int, data uint8) error {
|
||||
return cart.mapper.Patch(offset, data)
|
||||
}
|
||||
|
||||
|
@ -85,7 +83,7 @@ func (cart *Cartridge) Read(addr uint16) (uint8, error) {
|
|||
|
||||
// Write is an implementation of memory.CPUBus. Address must be normalised.
|
||||
func (cart *Cartridge) Write(addr uint16, data uint8) error {
|
||||
return cart.mapper.Write(addr^memorymap.OriginCart, data)
|
||||
return cart.mapper.Write(addr^memorymap.OriginCart, data, false)
|
||||
}
|
||||
|
||||
// Eject removes memory from cartridge space and unlike the real hardware,
|
||||
|
|
|
@ -32,8 +32,9 @@ type dpcPlus struct {
|
|||
description string
|
||||
|
||||
// banks and the currently selected bank
|
||||
banks [][]byte
|
||||
bank int
|
||||
bankSize int
|
||||
banks [][]byte
|
||||
bank int
|
||||
|
||||
registers DPCplusRegisters
|
||||
static DPCplusStatic
|
||||
|
@ -44,24 +45,36 @@ type dpcPlus struct {
|
|||
// music fetchers are clocked at a fixed (slower) rate than the reference
|
||||
// to the VCS's clock. see Step() function.
|
||||
beats int
|
||||
|
||||
// patch help. offsets in the original data file for the different areas
|
||||
// in the cartridge
|
||||
//
|
||||
// we only do this because of the complexity of the dpcPlus file and only
|
||||
// for the purposes of the Patch() function. we don't bother with anything
|
||||
// like this for the simpler cartridge formats
|
||||
banksOffset int
|
||||
dataOffset int
|
||||
freqOffset int
|
||||
fileSize int
|
||||
}
|
||||
|
||||
// NewDPCplus is the preferred method of initialisation for the harmony type
|
||||
func NewDPCplus(data []byte) (*dpcPlus, error) {
|
||||
const armSize = 3072
|
||||
const bankSize = 4096
|
||||
const dataSize = 4096
|
||||
const freqSize = 1024
|
||||
|
||||
cart := &dpcPlus{}
|
||||
cart.mappingID = "DPC+"
|
||||
cart.description = "DPC+ (Harmony)"
|
||||
cart := &dpcPlus{
|
||||
mappingID: "DPC+",
|
||||
description: "DPC+ (Harmony)",
|
||||
bankSize: 4096,
|
||||
}
|
||||
|
||||
// amount of data used for cartridges
|
||||
bankLen := len(data) - dataSize - armSize - freqSize
|
||||
|
||||
// size check
|
||||
if bankLen%bankSize != 0 {
|
||||
if bankLen%cart.bankSize != 0 {
|
||||
return nil, errors.New(errors.CartridgeError, fmt.Sprintf("%s: %d bytes not supported", cart.mappingID, len(data)))
|
||||
}
|
||||
|
||||
|
@ -69,24 +82,30 @@ func NewDPCplus(data []byte) (*dpcPlus, error) {
|
|||
cart.static.Arm = data[:armSize]
|
||||
|
||||
// allocate enough banks
|
||||
cart.banks = make([][]uint8, bankLen/bankSize)
|
||||
cart.banks = make([][]uint8, bankLen/cart.bankSize)
|
||||
|
||||
// partition data into banks
|
||||
for k := 0; k < cart.NumBanks(); k++ {
|
||||
cart.banks[k] = make([]uint8, bankSize)
|
||||
offset := k * bankSize
|
||||
cart.banks[k] = make([]uint8, cart.bankSize)
|
||||
offset := k * cart.bankSize
|
||||
offset += armSize
|
||||
cart.banks[k] = data[offset : offset+bankSize]
|
||||
cart.banks[k] = data[offset : offset+cart.bankSize]
|
||||
}
|
||||
|
||||
// gfx and frequency table at end of file
|
||||
s := armSize + (bankSize * cart.NumBanks())
|
||||
cart.static.Data = data[s : s+dataSize]
|
||||
cart.static.Freq = data[s+dataSize:]
|
||||
dataOffset := armSize + (cart.bankSize * cart.NumBanks())
|
||||
cart.static.Data = data[dataOffset : dataOffset+dataSize]
|
||||
cart.static.Freq = data[dataOffset+dataSize:]
|
||||
|
||||
// initialise cartridge before returning success
|
||||
cart.Initialise()
|
||||
|
||||
// patch offsets
|
||||
cart.banksOffset = armSize
|
||||
cart.dataOffset = dataOffset
|
||||
cart.freqOffset = dataOffset + dataSize
|
||||
cart.fileSize = len(data)
|
||||
|
||||
return cart, nil
|
||||
}
|
||||
|
||||
|
@ -141,7 +160,7 @@ func (cart *dpcPlus) Read(addr uint16) (uint8, error) {
|
|||
}
|
||||
|
||||
if addr > 0x0027 {
|
||||
return 0, errors.New(errors.BusError, addr)
|
||||
return 0, errors.New(errors.MemoryBusError, addr)
|
||||
}
|
||||
|
||||
switch addr {
|
||||
|
@ -260,7 +279,7 @@ func (cart *dpcPlus) Read(addr uint16) (uint8, error) {
|
|||
return data, nil
|
||||
}
|
||||
|
||||
func (cart *dpcPlus) Write(addr uint16, data uint8) error {
|
||||
func (cart *dpcPlus) Write(addr uint16, data uint8, poke bool) error {
|
||||
// if address is above register space then we only need to check for bank
|
||||
// switching before returning data at the quoted address
|
||||
if addr == 0x0ff6 {
|
||||
|
@ -278,7 +297,7 @@ func (cart *dpcPlus) Write(addr uint16, data uint8) error {
|
|||
}
|
||||
|
||||
if addr < 0x0028 || addr > 0x007f {
|
||||
return errors.New(errors.BusError, addr)
|
||||
return errors.New(errors.MemoryBusError, addr)
|
||||
}
|
||||
|
||||
switch addr {
|
||||
|
@ -548,7 +567,12 @@ func (cart *dpcPlus) Write(addr uint16, data uint8) error {
|
|||
cart.registers.Fetcher[f].inc()
|
||||
}
|
||||
|
||||
return nil
|
||||
if poke {
|
||||
cart.banks[cart.bank][addr] = data
|
||||
return nil
|
||||
}
|
||||
|
||||
return errors.New(errors.MemoryBusError, addr)
|
||||
}
|
||||
|
||||
func (cart dpcPlus) NumBanks() int {
|
||||
|
@ -572,12 +596,24 @@ func (cart *dpcPlus) RestoreState(state interface{}) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (cart *dpcPlus) Poke(addr uint16, data uint8) error {
|
||||
return errors.New(errors.UnpokeableAddress, addr)
|
||||
}
|
||||
func (cart *dpcPlus) Patch(offset int, data uint8) error {
|
||||
if offset >= cart.fileSize {
|
||||
return errors.New(errors.CartridgePatchOOB, offset)
|
||||
}
|
||||
|
||||
func (cart *dpcPlus) Patch(addr uint16, data uint8) error {
|
||||
return errors.New(errors.UnpatchableCartType, cart.description)
|
||||
if offset >= cart.freqOffset {
|
||||
cart.static.Freq[offset-cart.freqOffset] = data
|
||||
} else if offset >= cart.dataOffset {
|
||||
cart.static.Data[offset-cart.dataOffset] = data
|
||||
} else if offset >= cart.banksOffset {
|
||||
bank := int(offset) / cart.bankSize
|
||||
offset = offset % cart.bankSize
|
||||
cart.banks[bank][offset] = data
|
||||
} else {
|
||||
cart.static.Arm[offset-cart.banksOffset] = data
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cart *dpcPlus) Listen(addr uint16, data uint8) {
|
||||
|
|
|
@ -27,23 +27,18 @@ type cartMapper interface {
|
|||
Initialise()
|
||||
ID() string
|
||||
Read(addr uint16) (data uint8, err error)
|
||||
Write(addr uint16, data uint8) error
|
||||
Write(addr uint16, data uint8, poke bool) error
|
||||
NumBanks() int
|
||||
GetBank(addr uint16) (bank int)
|
||||
SetBank(addr uint16, bank int) error
|
||||
SaveState() interface{}
|
||||
RestoreState(interface{}) error
|
||||
|
||||
// poke new value anywhere into currently selected bank of cartridge memory
|
||||
// (including ROM).
|
||||
Poke(addr uint16, data uint8) error
|
||||
|
||||
// cartMapper does not need a dedicated Peek() function. the Cartridge type
|
||||
// implements Peek() and can just call the cartMapper's Read() function
|
||||
|
||||
// patch differs from poke in that it alters the data as though it was
|
||||
// being read from disk
|
||||
Patch(offset uint16, data uint8) error
|
||||
// patch differs from write/poke in that it alters the data as though it
|
||||
// was being read from disk. that is, the offset is measured from the start
|
||||
// of the file. the cartmapper must translate the offset and update the
|
||||
// correct data structure as appropriate.
|
||||
Patch(offset int, data uint8) error
|
||||
|
||||
// see the commentary for the Listen() function in the Cartridge type for
|
||||
// an explanation for what this does
|
||||
|
|
|
@ -72,11 +72,10 @@ type atari struct {
|
|||
mappingID string
|
||||
description string
|
||||
|
||||
bankSize int
|
||||
|
||||
// atari formats apart from 2k and 4k are divided into banks. 2k and 4k
|
||||
// ROMs conceptually have one bank
|
||||
banks [][]uint8
|
||||
bankSize int
|
||||
banks [][]uint8
|
||||
|
||||
// identifies the currently selected bank
|
||||
bank int
|
||||
|
@ -155,13 +154,19 @@ func (cart *atari) Read(addr uint16) (uint8, bool) {
|
|||
}
|
||||
|
||||
// Write implements the cartMapper interface
|
||||
func (cart *atari) Write(addr uint16, data uint8) bool {
|
||||
func (cart *atari) Write(addr uint16, data uint8, poke bool) bool {
|
||||
if cart.ram != nil {
|
||||
if addr <= 127 {
|
||||
cart.ram[addr] = data
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if poke {
|
||||
cart.banks[cart.bank][addr] = data
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
|
@ -194,22 +199,20 @@ func (cart *atari) addSuperchip() bool {
|
|||
return true
|
||||
}
|
||||
|
||||
// Poke implements the cartMapper interface
|
||||
func (cart *atari) Poke(addr uint16, data uint8) error {
|
||||
cart.banks[cart.bank][addr] = data
|
||||
return nil
|
||||
}
|
||||
|
||||
// Patch implements the cartMapper interface
|
||||
func (cart *atari) Patch(addr uint16, data uint8) error {
|
||||
bank := int(addr) / cart.bankSize
|
||||
addr = addr % uint16(cart.bankSize)
|
||||
cart.banks[bank][addr] = data
|
||||
func (cart *atari) Patch(offset int, data uint8) error {
|
||||
if offset >= cart.bankSize*len(cart.banks) {
|
||||
return errors.New(errors.CartridgePatchOOB, offset)
|
||||
}
|
||||
|
||||
bank := int(offset) / cart.bankSize
|
||||
offset = offset % cart.bankSize
|
||||
cart.banks[bank][offset] = data
|
||||
return nil
|
||||
}
|
||||
|
||||
// Listen implements the cartMapper interface
|
||||
func (cart *atari) Listen(addr uint16, data uint8) {
|
||||
func (cart *atari) Listen(_ uint16, _ uint8) {
|
||||
}
|
||||
|
||||
// Step implements the cartMapper interface
|
||||
|
@ -284,12 +287,12 @@ func (cart *atari4k) Read(addr uint16) (uint8, error) {
|
|||
}
|
||||
|
||||
// Write implements the cartMapper interface
|
||||
func (cart *atari4k) Write(addr uint16, data uint8) error {
|
||||
if ok := cart.atari.Write(addr, data); ok {
|
||||
func (cart *atari4k) Write(addr uint16, data uint8, poke bool) error {
|
||||
if ok := cart.atari.Write(addr, data, poke); ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
return errors.New(errors.BusError, addr)
|
||||
return errors.New(errors.MemoryBusError, addr)
|
||||
}
|
||||
|
||||
// atari2k is the half-size cartridge of 2048 bytes
|
||||
|
@ -335,12 +338,12 @@ func (cart *atari2k) Read(addr uint16) (uint8, error) {
|
|||
}
|
||||
|
||||
// Write implements the cartMapper interface
|
||||
func (cart *atari2k) Write(addr uint16, data uint8) error {
|
||||
if ok := cart.atari.Write(addr, data); ok {
|
||||
func (cart *atari2k) Write(addr uint16, data uint8, poke bool) error {
|
||||
if ok := cart.atari.Write(addr, data, poke); ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
return errors.New(errors.BusError, addr)
|
||||
return errors.New(errors.MemoryBusError, addr)
|
||||
}
|
||||
|
||||
// atari8k (F8)
|
||||
|
@ -396,17 +399,15 @@ func (cart *atari8k) Read(addr uint16) (uint8, error) {
|
|||
}
|
||||
|
||||
// Write implements the cartMapper interface
|
||||
func (cart *atari8k) Write(addr uint16, data uint8) error {
|
||||
if ok := cart.atari.Write(addr, data); ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cart *atari8k) Write(addr uint16, data uint8, poke bool) error {
|
||||
if addr == 0x0ff8 {
|
||||
cart.bank = 0
|
||||
} else if addr == 0x0ff9 {
|
||||
cart.bank = 1
|
||||
} else {
|
||||
return errors.New(errors.BusError, addr)
|
||||
}
|
||||
|
||||
if ok := cart.atari.Write(addr, data, poke); !ok {
|
||||
return errors.New(errors.MemoryBusError, addr)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -470,11 +471,7 @@ func (cart *atari16k) Read(addr uint16) (uint8, error) {
|
|||
}
|
||||
|
||||
// Write implements the cartMapper interface
|
||||
func (cart *atari16k) Write(addr uint16, data uint8) error {
|
||||
if ok := cart.atari.Write(addr, data); ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cart *atari16k) Write(addr uint16, data uint8, poke bool) error {
|
||||
if addr == 0x0ff6 {
|
||||
cart.bank = 0
|
||||
} else if addr == 0x0ff7 {
|
||||
|
@ -483,8 +480,10 @@ func (cart *atari16k) Write(addr uint16, data uint8) error {
|
|||
cart.bank = 2
|
||||
} else if addr == 0x0ff9 {
|
||||
cart.bank = 3
|
||||
} else {
|
||||
return errors.New(errors.BusError, addr)
|
||||
}
|
||||
|
||||
if ok := cart.atari.Write(addr, data, poke); !ok {
|
||||
return errors.New(errors.MemoryBusError, addr)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -556,11 +555,7 @@ func (cart *atari32k) Read(addr uint16) (uint8, error) {
|
|||
}
|
||||
|
||||
// Write implements the cartMapper interface
|
||||
func (cart *atari32k) Write(addr uint16, data uint8) error {
|
||||
if ok := cart.atari.Write(addr, data); ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cart *atari32k) Write(addr uint16, data uint8, poke bool) error {
|
||||
if addr == 0x0ff4 {
|
||||
cart.bank = 0
|
||||
} else if addr == 0x0ff5 {
|
||||
|
@ -577,8 +572,10 @@ func (cart *atari32k) Write(addr uint16, data uint8) error {
|
|||
cart.bank = 6
|
||||
} else if addr == 0x0ffb {
|
||||
cart.bank = 7
|
||||
} else {
|
||||
return errors.New(errors.BusError, addr)
|
||||
}
|
||||
|
||||
if ok := cart.atari.Write(addr, data, poke); !ok {
|
||||
return errors.New(errors.MemoryBusError, addr)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -31,7 +31,8 @@ type cbs struct {
|
|||
description string
|
||||
|
||||
// cbs cartridges have 3 banks of 4096 bytes
|
||||
banks [][]uint8
|
||||
bankSize int
|
||||
banks [][]uint8
|
||||
|
||||
// identifies the currently selected bank
|
||||
bank int
|
||||
|
@ -41,25 +42,24 @@ type cbs struct {
|
|||
}
|
||||
|
||||
func newCBS(data []byte) (cartMapper, error) {
|
||||
const bankSize = 4096
|
||||
cart := &cbs{
|
||||
description: "CBS",
|
||||
mappingID: "FA",
|
||||
bankSize: 4096,
|
||||
ram: make([]uint8, 256),
|
||||
}
|
||||
|
||||
cart := &cbs{}
|
||||
cart.description = "CBS"
|
||||
cart.mappingID = "FA"
|
||||
cart.banks = make([][]uint8, cart.NumBanks())
|
||||
|
||||
if len(data) != bankSize*cart.NumBanks() {
|
||||
if len(data) != cart.bankSize*cart.NumBanks() {
|
||||
return nil, errors.New(errors.CartridgeError, fmt.Sprintf("%s: wrong number of bytes in the cartridge file", cart.mappingID))
|
||||
}
|
||||
|
||||
for k := 0; k < cart.NumBanks(); k++ {
|
||||
cart.banks[k] = make([]uint8, bankSize)
|
||||
offset := k * bankSize
|
||||
copy(cart.banks[k], data[offset:offset+bankSize])
|
||||
}
|
||||
cart.banks = make([][]uint8, cart.NumBanks())
|
||||
|
||||
// 256 bytes of cartidge ram in all instances
|
||||
cart.ram = make([]uint8, 256)
|
||||
for k := 0; k < cart.NumBanks(); k++ {
|
||||
cart.banks[k] = make([]uint8, cart.bankSize)
|
||||
offset := k * cart.bankSize
|
||||
copy(cart.banks[k], data[offset:offset+cart.bankSize])
|
||||
}
|
||||
|
||||
cart.Initialise()
|
||||
|
||||
|
@ -103,7 +103,7 @@ func (cart *cbs) Read(addr uint16) (uint8, error) {
|
|||
}
|
||||
|
||||
// Write implements the cartMapper interface
|
||||
func (cart *cbs) Write(addr uint16, data uint8) error {
|
||||
func (cart *cbs) Write(addr uint16, data uint8, poke bool) error {
|
||||
if addr <= 0x00ff {
|
||||
cart.ram[addr] = data
|
||||
return nil
|
||||
|
@ -115,11 +115,14 @@ func (cart *cbs) Write(addr uint16, data uint8) error {
|
|||
cart.bank = 1
|
||||
} else if addr == 0x0ffa {
|
||||
cart.bank = 2
|
||||
} else {
|
||||
return errors.New(errors.BusError, addr)
|
||||
}
|
||||
|
||||
return nil
|
||||
if poke {
|
||||
cart.banks[cart.bank][addr] = data
|
||||
return nil
|
||||
}
|
||||
|
||||
return errors.New(errors.MemoryBusError, addr)
|
||||
}
|
||||
|
||||
// NumBanks implements the cartMapper interface
|
||||
|
@ -154,18 +157,20 @@ func (cart *cbs) RestoreState(state interface{}) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Poke implements the cartMapper interface
|
||||
func (cart *cbs) Poke(addr uint16, data uint8) error {
|
||||
return errors.New(errors.UnpokeableAddress, addr)
|
||||
}
|
||||
|
||||
// Patch implements the cartMapper interface
|
||||
func (cart *cbs) Patch(addr uint16, data uint8) error {
|
||||
return errors.New(errors.UnpatchableCartType, cart.mappingID)
|
||||
func (cart *cbs) Patch(offset int, data uint8) error {
|
||||
if offset >= cart.bankSize*len(cart.banks) {
|
||||
return errors.New(errors.CartridgePatchOOB, offset)
|
||||
}
|
||||
|
||||
bank := int(offset) / cart.bankSize
|
||||
offset = offset % cart.bankSize
|
||||
cart.banks[bank][offset] = data
|
||||
return nil
|
||||
}
|
||||
|
||||
// Listen implements the cartMapper interface
|
||||
func (cart *cbs) Listen(addr uint16, data uint8) {
|
||||
func (cart *cbs) Listen(_ uint16, _ uint8) {
|
||||
}
|
||||
|
||||
// Step implements the cartMapper interface
|
||||
|
|
|
@ -33,12 +33,19 @@ type dpc struct {
|
|||
description string
|
||||
|
||||
// banks and the currently selected bank
|
||||
banks [][]byte
|
||||
bank int
|
||||
bankSize int
|
||||
banks [][]byte
|
||||
bank int
|
||||
|
||||
static DPCstatic
|
||||
// DPC registers are directly accessible by the VCS but have a special
|
||||
// meaning when written to and read. the DPCregisters type implements the
|
||||
// functionality of these special addresses and a copy of the field is
|
||||
// returned by the GetRegisters() function
|
||||
registers DPCregisters
|
||||
|
||||
// dpc specific areas of the cartridge, not accessible by the normal VCS bus
|
||||
static DPCstatic
|
||||
|
||||
// the OSC clock found in DPC cartridges runs at slower than the VCS itself
|
||||
// to effectively emulate the slower clock therefore, we need to discount
|
||||
// the excess steps. see the step() function for details
|
||||
|
@ -126,7 +133,6 @@ func (df *DPCdataFetcher) clk() {
|
|||
|
||||
func (df *DPCdataFetcher) setFlag() {
|
||||
// set flag register [col 6, ln 7-12]
|
||||
|
||||
if df.Low == df.Top {
|
||||
df.Flag = true
|
||||
} else if df.Low == df.Bottom {
|
||||
|
@ -135,26 +141,28 @@ func (df *DPCdataFetcher) setFlag() {
|
|||
}
|
||||
|
||||
func newDPC(data []byte) (cartMapper, error) {
|
||||
const bankSize = 4096
|
||||
const gfxSize = 2048
|
||||
const staticSize = 2048
|
||||
|
||||
cart := &dpc{
|
||||
mappingID: "DPC",
|
||||
description: "DPC Pitfall2 style",
|
||||
bankSize: 4096,
|
||||
}
|
||||
|
||||
cart := &dpc{}
|
||||
cart.mappingID = "DPC"
|
||||
cart.description = "DPC Pitfall2 style"
|
||||
cart.banks = make([][]uint8, cart.NumBanks())
|
||||
|
||||
if len(data) < bankSize*cart.NumBanks()+gfxSize {
|
||||
if len(data) < cart.bankSize*cart.NumBanks()+staticSize {
|
||||
return nil, errors.New(errors.CartridgeError, fmt.Sprintf("%s: wrong number of bytes in the cartridge file", cart.mappingID))
|
||||
}
|
||||
|
||||
for k := 0; k < cart.NumBanks(); k++ {
|
||||
cart.banks[k] = make([]uint8, bankSize)
|
||||
offset := k * bankSize
|
||||
cart.banks[k] = data[offset : offset+bankSize]
|
||||
cart.banks[k] = make([]uint8, cart.bankSize)
|
||||
offset := k * cart.bankSize
|
||||
cart.banks[k] = data[offset : offset+cart.bankSize]
|
||||
}
|
||||
|
||||
gfxStart := cart.NumBanks() * bankSize
|
||||
cart.static.Gfx = data[gfxStart : gfxStart+gfxSize]
|
||||
staticStart := cart.NumBanks() * cart.bankSize
|
||||
cart.static.Gfx = data[staticStart : staticStart+staticSize]
|
||||
|
||||
cart.Initialise()
|
||||
|
||||
|
@ -293,7 +301,7 @@ func (cart *dpc) Read(addr uint16) (uint8, error) {
|
|||
}
|
||||
|
||||
// Write implements the cartMapper interface
|
||||
func (cart *dpc) Write(addr uint16, data uint8) error {
|
||||
func (cart *dpc) Write(addr uint16, data uint8, poke bool) error {
|
||||
if addr == 0x0ff8 {
|
||||
cart.bank = 0
|
||||
} else if addr == 0x0ff9 {
|
||||
|
@ -348,7 +356,12 @@ func (cart *dpc) Write(addr uint16, data uint8) error {
|
|||
// other addresses are not write registers and are ignored
|
||||
}
|
||||
|
||||
return nil
|
||||
if poke {
|
||||
cart.banks[cart.bank][addr] = data
|
||||
return nil
|
||||
}
|
||||
|
||||
return errors.New(errors.MemoryBusError, addr)
|
||||
}
|
||||
|
||||
// NumBanks implements the cartMapper interface
|
||||
|
@ -377,18 +390,25 @@ func (cart *dpc) RestoreState(state interface{}) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Poke implements the cartMapper interface
|
||||
func (cart *dpc) Poke(addr uint16, data uint8) error {
|
||||
return errors.New(errors.UnpokeableAddress, addr)
|
||||
}
|
||||
|
||||
// Patch implements the cartMapper interface
|
||||
func (cart *dpc) Patch(addr uint16, data uint8) error {
|
||||
return errors.New(errors.UnpatchableCartType, cart.description)
|
||||
func (cart *dpc) Patch(offset int, data uint8) error {
|
||||
if offset >= cart.bankSize*len(cart.banks)+len(cart.static.Gfx) {
|
||||
return errors.New(errors.CartridgePatchOOB, offset)
|
||||
}
|
||||
|
||||
staticStart := cart.NumBanks() * cart.bankSize
|
||||
if staticStart >= staticStart {
|
||||
cart.static.Gfx[offset] = data
|
||||
} else {
|
||||
bank := int(offset) / cart.bankSize
|
||||
offset = offset % cart.bankSize
|
||||
cart.banks[bank][offset] = data
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Listen implements the cartMapper interface
|
||||
func (cart *dpc) Listen(addr uint16, data uint8) {
|
||||
func (cart *dpc) Listen(_ uint16, _ uint8) {
|
||||
}
|
||||
|
||||
// Step implements the cartMapper interface
|
||||
|
|
|
@ -20,8 +20,6 @@
|
|||
package cartridge
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/jetsetilly/gopher2600/errors"
|
||||
)
|
||||
|
||||
|
@ -56,12 +54,12 @@ func (cart *ejected) Initialise() {
|
|||
}
|
||||
|
||||
// Read implements the cartMapper interface
|
||||
func (cart *ejected) Read(addr uint16) (uint8, error) {
|
||||
func (cart *ejected) Read(_ uint16) (uint8, error) {
|
||||
return 0, errors.New(errors.CartridgeEjected)
|
||||
}
|
||||
|
||||
// Write implements the cartMapper interface
|
||||
func (cart *ejected) Write(addr uint16, data uint8) error {
|
||||
func (cart *ejected) Write(_ uint16, _ uint8, _ bool) error {
|
||||
return errors.New(errors.CartridgeEjected)
|
||||
}
|
||||
|
||||
|
@ -71,12 +69,12 @@ func (cart ejected) NumBanks() int {
|
|||
}
|
||||
|
||||
// SetBank implements the cartMapper interface
|
||||
func (cart *ejected) SetBank(addr uint16, bank int) error {
|
||||
return errors.New(errors.CartridgeError, fmt.Sprintf("ejected cartridge"))
|
||||
func (cart *ejected) SetBank(_ uint16, _ int) error {
|
||||
return errors.New(errors.CartridgeEjected)
|
||||
}
|
||||
|
||||
// GetBank implements the cartMapper interface
|
||||
func (cart ejected) GetBank(addr uint16) int {
|
||||
func (cart ejected) GetBank(_ uint16) int {
|
||||
return 0
|
||||
}
|
||||
|
||||
|
@ -86,22 +84,17 @@ func (cart *ejected) SaveState() interface{} {
|
|||
}
|
||||
|
||||
// RestoreState implements the cartMapper interface
|
||||
func (cart *ejected) RestoreState(state interface{}) error {
|
||||
func (cart *ejected) RestoreState(_ interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Poke implements the cartMapper interface
|
||||
func (cart *ejected) Poke(addr uint16, data uint8) error {
|
||||
return errors.New(errors.UnpokeableAddress, addr)
|
||||
}
|
||||
|
||||
// Patch implements the cartMapper interface
|
||||
func (cart *ejected) Patch(addr uint16, data uint8) error {
|
||||
return errors.New(errors.UnpatchableCartType, cart.description)
|
||||
func (cart *ejected) Patch(_ int, _ uint8) error {
|
||||
return errors.New(errors.CartridgeEjected)
|
||||
}
|
||||
|
||||
// Listen implements the cartMapper interface
|
||||
func (cart *ejected) Listen(addr uint16, data uint8) {
|
||||
func (cart *ejected) Listen(_ uint16, _ uint8) {
|
||||
}
|
||||
|
||||
// Step implements the cartMapper interface
|
||||
|
|
|
@ -86,6 +86,8 @@ type mnetwork struct {
|
|||
mappingID string
|
||||
description string
|
||||
|
||||
bankSize int
|
||||
|
||||
banks [][]uint8
|
||||
bank int
|
||||
|
||||
|
@ -104,21 +106,22 @@ type mnetwork struct {
|
|||
}
|
||||
|
||||
func newMnetwork(data []byte) (cartMapper, error) {
|
||||
const bankSize = 2048
|
||||
cart := &mnetwork{
|
||||
description: "m-network",
|
||||
mappingID: "E7",
|
||||
bankSize: 2048,
|
||||
}
|
||||
|
||||
cart := &mnetwork{}
|
||||
cart.description = "m-network"
|
||||
cart.mappingID = "E7"
|
||||
cart.banks = make([][]uint8, cart.NumBanks())
|
||||
|
||||
if len(data) != bankSize*cart.NumBanks() {
|
||||
if len(data) != cart.bankSize*cart.NumBanks() {
|
||||
return nil, errors.New(errors.CartridgeError, fmt.Sprintf("%s: wrong number of bytes in the cartridge file", cart.mappingID))
|
||||
}
|
||||
|
||||
for k := 0; k < cart.NumBanks(); k++ {
|
||||
cart.banks[k] = make([]uint8, bankSize)
|
||||
offset := k * bankSize
|
||||
copy(cart.banks[k], data[offset:offset+bankSize])
|
||||
cart.banks[k] = make([]uint8, cart.bankSize)
|
||||
offset := k * cart.bankSize
|
||||
copy(cart.banks[k], data[offset:offset+cart.bankSize])
|
||||
}
|
||||
|
||||
// not all m-network cartridges have any RAM but we'll allocate it for all
|
||||
|
@ -186,14 +189,14 @@ func (cart *mnetwork) Read(addr uint16) (uint8, error) {
|
|||
cart.bankSwitchOnAccess(addr)
|
||||
}
|
||||
} else {
|
||||
return 0, errors.New(errors.BusError, addr)
|
||||
return 0, errors.New(errors.MemoryBusError, addr)
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// Write implements the cartMapper interface
|
||||
func (cart *mnetwork) Write(addr uint16, data uint8) error {
|
||||
func (cart *mnetwork) Write(addr uint16, data uint8, poke bool) error {
|
||||
if addr >= 0x0000 && addr <= 0x07ff {
|
||||
if addr <= 0x03ff && cart.bank == 7 {
|
||||
cart.ram1k[addr&0x03ff] = data
|
||||
|
@ -206,7 +209,12 @@ func (cart *mnetwork) Write(addr uint16, data uint8) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
return errors.New(errors.BusError, addr)
|
||||
if poke {
|
||||
cart.banks[cart.bank][addr] = data
|
||||
return nil
|
||||
}
|
||||
|
||||
return errors.New(errors.MemoryBusError, addr)
|
||||
}
|
||||
|
||||
func (cart *mnetwork) bankSwitchOnAccess(addr uint16) bool {
|
||||
|
@ -311,18 +319,20 @@ func (cart *mnetwork) RestoreState(state interface{}) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Poke implements the cartMapper interface
|
||||
func (cart *mnetwork) Poke(addr uint16, data uint8) error {
|
||||
return errors.New(errors.UnpokeableAddress, addr)
|
||||
}
|
||||
|
||||
// Patch implements the cartMapper interface
|
||||
func (cart *mnetwork) Patch(addr uint16, data uint8) error {
|
||||
return errors.New(errors.UnpatchableCartType, cart.mappingID)
|
||||
func (cart *mnetwork) Patch(offset int, data uint8) error {
|
||||
if offset >= cart.bankSize*len(cart.banks) {
|
||||
return errors.New(errors.CartridgePatchOOB, offset)
|
||||
}
|
||||
|
||||
bank := int(offset) / cart.bankSize
|
||||
offset = offset % cart.bankSize
|
||||
cart.banks[bank][offset] = data
|
||||
return nil
|
||||
}
|
||||
|
||||
// Listen implements the cartMapper interface
|
||||
func (cart *mnetwork) Listen(addr uint16, data uint8) {
|
||||
func (cart *mnetwork) Listen(_ uint16, _ uint8) {
|
||||
}
|
||||
|
||||
// Step implements the cartMapper interface
|
||||
|
|
|
@ -62,7 +62,8 @@ type parkerBros struct {
|
|||
mappingID string
|
||||
description string
|
||||
|
||||
banks [][]uint8
|
||||
bankSize int
|
||||
banks [][]uint8
|
||||
|
||||
// parker bros. cartridges divide memory into 4 segments
|
||||
// o the last segment always points to the last bank
|
||||
|
@ -74,21 +75,22 @@ type parkerBros struct {
|
|||
}
|
||||
|
||||
func newparkerBros(data []byte) (cartMapper, error) {
|
||||
const bankSize = 1024
|
||||
cart := &parkerBros{
|
||||
description: "parker bros",
|
||||
mappingID: "E0",
|
||||
bankSize: 1024,
|
||||
}
|
||||
|
||||
cart := &parkerBros{}
|
||||
cart.description = "parker bros"
|
||||
cart.mappingID = "E0"
|
||||
cart.banks = make([][]uint8, cart.NumBanks())
|
||||
|
||||
if len(data) != bankSize*cart.NumBanks() {
|
||||
if len(data) != cart.bankSize*cart.NumBanks() {
|
||||
return nil, errors.New(errors.CartridgeError, fmt.Sprintf("%s: wrong number of bytes in the cartridge file", cart.mappingID))
|
||||
}
|
||||
|
||||
for k := 0; k < cart.NumBanks(); k++ {
|
||||
cart.banks[k] = make([]uint8, bankSize)
|
||||
offset := k * bankSize
|
||||
copy(cart.banks[k], data[offset:offset+bankSize])
|
||||
cart.banks[k] = make([]uint8, cart.bankSize)
|
||||
offset := k * cart.bankSize
|
||||
copy(cart.banks[k], data[offset:offset+cart.bankSize])
|
||||
}
|
||||
|
||||
cart.Initialise()
|
||||
|
@ -130,11 +132,24 @@ func (cart *parkerBros) Read(addr uint16) (uint8, error) {
|
|||
}
|
||||
|
||||
// Write implements the cartMapper interface
|
||||
func (cart *parkerBros) Write(addr uint16, data uint8) error {
|
||||
func (cart *parkerBros) Write(addr uint16, data uint8, poke bool) error {
|
||||
if cart.bankSwitchOnAccess(addr) {
|
||||
return nil
|
||||
}
|
||||
return errors.New(errors.BusError, addr)
|
||||
|
||||
if poke {
|
||||
if addr >= 0x0000 && addr <= 0x03ff {
|
||||
cart.banks[cart.segment[0]][addr&0x3dd] = data
|
||||
} else if addr >= 0x0400 && addr <= 0x07ff {
|
||||
cart.banks[cart.segment[1]][addr&0x3dd] = data
|
||||
} else if addr >= 0x0800 && addr <= 0x0bff {
|
||||
cart.banks[cart.segment[2]][addr&0x3dd] = data
|
||||
} else if addr >= 0x0c00 && addr <= 0x0fff {
|
||||
cart.banks[cart.segment[3]][addr&0x3dd] = data
|
||||
}
|
||||
}
|
||||
|
||||
return errors.New(errors.MemoryBusError, addr)
|
||||
}
|
||||
|
||||
func (cart *parkerBros) bankSwitchOnAccess(addr uint16) bool {
|
||||
|
@ -247,18 +262,20 @@ func (cart *parkerBros) RestoreState(state interface{}) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Poke implements the cartMapper interface
|
||||
func (cart *parkerBros) Poke(addr uint16, data uint8) error {
|
||||
return errors.New(errors.UnpokeableAddress, addr)
|
||||
}
|
||||
|
||||
// Patch implements the cartMapper interface
|
||||
func (cart *parkerBros) Patch(addr uint16, data uint8) error {
|
||||
return errors.New(errors.UnpatchableCartType, cart.mappingID)
|
||||
func (cart *parkerBros) Patch(offset int, data uint8) error {
|
||||
if offset >= cart.bankSize*len(cart.banks) {
|
||||
return errors.New(errors.CartridgePatchOOB, offset)
|
||||
}
|
||||
|
||||
bank := int(offset) / cart.bankSize
|
||||
offset = offset % cart.bankSize
|
||||
cart.banks[bank][offset] = data
|
||||
return nil
|
||||
}
|
||||
|
||||
// Listen implements the cartMapper interface
|
||||
func (cart *parkerBros) Listen(addr uint16, data uint8) {
|
||||
func (cart *parkerBros) Listen(_ uint16, _ uint8) {
|
||||
}
|
||||
|
||||
// Step implements the cartMapper interface
|
||||
|
|
|
@ -60,7 +60,8 @@ type tigervision struct {
|
|||
mappingID string
|
||||
description string
|
||||
|
||||
banks [][]uint8
|
||||
bankSize int
|
||||
banks [][]uint8
|
||||
|
||||
// tigervision cartridges divide memory into two 2k segments
|
||||
// o the last segment always points to the last bank
|
||||
|
@ -74,27 +75,27 @@ type tigervision struct {
|
|||
// should work with any size cartridge that is a multiple of 2048
|
||||
// - tested with 8k (Miner2049 etc.) and 32k (Genesis_Egypt demo)
|
||||
func newTigervision(data []byte) (cartMapper, error) {
|
||||
const bankSize = 2048
|
||||
cart := &tigervision{
|
||||
description: "tigervision",
|
||||
mappingID: "3F",
|
||||
bankSize: 2048,
|
||||
}
|
||||
|
||||
if len(data)%bankSize != 0 {
|
||||
if len(data)%cart.bankSize != 0 {
|
||||
return nil, errors.New(errors.CartridgeError, "tigervision (3F): cartridge size must be multiple of 2048")
|
||||
}
|
||||
|
||||
numBanks := len(data) / bankSize
|
||||
|
||||
cart := &tigervision{}
|
||||
cart.description = "tigervision"
|
||||
cart.mappingID = "3F"
|
||||
numBanks := len(data) / cart.bankSize
|
||||
cart.banks = make([][]uint8, numBanks)
|
||||
|
||||
if len(data) != bankSize*numBanks {
|
||||
if len(data) != cart.bankSize*numBanks {
|
||||
return nil, errors.New(errors.CartridgeError, fmt.Sprintf("%s: wrong number bytes in the cartridge file", cart.mappingID))
|
||||
}
|
||||
|
||||
for k := 0; k < numBanks; k++ {
|
||||
cart.banks[k] = make([]uint8, bankSize)
|
||||
offset := k * bankSize
|
||||
copy(cart.banks[k], data[offset:offset+bankSize])
|
||||
cart.banks[k] = make([]uint8, cart.bankSize)
|
||||
offset := k * cart.bankSize
|
||||
copy(cart.banks[k], data[offset:offset+cart.bankSize])
|
||||
}
|
||||
|
||||
cart.Initialise()
|
||||
|
@ -131,8 +132,15 @@ func (cart *tigervision) Read(addr uint16) (uint8, error) {
|
|||
}
|
||||
|
||||
// Write implements the cartMapper interface
|
||||
func (cart *tigervision) Write(addr uint16, data uint8) error {
|
||||
return errors.New(errors.BusError, addr)
|
||||
func (cart *tigervision) Write(addr uint16, data uint8, poke bool) error {
|
||||
if poke {
|
||||
if addr >= 0x0000 && addr <= 0x07ff {
|
||||
cart.banks[cart.segment[0]][addr&0x07ff] = data
|
||||
} else if addr >= 0x0800 && addr <= 0x0fff {
|
||||
cart.banks[cart.segment[1]][addr&0x07ff] = data
|
||||
}
|
||||
}
|
||||
return errors.New(errors.MemoryBusError, addr)
|
||||
}
|
||||
|
||||
// NumBanks implements the cartMapper interface
|
||||
|
@ -172,14 +180,16 @@ func (cart *tigervision) RestoreState(state interface{}) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Poke implements the cartMapper interface
|
||||
func (cart *tigervision) Poke(addr uint16, data uint8) error {
|
||||
return errors.New(errors.UnpokeableAddress, addr)
|
||||
}
|
||||
|
||||
// Patch implements the cartMapper interface
|
||||
func (cart *tigervision) Patch(addr uint16, data uint8) error {
|
||||
return errors.New(errors.UnpatchableCartType, cart.mappingID)
|
||||
func (cart *tigervision) Patch(offset int, data uint8) error {
|
||||
if offset >= cart.bankSize*len(cart.banks) {
|
||||
return errors.New(errors.CartridgePatchOOB, offset)
|
||||
}
|
||||
|
||||
bank := int(offset) / cart.bankSize
|
||||
offset = offset % cart.bankSize
|
||||
cart.banks[bank][offset] = data
|
||||
return nil
|
||||
}
|
||||
|
||||
// Listen implements the cartMapper interface
|
||||
|
|
|
@ -108,7 +108,7 @@ func (area *ChipMemory) Read(address uint16) (uint8, error) {
|
|||
|
||||
// do not allow reads from memory that do not have symbol name
|
||||
if _, ok := addresses.CanonicalReadSymbols[address]; !ok {
|
||||
return 0, errors.New(errors.BusError, address)
|
||||
return 0, errors.New(errors.MemoryBusError, address)
|
||||
}
|
||||
|
||||
return area.memory[address^area.origin], nil
|
||||
|
@ -116,14 +116,15 @@ func (area *ChipMemory) Read(address uint16) (uint8, error) {
|
|||
|
||||
// Write is an implementation of memory.CPUBus. Address must be normalised.
|
||||
func (area *ChipMemory) Write(address uint16, data uint8) error {
|
||||
// check that the last write to this memory area has been serviced
|
||||
// check that the last write to this memory area has been serviced. this
|
||||
// shouldn't ever happen.
|
||||
if area.writeSignal {
|
||||
return errors.New(errors.MemoryError, fmt.Sprintf("unserviced write to chip memory (%s)", addresses.Write[area.writeAddress]))
|
||||
panic(fmt.Sprintf("unserviced write to chip memory (%s)", addresses.Write[area.writeAddress]))
|
||||
}
|
||||
|
||||
// do not allow writes to memory that do not have symbol name
|
||||
if _, ok := addresses.CanonicalWriteSymbols[address]; !ok {
|
||||
return errors.New(errors.BusError, address)
|
||||
return errors.New(errors.MemoryBusError, address)
|
||||
}
|
||||
|
||||
// signal the chips that their chip memory has been written to
|
||||
|
|
|
@ -22,7 +22,6 @@ package memory
|
|||
import (
|
||||
"math/rand"
|
||||
|
||||
"github.com/jetsetilly/gopher2600/errors"
|
||||
"github.com/jetsetilly/gopher2600/hardware/memory/addresses"
|
||||
"github.com/jetsetilly/gopher2600/hardware/memory/bus"
|
||||
"github.com/jetsetilly/gopher2600/hardware/memory/cartridge"
|
||||
|
@ -83,10 +82,6 @@ func NewVCSMemory() (*VCSMemory, error) {
|
|||
mem.RAM = newRAM()
|
||||
mem.Cart = cartridge.NewCartridge()
|
||||
|
||||
if mem.RIOT == nil || mem.TIA == nil || mem.RAM == nil || mem.Cart == nil {
|
||||
return nil, errors.New(errors.MemoryError, "cannot create memory areas")
|
||||
}
|
||||
|
||||
// create the memory map by associating all addresses in each memory area
|
||||
// with that area
|
||||
for i := memorymap.OriginTIA; i <= memorymap.MemtopTIA; i++ {
|
||||
|
@ -109,19 +104,19 @@ func NewVCSMemory() (*VCSMemory, error) {
|
|||
}
|
||||
|
||||
// GetArea returns the actual memory of the specified area type
|
||||
func (mem *VCSMemory) GetArea(area memorymap.Area) (bus.DebuggerBus, error) {
|
||||
func (mem *VCSMemory) GetArea(area memorymap.Area) bus.DebuggerBus {
|
||||
switch area {
|
||||
case memorymap.TIA:
|
||||
return mem.TIA, nil
|
||||
return mem.TIA
|
||||
case memorymap.RAM:
|
||||
return mem.RAM, nil
|
||||
return mem.RAM
|
||||
case memorymap.RIOT:
|
||||
return mem.RIOT, nil
|
||||
return mem.RIOT
|
||||
case memorymap.Cartridge:
|
||||
return mem.Cart, nil
|
||||
return mem.Cart
|
||||
}
|
||||
|
||||
return nil, errors.New(errors.MemoryError, "area not mapped correctly")
|
||||
panic("memory areas are not mapped correctly")
|
||||
}
|
||||
|
||||
// read maps an address to the normalised for all memory areas.
|
||||
|
@ -129,10 +124,7 @@ func (mem *VCSMemory) read(address uint16, zeroPage bool) (uint8, error) {
|
|||
// optimisation: called a lot. pointer to VCSMemory to prevent duffcopy
|
||||
|
||||
ma, ar := memorymap.MapAddress(address, true)
|
||||
area, err := mem.GetArea(ar)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
area := mem.GetArea(ar)
|
||||
|
||||
data, err := area.(bus.CPUBus).Read(ma)
|
||||
|
||||
|
@ -187,10 +179,7 @@ func (mem *VCSMemory) ReadZeroPage(address uint8) (uint8, error) {
|
|||
// processed by the correct memory areas.
|
||||
func (mem *VCSMemory) Write(address uint16, data uint8) error {
|
||||
ma, ar := memorymap.MapAddress(address, false)
|
||||
area, err := mem.GetArea(ar)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
area := mem.GetArea(ar)
|
||||
|
||||
mem.LastAccessAddress = ma
|
||||
mem.LastAccessWrite = true
|
||||
|
|
10
patch/doc.go
10
patch/doc.go
|
@ -42,12 +42,12 @@
|
|||
// Rules:
|
||||
//
|
||||
// 1. Lines beginning with a hyphen or white space are ignored
|
||||
// 2. Addresses and values are expressed in hex (case-insensitive)
|
||||
// 3. Values and addresses are separated by a colon
|
||||
// 4. Multiple values on a line are poked into consecutive addresses, starting
|
||||
// from the address value
|
||||
// 2. Offset and values are expressed in hex (case-insensitive)
|
||||
// 3. Values and offsets are separated by a colon
|
||||
// 4. Multiple values on a line are poked into consecutive offsets, starting
|
||||
// from the offset value
|
||||
//
|
||||
// Note that addresses are expressed with origin zero and have no relationship
|
||||
// Note that offsets are expressed with origin zero and have no relationship
|
||||
// to how memory is mapped inside the VCS. Imagine that the patches are being
|
||||
// applied to the cartridge file image. The cartridge mapper handles the VCS
|
||||
// memory side of things.
|
||||
|
|
|
@ -86,7 +86,7 @@ func CartridgeMemory(mem *cartridge.Cartridge, patchFile string) (bool, error) {
|
|||
|
||||
pokeLine := strings.Split(lines[i], pokeLineSeparator)
|
||||
|
||||
// ignore any lines that don't match the required [address: values...] format
|
||||
// ignore any lines that don't match the required [offset: values...] format
|
||||
if len(pokeLine) != 2 {
|
||||
continue // for loop
|
||||
}
|
||||
|
@ -95,8 +95,8 @@ func CartridgeMemory(mem *cartridge.Cartridge, patchFile string) (bool, error) {
|
|||
pokeLine[0] = strings.TrimSpace(pokeLine[0])
|
||||
pokeLine[1] = strings.TrimSpace(pokeLine[1])
|
||||
|
||||
// parse address
|
||||
address, err := strconv.ParseInt(pokeLine[0], 16, 16)
|
||||
// parse offset
|
||||
offset, err := strconv.ParseInt(pokeLine[0], 16, 16)
|
||||
if err != nil {
|
||||
continue // for loop
|
||||
}
|
||||
|
@ -120,14 +120,14 @@ func CartridgeMemory(mem *cartridge.Cartridge, patchFile string) (bool, error) {
|
|||
}
|
||||
|
||||
// patch memory
|
||||
err = mem.Patch(uint16(address), uint8(v))
|
||||
err = mem.Patch(int(offset), uint8(v))
|
||||
if err != nil {
|
||||
return patched, errors.New(errors.PatchError, err)
|
||||
}
|
||||
patched = true
|
||||
|
||||
// advance address
|
||||
address++
|
||||
// advance offset
|
||||
offset++
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue