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/*/snapshot.gif
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`
- 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
### Command line options

View File

@ -10,25 +10,26 @@ import (
// Apple2 represents all the components and state of the emulated machine
type Apple2 struct {
Name string
cpu *iz6502.State
mmu *memoryManager
io *ioC0Page
cg *CharacterGenerator
cards [8]Card
softVideoSwitch *SoftVideoSwitch
isApple2e bool
commandChannel chan int
cycleDurationNs float64 // Current speed. Inverse of the cpu clock in Ghz
fastMode bool
fastRequestsCounter int32
cycleBreakpoint uint64
breakPoint bool
profile bool
showSpeed bool
paused bool
tracers []executionTracer
forceCaps bool
Name string
cpu *iz6502.State
mmu *memoryManager
io *ioC0Page
cg *CharacterGenerator
cards [8]Card
softVideoSwitch *SoftVideoSwitch
isApple2e bool
commandChannel chan command
cycleDurationNs float64 // Current speed. Inverse of the cpu clock in Ghz
fastMode bool
fastRequestsCounter int32
cycleBreakpoint uint64
breakPoint bool
profile bool
showSpeed bool
paused bool
forceCaps bool
tracers []executionTracer
removableMediaDrives []drive
}
type executionTracer interface {
@ -90,7 +91,7 @@ func (a *Apple2) Start(paused bool) {
for commandsPending {
select {
case command := <-a.commandChannel:
switch command {
switch command.getId() {
case CommandKill:
return
case CommandPause:
@ -192,58 +193,6 @@ func (a *Apple2) IsForceCaps() bool {
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() {
// Note: if the fastMode is shorter than maxWaitDuration, there won't be any gain.
if a.fastMode {
@ -258,10 +207,8 @@ func (a *Apple2) ReleaseFastMode() {
}
func (a *Apple2) executionTrace() {
if a.tracers != nil {
for _, v := range a.tracers {
v.inspect()
}
for _, v := range a.tracers {
v.inspect()
}
}

View File

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

View File

@ -36,6 +36,10 @@ type CardDisk2 struct {
trackTracer trackTracer
}
type drive interface {
insertDiskette(path string) error
}
type cardDisk2Drive struct {
name string
diskette storage.Diskette
@ -77,6 +81,9 @@ func (c *CardDisk2) reset() {
}
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,
for i := uint8(0); i < 4; 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.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) {
a.registerRemovableMediaDrive(&c.drive[0])
a.registerRemovableMediaDrive(&c.drive[1])
c.addCardSoftSwitches(func(address uint8, data uint8, write bool) uint8 {
/*
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()
var x int32
paused := false
running := true
for running {
@ -73,9 +74,18 @@ func sdlRun(a *izapple2.Apple2) {
w, h := window.GetSize()
j.putMouseMotionEvent(t, w, h)
m.putMouseMotionEvent(t, w, h)
x = t.X
case *sdl.MouseButtonEvent:
j.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
- Stickybear Town Builder: Working
- 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)
- Mr Do: Working
- Wavy Navy: Working
@ -68,7 +68,7 @@
- Wings of Fury: ***Not working***
- Stickybear Town Builder: Working
- 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)
- Mr Do: ***Not working***
- Wavy Navy: Working