Move disk related code to a separate package
This commit is contained in:
parent
7329d4f042
commit
18e0363d84
|
@ -219,8 +219,6 @@ Only valid on SDL mode
|
||||||
dump to the console the sofswitch registrations
|
dump to the console the sofswitch registrations
|
||||||
-vidHDSlot int
|
-vidHDSlot int
|
||||||
slot for the VidHD card, only for //e models. -1 for none (default 2)
|
slot for the VidHD card, only for //e models. -1 for none (default 2)
|
||||||
-woz string
|
|
||||||
show WOZ file information
|
|
||||||
|
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
|
@ -148,10 +148,10 @@ func (a *Apple2) executeCommand(command int) {
|
||||||
switch command {
|
switch command {
|
||||||
case CommandToggleSpeed:
|
case CommandToggleSpeed:
|
||||||
if a.cycleDurationNs == 0 {
|
if a.cycleDurationNs == 0 {
|
||||||
fmt.Println("Slow")
|
//fmt.Println("Slow")
|
||||||
a.cycleDurationNs = 1000.0 / CPUClockMhz
|
a.cycleDurationNs = 1000.0 / CPUClockMhz
|
||||||
} else {
|
} else {
|
||||||
fmt.Println("Fast")
|
//fmt.Println("Fast")
|
||||||
a.cycleDurationNs = 0
|
a.cycleDurationNs = 0
|
||||||
}
|
}
|
||||||
case CommandShowSpeed:
|
case CommandShowSpeed:
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/ivanizag/izapple2/core6502"
|
"github.com/ivanizag/izapple2/core6502"
|
||||||
|
"github.com/ivanizag/izapple2/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newApple2() *Apple2 {
|
func newApple2() *Apple2 {
|
||||||
|
@ -71,7 +72,7 @@ const (
|
||||||
|
|
||||||
// LoadRom loads a standard Apple2+ or 2e ROM
|
// LoadRom loads a standard Apple2+ or 2e ROM
|
||||||
func (a *Apple2) LoadRom(filename string) error {
|
func (a *Apple2) LoadRom(filename string) error {
|
||||||
data, err := loadResource(filename)
|
data, err := storage.LoadResource(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -92,7 +93,7 @@ func (a *Apple2) AddDisk2(slot int, diskImage, diskBImage string) error {
|
||||||
a.insertCard(c, slot)
|
a.insertCard(c, slot)
|
||||||
|
|
||||||
if diskImage != "" {
|
if diskImage != "" {
|
||||||
diskette, err := loadDiskette(diskImage)
|
diskette, err := storage.LoadDiskette(diskImage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -100,7 +101,7 @@ func (a *Apple2) AddDisk2(slot int, diskImage, diskBImage string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if diskBImage != "" {
|
if diskBImage != "" {
|
||||||
diskette, err := loadDiskette(diskBImage)
|
diskette, err := storage.LoadDiskette(diskBImage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,8 @@ package izapple2
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/ivanizag/izapple2/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
const defaultInternal = "<default>"
|
const defaultInternal = "<default>"
|
||||||
|
@ -25,10 +27,6 @@ func MainApple() *Apple2 {
|
||||||
"diskb",
|
"diskb",
|
||||||
"",
|
"",
|
||||||
"file to load on the second disk drive")
|
"file to load on the second disk drive")
|
||||||
wozImage := flag.String(
|
|
||||||
"woz",
|
|
||||||
"",
|
|
||||||
"show WOZ file information")
|
|
||||||
hardDiskImage := flag.String(
|
hardDiskImage := flag.String(
|
||||||
"hd",
|
"hd",
|
||||||
"",
|
"",
|
||||||
|
@ -133,23 +131,13 @@ func MainApple() *Apple2 {
|
||||||
diskImageFinal := *diskImage
|
diskImageFinal := *diskImage
|
||||||
hardDiskImageFinal := *hardDiskImage
|
hardDiskImageFinal := *hardDiskImage
|
||||||
if filename != "" {
|
if filename != "" {
|
||||||
if isDiskette(filename) {
|
if storage.IsDiskette(filename) {
|
||||||
diskImageFinal = filename
|
diskImageFinal = filename
|
||||||
} else {
|
} else {
|
||||||
hardDiskImageFinal = filename
|
hardDiskImageFinal = filename
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if *wozImage != "" {
|
|
||||||
f, err := loadFileWoz(*wozImage)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
f.dump()
|
|
||||||
return nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
a := newApple2()
|
a := newApple2()
|
||||||
a.setup(*cpuClock, *fastDisk, *traceMLI)
|
a.setup(*cpuClock, *fastDisk, *traceMLI)
|
||||||
a.io.setTrace(*traceSS)
|
a.io.setTrace(*traceSS)
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/ivanizag/izapple2/core6502"
|
"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++ {
|
for i := 0; i < base64aRomChipCount; i++ {
|
||||||
filename := fmt.Sprintf("<internal>/BASE64A_%X.BIN", 0xd0+i*0x08)
|
filename := fmt.Sprintf("<internal>/BASE64A_%X.BIN", 0xd0+i*0x08)
|
||||||
data, err := loadResource(filename)
|
data, err := storage.LoadResource(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package izapple2
|
package izapple2
|
||||||
|
|
||||||
|
import "github.com/ivanizag/izapple2/storage"
|
||||||
|
|
||||||
// Card represents an Apple II card to be inserted in a slot
|
// Card represents an Apple II card to be inserted in a slot
|
||||||
type Card interface {
|
type Card interface {
|
||||||
loadRom(data []uint8)
|
loadRom(data []uint8)
|
||||||
|
@ -32,7 +34,7 @@ func (c *cardBase) GetInfo() map[string]string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *cardBase) loadRomFromResource(resource string) {
|
func (c *cardBase) loadRomFromResource(resource string) {
|
||||||
data, err := loadResource(resource)
|
data, err := storage.LoadResource(resource)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// The resource should be internal and never fail
|
// The resource should be internal and never fail
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|
14
cardDisk2.go
14
cardDisk2.go
|
@ -3,6 +3,8 @@ package izapple2
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/ivanizag/izapple2/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -33,7 +35,7 @@ type CardDisk2 struct {
|
||||||
|
|
||||||
type cardDisk2Drive struct {
|
type cardDisk2Drive struct {
|
||||||
name string
|
name string
|
||||||
diskette diskette
|
diskette storage.Diskette
|
||||||
power bool // q4
|
power bool // q4
|
||||||
phases uint8 // q3, q2, q1 and q0 with q0 on the LSB. Magnets that are active on the stepper motor
|
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
|
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
|
drive.power = false
|
||||||
c.a.releaseFastMode()
|
c.a.releaseFastMode()
|
||||||
if drive.diskette != nil {
|
if drive.diskette != nil {
|
||||||
drive.diskette.powerOff(c.a.cpu.GetCycles())
|
drive.diskette.PowerOff(c.a.cpu.GetCycles())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return c.dataLatch
|
return c.dataLatch
|
||||||
|
@ -113,7 +115,7 @@ func (c *CardDisk2) assign(a *Apple2, slot int) {
|
||||||
drive.power = true
|
drive.power = true
|
||||||
c.a.requestFastMode()
|
c.a.requestFastMode()
|
||||||
if drive.diskette != nil {
|
if drive.diskette != nil {
|
||||||
drive.diskette.powerOn(c.a.cpu.GetCycles())
|
drive.diskette.PowerOn(c.a.cpu.GetCycles())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
|
@ -173,9 +175,9 @@ func (c *CardDisk2) processQ6Q7(in uint8) {
|
||||||
}
|
}
|
||||||
if !c.q6 { // shift
|
if !c.q6 { // shift
|
||||||
if !c.q7 { // Q6L-Q7L: Read
|
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
|
} 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
|
} else { // load
|
||||||
if !c.q7 { // Q6H-Q7L: Sense write protect / prewrite state
|
if !c.q7 { // Q6H-Q7L: Sense write protect / prewrite state
|
||||||
|
@ -269,7 +271,7 @@ func moveStep(phases uint8, prevStep int) int {
|
||||||
return nextStep
|
return nextStep
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *cardDisk2Drive) insertDiskette(name string, dt diskette) {
|
func (d *cardDisk2Drive) insertDiskette(name string, dt storage.Diskette) {
|
||||||
d.name = name
|
d.name = name
|
||||||
d.diskette = dt
|
d.diskette = dt
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,8 @@ package izapple2
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/ivanizag/izapple2/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -24,7 +26,7 @@ type CardHardDisk struct {
|
||||||
filename string
|
filename string
|
||||||
trace bool
|
trace bool
|
||||||
|
|
||||||
disk *blockDisk
|
disk *storage.BlockDisk
|
||||||
mliParams uint16
|
mliParams uint16
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,7 +47,7 @@ func (c *CardHardDisk) GetInfo() map[string]string {
|
||||||
|
|
||||||
// LoadImage loads a disk image
|
// LoadImage loads a disk image
|
||||||
func (c *CardHardDisk) LoadImage(filename string) error {
|
func (c *CardHardDisk) LoadImage(filename string) error {
|
||||||
hd, err := openBlockDisk(filename)
|
hd, err := storage.OpenBlockDisk(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -95,11 +97,11 @@ func (c *CardHardDisk) assign(a *Apple2, slot int) {
|
||||||
}, "HDCOMMAND")
|
}, "HDCOMMAND")
|
||||||
c.addCardSoftSwitchR(1, func(*ioC0Page) uint8 {
|
c.addCardSoftSwitchR(1, func(*ioC0Page) uint8 {
|
||||||
// Blocks available, low byte
|
// Blocks available, low byte
|
||||||
return uint8(c.disk.blocks)
|
return uint8(c.disk.GetSizeInBlocks())
|
||||||
}, "HDBLOCKSLO")
|
}, "HDBLOCKSLO")
|
||||||
c.addCardSoftSwitchR(2, func(*ioC0Page) uint8 {
|
c.addCardSoftSwitchR(2, func(*ioC0Page) uint8 {
|
||||||
// Blocks available, high byte
|
// Blocks available, high byte
|
||||||
return uint8(c.disk.blocks >> 8)
|
return uint8(c.disk.GetSizeInBlocks() >> 8)
|
||||||
}, "HDBLOCKHI")
|
}, "HDBLOCKHI")
|
||||||
|
|
||||||
c.addCardSoftSwitchR(3, func(*ioC0Page) uint8 {
|
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)
|
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 {
|
if err != nil {
|
||||||
return proDosDeviceErrorIO
|
return proDosDeviceErrorIO
|
||||||
}
|
}
|
||||||
// Byte by byte transfer to memory using the full Poke code path
|
// 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])
|
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)
|
fmt.Printf("[CardHardDisk] Write block %v from $%x.\n", block, source)
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.disk.readOnly {
|
if c.disk.IsReadOnly() {
|
||||||
return proDosDeviceErrorWriteProtected
|
return proDosDeviceErrorWriteProtected
|
||||||
}
|
}
|
||||||
|
|
||||||
// Byte by byte transfer from memory using the full Peek code path
|
// Byte by byte transfer from memory using the full Peek code path
|
||||||
buf := make([]uint8, proDosBlockSize)
|
buf := make([]uint8, storage.ProDosBlockSize)
|
||||||
for i := uint16(0); i < uint16(proDosBlockSize); i++ {
|
for i := uint16(0); i < uint16(len(buf)); i++ {
|
||||||
buf[i] = c.a.mmu.Peek(source + 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 {
|
if err != nil {
|
||||||
return proDosDeviceErrorIO
|
return proDosDeviceErrorIO
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,8 @@ package izapple2
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/ivanizag/izapple2/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -43,7 +45,7 @@ func newCharacterGenerator(filename string, order charColumnMap) (*CharacterGene
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cg *CharacterGenerator) load(filename string) error {
|
func (cg *CharacterGenerator) load(filename string) error {
|
||||||
bytes, err := loadResource(filename)
|
bytes, err := storage.LoadResource(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,9 +99,6 @@ func fyneRun(s *state) {
|
||||||
})
|
})
|
||||||
|
|
||||||
s.win.Show()
|
s.win.Show()
|
||||||
|
|
||||||
fmt.Printf("%v\n", s.win.Canvas().Scale())
|
|
||||||
|
|
||||||
s.app.Run()
|
s.app.Run()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Binary file not shown.
|
@ -1,4 +1,4 @@
|
||||||
package izapple2
|
package storage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"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 (
|
const (
|
||||||
proDosBlockSize = uint32(512)
|
// ProDosBlockSize is the size of the blocks on the ProDOS devices
|
||||||
|
ProDosBlockSize = uint32(512)
|
||||||
proDosMaxBlocks = uint32(65536)
|
proDosMaxBlocks = uint32(65536)
|
||||||
)
|
)
|
||||||
|
|
||||||
type blockDisk struct {
|
// BlockDisk is any block device with 512 bytes blocks
|
||||||
|
type BlockDisk struct {
|
||||||
file *os.File
|
file *os.File
|
||||||
readOnly bool
|
readOnly bool
|
||||||
dataOffset uint32
|
dataOffset uint32
|
||||||
blocks uint32
|
blocks uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
func openBlockDisk(filename string) (*blockDisk, error) {
|
// OpenBlockDisk creates a new block device links to a file
|
||||||
var bd blockDisk
|
func OpenBlockDisk(filename string) (*BlockDisk, error) {
|
||||||
|
var bd BlockDisk
|
||||||
|
|
||||||
bd.readOnly = false
|
bd.readOnly = false
|
||||||
file, err := os.OpenFile(filename, os.O_RDWR, 0)
|
file, err := os.OpenFile(filename, os.O_RDWR, 0)
|
||||||
|
@ -49,29 +52,39 @@ func openBlockDisk(filename string) (*blockDisk, error) {
|
||||||
return nil, err
|
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())
|
return nil, fmt.Errorf("File is too big OR %s", err2mg.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
size := uint32(fileInfo.Size())
|
size := uint32(fileInfo.Size())
|
||||||
if size%proDosBlockSize != 0 {
|
if size%ProDosBlockSize != 0 {
|
||||||
return nil, fmt.Errorf("File size os invalid OR %s", err2mg.Error())
|
return nil, fmt.Errorf("File size os invalid OR %s", err2mg.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// It's a valid raw file
|
// It's a valid raw file
|
||||||
bd.blocks = size / proDosBlockSize
|
bd.blocks = size / ProDosBlockSize
|
||||||
bd.dataOffset = 0
|
bd.dataOffset = 0
|
||||||
return &bd, nil
|
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 {
|
if block >= bd.blocks {
|
||||||
return nil, errors.New("disk block number is too big")
|
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)
|
_, err := bd.file.ReadAt(buf, offset)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -80,7 +93,7 @@ func (bd *blockDisk) read(block uint32) ([]uint8, error) {
|
||||||
return buf, nil
|
return buf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bd *blockDisk) write(block uint32, data []uint8) error {
|
func (bd *BlockDisk) Write(block uint32, data []uint8) error {
|
||||||
if bd.readOnly {
|
if bd.readOnly {
|
||||||
return errors.New("can't write in a readonly disk")
|
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")
|
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)
|
_, err := bd.file.WriteAt(data, offset)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
|
@ -1,18 +1,20 @@
|
||||||
package izapple2
|
package storage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
type diskette interface {
|
// Diskette represents a diskette and it's RW mechanism
|
||||||
powerOn(cycle uint64)
|
type Diskette interface {
|
||||||
powerOff(cycle uint64)
|
PowerOn(cycle uint64)
|
||||||
read(quarterTrack int, cycle uint64) uint8
|
PowerOff(cycle uint64)
|
||||||
write(quarterTrack int, value uint8, cycle uint64)
|
Read(quarterTrack int, cycle uint64) uint8
|
||||||
|
Write(quarterTrack int, value uint8, cycle uint64)
|
||||||
}
|
}
|
||||||
|
|
||||||
func isDiskette(filename string) bool {
|
// IsDiskette returns true if the files looks like a 5 1/4 diskette
|
||||||
data, err := loadResource(filename)
|
func IsDiskette(filename string) bool {
|
||||||
|
data, err := LoadResource(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -20,8 +22,9 @@ func isDiskette(filename string) bool {
|
||||||
return isFileNib(data) || isFileDsk(data) || isFileWoz(data)
|
return isFileNib(data) || isFileDsk(data) || isFileWoz(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadDiskette(filename string) (diskette, error) {
|
// LoadDiskette returns a Diskette by detecting the format
|
||||||
data, err := loadResource(filename)
|
func LoadDiskette(filename string) (Diskette, error) {
|
||||||
|
data, err := LoadResource(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package izapple2
|
package storage
|
||||||
|
|
||||||
/*
|
/*
|
||||||
See:
|
See:
|
||||||
|
@ -11,23 +11,23 @@ type diskette16sector struct {
|
||||||
position int
|
position int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *diskette16sector) powerOn(cycle uint64) {
|
func (d *diskette16sector) PowerOn(cycle uint64) {
|
||||||
// Not used
|
// Not used
|
||||||
}
|
}
|
||||||
func (d *diskette16sector) powerOff(_ uint64) {
|
func (d *diskette16sector) PowerOff(_ uint64) {
|
||||||
// Not used
|
// Not used
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *diskette16sector) read(quarterTrack int, cycle uint64) uint8 {
|
func (d *diskette16sector) Read(quarterTrack int, cycle uint64) uint8 {
|
||||||
track := d.nib.track[quarterTrack/stepsPerTrack]
|
track := d.nib.track[quarterTrack/4]
|
||||||
value := track[d.position]
|
value := track[d.position]
|
||||||
d.position = (d.position + 1) % nibBytesPerTrack
|
d.position = (d.position + 1) % nibBytesPerTrack
|
||||||
//fmt.Printf("%v, %v, %v, %x\n", 0, 0, d.position, uint8(value))
|
//fmt.Printf("%v, %v, %v, %x\n", 0, 0, d.position, uint8(value))
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *diskette16sector) write(quarterTrack int, value uint8, _ uint64) {
|
func (d *diskette16sector) Write(quarterTrack int, value uint8, _ uint64) {
|
||||||
track := quarterTrack / stepsPerTrack
|
track := quarterTrack / 4
|
||||||
d.nib.track[track][d.position] = value
|
d.nib.track[track][d.position] = value
|
||||||
d.position = (d.position + 1) % nibBytesPerTrack
|
d.position = (d.position + 1) % nibBytesPerTrack
|
||||||
}
|
}
|
|
@ -1,14 +1,14 @@
|
||||||
package izapple2
|
package storage
|
||||||
|
|
||||||
type diskette16sectorTimed struct {
|
type diskette16sectorTimed struct {
|
||||||
nib *fileNib
|
nib *fileNib
|
||||||
cycleOn uint64 // Cycle when the disk was last turned on
|
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
|
d.cycleOn = cycle
|
||||||
}
|
}
|
||||||
func (d *diskette16sectorTimed) powerOff(_ uint64) {
|
func (d *diskette16sectorTimed) PowerOff(_ uint64) {
|
||||||
// Not needed
|
// Not needed
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,8 +20,8 @@ func (d *diskette16sectorTimed) getBitPositionInTrack(cycle uint64) int {
|
||||||
return int(position % (8 * nibBytesPerTrack)) // Ignore full turns
|
return int(position % (8 * nibBytesPerTrack)) // Ignore full turns
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *diskette16sectorTimed) read(quarterTrack int, cycle uint64) uint8 {
|
func (d *diskette16sectorTimed) Read(quarterTrack int, cycle uint64) uint8 {
|
||||||
track := d.nib.track[quarterTrack/stepsPerTrack]
|
track := d.nib.track[quarterTrack/4]
|
||||||
bitPosition := d.getBitPositionInTrack(cycle)
|
bitPosition := d.getBitPositionInTrack(cycle)
|
||||||
bytePosition := bitPosition / 8
|
bytePosition := bitPosition / 8
|
||||||
shift := uint(bitPosition % 8)
|
shift := uint(bitPosition % 8)
|
||||||
|
@ -35,6 +35,6 @@ func (d *diskette16sectorTimed) read(quarterTrack int, cycle uint64) uint8 {
|
||||||
return value
|
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")
|
panic("Write not implemented on time based disk implementation")
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package izapple2
|
package storage
|
||||||
|
|
||||||
/*
|
/*
|
||||||
See:
|
See:
|
||||||
|
@ -15,22 +15,22 @@ type diskette16sectorWritable struct {
|
||||||
dirtyTrack int
|
dirtyTrack int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *diskette16sectorWritable) powerOn(cycle uint64) {
|
func (d *diskette16sectorWritable) PowerOn(cycle uint64) {
|
||||||
// Not used
|
// Not used
|
||||||
}
|
}
|
||||||
func (d *diskette16sectorWritable) powerOff(_ uint64) {
|
func (d *diskette16sectorWritable) PowerOff(_ uint64) {
|
||||||
d.commit()
|
d.commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *diskette16sectorWritable) read(quarterTrack int, cycle uint64) uint8 {
|
func (d *diskette16sectorWritable) Read(quarterTrack int, cycle uint64) uint8 {
|
||||||
track := d.nib.track[quarterTrack/stepsPerTrack]
|
track := d.nib.track[quarterTrack/4]
|
||||||
value := track[d.position]
|
value := track[d.position]
|
||||||
d.position = (d.position + 1) % nibBytesPerTrack
|
d.position = (d.position + 1) % nibBytesPerTrack
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *diskette16sectorWritable) write(quarterTrack int, value uint8, _ uint64) {
|
func (d *diskette16sectorWritable) Write(quarterTrack int, value uint8, _ uint64) {
|
||||||
track := quarterTrack / stepsPerTrack
|
track := quarterTrack / 4
|
||||||
|
|
||||||
if d.hasDirtyTrack && track != d.dirtyTrack {
|
if d.hasDirtyTrack && track != d.dirtyTrack {
|
||||||
d.commit()
|
d.commit()
|
|
@ -1,4 +1,4 @@
|
||||||
package izapple2
|
package storage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
@ -71,16 +71,16 @@ func newDisquetteWoz(f *fileWoz) (*disketteWoz, error) {
|
||||||
return &d, nil
|
return &d, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *disketteWoz) powerOn(cycle uint64) {
|
func (d *disketteWoz) PowerOn(cycle uint64) {
|
||||||
d.turning = true
|
d.turning = true
|
||||||
d.cycleOn = cycle
|
d.cycleOn = cycle
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *disketteWoz) powerOff(_ uint64) {
|
func (d *disketteWoz) PowerOff(_ uint64) {
|
||||||
d.turning = false
|
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
|
// Count cycles to know how many bits have been read
|
||||||
cycles := cycle - d.cycle
|
cycles := cycle - d.cycle
|
||||||
deltaBits := cycles / cyclesPerBit // TODO: Use Woz optimal bit timing
|
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
|
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")
|
panic("Write not implemented on woz disk implementation")
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package izapple2
|
package storage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
@ -35,7 +35,7 @@ type file2mgHeader struct {
|
||||||
LengthCreator uint32
|
LengthCreator uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
func parse2mg(bd *blockDisk) error {
|
func parse2mg(bd *BlockDisk) error {
|
||||||
fileInfo, err := bd.file.Stat()
|
fileInfo, err := bd.file.Stat()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -55,7 +55,7 @@ func parse2mg(bd *blockDisk) error {
|
||||||
bd.blocks = header.Blocks
|
bd.blocks = header.Blocks
|
||||||
bd.dataOffset = header.OffsetData
|
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")
|
return errors.New("The 2MG file is too small")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package izapple2
|
package storage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
|
@ -1,4 +1,4 @@
|
||||||
package izapple2
|
package storage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
|
@ -1,4 +1,4 @@
|
||||||
package izapple2
|
package storage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
@ -86,15 +86,6 @@ func (f *fileWoz) getBit(position uint32, quarterTrack int) uint8 {
|
||||||
return trackWoz.data[position/8] >> (7 - position%8) & 1
|
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 {
|
func isFileWoz(data []uint8) bool {
|
||||||
header := data[:len(headerWoz2)]
|
header := data[:len(headerWoz2)]
|
||||||
if bytes.Equal(headerWoz1, header) {
|
if bytes.Equal(headerWoz1, header) {
|
|
@ -1,4 +1,4 @@
|
||||||
package izapple2
|
package storage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
|
@ -25,7 +25,8 @@ func isHTTPResource(filename string) bool {
|
||||||
strings.HasPrefix(filename, httpsPrefix)
|
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
|
var file io.Reader
|
||||||
if isInternalResource(filename) {
|
if isInternalResource(filename) {
|
||||||
// load from embedded resource
|
// load from embedded resource
|
Loading…
Reference in New Issue