o cartridgeloader

- moved from memory package
This commit is contained in:
steve 2019-11-11 21:33:22 +00:00
parent 11d9676ba9
commit 28d7352b52
23 changed files with 161 additions and 126 deletions

81
cartridgeloader/loader.go Normal file
View file

@ -0,0 +1,81 @@
package cartridgeloader
import (
"gopher2600/errors"
"net/http"
"os"
"path"
"strings"
)
// Loader is used to specify the cartridge to use when Attach()ing to
// the VCS. it also permits the called to specify the format of the cartridge
// (if necessary. fingerprinting is pretty good)
type Loader struct {
Filename string
// empty string or "AUTO" indicates automatic fingerprinting
Format string
// expected hash of the loaded cartridge. empty string indicates that the
// hash is unknown and need not be validated
Hash string
data []byte
}
// ShortName returns a shortened version of the CartridgeLoader filename
func (cl Loader) ShortName() string {
shortCartName := path.Base(cl.Filename)
shortCartName = strings.TrimSuffix(shortCartName, path.Ext(cl.Filename))
return shortCartName
}
// Load the cartridge
func (cl Loader) Load() ([]byte, error) {
if len(cl.data) > 0 {
return cl.data, nil
}
var err error
if strings.HasPrefix(cl.Filename, "http://") {
var resp *http.Response
resp, err = http.Get(cl.Filename)
if err != nil {
return nil, errors.New(errors.CartridgeLoader, cl.Filename)
}
defer resp.Body.Close()
size := resp.ContentLength
cl.data = make([]byte, size)
_, err = resp.Body.Read(cl.data)
if err != nil {
return nil, errors.New(errors.CartridgeLoader, cl.Filename)
}
} else {
var f *os.File
f, err = os.Open(cl.Filename)
if err != nil {
return nil, errors.New(errors.CartridgeLoader, cl.Filename)
}
defer f.Close()
// get file info
cfi, err := f.Stat()
if err != nil {
return nil, errors.New(errors.CartridgeLoader, cl.Filename)
}
size := cfi.Size()
cl.data = make([]byte, size)
_, err = f.Read(cl.data)
if err != nil {
return nil, errors.New(errors.CartridgeLoader, cl.Filename)
}
}
return cl.data, nil
}

View file

