diff --git a/README.md b/README.md index 299dd40..b7db6a2 100644 --- a/README.md +++ b/README.md @@ -219,8 +219,6 @@ Only valid on SDL mode dump to the console the sofswitch registrations -vidHDSlot int slot for the VidHD card, only for //e models. -1 for none (default 2) - -woz string - show WOZ file information ``` diff --git a/apple2.go b/apple2.go index 541276a..70ea0bd 100644 --- a/apple2.go +++ b/apple2.go @@ -148,10 +148,10 @@ func (a *Apple2) executeCommand(command int) { switch command { case CommandToggleSpeed: if a.cycleDurationNs == 0 { - fmt.Println("Slow") + //fmt.Println("Slow") a.cycleDurationNs = 1000.0 / CPUClockMhz } else { - fmt.Println("Fast") + //fmt.Println("Fast") a.cycleDurationNs = 0 } case CommandShowSpeed: diff --git a/apple2Setup.go b/apple2Setup.go index 683a3eb..3722ae8 100644 --- a/apple2Setup.go +++ b/apple2Setup.go @@ -4,6 +4,7 @@ import ( "errors" "github.com/ivanizag/izapple2/core6502" + "github.com/ivanizag/izapple2/storage" ) func newApple2() *Apple2 { @@ -71,7 +72,7 @@ const ( // LoadRom loads a standard Apple2+ or 2e ROM func (a *Apple2) LoadRom(filename string) error { - data, err := loadResource(filename) + data, err := storage.LoadResource(filename) if err != nil { return err } @@ -92,7 +93,7 @@ func (a *Apple2) AddDisk2(slot int, diskImage, diskBImage string) error { a.insertCard(c, slot) if diskImage != "" { - diskette, err := loadDiskette(diskImage) + diskette, err := storage.LoadDiskette(diskImage) if err != nil { return err } @@ -100,7 +101,7 @@ func (a *Apple2) AddDisk2(slot int, diskImage, diskBImage string) error { } if diskBImage != "" { - diskette, err := loadDiskette(diskBImage) + diskette, err := storage.LoadDiskette(diskBImage) if err != nil { return err } diff --git a/apple2main.go b/apple2main.go index ed16b04..df1722c 100644 --- a/apple2main.go +++ b/apple2main.go @@ -3,6 +3,8 @@ package izapple2 import ( "flag" "os" + + "github.com/ivanizag/izapple2/storage" ) const defaultInternal = "" @@ -25,10 +27,6 @@ func MainApple() *Apple2 { "diskb", "", "file to load on the second disk drive") - wozImage := flag.String( - "woz", - "", - "show WOZ file information") hardDiskImage := flag.String( "hd", "", @@ -133,23 +131,13 @@ func MainApple() *Apple2 { diskImageFinal := *diskImage hardDiskImageFinal := *hardDiskImage if filename != "" { - if isDiskette(filename) { + if storage.IsDiskette(filename) { diskImageFinal = filename } else { hardDiskImageFinal = filename } } - if *wozImage != "" { - f, err := loadFileWoz(*wozImage) - if err != nil { - panic(err) - } - f.dump() - return nil - - } - a := newApple2() a.setup(*cpuClock, *fastDisk, *traceMLI) a.io.setTrace(*traceSS) diff --git a/base64a.go b/base64a.go index 82bef57..be237fc 100644 --- a/base64a.go +++ b/base64a.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/ivanizag/izapple2/core6502" + "github.com/ivanizag/izapple2/storage" ) /* @@ -35,7 +36,7 @@ func loadBase64aRom(a *Apple2) error { for i := 0; i < base64aRomChipCount; i++ { filename := fmt.Sprintf("/BASE64A_%X.BIN", 0xd0+i*0x08) - data, err := loadResource(filename) + data, err := storage.LoadResource(filename) if err != nil { return err } diff --git a/cardBase.go b/cardBase.go index 259d73f..e30519a 100644 --- a/cardBase.go +++ b/cardBase.go @@ -1,5 +1,7 @@ package izapple2 +import "github.com/ivanizag/izapple2/storage" + // Card represents an Apple II card to be inserted in a slot type Card interface { loadRom(data []uint8) @@ -32,7 +34,7 @@ func (c *cardBase) GetInfo() map[string]string { } func (c *cardBase) loadRomFromResource(resource string) { - data, err := loadResource(resource) + data, err := storage.LoadResource(resource) if err != nil { // The resource should be internal and never fail panic(err) diff --git a/cardDisk2.go b/cardDisk2.go index e666b23..d03c79a 100644 --- a/cardDisk2.go +++ b/cardDisk2.go @@ -3,6 +3,8 @@ package izapple2 import ( "fmt" "strconv" + + "github.com/ivanizag/izapple2/storage" ) /* @@ -33,7 +35,7 @@ type CardDisk2 struct { type cardDisk2Drive struct { name string - diskette diskette + diskette storage.Diskette power bool // q4 phases uint8 // q3, q2, q1 and q0 with q0 on the LSB. Magnets that are active on the stepper motor tracksStep int // Stepmotor for tracks position. 4 steps per track @@ -102,7 +104,7 @@ func (c *CardDisk2) assign(a *Apple2, slot int) { drive.power = false c.a.releaseFastMode() if drive.diskette != nil { - drive.diskette.powerOff(c.a.cpu.GetCycles()) + drive.diskette.PowerOff(c.a.cpu.GetCycles()) } } return c.dataLatch @@ -113,7 +115,7 @@ func (c *CardDisk2) assign(a *Apple2, slot int) { drive.power = true c.a.requestFastMode() if drive.diskette != nil { - drive.diskette.powerOn(c.a.cpu.GetCycles()) + drive.diskette.PowerOn(c.a.cpu.GetCycles()) } } return 0 @@ -173,9 +175,9 @@ func (c *CardDisk2) processQ6Q7(in uint8) { } if !c.q6 { // shift if !c.q7 { // Q6L-Q7L: Read - c.dataLatch = d.diskette.read(d.tracksStep, c.a.cpu.GetCycles()) + c.dataLatch = d.diskette.Read(d.tracksStep, c.a.cpu.GetCycles()) } else { // Q6L-Q7H: Write the dataLatch value to disk. Shift data out - d.diskette.write(d.tracksStep, c.dataLatch, c.a.cpu.GetCycles()) + d.diskette.Write(d.tracksStep, c.dataLatch, c.a.cpu.GetCycles()) } } else { // load if !c.q7 { // Q6H-Q7L: Sense write protect / prewrite state @@ -269,7 +271,7 @@ func moveStep(phases uint8, prevStep int) int { return nextStep } -func (d *cardDisk2Drive) insertDiskette(name string, dt diskette) { +func (d *cardDisk2Drive) insertDiskette(name string, dt storage.Diskette) { d.name = name d.diskette = dt } diff --git a/cardHardDisk.go b/cardHardDisk.go index 4a6c740..5a051df 100644 --- a/cardHardDisk.go +++ b/cardHardDisk.go @@ -3,6 +3,8 @@ package izapple2 import ( "fmt" "strconv" + + "github.com/ivanizag/izapple2/storage" ) /* @@ -24,7 +26,7 @@ type CardHardDisk struct { filename string trace bool - disk *blockDisk + disk *storage.BlockDisk mliParams uint16 } @@ -45,7 +47,7 @@ func (c *CardHardDisk) GetInfo() map[string]string { // LoadImage loads a disk image func (c *CardHardDisk) LoadImage(filename string) error { - hd, err := openBlockDisk(filename) + hd, err := storage.OpenBlockDisk(filename) if err != nil { return err } @@ -95,11 +97,11 @@ func (c *CardHardDisk) assign(a *Apple2, slot int) { }, "HDCOMMAND") c.addCardSoftSwitchR(1, func(*ioC0Page) uint8 { // Blocks available, low byte - return uint8(c.disk.blocks) + return uint8(c.disk.GetSizeInBlocks()) }, "HDBLOCKSLO") c.addCardSoftSwitchR(2, func(*ioC0Page) uint8 { // Blocks available, high byte - return uint8(c.disk.blocks >> 8) + return uint8(c.disk.GetSizeInBlocks() >> 8) }, "HDBLOCKHI") c.addCardSoftSwitchR(3, func(*ioC0Page) uint8 { @@ -146,12 +148,12 @@ func (c *CardHardDisk) readBlock(block uint16, dest uint16) uint8 { fmt.Printf("[CardHardDisk] Read block %v into $%x.\n", block, dest) } - data, err := c.disk.read(uint32(block)) + data, err := c.disk.Read(uint32(block)) if err != nil { return proDosDeviceErrorIO } // Byte by byte transfer to memory using the full Poke code path - for i := uint16(0); i < uint16(proDosBlockSize); i++ { + for i := uint16(0); i < uint16(len(data)); i++ { c.a.mmu.Poke(dest+i, data[i]) } @@ -163,17 +165,17 @@ func (c *CardHardDisk) writeBlock(block uint16, source uint16) uint8 { fmt.Printf("[CardHardDisk] Write block %v from $%x.\n", block, source) } - if c.disk.readOnly { + if c.disk.IsReadOnly() { return proDosDeviceErrorWriteProtected } // Byte by byte transfer from memory using the full Peek code path - buf := make([]uint8, proDosBlockSize) - for i := uint16(0); i < uint16(proDosBlockSize); i++ { + buf := make([]uint8, storage.ProDosBlockSize) + for i := uint16(0); i < uint16(len(buf)); i++ { buf[i] = c.a.mmu.Peek(source + i) } - err := c.disk.write(uint32(block), buf) + err := c.disk.Write(uint32(block), buf) if err != nil { return proDosDeviceErrorIO } diff --git a/characterGenerator.go b/characterGenerator.go index 7e1bee1..c579456 100644 --- a/characterGenerator.go +++ b/characterGenerator.go @@ -3,6 +3,8 @@ package izapple2 import ( "errors" "fmt" + + "github.com/ivanizag/izapple2/storage" ) /* @@ -43,7 +45,7 @@ func newCharacterGenerator(filename string, order charColumnMap) (*CharacterGene } func (cg *CharacterGenerator) load(filename string) error { - bytes, err := loadResource(filename) + bytes, err := storage.LoadResource(filename) if err != nil { return err } diff --git a/izapple2fyne/main.go b/izapple2fyne/main.go index 51370f5..5b94391 100644 --- a/izapple2fyne/main.go +++ b/izapple2fyne/main.go @@ -99,9 +99,6 @@ func fyneRun(s *state) { }) s.win.Show() - - fmt.Printf("%v\n", s.win.Canvas().Scale()) - s.app.Run() } diff --git a/izapple2sdl/izapple2sdl b/izapple2sdl/izapple2sdl deleted file mode 100755 index 66824aa..0000000 Binary files a/izapple2sdl/izapple2sdl and /dev/null differ diff --git a/blockDisk.go b/storage/blockDisk.go similarity index 55% rename from blockDisk.go rename to storage/blockDisk.go index 368cb0c..73723f6 100644 --- a/blockDisk.go +++ b/storage/blockDisk.go @@ -1,4 +1,4 @@ -package izapple2 +package storage import ( "errors" @@ -7,23 +7,26 @@ import ( ) /* -Valid for ProDos disks with 512 bytes blocks. Can be diskettes or hard disks +Valid for ProDOS disks with 512 bytes blocks. Can be diskettes or hard disks */ const ( - proDosBlockSize = uint32(512) + // ProDosBlockSize is the size of the blocks on the ProDOS devices + ProDosBlockSize = uint32(512) proDosMaxBlocks = uint32(65536) ) -type blockDisk struct { +// BlockDisk is any block device with 512 bytes blocks +type BlockDisk struct { file *os.File readOnly bool dataOffset uint32 blocks uint32 } -func openBlockDisk(filename string) (*blockDisk, error) { - var bd blockDisk +// OpenBlockDisk creates a new block device links to a file +func OpenBlockDisk(filename string) (*BlockDisk, error) { + var bd BlockDisk bd.readOnly = false file, err := os.OpenFile(filename, os.O_RDWR, 0) @@ -49,29 +52,39 @@ func openBlockDisk(filename string) (*blockDisk, error) { return nil, err } - if fileInfo.Size() > int64(proDosBlockSize*proDosMaxBlocks) { + if fileInfo.Size() > int64(ProDosBlockSize*proDosMaxBlocks) { return nil, fmt.Errorf("File is too big OR %s", err2mg.Error()) } size := uint32(fileInfo.Size()) - if size%proDosBlockSize != 0 { + if size%ProDosBlockSize != 0 { return nil, fmt.Errorf("File size os invalid OR %s", err2mg.Error()) } // It's a valid raw file - bd.blocks = size / proDosBlockSize + bd.blocks = size / ProDosBlockSize bd.dataOffset = 0 return &bd, nil } -func (bd *blockDisk) read(block uint32) ([]uint8, error) { +// GetSizeInBlocks returns the number of blocks of the device +func (bd *BlockDisk) GetSizeInBlocks() uint32 { + return bd.blocks +} + +// IsReadOnly returns true if the device is read only +func (bd *BlockDisk) IsReadOnly() bool { + return bd.readOnly +} + +func (bd *BlockDisk) Read(block uint32) ([]uint8, error) { if block >= bd.blocks { return nil, errors.New("disk block number is too big") } - buf := make([]uint8, proDosBlockSize) + buf := make([]uint8, ProDosBlockSize) - offset := int64(bd.dataOffset + block*proDosBlockSize) + offset := int64(bd.dataOffset + block*ProDosBlockSize) _, err := bd.file.ReadAt(buf, offset) if err != nil { return nil, err @@ -80,7 +93,7 @@ func (bd *blockDisk) read(block uint32) ([]uint8, error) { return buf, nil } -func (bd *blockDisk) write(block uint32, data []uint8) error { +func (bd *BlockDisk) Write(block uint32, data []uint8) error { if bd.readOnly { return errors.New("can't write in a readonly disk") } @@ -88,7 +101,7 @@ func (bd *blockDisk) write(block uint32, data []uint8) error { return errors.New("disk block number is too big") } - offset := int64(bd.dataOffset + block*proDosBlockSize) + offset := int64(bd.dataOffset + block*ProDosBlockSize) _, err := bd.file.WriteAt(data, offset) if err != nil { return err diff --git a/diskette.go b/storage/diskette.go similarity index 50% rename from diskette.go rename to storage/diskette.go index 7cc5212..5c40af7 100644 --- a/diskette.go +++ b/storage/diskette.go @@ -1,18 +1,20 @@ -package izapple2 +package storage import ( "errors" ) -type diskette interface { - powerOn(cycle uint64) - powerOff(cycle uint64) - read(quarterTrack int, cycle uint64) uint8 - write(quarterTrack int, value uint8, cycle uint64) +// Diskette represents a diskette and it's RW mechanism +type Diskette interface { + PowerOn(cycle uint64) + PowerOff(cycle uint64) + Read(quarterTrack int, cycle uint64) uint8 + Write(quarterTrack int, value uint8, cycle uint64) } -func isDiskette(filename string) bool { - data, err := loadResource(filename) +// IsDiskette returns true if the files looks like a 5 1/4 diskette +func IsDiskette(filename string) bool { + data, err := LoadResource(filename) if err != nil { return false } @@ -20,8 +22,9 @@ func isDiskette(filename string) bool { return isFileNib(data) || isFileDsk(data) || isFileWoz(data) } -func loadDiskette(filename string) (diskette, error) { - data, err := loadResource(filename) +// LoadDiskette returns a Diskette by detecting the format +func LoadDiskette(filename string) (Diskette, error) { + data, err := LoadResource(filename) if err != nil { return nil, err } diff --git a/diskette16sector.go b/storage/diskette16sector.go similarity index 62% rename from diskette16sector.go rename to storage/diskette16sector.go index 6c2abee..30521dc 100644 --- a/diskette16sector.go +++ b/storage/diskette16sector.go @@ -1,4 +1,4 @@ -package izapple2 +package storage /* See: @@ -11,23 +11,23 @@ type diskette16sector struct { position int } -func (d *diskette16sector) powerOn(cycle uint64) { +func (d *diskette16sector) PowerOn(cycle uint64) { // Not used } -func (d *diskette16sector) powerOff(_ uint64) { +func (d *diskette16sector) PowerOff(_ uint64) { // Not used } -func (d *diskette16sector) read(quarterTrack int, cycle uint64) uint8 { - track := d.nib.track[quarterTrack/stepsPerTrack] +func (d *diskette16sector) Read(quarterTrack int, cycle uint64) uint8 { + track := d.nib.track[quarterTrack/4] value := track[d.position] d.position = (d.position + 1) % nibBytesPerTrack //fmt.Printf("%v, %v, %v, %x\n", 0, 0, d.position, uint8(value)) return value } -func (d *diskette16sector) write(quarterTrack int, value uint8, _ uint64) { - track := quarterTrack / stepsPerTrack +func (d *diskette16sector) Write(quarterTrack int, value uint8, _ uint64) { + track := quarterTrack / 4 d.nib.track[track][d.position] = value d.position = (d.position + 1) % nibBytesPerTrack } diff --git a/diskette16sectorTimed.go b/storage/diskette16sectorTimed.go similarity index 76% rename from diskette16sectorTimed.go rename to storage/diskette16sectorTimed.go index 76d444d..51ca6ad 100644 --- a/diskette16sectorTimed.go +++ b/storage/diskette16sectorTimed.go @@ -1,14 +1,14 @@ -package izapple2 +package storage type diskette16sectorTimed struct { nib *fileNib cycleOn uint64 // Cycle when the disk was last turned on } -func (d *diskette16sectorTimed) powerOn(cycle uint64) { +func (d *diskette16sectorTimed) PowerOn(cycle uint64) { d.cycleOn = cycle } -func (d *diskette16sectorTimed) powerOff(_ uint64) { +func (d *diskette16sectorTimed) PowerOff(_ uint64) { // Not needed } @@ -20,8 +20,8 @@ func (d *diskette16sectorTimed) getBitPositionInTrack(cycle uint64) int { return int(position % (8 * nibBytesPerTrack)) // Ignore full turns } -func (d *diskette16sectorTimed) read(quarterTrack int, cycle uint64) uint8 { - track := d.nib.track[quarterTrack/stepsPerTrack] +func (d *diskette16sectorTimed) Read(quarterTrack int, cycle uint64) uint8 { + track := d.nib.track[quarterTrack/4] bitPosition := d.getBitPositionInTrack(cycle) bytePosition := bitPosition / 8 shift := uint(bitPosition % 8) @@ -35,6 +35,6 @@ func (d *diskette16sectorTimed) read(quarterTrack int, cycle uint64) uint8 { return value } -func (d *diskette16sectorTimed) write(quarterTrack int, value uint8, _ uint64) { +func (d *diskette16sectorTimed) Write(quarterTrack int, value uint8, _ uint64) { panic("Write not implemented on time based disk implementation") } diff --git a/diskette16sectorWritable.go b/storage/diskette16sectorWritable.go similarity index 69% rename from diskette16sectorWritable.go rename to storage/diskette16sectorWritable.go index 0c2266e..e9f8870 100644 --- a/diskette16sectorWritable.go +++ b/storage/diskette16sectorWritable.go @@ -1,4 +1,4 @@ -package izapple2 +package storage /* See: @@ -15,22 +15,22 @@ type diskette16sectorWritable struct { dirtyTrack int } -func (d *diskette16sectorWritable) powerOn(cycle uint64) { +func (d *diskette16sectorWritable) PowerOn(cycle uint64) { // Not used } -func (d *diskette16sectorWritable) powerOff(_ uint64) { +func (d *diskette16sectorWritable) PowerOff(_ uint64) { d.commit() } -func (d *diskette16sectorWritable) read(quarterTrack int, cycle uint64) uint8 { - track := d.nib.track[quarterTrack/stepsPerTrack] +func (d *diskette16sectorWritable) Read(quarterTrack int, cycle uint64) uint8 { + track := d.nib.track[quarterTrack/4] value := track[d.position] d.position = (d.position + 1) % nibBytesPerTrack return value } -func (d *diskette16sectorWritable) write(quarterTrack int, value uint8, _ uint64) { - track := quarterTrack / stepsPerTrack +func (d *diskette16sectorWritable) Write(quarterTrack int, value uint8, _ uint64) { + track := quarterTrack / 4 if d.hasDirtyTrack && track != d.dirtyTrack { d.commit() diff --git a/disketteWoz.go b/storage/disketteWoz.go similarity index 93% rename from disketteWoz.go rename to storage/disketteWoz.go index 90ff47d..51d8b29 100644 --- a/disketteWoz.go +++ b/storage/disketteWoz.go @@ -1,4 +1,4 @@ -package izapple2 +package storage import ( "errors" @@ -71,16 +71,16 @@ func newDisquetteWoz(f *fileWoz) (*disketteWoz, error) { return &d, nil } -func (d *disketteWoz) powerOn(cycle uint64) { +func (d *disketteWoz) PowerOn(cycle uint64) { d.turning = true d.cycleOn = cycle } -func (d *disketteWoz) powerOff(_ uint64) { +func (d *disketteWoz) PowerOff(_ uint64) { d.turning = false } -func (d *disketteWoz) read(quarterTrack int, cycle uint64) uint8 { +func (d *disketteWoz) Read(quarterTrack int, cycle uint64) uint8 { // Count cycles to know how many bits have been read cycles := cycle - d.cycle deltaBits := cycles / cyclesPerBit // TODO: Use Woz optimal bit timing @@ -123,6 +123,6 @@ func (d *disketteWoz) read(quarterTrack int, cycle uint64) uint8 { return d.visibleLatch } -func (d *disketteWoz) write(quarterTrack int, value uint8, _ uint64) { +func (d *disketteWoz) Write(quarterTrack int, value uint8, _ uint64) { panic("Write not implemented on woz disk implementation") } diff --git a/file2mg.go b/storage/file2mg.go similarity index 92% rename from file2mg.go rename to storage/file2mg.go index ece6c02..92275ad 100644 --- a/file2mg.go +++ b/storage/file2mg.go @@ -1,4 +1,4 @@ -package izapple2 +package storage import ( "encoding/binary" @@ -35,7 +35,7 @@ type file2mgHeader struct { LengthCreator uint32 } -func parse2mg(bd *blockDisk) error { +func parse2mg(bd *BlockDisk) error { fileInfo, err := bd.file.Stat() if err != nil { return err @@ -55,7 +55,7 @@ func parse2mg(bd *blockDisk) error { bd.blocks = header.Blocks bd.dataOffset = header.OffsetData - if fileInfo.Size() < int64(bd.dataOffset+bd.blocks*proDosBlockSize) { + if fileInfo.Size() < int64(bd.dataOffset+bd.blocks*ProDosBlockSize) { return errors.New("The 2MG file is too small") } diff --git a/fileNib.go b/storage/fileNib.go similarity index 99% rename from fileNib.go rename to storage/fileNib.go index bd6051e..7e3d622 100644 --- a/fileNib.go +++ b/storage/fileNib.go @@ -1,4 +1,4 @@ -package izapple2 +package storage import ( "errors" diff --git a/fileNib_test.go b/storage/fileNib_test.go similarity index 96% rename from fileNib_test.go rename to storage/fileNib_test.go index 10500c8..eef7eca 100644 --- a/fileNib_test.go +++ b/storage/fileNib_test.go @@ -1,4 +1,4 @@ -package izapple2 +package storage import ( "testing" diff --git a/fileWoz.go b/storage/fileWoz.go similarity index 97% rename from fileWoz.go rename to storage/fileWoz.go index caed076..a6f89a9 100644 --- a/fileWoz.go +++ b/storage/fileWoz.go @@ -1,4 +1,4 @@ -package izapple2 +package storage import ( "bytes" @@ -86,15 +86,6 @@ func (f *fileWoz) getBit(position uint32, quarterTrack int) uint8 { return trackWoz.data[position/8] >> (7 - position%8) & 1 } -func loadFileWoz(filename string) (*fileWoz, error) { - data, err := loadResource(filename) - if err != nil { - return nil, err - } - - return newFileWoz(data) -} - func isFileWoz(data []uint8) bool { header := data[:len(headerWoz2)] if bytes.Equal(headerWoz1, header) { diff --git a/resources.go b/storage/resources.go similarity index 88% rename from resources.go rename to storage/resources.go index aa8a450..b985964 100644 --- a/resources.go +++ b/storage/resources.go @@ -1,4 +1,4 @@ -package izapple2 +package storage import ( "io" @@ -25,7 +25,8 @@ func isHTTPResource(filename string) bool { strings.HasPrefix(filename, httpsPrefix) } -func loadResource(filename string) ([]uint8, error) { +// LoadResource loads in memory a file from the filesystem, http or embedded +func LoadResource(filename string) ([]uint8, error) { var file io.Reader if isInternalResource(filename) { // load from embedded resource