Change diskettes with drag and drop

This commit is contained in:
Iván Izaguirre 2023-04-04 18:35:13 +02:00
parent e38c4cf57b
commit a89cf24fe7
9 changed files with 170 additions and 90 deletions

3
.gitignore vendored
View File

@ -10,4 +10,5 @@ frontend/a2fyne/a2fyne
frontend/headless/headless frontend/headless/headless
frontend/*/snapshot.gif frontend/*/snapshot.gif
frontend/*/snapshot.png frontend/*/snapshot.png
frontend/*/printer.out frontend/*/printer.out
.DS_STORE

View File

@ -178,6 +178,8 @@ Line:
- F12: Save a screen snapshot to a file `snapshot.png` - F12: Save a screen snapshot to a file `snapshot.png`
- Pause: Pause the emulation - Pause: Pause the emulation
Drag and drop a diskette file on the left side of the window to change Drive 1; to the right side to change the disk on Drive 2.
Only valid on SDL mode Only valid on SDL mode
### Command line options ### Command line options

View File

@ -10,25 +10,26 @@ import (
// Apple2 represents all the components and state of the emulated machine // Apple2 represents all the components and state of the emulated machine
type Apple2 struct { type Apple2 struct {
Name string Name string
cpu *iz6502.State cpu *iz6502.State
mmu *memoryManager mmu *memoryManager
io *ioC0Page io *ioC0Page
cg *CharacterGenerator cg *CharacterGenerator
cards [8]Card cards [8]Card
softVideoSwitch *SoftVideoSwitch softVideoSwitch *SoftVideoSwitch
isApple2e bool isApple2e bool
commandChannel chan int commandChannel chan command
cycleDurationNs float64 // Current speed. Inverse of the cpu clock in Ghz cycleDurationNs float64 // Current speed. Inverse of the cpu clock in Ghz
fastMode bool fastMode bool
fastRequestsCounter int32 fastRequestsCounter int32
cycleBreakpoint uint64 cycleBreakpoint uint64
breakPoint bool breakPoint bool
profile bool profile bool
showSpeed bool showSpeed bool
paused bool paused bool
tracers []executionTracer forceCaps bool
forceCaps bool tracers []executionTracer
removableMediaDrives []drive
} }
type executionTracer interface { type executionTracer interface {
@ -90,7 +91,7 @@ func (a *Apple2) Start(paused bool) {
for commandsPending { for commandsPending {
select { select {
case command := <-a.commandChannel: case command := <-a.commandChannel:
switch command { switch command.getId() {
case CommandKill: case CommandKill:
return return
case CommandPause: case CommandPause:
@ -192,58 +193,6 @@ func (a *Apple2) IsForceCaps() bool {
return a.forceCaps return a.forceCaps
} }
const (
// CommandToggleSpeed toggles cpu speed between full speed and actual Apple II speed
CommandToggleSpeed = iota + 1
// CommandShowSpeed toggles printinf the current freq in Mhz
CommandShowSpeed
// CommandDumpDebugInfo dumps useful info
CommandDumpDebugInfo
// CommandNextCharGenPage cycles the CharGen page if several
CommandNextCharGenPage
// CommandToggleCPUTrace toggle tracing of CPU execution
CommandToggleCPUTrace
// CommandKill stops the cpu execution loop
CommandKill
// CommandReset executes a 6502 reset
CommandReset
// CommandPauseUnpause allows the Pause button to freeze the emulator for a coffee break
CommandPauseUnpause
// CommandPause pauses the emulator
CommandPause
// CommandStart restarts the emulator
CommandStart
)
// SendCommand enqueues a command to the emulator thread
func (a *Apple2) SendCommand(command int) {
a.commandChannel <- command
}
func (a *Apple2) executeCommand(command int) {
switch command {
case CommandToggleSpeed:
if a.cycleDurationNs == 0 {
//fmt.Println("Slow")
a.cycleDurationNs = 1000.0 / CPUClockMhz
} else {
//fmt.Println("Fast")
a.cycleDurationNs = 0
}
case CommandShowSpeed:
a.showSpeed = !a.showSpeed
case CommandDumpDebugInfo:
a.dumpDebugInfo()
case CommandNextCharGenPage:
a.cg.nextPage()
fmt.Printf("Chargen page %v\n", a.cg.page)
case CommandToggleCPUTrace:
a.cpu.SetTrace(!a.cpu.GetTrace())
case CommandReset:
a.reset()
}
}
func (a *Apple2) RequestFastMode() { func (a *Apple2) RequestFastMode() {
// Note: if the fastMode is shorter than maxWaitDuration, there won't be any gain. // Note: if the fastMode is shorter than maxWaitDuration, there won't be any gain.
if a.fastMode { if a.fastMode {
@ -258,10 +207,8 @@ func (a *Apple2) ReleaseFastMode() {
} }
func (a *Apple2) executionTrace() { func (a *Apple2) executionTrace() {
if a.tracers != nil { for _, v := range a.tracers {
for _, v := range a.tracers { v.inspect()
v.inspect()
}
} }
} }

View File

@ -16,7 +16,7 @@ func newApple2() *Apple2 {
} }
func (a *Apple2) setup(clockMhz float64, fastMode bool) { func (a *Apple2) setup(clockMhz float64, fastMode bool) {
a.commandChannel = make(chan int, 100) a.commandChannel = make(chan command, 100)
a.fastMode = fastMode a.fastMode = fastMode
if clockMhz <= 0 { if clockMhz <= 0 {
@ -52,13 +52,13 @@ func setApple2eEnhanced(a *Apple2) {
} }
func (a *Apple2) addTracer(tracer executionTracer) { func (a *Apple2) addTracer(tracer executionTracer) {
if a.tracers == nil {
a.tracers = make([]executionTracer, 0)
}
a.tracers = append(a.tracers, tracer) a.tracers = append(a.tracers, tracer)
} }
func (a *Apple2) registerRemovableMediaDrive(d drive) {
a.removableMediaDrives = append(a.removableMediaDrives, d)
}
func (a *Apple2) insertCard(c Card, slot int) { func (a *Apple2) insertCard(c Card, slot int) {
c.assign(a, slot) c.assign(a, slot)
a.cards[slot] = c a.cards[slot] = c
@ -97,19 +97,17 @@ func (a *Apple2) AddDisk2(slot int, diskImage, diskBImage string, trackTracer tr
a.insertCard(c, slot) a.insertCard(c, slot)
if diskImage != "" { if diskImage != "" {
diskette, err := LoadDiskette(diskImage) err := c.drive[0].insertDiskette(diskImage)
if err != nil { if err != nil {
return err return err
} }
c.drive[0].insertDiskette(diskImage, diskette)
} }
if diskBImage != "" { if diskBImage != "" {
diskette, err := LoadDiskette(diskBImage) err := c.drive[1].insertDiskette(diskBImage)
if err != nil { if err != nil {
return err return err
} }
c.drive[1].insertDiskette(diskImage, diskette)
} }
return nil return nil

View File

@ -36,6 +36,10 @@ type CardDisk2 struct {
trackTracer trackTracer trackTracer trackTracer
} }
type drive interface {
insertDiskette(path string) error
}
type cardDisk2Drive struct { type cardDisk2Drive struct {
name string name string
diskette storage.Diskette diskette storage.Diskette
@ -77,6 +81,9 @@ func (c *CardDisk2) reset() {
} }
func (c *CardDisk2) assign(a *Apple2, slot int) { func (c *CardDisk2) assign(a *Apple2, slot int) {
a.registerRemovableMediaDrive(&c.drive[0])
a.registerRemovableMediaDrive(&c.drive[1])
// Q1, Q2, Q3 and Q4 phase control soft switches, // Q1, Q2, Q3 and Q4 phase control soft switches,
for i := uint8(0); i < 4; i++ { for i := uint8(0); i < 4; i++ {
phase := i phase := i
@ -225,7 +232,13 @@ func (c *CardDisk2) processQ6Q7(in uint8) {
*/ */
} }
func (d *cardDisk2Drive) insertDiskette(name string, dt storage.Diskette) { func (d *cardDisk2Drive) insertDiskette(name string) error {
diskette, err := LoadDiskette(name)
if err != nil {
return err
}
d.name = name d.name = name
d.diskette = dt d.diskette = diskette
return nil
} }

View File

@ -80,6 +80,9 @@ func (c *CardDisk2Sequencer) reset() {
} }
func (c *CardDisk2Sequencer) assign(a *Apple2, slot int) { func (c *CardDisk2Sequencer) assign(a *Apple2, slot int) {
a.registerRemovableMediaDrive(&c.drive[0])
a.registerRemovableMediaDrive(&c.drive[1])
c.addCardSoftSwitches(func(address uint8, data uint8, write bool) uint8 { c.addCardSoftSwitches(func(address uint8, data uint8, write bool) uint8 {
/* /*
Slot card pins to SN74LS259 latch mapping: Slot card pins to SN74LS259 latch mapping:

106
command.go Normal file
View File

@ -0,0 +1,106 @@
package izapple2
import "fmt"
const (
// CommandToggleSpeed toggles cpu speed between full speed and actual Apple II speed
CommandToggleSpeed = iota + 1
// CommandShowSpeed toggles printinf the current freq in Mhz
CommandShowSpeed
// CommandDumpDebugInfo dumps useful info
CommandDumpDebugInfo
// CommandNextCharGenPage cycles the CharGen page if several
CommandNextCharGenPage
// CommandToggleCPUTrace toggle tracing of CPU execution
CommandToggleCPUTrace
// CommandKill stops the cpu execution loop
CommandKill
// CommandReset executes a 6502 reset
CommandReset
// CommandPauseUnpause allows the Pause button to freeze the emulator for a coffee break
CommandPauseUnpause
// CommandPause pauses the emulator
CommandPause
// CommandStart restarts the emulator
CommandStart
// CommandComplex for commands that use a struct with parameters
CommandComplex
)
type command interface {
getId() int
}
type commandSimple struct {
id int
}
type commandLoadDisk struct {
drive int
path string
}
func (c *commandSimple) getId() int {
return c.id
}
func (c *commandLoadDisk) getId() int {
return CommandComplex
}
func (a *Apple2) queueCommand(c command) {
a.commandChannel <- c
}
// SendCommand enqueues a command to the emulator thread
func (a *Apple2) SendCommand(commandId int) {
var c commandSimple
c.id = commandId
a.queueCommand(&c)
}
func (a *Apple2) SendLoadDisk(drive int, path string) {
var c commandLoadDisk
c.drive = drive
c.path = path
a.queueCommand(&c)
}
func (a *Apple2) executeCommand(command command) {
switch command.getId() {
case CommandToggleSpeed:
if a.cycleDurationNs == 0 {
//fmt.Println("Slow")
a.cycleDurationNs = 1000.0 / CPUClockMhz
} else {
//fmt.Println("Fast")
a.cycleDurationNs = 0
}
case CommandShowSpeed:
a.showSpeed = !a.showSpeed
case CommandDumpDebugInfo:
a.dumpDebugInfo()
case CommandNextCharGenPage:
a.cg.nextPage()
fmt.Printf("Chargen page %v\n", a.cg.page)
case CommandToggleCPUTrace:
a.cpu.SetTrace(!a.cpu.GetTrace())
case CommandReset:
a.reset()
case CommandComplex:
switch t := command.(type) {
case *commandLoadDisk:
err := a.changeDisk(t.drive, t.path)
if err != nil {
fmt.Printf("Could no load file %v\n%v\n", t.path, err)
}
}
}
}
func (a *Apple2) changeDisk(unit int, path string) error {
if unit < len(a.removableMediaDrives) {
return a.removableMediaDrives[unit].insertDiskette(path)
}
return fmt.Errorf("Unit %v not defined", unit)
}

View File

@ -52,6 +52,7 @@ func sdlRun(a *izapple2.Apple2) {
go a.Run() go a.Run()
var x int32
paused := false paused := false
running := true running := true
for running { for running {
@ -73,9 +74,18 @@ func sdlRun(a *izapple2.Apple2) {
w, h := window.GetSize() w, h := window.GetSize()
j.putMouseMotionEvent(t, w, h) j.putMouseMotionEvent(t, w, h)
m.putMouseMotionEvent(t, w, h) m.putMouseMotionEvent(t, w, h)
x = t.X
case *sdl.MouseButtonEvent: case *sdl.MouseButtonEvent:
j.putMouseButtonEvent(t) j.putMouseButtonEvent(t)
m.putMouseButtonEvent(t) m.putMouseButtonEvent(t)
case *sdl.DropEvent:
switch t.Type {
case sdl.DROPFILE:
w, _ := window.GetSize()
drive := int(2 * x / w)
fmt.Printf("Loading '%s' in drive %v\n", t.File, drive+1)
a.SendLoadDisk(drive, t.File)
}
} }
} }

View File

@ -30,7 +30,7 @@
- Wings of Fury: Working - Wings of Fury: Working
- Stickybear Town Builder: Working - Stickybear Town Builder: Working
- Optimal bit timing of WOZ 2.0 - Optimal bit timing of WOZ 2.0
- Border Zone: **Unknown, there is no UI to swap disks** - Border Zone: Slow (optimal bit timing not implemented)
- 4am on Slack (2021-06-29) - 4am on Slack (2021-06-29)
- Mr Do: Working - Mr Do: Working
- Wavy Navy: Working - Wavy Navy: Working
@ -68,7 +68,7 @@
- Wings of Fury: ***Not working*** - Wings of Fury: ***Not working***
- Stickybear Town Builder: Working - Stickybear Town Builder: Working
- Optimal bit timing of WOZ 2.0 - Optimal bit timing of WOZ 2.0
- Border Zone: **Unknown, there is no UI to swap disks** - Border Zone: Slow (optimal bit timing not implemented)
- 4am on Slack (2021-06-29) - 4am on Slack (2021-06-29)
- Mr Do: ***Not working*** - Mr Do: ***Not working***
- Wavy Navy: Working - Wavy Navy: Working