@ -2,6 +2,7 @@ package debugger
import (
"fmt"
"gopher2600/cartridgeloader"
"gopher2600/debugger/commandline"
"gopher2600/debugger/console"
"gopher2600/debugger/script"
@ -9,7 +10,6 @@ import (
"gopher2600/gui"
"gopher2600/hardware/cpu/register"
"gopher2600/hardware/cpu/result"
"gopher2600/hardware/memory"
"gopher2600/hardware/memory/addresses"
"gopher2600/hardware/peripherals"
"gopher2600/symbols"
@ -274,7 +274,7 @@ func (dbg *Debugger) enactCommand(tokens *commandline.Tokens, interactive bool)
case cmdInsert:
cart, _ := tokens.Get()
err := dbg.loadCartridge(memory.CartridgeLoader{Filename: cart})
err := dbg.loadCartridge(cartridgeloader.Loader{Filename: cart})
if err != nil {
return doNothing, err
}

View file

@ -1,6 +1,7 @@
package debugger
import (
"gopher2600/cartridgeloader"
"gopher2600/debugger/commandline"
"gopher2600/debugger/console"
"gopher2600/debugger/reflection"
@ -11,7 +12,6 @@ import (
"gopher2600/gui/sdldebug"
"gopher2600/hardware"
"gopher2600/hardware/cpu/definitions"
"gopher2600/hardware/memory"
"gopher2600/screendigest"
"gopher2600/setup"
"gopher2600/symbols"
@ -190,7 +190,7 @@ func NewDebugger(tvType string) (*Debugger, error) {
// Start the main debugger sequence. starting the debugger is a distinct
// operation to creating the debugger.
func (dbg *Debugger) Start(cons console.UserInterface, initScript string, cartload memory.CartridgeLoader) error {
func (dbg *Debugger) Start(cons console.UserInterface, initScript string, cartload cartridgeloader.Loader) error {
// prepare user interface
if cons == nil {
dbg.console = new(console.PlainTerminal)
@ -247,7 +247,7 @@ func (dbg *Debugger) Start(cons console.UserInterface, initScript string, cartlo
//
// this is the glue that hold the cartridge and disassembly packages
// together
func (dbg *Debugger) loadCartridge(cartload memory.CartridgeLoader) error {
func (dbg *Debugger) loadCartridge(cartload cartridgeloader.Loader) error {
err := setup.AttachCartridge(dbg.vcs, cartload)
if err != nil {
return err

View file

@ -2,6 +2,7 @@ package disassembly
import (
"fmt"
"gopher2600/cartridgeloader"
"gopher2600/errors"
"gopher2600/hardware/cpu"
"gopher2600/hardware/memory"
@ -63,7 +64,7 @@ func (dsm *Disassembly) Dump(output io.Writer) {
// FromCartrige initialises a new partial emulation and returns a
// disassembly from the supplied cartridge filename. - useful for one-shot
// disassemblies, like the gopher2600 "disasm" mode
func FromCartrige(cartload memory.CartridgeLoader) (*Disassembly, error) {
func FromCartrige(cartload cartridgeloader.Loader) (*Disassembly, error) {
// ignore errors caused by loading of symbols table - we always get a
// standard symbols table even in the event of an error
symtable, _ := symbols.ReadSymbolsFile(cartload.Filename)

View file

@ -71,6 +71,9 @@ const (
SymbolsFileError
SymbolUnknown
// cartridgeloader
CartridgeLoader
// vcs
VCSError
@ -90,8 +93,6 @@ const (
UnrecognisedAddress
// cartridges
CartridgeFileError
CartridgeFileUnavailable
CartridgeError
CartridgeEjected

View file

@ -58,6 +58,9 @@ var messages = map[Errno]string{
SymbolsFileUnavailable: "symbols error: no symbols file for %s",
SymbolUnknown: "symbols error: unrecognised symbol (%s)",
// cartridgeloader
CartridgeLoader: "cartridge loading error: %s",
// vcs
VCSError: "vcs error: %s",
@ -77,10 +80,8 @@ var messages = map[Errno]string{
UnrecognisedAddress: "memory error: address unrecognised (%v)",
// cartridges
CartridgeFileError: "cartridge error: %s",
CartridgeFileUnavailable: "cartridge error: cannot open cartridge file (%s)",
CartridgeError: "cartridge error: %s",
CartridgeEjected: "cartridge error: no cartridge attached",
CartridgeError: "cartridge error: %s",
CartridgeEjected: "cartridge error: no cartridge attached",
// peripherals
PeriphHardwareUnavailable: "peripheral error: controller hardware unavailable (%s)",

View file

@ -3,11 +3,11 @@ package main
import (
"flag"
"fmt"
"gopher2600/cartridgeloader"
"gopher2600/debugger"
"gopher2600/debugger/colorterm"
"gopher2600/debugger/console"
"gopher2600/disassembly"
"gopher2600/hardware/memory"
"gopher2600/magicflags"
"gopher2600/performance"
"gopher2600/playmode"
@ -94,7 +94,7 @@ func play(mf *magicflags.MagicFlags) bool {
}
fallthrough
case 1:
cartload := memory.CartridgeLoader{
cartload := cartridgeloader.Loader{
Filename: mf.SubModeFlags.Arg(0),
Format: *cartFormat,
}
@ -153,7 +153,7 @@ func debug(mf *magicflags.MagicFlags) bool {
fallthrough
case 1:
runner := func() error {
cartload := memory.CartridgeLoader{
cartload := cartridgeloader.Loader{
Filename: mf.SubModeFlags.Arg(0),
Format: *cartFormat,
}
@ -202,7 +202,7 @@ func disasm(mf *magicflags.MagicFlags) bool {
fmt.Println("* 2600 cartridge required")
return false
case 1:
cartload := memory.CartridgeLoader{
cartload := cartridgeloader.Loader{
Filename: mf.SubModeFlags.Arg(0),
Format: *cartFormat,
}
@ -243,7 +243,7 @@ func perform(mf *magicflags.MagicFlags) bool {
fmt.Println("* 2600 cartridge required")
return false
case 1:
cartload := memory.CartridgeLoader{
cartload := cartridgeloader.Loader{
Filename: mf.SubModeFlags.Arg(0),
Format: *cartFormat,
}
@ -383,7 +383,7 @@ func regressAdd(mf *magicflags.MagicFlags) bool {
Notes: *notes,
}
} else {
cartload := memory.CartridgeLoader{
cartload := cartridgeloader.Loader{
Filename: mf.SubModeFlags.Arg(0),
Format: *cartFormat,
}

View file

@ -3,34 +3,11 @@ package memory
import (
"crypto/sha1"
"fmt"
"gopher2600/cartridgeloader"
"gopher2600/errors"
"net/http"
"os"
"path"
"strings"
)
// CartridgeLoader is used to specify the cartridge to use when Attach()ing to
// the VCS. it also permits the called to specify the format of the cartridge
// (if necessary. fingerprinting is pretty good)
type CartridgeLoader struct {
Filename string
// empty string or "AUTO" indicates automatic fingerprinting
Format string
// expected hash of the loaded cartridge. empty string indicates that the
// hash is unknown and need not be validated
Hash string
}
// ShortName returns a shortened version of the CartridgeLoader filename
func (cl CartridgeLoader) ShortName() string {
shortCartName := path.Base(cl.Filename)
shortCartName = strings.TrimSuffix(shortCartName, path.Ext(cl.Filename))
return shortCartName
}
// 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
@ -210,10 +187,10 @@ func (cart *Cartridge) fingerprint(data []byte) error {
}
case 65536:
return errors.New(errors.CartridgeFileError, "65536 bytes not yet supported")
return errors.New(errors.CartridgeError, "65536 bytes not yet supported")
default:
return errors.New(errors.CartridgeFileError, fmt.Sprintf("unrecognised cartridge size (%d bytes)", len(data)))
return errors.New(errors.CartridgeError, fmt.Sprintf("unrecognised cartridge size (%d bytes)", len(data)))
}
// if cartridge mapper implements the optionalSuperChip interface then try
@ -226,46 +203,10 @@ func (cart *Cartridge) fingerprint(data []byte) error {
}
// Attach loads the bytes from a cartridge (represented by 'filename')
func (cart *Cartridge) Attach(cartload CartridgeLoader) error {
var err error
var data []byte
if strings.HasPrefix(cartload.Filename, "http://") {
var resp *http.Response
resp, err = http.Get(cartload.Filename)
if err != nil {
return errors.New(errors.CartridgeFileUnavailable, cartload.Filename)
}
defer resp.Body.Close()
size := resp.ContentLength
data = make([]byte, size)
_, err = resp.Body.Read(data)
if err != nil {
return nil
}
} else {
var f *os.File
f, err = os.Open(cartload.Filename)
if err != nil {
return errors.New(errors.CartridgeFileUnavailable, cartload.Filename)
}
defer f.Close()
// get file info
cfi, err := f.Stat()
if err != nil {
return err
}
size := cfi.Size()
data = make([]byte, size)
_, err = f.Read(data)
if err != nil {
return nil
}
func (cart *Cartridge) Attach(cartload cartridgeloader.Loader) error {
data, err := cartload.Load()
if err != nil {
return err
}
// note name of cartridge

View file

@ -172,7 +172,7 @@ func newAtari4k(data []byte) (cartMapper, error) {
cart.banks = make([][]uint8, 1)
if len(data) != bankSize*cart.numBanks() {
return nil, errors.New(errors.CartridgeFileError, "not enough bytes in the cartridge file")
return nil, errors.New(errors.CartridgeError, "not enough bytes in the cartridge file")
}
cart.banks[0] = make([]uint8, bankSize)
@ -220,7 +220,7 @@ func newAtari2k(data []byte) (cartMapper, error) {
cart.banks = make([][]uint8, 1)
if len(data) != bankSize*cart.numBanks() {
return nil, errors.New(errors.CartridgeFileError, "not enough bytes in the cartridge file")
return nil, errors.New(errors.CartridgeError, "not enough bytes in the cartridge file")
}
cart.banks[0] = make([]uint8, bankSize)
@ -266,7 +266,7 @@ func newAtari8k(data []uint8) (cartMapper, error) {
cart.banks = make([][]uint8, cart.numBanks())
if len(data) != bankSize*cart.numBanks() {
return nil, errors.New(errors.CartridgeFileError, "not enough bytes in the cartridge file")
return nil, errors.New(errors.CartridgeError, "not enough bytes in the cartridge file")
}
for k := 0; k < cart.numBanks(); k++ {
@ -333,7 +333,7 @@ func newAtari16k(data []byte) (cartMapper, error) {
cart.banks = make([][]uint8, cart.numBanks())
if len(data) != bankSize*cart.numBanks() {
return nil, errors.New(errors.CartridgeFileError, "not enough bytes in the cartridge file")
return nil, errors.New(errors.CartridgeError, "not enough bytes in the cartridge file")
}
for k := 0; k < cart.numBanks(); k++ {
@ -408,7 +408,7 @@ func newAtari32k(data []byte) (cartMapper, error) {
cart.banks = make([][]uint8, cart.numBanks())
if len(data) != bankSize*cart.numBanks() {
return nil, errors.New(errors.CartridgeFileError, "not enough bytes in the cartridge file")
return nil, errors.New(errors.CartridgeError, "not enough bytes in the cartridge file")
}
for k := 0; k < cart.numBanks(); k++ {

View file

@ -27,7 +27,7 @@ func newCBS(data []byte) (cartMapper, error) {
cart.banks = make([][]uint8, cart.numBanks())
if len(data) != bankSize*cart.numBanks() {
return nil, errors.New(errors.CartridgeFileError, "not enough bytes in the cartridge file")
return nil, errors.New(errors.CartridgeError, "not enough bytes in the cartridge file")
}
for k := 0; k < cart.numBanks(); k++ {

View file

@ -75,7 +75,7 @@ func newMnetwork(data []byte) (cartMapper, error) {
cart.banks = make([][]uint8, cart.numBanks())
if len(data) != bankSize*cart.numBanks() {
return nil, errors.New(errors.CartridgeFileError, "not enough bytes in the cartridge file")
return nil, errors.New(errors.CartridgeError, "not enough bytes in the cartridge file")
}
for k := 0; k < cart.numBanks(); k++ {

View file

@ -59,7 +59,7 @@ func newparkerBros(data []byte) (cartMapper, error) {
cart.banks = make([][]uint8, cart.numBanks())
if len(data) != bankSize*cart.numBanks() {
return nil, errors.New(errors.CartridgeFileError, "not enough bytes in the cartridge file")
return nil, errors.New(errors.CartridgeError, "not enough bytes in the cartridge file")
}
for k := 0; k < cart.numBanks(); k++ {

View file

@ -57,7 +57,7 @@ func newTigervision(data []byte) (cartMapper, error) {
cart.banks = make([][]uint8, cart.numBanks())
if len(data) != bankSize*cart.numBanks() {
return nil, errors.New(errors.CartridgeFileError, "not enough bytes in the cartridge file")
return nil, errors.New(errors.CartridgeError, "not enough bytes in the cartridge file")
}
for k := 0; k < cart.numBanks(); k++ {

View file

@ -1,6 +1,7 @@
package hardware
import (
"gopher2600/cartridgeloader"
"gopher2600/errors"
"gopher2600/hardware/cpu"
"gopher2600/hardware/memory"
@ -69,7 +70,7 @@ func NewVCS(tv television.Television) (*VCS, error) {
// memory. While this function can be called directly it is advised that the
// equivalent function call in the setup package is used. that function in turn
// calls this function in this package
func (vcs *VCS) AttachCartridge(cartload memory.CartridgeLoader) error {
func (vcs *VCS) AttachCartridge(cartload cartridgeloader.Loader) error {
if cartload.Filename == "" {
vcs.Mem.Cart.Eject()
} else {

View file

@ -2,11 +2,11 @@ package performance
import (
"fmt"
"gopher2600/cartridgeloader"
"gopher2600/errors"
"gopher2600/gui"
"gopher2600/gui/sdlplay"
"gopher2600/hardware"
"gopher2600/hardware/memory"
"gopher2600/setup"
"gopher2600/television"
"io"
@ -14,7 +14,7 @@ import (
)
// Check is a very rough and ready calculation of the emulator's performance
func Check(output io.Writer, profile bool, display bool, tvType string, scaling float32, runTime string, cartload memory.CartridgeLoader) error {
func Check(output io.Writer, profile bool, display bool, tvType string, scaling float32, runTime string, cartload cartridgeloader.Loader) error {
var ftv television.Television
var err error

View file

@ -2,11 +2,11 @@ package playmode
import (
"fmt"
"gopher2600/cartridgeloader"
"gopher2600/errors"
"gopher2600/gui"
"gopher2600/gui/sdlplay"
"gopher2600/hardware"
"gopher2600/hardware/memory"
"gopher2600/recorder"
"gopher2600/setup"
"os"
@ -14,14 +14,14 @@ import (
"time"
)
func uniqueFilename(cartload memory.CartridgeLoader) string {
func uniqueFilename(cartload cartridgeloader.Loader) string {
n := time.Now()
timestamp := fmt.Sprintf("%04d%02d%02d_%02d%02d%02d", n.Year(), n.Month(), n.Day(), n.Hour(), n.Minute(), n.Second())
return fmt.Sprintf("recording_%s_%s", cartload.ShortName(), timestamp)
}
// Play sets the emulation running - without any debugging features
func Play(tvType string, scaling float32, stable bool, transcript string, newRecording bool, cartload memory.CartridgeLoader) error {
func Play(tvType string, scaling float32, stable bool, transcript string, newRecording bool, cartload cartridgeloader.Loader) error {
if recorder.IsPlaybackFile(cartload.Filename) {
return errors.New(errors.PlayError, "specified cartridge is a playback file. use -recording flag")
}

View file

@ -2,9 +2,9 @@ package recorder
import (
"fmt"
"gopher2600/cartridgeloader"
"gopher2600/errors"
"gopher2600/hardware"
"gopher2600/hardware/memory"
"gopher2600/hardware/peripherals"
"gopher2600/screendigest"
"gopher2600/television"
@ -35,7 +35,7 @@ type playbackSequence struct {
type Playback struct {
transcript string
CartLoad memory.CartridgeLoader
CartLoad cartridgeloader.Loader
TVtype string
sequences []*playbackSequence

View file

@ -3,10 +3,10 @@ package regression
import (
"bufio"
"fmt"
"gopher2600/cartridgeloader"
"gopher2600/database"
"gopher2600/errors"
"gopher2600/hardware"
"gopher2600/hardware/memory"
"gopher2600/performance/limiter"
"gopher2600/screendigest"
"gopher2600/setup"
@ -35,7 +35,7 @@ const (
// regression tests pass if the screen digest after N frames matches the stored
// value.
type FrameRegression struct {
CartLoad memory.CartridgeLoader
CartLoad cartridgeloader.Loader
TVtype string
NumFrames int
State bool

View file

@ -2,14 +2,14 @@ package regression
import (
"fmt"
"gopher2600/hardware/memory"
"gopher2600/cartridgeloader"
"path/filepath"
"time"
)
// create a unique filename from a CatridgeLoader instance
func uniqueFilename(cartload memory.CartridgeLoader) string {
func uniqueFilename(cartload cartridgeloader.Loader) string {
n := time.Now()
timestamp := fmt.Sprintf("%04d%02d%02d_%02d%02d%02d", n.Year(), n.Month(), n.Day(), n.Hour(), n.Minute(), n.Second())
newScript := fmt.Sprintf("%s_%s", cartload.ShortName(), timestamp)

View file

@ -16,11 +16,13 @@ import (
// cryptographic task.
type SHA1 struct {
television.Television
digest [sha1.Size]byte
frameData []byte
frameNum int
digest [sha1.Size]byte
pixels []byte
frameNum int
}
const pixelDepth = 3
// NewSHA1 initialises a new instance of DigestTV. For convenience, the
// television argument can be nil, in which case an instance of
// StellaTelevision will be created.
@ -71,7 +73,14 @@ func (dig *SHA1) ResetDigest() {
// Resize implements television.Television interface
func (dig *SHA1) Resize(_, _ int) error {
dig.frameData = make([]byte, len(dig.digest)+((television.ClocksPerScanline+1)*(dig.GetSpec().ScanlinesTotal+1)*3))
// length of pixels array contains enough room for the previous frames
// digest value
l := len(dig.digest)
// alloscate enough pixels for entire frame
l += ((television.ClocksPerScanline + 1) * (dig.GetSpec().ScanlinesTotal + 1) * pixelDepth)
dig.pixels = make([]byte, l)
return nil
}
@ -79,11 +88,11 @@ func (dig *SHA1) Resize(_, _ int) error {
func (dig *SHA1) NewFrame(frameNum int) error {
// chain fingerprints by copying the value of the last fingerprint
// to the head of the screen data
n := copy(dig.frameData, dig.digest[:])
n := copy(dig.pixels, dig.digest[:])
if n != len(dig.digest) {
return errors.New(errors.ScreenDigest, fmt.Sprintf("unexpected amount of data copied"))
return errors.New(errors.ScreenDigest, fmt.Sprintf("digest error during new frame"))
}
dig.digest = sha1.Sum(dig.frameData)
dig.digest = sha1.Sum(dig.pixels)
dig.frameNum = frameNum
return nil
}
@ -96,17 +105,17 @@ func (dig *SHA1) NewScanline(scanline int) error {
// SetPixel implements television.Renderer interface
func (dig *SHA1) SetPixel(x, y int, red, green, blue byte, vblank bool) error {
// preserve the first few bytes for a chained fingerprint
offset := television.ClocksPerScanline * y * 3
offset += x * 3
i := len(dig.digest)
i += television.ClocksPerScanline * y * pixelDepth
i += x * pixelDepth
if offset >= len(dig.frameData) {
return errors.New(errors.ScreenDigest, fmt.Sprintf("the coordinates (%d, %d) passed to SetPixel will cause an invalid access of the frameData array", x, y))
if i <= len(dig.pixels)-pixelDepth {
// setting every pixel regardless of vblank value
dig.pixels[i] = red
dig.pixels[i+1] = green
dig.pixels[i+2] = blue
}
dig.frameData[offset] = red
dig.frameData[offset+1] = green
dig.frameData[offset+2] = blue
return nil
}

View file

@ -1,10 +1,10 @@
package setup
import (
"gopher2600/cartridgeloader"
"gopher2600/database"
"gopher2600/errors"
"gopher2600/hardware"
"gopher2600/hardware/memory"
)
// the location of the setupDB file
@ -31,7 +31,7 @@ func initDBSession(db *database.Session) error {
}
// AttachCartridge to the VCS and apply setup information from the setupDB
func AttachCartridge(vcs *hardware.VCS, cartload memory.CartridgeLoader) error {
func AttachCartridge(vcs *hardware.VCS, cartload cartridgeloader.Loader) error {
err := vcs.AttachCartridge(cartload)
if err != nil {
return err

View file

@ -2,10 +2,10 @@ package main_test
import (
"fmt"
"gopher2600/cartridgeloader"
"gopher2600/gui"
"gopher2600/gui/sdldebug"
"gopher2600/hardware"
"gopher2600/hardware/memory"
"testing"
)
@ -27,7 +27,7 @@ func BenchmarkSDL(b *testing.B) {
panic(fmt.Errorf("error preparing VCS: %s", err))
}
err = vcs.AttachCartridge(memory.CartridgeLoader{Filename: "../roms/ROMs/Pitfall.bin"})
err = vcs.AttachCartridge(cartridgeloader.Loader{Filename: "../roms/ROMs/Pitfall.bin"})
if err != nil {
panic(err)
}

View file

@ -4,8 +4,8 @@
package main
import (
"gopher2600/cartridgeloader"
"gopher2600/hardware"
"gopher2600/hardware/memory"
"gopher2600/hardware/peripherals"
"gopher2600/setup"
"syscall/js"
@ -22,7 +22,7 @@ func main() {
}
// load cartridge
cartload := memory.CartridgeLoader{
cartload := cartridgeloader.Loader{
Filename: "http://localhost:8080/Pitfall.bin",
}