Move disk related code to a separate package

This commit is contained in:
Ivan Izaguirre 2020-10-17 20:10:48 +02:00
parent 7329d4f042
commit 18e0363d84
22 changed files with 111 additions and 110 deletions

View File

@ -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
```

View File

@ -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:

View File

@ -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
}

View File

@ -3,6 +3,8 @@ package izapple2
import (
"flag"
"os"
"github.com/ivanizag/izapple2/storage"
)
const defaultInternal = "<default>"
@ -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)

View File

@ -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("<internal>/BASE64A_%X.BIN", 0xd0+i*0x08)
data, err := loadResource(filename)
data, err := storage.LoadResource(filename)
if err != nil {
return err
}

View File

@ -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)

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -99,9 +99,6 @@ func fyneRun(s *state) {
})
s.win.Show()
fmt.Printf("%v\n", s.win.Canvas().Scale())
s.app.Run()
}

Binary file not shown.

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -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")
}

View File

@ -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()

View File

@ -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")
}

View File

@ -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")
}

View File

@ -1,4 +1,4 @@
package izapple2
package storage
import (
"errors"

View File

@ -1,4 +1,4 @@
package izapple2
package storage
import (
"testing"

View File

@ -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) {

View File

@ -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