Configuration redone
This commit is contained in:
parent
ba4c7437b4
commit
0a1a619586
|
@ -2,15 +2,16 @@
|
||||||
SDL2.dll
|
SDL2.dll
|
||||||
a2sdl.exe
|
a2sdl.exe
|
||||||
frontend/a2sdl/a2sdl
|
frontend/a2sdl/a2sdl
|
||||||
frontend/a2sdl/*.woz
|
frontend/*/*.woz
|
||||||
frontend/a2sdl/*.dsk
|
frontend/*/*.dsk
|
||||||
frontend/a2sdl/*.po
|
frontend/*/*.po
|
||||||
frontend/a2sdl/*.2mg
|
frontend/*/*.2mg
|
||||||
|
frontend/*/*.hdv
|
||||||
frontend/a2fyne/a2fyne
|
frontend/a2fyne/a2fyne
|
||||||
frontend/headless/headless
|
frontend/headless/headless
|
||||||
frontend/*/snapshot.gif
|
frontend/*/snapshot.gif
|
||||||
frontend/*/snapshot.png
|
frontend/*/snapshot.png
|
||||||
frontend/*/printer.out
|
printer.out
|
||||||
.DS_STORE
|
.DS_STORE
|
||||||
.scannerwork
|
.scannerwork
|
||||||
coverage.out
|
coverage.out
|
||||||
|
|
120
README.md
120
README.md
|
@ -186,93 +186,53 @@ Only valid on SDL mode
|
||||||
### Command line options
|
### Command line options
|
||||||
|
|
||||||
```terminal
|
```terminal
|
||||||
-brainBoardSlot int
|
Usage: izapple [file]
|
||||||
slot for the Brain Board II. -1 for none (default -1)
|
file
|
||||||
-charRom string
|
path to image to use on the boot device
|
||||||
rom file for the character generator (default "<default>")
|
-charrom string
|
||||||
-consoleCardSlot int
|
rom file for the character generator (default "<internal>/Apple IIe Video Enhanced.bin")
|
||||||
slot for the host console card. -1 for none (default -1)
|
-cpu string
|
||||||
-disk string
|
cpu type, can be '6502' or '65c02' (default "65c02")
|
||||||
file to load on the first disk drive (default "<internal>/dos33.dsk")
|
|
||||||
-disk2Slot int
|
|
||||||
slot for the disk driver. -1 for none. (default 6)
|
|
||||||
-disk35 string
|
|
||||||
file to load on the SmartPort disk (slot 5)
|
|
||||||
-diskRom string
|
|
||||||
rom file for the disk drive controller (default "<internal>/DISK2.rom")
|
|
||||||
-diskb string
|
|
||||||
file to load on the second disk drive
|
|
||||||
-diskc string
|
|
||||||
file to load on the third disk drive, slot 5
|
|
||||||
-diskd string
|
|
||||||
file to load on the fourth disk drive, slot 5
|
|
||||||
-fastChipSlot int
|
|
||||||
slot for the FASTChip accelerator card, -1 for none (default 3)
|
|
||||||
-forceCaps
|
-forceCaps
|
||||||
force all letters to be uppercased (no need for caps lock!)
|
force all letters to be uppercased (no need for caps lock!)
|
||||||
-fastDisk
|
|
||||||
set fast mode when the disks are spinning (default true)
|
|
||||||
-hd string
|
|
||||||
file to load on the boot hard disk
|
|
||||||
-hdSlot int
|
|
||||||
slot for the hard drive if present. -1 for none. (default -1)
|
|
||||||
-languageCardSlot int
|
|
||||||
slot for the 16kb language card. -1 for none
|
|
||||||
-memoryExpSlot int
|
|
||||||
slot for the Memory Expansion card with 1GB. -1 for none (default -1)
|
|
||||||
-mhz float
|
|
||||||
cpu speed in Mhz, use 0 for full speed. Use F5 to toggle. (default 1.0227142857142857)
|
|
||||||
-model string
|
-model string
|
||||||
set base model. Models available 2plus, 2e, 2enh, base64a (default "2enh")
|
set base model (default "2enh")
|
||||||
-mouseCardSlot int
|
-nsc string
|
||||||
slot for the Mouse card. -1 for none (default 4)
|
add a DS1216 No-Slot-Clock on the main ROM (use 'main') or a slot ROM (default "none")
|
||||||
-nsc int
|
|
||||||
add a DS1216 No-Slot-Clock on the main ROM (use 0) or a slot ROM. -1 for none (default -1)
|
|
||||||
-panicSS
|
|
||||||
panic if a not implemented softswitch is used
|
|
||||||
-printer int
|
|
||||||
slot for the Parallel Printer Interface. -1 for none (default 1)
|
|
||||||
-profile
|
-profile
|
||||||
generate profile trace to analyse with pprof
|
generate profile trace to analyse with pprof
|
||||||
-ramworks int
|
-ramworks string
|
||||||
memory to use with RAMWorks card, 0 for no card, max is 16384 (default 8192)
|
memory to use with RAMWorks card, max is 16384 (default "none")
|
||||||
-rgb
|
-rgb
|
||||||
emulate the RGB modes of the 80col RGB card for DHGR (default true)
|
emulate the RGB modes of the 80col RGB card for DHGR
|
||||||
-rom string
|
-rom string
|
||||||
main rom file (default "<default>")
|
main rom file (default "<internal>/Apple2e_Enhanced.rom")
|
||||||
-romx
|
-romx
|
||||||
emulate a RomX
|
emulate a RomX
|
||||||
-saturnCardSlot int
|
-s0 string
|
||||||
slot for the 256kb Saturn card. -1 for none (default -1)
|
slot 0 configuration. (default "language")
|
||||||
-sequencer
|
-s1 string
|
||||||
use the sequencer based Disk II card
|
slot 1 configuration. (default "parallel")
|
||||||
-swyftCard
|
-s2 string
|
||||||
activate a Swyft Card in slot 3. Load the tutorial disk if none provided
|
slot 2 configuration. (default "vidhd")
|
||||||
-thunderClockCardSlot int
|
-s3 string
|
||||||
slot for the ThunderClock Plus card. -1 for none (default 4)
|
slot 3 configuration. (default "fastchip")
|
||||||
-traceBBC
|
-s4 string
|
||||||
trace BBC MOS API calls used with Applecorn, skip console I/O calls
|
slot 4 configuration. (default "mouse")
|
||||||
-traceBBCFull
|
-s5 string
|
||||||
trace BBC MOS API calls used with Applecorn
|
slot 5 configuration. (default "empty")
|
||||||
-traceCpu
|
-s6 string
|
||||||
dump to the console the CPU execution. Use F11 to toggle.
|
slot 6 configuration. (default "diskii,disk1=<internal>/dos33.dsk")
|
||||||
-traceHD
|
-s7 string
|
||||||
dump to the console the hd/smartPort commands
|
slot 7 configuration. (default "empty")
|
||||||
-traceMLI
|
-speed string
|
||||||
dump to the console the calls to ProDOS machine language interface calls to $BF00
|
cpu speed in Mhz, can be 'ntsc', 'pal', 'full' or a decimal nunmber (default "ntsc")
|
||||||
-tracePascal
|
-trace string
|
||||||
dump to the console the calls to the Apple Pascal BIOS
|
trace CPU execution with one or more comma separated tracers (default "none")
|
||||||
-traceSS
|
|
||||||
dump to the console the sofswitches calls
|
|
||||||
-traceSSReg
|
|
||||||
dump to the console the sofswitch registrations
|
|
||||||
-traceTracks
|
|
||||||
dump to the console the disk tracks changes
|
|
||||||
-vidHDSlot int
|
|
||||||
slot for the VidHD card, only for //e models. -1 for none (default 2)
|
|
||||||
-videxCardSlot int
|
|
||||||
slot for the Videx Videoterm 80 columns card. For pre-2e models. -1 for none (default 3)
|
|
||||||
|
|
||||||
|
The available pre configured models are: swyft, 2e, 2enh, 2plus, base64a.
|
||||||
|
The available cards are: brainboard, diskii, memexp, mouse, swyftcard, inout, smartport, thunderclock, fujinet, videx, vidhd, diskiiseq, fastchip, language, softswitchlogger, parallel, saturn.
|
||||||
|
The available tracers are: ucsd, cpu, ss, ssreg, panicSS, mos, mosfull, mli.
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
203
apple2.go
203
apple2.go
|
@ -1,26 +1,27 @@
|
||||||
package izapple2
|
package izapple2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/ivanizag/iz6502"
|
"github.com/ivanizag/iz6502"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 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
|
tracers []executionTracer
|
||||||
isApple2e bool
|
|
||||||
commandChannel chan command
|
softVideoSwitch *SoftVideoSwitch
|
||||||
|
board string
|
||||||
|
isApple2e bool
|
||||||
|
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
|
|
||||||
fastRequestsCounter int32
|
fastRequestsCounter int32
|
||||||
cycleBreakpoint uint64
|
cycleBreakpoint uint64
|
||||||
breakPoint bool
|
breakPoint bool
|
||||||
|
@ -28,131 +29,32 @@ type Apple2 struct {
|
||||||
showSpeed bool
|
showSpeed bool
|
||||||
paused bool
|
paused bool
|
||||||
forceCaps bool
|
forceCaps bool
|
||||||
tracers []executionTracer
|
|
||||||
removableMediaDrives []drive
|
removableMediaDrives []drive
|
||||||
}
|
}
|
||||||
|
|
||||||
type executionTracer interface {
|
// GetCards returns the array of inserted cards
|
||||||
inspect()
|
func (a *Apple2) GetCards() [8]Card {
|
||||||
|
return a.cards
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
// SetKeyboardProvider attaches an external keyboard provider
|
||||||
// CPUClockMhz is the actual Apple II clock speed
|
func (a *Apple2) SetKeyboardProvider(kb KeyboardProvider) {
|
||||||
CPUClockMhz = 14.318 / 14
|
a.io.setKeyboardProvider(kb)
|
||||||
cpuClockEuroMhz = 14.238 / 14
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
maxWaitDuration = 100 * time.Millisecond
|
|
||||||
cpuSpinLoops = 100
|
|
||||||
)
|
|
||||||
|
|
||||||
// Run starts the Apple2 emulation
|
|
||||||
func (a *Apple2) Run() {
|
|
||||||
a.Start(false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the Apple2 emulation, can start paused
|
// SetSpeakerProvider attaches an external keyboard provider
|
||||||
func (a *Apple2) Start(paused bool) {
|
func (a *Apple2) SetSpeakerProvider(s SpeakerProvider) {
|
||||||
// Start the processor
|
a.io.setSpeakerProvider(s)
|
||||||
a.cpu.Reset()
|
|
||||||
referenceTime := time.Now()
|
|
||||||
speedReferenceTime := referenceTime
|
|
||||||
speedReferenceCycles := uint64(0)
|
|
||||||
|
|
||||||
a.paused = paused
|
|
||||||
|
|
||||||
for {
|
|
||||||
// Run 6502 steps
|
|
||||||
if !a.paused {
|
|
||||||
for i := 0; i < cpuSpinLoops; i++ {
|
|
||||||
// Conditional tracing
|
|
||||||
//pc, _ := a.cpu.GetPCAndSP()
|
|
||||||
//a.cpu.SetTrace((pc >= 0xc500 && pc < 0xc600) || (pc >= 0xc700 && pc < 0xc800))
|
|
||||||
|
|
||||||
// Execution
|
|
||||||
a.cpu.ExecuteInstruction()
|
|
||||||
|
|
||||||
// Special tracing
|
|
||||||
a.executionTrace()
|
|
||||||
}
|
|
||||||
|
|
||||||
if a.cycleBreakpoint != 0 && a.cpu.GetCycles() >= a.cycleBreakpoint {
|
|
||||||
a.breakPoint = true
|
|
||||||
a.cycleBreakpoint = 0
|
|
||||||
a.paused = true
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
time.Sleep(200 * time.Millisecond)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute meta commands
|
|
||||||
commandsPending := true
|
|
||||||
for commandsPending {
|
|
||||||
select {
|
|
||||||
case command := <-a.commandChannel:
|
|
||||||
switch command.getId() {
|
|
||||||
case CommandKill:
|
|
||||||
return
|
|
||||||
case CommandPause:
|
|
||||||
if !a.paused {
|
|
||||||
a.paused = true
|
|
||||||
}
|
|
||||||
case CommandStart:
|
|
||||||
if a.paused {
|
|
||||||
a.paused = false
|
|
||||||
referenceTime = time.Now()
|
|
||||||
speedReferenceTime = referenceTime
|
|
||||||
}
|
|
||||||
case CommandPauseUnpause:
|
|
||||||
a.paused = !a.paused
|
|
||||||
referenceTime = time.Now()
|
|
||||||
speedReferenceTime = referenceTime
|
|
||||||
default:
|
|
||||||
// Execute the other commands
|
|
||||||
a.executeCommand(command)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
commandsPending = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if a.cycleDurationNs != 0 && a.fastRequestsCounter <= 0 {
|
|
||||||
// Wait until next 6502 step has to run
|
|
||||||
clockDuration := time.Since(referenceTime)
|
|
||||||
simulatedDuration := time.Duration(float64(a.cpu.GetCycles()) * a.cycleDurationNs)
|
|
||||||
waitDuration := simulatedDuration - clockDuration
|
|
||||||
if waitDuration > maxWaitDuration || -waitDuration > maxWaitDuration {
|
|
||||||
// We have to wait too long or are too much behind. Let's fast forward
|
|
||||||
referenceTime = referenceTime.Add(-waitDuration)
|
|
||||||
waitDuration = 0
|
|
||||||
}
|
|
||||||
if waitDuration > 0 {
|
|
||||||
time.Sleep(waitDuration)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if a.showSpeed && a.cpu.GetCycles()-speedReferenceCycles > 1000000 {
|
|
||||||
// Calculate speed in MHz every million cycles
|
|
||||||
newTime := time.Now()
|
|
||||||
newCycles := a.cpu.GetCycles()
|
|
||||||
elapsedCycles := float64(newCycles - speedReferenceCycles)
|
|
||||||
freq := 1000.0 * elapsedCycles / float64(newTime.Sub(speedReferenceTime).Nanoseconds())
|
|
||||||
fmt.Printf("Freq: %f Mhz\n", freq)
|
|
||||||
speedReferenceTime = newTime
|
|
||||||
speedReferenceCycles = newCycles
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Apple2) reset() {
|
// SetJoysticksProvider attaches an external joysticks provider
|
||||||
a.cpu.Reset()
|
func (a *Apple2) SetJoysticksProvider(j JoysticksProvider) {
|
||||||
a.mmu.reset()
|
a.io.setJoysticksProvider(j)
|
||||||
for _, c := range a.cards {
|
}
|
||||||
if c != nil {
|
|
||||||
c.reset()
|
// SetMouseProvider attaches an external joysticks provider
|
||||||
}
|
func (a *Apple2) SetMouseProvider(m MouseProvider) {
|
||||||
}
|
a.io.setMouseProvider(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsPaused returns true when emulator is paused
|
// IsPaused returns true when emulator is paused
|
||||||
|
@ -174,20 +76,11 @@ func (a *Apple2) BreakPoint() bool {
|
||||||
return a.breakPoint
|
return a.breakPoint
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Apple2) setProfiling(value bool) {
|
|
||||||
a.profile = value
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsProfiling returns true when profiling
|
// IsProfiling returns true when profiling
|
||||||
func (a *Apple2) IsProfiling() bool {
|
func (a *Apple2) IsProfiling() bool {
|
||||||
return a.profile
|
return a.profile
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetForceCaps allows the caps state to be toggled at runtime
|
|
||||||
func (a *Apple2) SetForceCaps(value bool) {
|
|
||||||
a.forceCaps = value
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsForceCaps returns true when all letters are forced to upper case
|
// IsForceCaps returns true when all letters are forced to upper case
|
||||||
func (a *Apple2) IsForceCaps() bool {
|
func (a *Apple2) IsForceCaps() bool {
|
||||||
return a.forceCaps
|
return a.forceCaps
|
||||||
|
@ -195,41 +88,13 @@ func (a *Apple2) IsForceCaps() bool {
|
||||||
|
|
||||||
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 {
|
atomic.AddInt32(&a.fastRequestsCounter, 1)
|
||||||
atomic.AddInt32(&a.fastRequestsCounter, 1)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Apple2) ReleaseFastMode() {
|
func (a *Apple2) ReleaseFastMode() {
|
||||||
if a.fastMode {
|
atomic.AddInt32(&a.fastRequestsCounter, -1)
|
||||||
atomic.AddInt32(&a.fastRequestsCounter, -1)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Apple2) executionTrace() {
|
func (a *Apple2) registerRemovableMediaDrive(d drive) {
|
||||||
for _, v := range a.tracers {
|
a.removableMediaDrives = append(a.removableMediaDrives, d)
|
||||||
v.inspect()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Apple2) dumpDebugInfo() {
|
|
||||||
// See "Apple II Monitors Peeled"
|
|
||||||
pageZeroSymbols := map[int]string{
|
|
||||||
0x36: "CSWL",
|
|
||||||
0x37: "CSWH",
|
|
||||||
0x38: "KSWL",
|
|
||||||
0x39: "KSWH",
|
|
||||||
0xe2: "ACJVAFLDL", // Apple Pascal
|
|
||||||
0xe3: "ACJVAFLDH", // Apple Pascal
|
|
||||||
0xec: "JVBFOLDL", // Apple Pascal
|
|
||||||
0xed: "JVBFOLDH", // Apple Pascal
|
|
||||||
0xee: "JVAFOLDL", // Apple Pascal
|
|
||||||
0xef: "JVAFOLDH", // Apple Pascal
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("Page zero values:\n")
|
|
||||||
for _, k := range []int{0x36, 0x37, 0x38, 0x39, 0xe2, 0xe3, 0xec, 0xed, 0xee, 0xef} {
|
|
||||||
d := a.mmu.physicalMainRAM.data[k]
|
|
||||||
fmt.Printf(" %v(0x%x): 0x%02x\n", pageZeroSymbols[k], k, d)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,153 @@
|
||||||
|
package izapple2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// CPUClockMhz is the actual Apple II clock speed
|
||||||
|
CPUClockMhz = 14.318 / 14
|
||||||
|
cpuClockEuroMhz = 14.238 / 14
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
maxWaitDuration = 100 * time.Millisecond
|
||||||
|
cpuSpinLoops = 100
|
||||||
|
)
|
||||||
|
|
||||||
|
// Run starts the Apple2 emulation
|
||||||
|
func (a *Apple2) Run() {
|
||||||
|
a.Start(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the Apple2 emulation, can start paused
|
||||||
|
func (a *Apple2) Start(paused bool) {
|
||||||
|
// Start the processor
|
||||||
|
a.cpu.Reset()
|
||||||
|
referenceTime := time.Now()
|
||||||
|
speedReferenceTime := referenceTime
|
||||||
|
speedReferenceCycles := uint64(0)
|
||||||
|
|
||||||
|
a.paused = paused
|
||||||
|
|
||||||
|
for {
|
||||||
|
// Run 6502 steps
|
||||||
|
if !a.paused {
|
||||||
|
for i := 0; i < cpuSpinLoops; i++ {
|
||||||
|
// Conditional tracing
|
||||||
|
//pc, _ := a.cpu.GetPCAndSP()
|
||||||
|
//a.cpu.SetTrace((pc >= 0xc500 && pc < 0xc600) || (pc >= 0xc700 && pc < 0xc800))
|
||||||
|
|
||||||
|
// Execution
|
||||||
|
a.cpu.ExecuteInstruction()
|
||||||
|
|
||||||
|
// Special tracing
|
||||||
|
a.executionTrace()
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.cycleBreakpoint != 0 && a.cpu.GetCycles() >= a.cycleBreakpoint {
|
||||||
|
a.breakPoint = true
|
||||||
|
a.cycleBreakpoint = 0
|
||||||
|
a.paused = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
time.Sleep(200 * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute meta commands
|
||||||
|
commandsPending := true
|
||||||
|
for commandsPending {
|
||||||
|
select {
|
||||||
|
case command := <-a.commandChannel:
|
||||||
|
switch command.getId() {
|
||||||
|
case CommandKill:
|
||||||
|
return
|
||||||
|
case CommandPause:
|
||||||
|
if !a.paused {
|
||||||
|
a.paused = true
|
||||||
|
}
|
||||||
|
case CommandStart:
|
||||||
|
if a.paused {
|
||||||
|
a.paused = false
|
||||||
|
referenceTime = time.Now()
|
||||||
|
speedReferenceTime = referenceTime
|
||||||
|
}
|
||||||
|
case CommandPauseUnpause:
|
||||||
|
a.paused = !a.paused
|
||||||
|
referenceTime = time.Now()
|
||||||
|
speedReferenceTime = referenceTime
|
||||||
|
default:
|
||||||
|
// Execute the other commands
|
||||||
|
a.executeCommand(command)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
commandsPending = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.cycleDurationNs != 0 && a.fastRequestsCounter <= 0 {
|
||||||
|
// Wait until next 6502 step has to run
|
||||||
|
clockDuration := time.Since(referenceTime)
|
||||||
|
simulatedDuration := time.Duration(float64(a.cpu.GetCycles()) * a.cycleDurationNs)
|
||||||
|
waitDuration := simulatedDuration - clockDuration
|
||||||
|
if waitDuration > maxWaitDuration || -waitDuration > maxWaitDuration {
|
||||||
|
// We have to wait too long or are too much behind. Let's fast forward
|
||||||
|
referenceTime = referenceTime.Add(-waitDuration)
|
||||||
|
waitDuration = 0
|
||||||
|
}
|
||||||
|
if waitDuration > 0 {
|
||||||
|
time.Sleep(waitDuration)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.showSpeed && a.cpu.GetCycles()-speedReferenceCycles > 1000000 {
|
||||||
|
// Calculate speed in MHz every million cycles
|
||||||
|
newTime := time.Now()
|
||||||
|
newCycles := a.cpu.GetCycles()
|
||||||
|
elapsedCycles := float64(newCycles - speedReferenceCycles)
|
||||||
|
freq := 1000.0 * elapsedCycles / float64(newTime.Sub(speedReferenceTime).Nanoseconds())
|
||||||
|
fmt.Printf("Freq: %f Mhz\n", freq)
|
||||||
|
speedReferenceTime = newTime
|
||||||
|
speedReferenceCycles = newCycles
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Apple2) reset() {
|
||||||
|
a.cpu.Reset()
|
||||||
|
a.mmu.reset()
|
||||||
|
for _, c := range a.cards {
|
||||||
|
if c != nil {
|
||||||
|
c.reset()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Apple2) executionTrace() {
|
||||||
|
for _, v := range a.tracers {
|
||||||
|
v.inspect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Apple2) dumpDebugInfo() {
|
||||||
|
// See "Apple II Monitors Peeled"
|
||||||
|
pageZeroSymbols := map[int]string{
|
||||||
|
0x36: "CSWL",
|
||||||
|
0x37: "CSWH",
|
||||||
|
0x38: "KSWL",
|
||||||
|
0x39: "KSWH",
|
||||||
|
0xe2: "ACJVAFLDL", // Apple Pascal
|
||||||
|
0xe3: "ACJVAFLDH", // Apple Pascal
|
||||||
|
0xec: "JVBFOLDL", // Apple Pascal
|
||||||
|
0xed: "JVBFOLDH", // Apple Pascal
|
||||||
|
0xee: "JVAFOLDL", // Apple Pascal
|
||||||
|
0xef: "JVAFOLDH", // Apple Pascal
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Page zero values:\n")
|
||||||
|
for _, k := range []int{0x36, 0x37, 0x38, 0x39, 0xe2, 0xe3, 0xec, 0xed, 0xee, 0xef} {
|
||||||
|
d := a.mmu.physicalMainRAM.data[k]
|
||||||
|
fmt.Printf(" %v(0x%x): 0x%02x\n", pageZeroSymbols[k], k, d)
|
||||||
|
}
|
||||||
|
}
|
288
apple2Setup.go
288
apple2Setup.go
|
@ -1,288 +0,0 @@
|
||||||
package izapple2
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
"github.com/ivanizag/iz6502"
|
|
||||||
)
|
|
||||||
|
|
||||||
func newApple2() *Apple2 {
|
|
||||||
var a Apple2
|
|
||||||
|
|
||||||
a.Name = "Pending"
|
|
||||||
a.mmu = newMemoryManager(&a)
|
|
||||||
a.io = newIoC0Page(&a)
|
|
||||||
return &a
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Apple2) setup(clockMhz float64, fastMode bool) {
|
|
||||||
a.commandChannel = make(chan command, 100)
|
|
||||||
a.fastMode = fastMode
|
|
||||||
|
|
||||||
if clockMhz <= 0 {
|
|
||||||
// Full speed
|
|
||||||
a.cycleDurationNs = 0
|
|
||||||
} else {
|
|
||||||
a.cycleDurationNs = 1000.0 / clockMhz
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func setApple2plus(a *Apple2) {
|
|
||||||
a.Name = "Apple ][+"
|
|
||||||
a.cpu = iz6502.NewNMOS6502(a.mmu)
|
|
||||||
addApple2SoftSwitches(a.io)
|
|
||||||
}
|
|
||||||
|
|
||||||
func setApple2e(a *Apple2) {
|
|
||||||
a.Name = "Apple IIe"
|
|
||||||
a.isApple2e = true
|
|
||||||
a.cpu = iz6502.NewNMOS6502(a.mmu)
|
|
||||||
a.mmu.initExtendedRAM(1)
|
|
||||||
addApple2SoftSwitches(a.io)
|
|
||||||
addApple2ESoftSwitches(a.io)
|
|
||||||
}
|
|
||||||
|
|
||||||
func setApple2eEnhanced(a *Apple2) {
|
|
||||||
a.Name = "Apple //e"
|
|
||||||
a.isApple2e = true
|
|
||||||
a.cpu = iz6502.NewCMOS65c02(a.mmu)
|
|
||||||
a.mmu.initExtendedRAM(1)
|
|
||||||
addApple2SoftSwitches(a.io)
|
|
||||||
addApple2ESoftSwitches(a.io)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Apple2) addTracer(tracer executionTracer) {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCards returns the array of inserted cards
|
|
||||||
func (a *Apple2) GetCards() [8]Card {
|
|
||||||
return a.cards
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
apple2RomSize = 12 * 1024
|
|
||||||
apple2eRomSize = 16 * 1024
|
|
||||||
)
|
|
||||||
|
|
||||||
// LoadRom loads a standard Apple2+ or 2e ROM
|
|
||||||
func (a *Apple2) LoadRom(filename string) error {
|
|
||||||
data, _, err := LoadResource(filename)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
size := len(data)
|
|
||||||
if size != apple2RomSize && size != apple2eRomSize {
|
|
||||||
return errors.New("rom size not supported")
|
|
||||||
}
|
|
||||||
|
|
||||||
romBase := 0x10000 - size
|
|
||||||
a.mmu.physicalROM[0] = newMemoryRangeROM(uint16(romBase), data, "Main ROM")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddDisk2 inserts a DiskII controller
|
|
||||||
func (a *Apple2) AddDisk2(slot int, diskImage, diskBImage string, trackTracer trackTracer) error {
|
|
||||||
c := NewCardDisk2(trackTracer)
|
|
||||||
a.insertCard(c, slot)
|
|
||||||
|
|
||||||
if diskImage != "" {
|
|
||||||
err := c.drive[0].insertDiskette(diskImage)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if diskBImage != "" {
|
|
||||||
err := c.drive[1].insertDiskette(diskBImage)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddDisk2 inserts a DiskII controller
|
|
||||||
func (a *Apple2) AddDisk2Sequencer(slot int, diskImage, diskBImage string, trackTracer trackTracer) error {
|
|
||||||
c := NewCardDisk2Sequencer(trackTracer)
|
|
||||||
a.insertCard(c, slot)
|
|
||||||
|
|
||||||
if diskImage != "" {
|
|
||||||
err := c.drive[0].insertDiskette(diskImage)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if diskBImage != "" {
|
|
||||||
err := c.drive[1].insertDiskette(diskBImage)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddSmartPortDisk adds a smart port card and image
|
|
||||||
func (a *Apple2) AddSmartPortDisk(slot int, hdImage string, traceHD bool, traceSP bool) error {
|
|
||||||
c := NewCardSmartPort()
|
|
||||||
c.trace = traceSP
|
|
||||||
a.insertCard(c, slot)
|
|
||||||
|
|
||||||
err := c.LoadImage(hdImage, traceHD)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddSmartPortDisk adds a smart port card and image
|
|
||||||
func (a *Apple2) AddFujinet(slot int, trace bool) {
|
|
||||||
c := NewCardSmartPort()
|
|
||||||
c.trace = trace
|
|
||||||
a.insertCard(c, slot)
|
|
||||||
|
|
||||||
net := NewSmartPortFujinetNetwork(c)
|
|
||||||
net.trace = trace
|
|
||||||
c.AddDevice(net)
|
|
||||||
|
|
||||||
clock := NewSmartPortFujinetClock(c)
|
|
||||||
clock.trace = trace
|
|
||||||
c.AddDevice(clock)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddVidHD adds a card with the signature of VidHD
|
|
||||||
func (a *Apple2) AddVidHD(slot int) {
|
|
||||||
a.insertCard(NewCardVidHD(), slot)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddFastChip adds a card with the signature of VidHD
|
|
||||||
func (a *Apple2) AddFastChip(slot int) {
|
|
||||||
a.insertCard(NewCardFastChip(), slot)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddLanguageCard inserts a 16Kb card
|
|
||||||
func (a *Apple2) AddLanguageCard(slot int) {
|
|
||||||
a.insertCard(NewCardLanguage(), slot)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddSaturnCard inserts a 128Kb card
|
|
||||||
func (a *Apple2) AddSaturnCard(slot int) {
|
|
||||||
a.insertCard(NewCardSaturn(), slot)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddParallelPrinter inserts an Apple II Parallel Printer card
|
|
||||||
func (a *Apple2) AddParallelPrinter(slot int) {
|
|
||||||
a.insertCard(NewCardParallelPrinter(), slot)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddMemoryExpansionCard inserts an Apple II Memory Expansion card with 1GB
|
|
||||||
func (a *Apple2) AddMemoryExpansionCard(slot int) {
|
|
||||||
a.insertCard(NewCardMemoryExpansion(), slot)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddThunderClockPlusCard inserts a ThunderClock Plus clock card
|
|
||||||
func (a *Apple2) AddThunderClockPlusCard(slot int) {
|
|
||||||
a.insertCard(NewCardThunderClockPlus(), slot)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddMouseCard inserts a Mouse card
|
|
||||||
func (a *Apple2) AddMouseCard(slot int) {
|
|
||||||
a.insertCard(NewCardMouse(), slot)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddVidexCard inserts a Videx card
|
|
||||||
func (a *Apple2) AddVidexCard(slot int) {
|
|
||||||
c := NewCardVidex()
|
|
||||||
a.insertCard(c, slot)
|
|
||||||
a.softVideoSwitch = NewSoftVideoSwitch(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddSwyftCard inserts a Swyft card in slot 3
|
|
||||||
func (a *Apple2) AddSwyftCard() {
|
|
||||||
c := NewCardSwyft()
|
|
||||||
a.insertCard(c, 3)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddBrainBoardII inserts a Brain Board II card
|
|
||||||
func (a *Apple2) AddBrainBoardII(slot int) {
|
|
||||||
a.insertCard(NewCardBrainBoardII(), slot)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddRGBCard inserts an RBG option to the Apple IIe 80 col 64KB card
|
|
||||||
func (a *Apple2) AddRGBCard() {
|
|
||||||
setupRGBCard(a)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddRAMWorks inserts adds RAMWorks style RAM to the Apple IIe 80 col 64KB card
|
|
||||||
func (a *Apple2) AddRAMWorks(banks int) {
|
|
||||||
setupRAMWorksCard(a, banks)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddNoSlotClock inserts a DS1215 no slot clock under the main ROM
|
|
||||||
func (a *Apple2) AddNoSlotClock() {
|
|
||||||
nsc := newNoSlotClockDS1216(a, a.mmu.physicalROM[0])
|
|
||||||
a.mmu.physicalROM[0] = nsc
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddRomX inserts a RomX. It intercepts all memory accesses
|
|
||||||
func (a *Apple2) AddRomX() error {
|
|
||||||
rx, err := newRomX(a, a.mmu)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
a.cpu.SetMemory(rx)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddNoSlotClockInCard inserts a DS1215 no slot clock under a card ROM
|
|
||||||
func (a *Apple2) AddNoSlotClockInCard(slot int) error {
|
|
||||||
cardRom := a.mmu.cardsROM[slot]
|
|
||||||
if cardRom == nil {
|
|
||||||
return errors.New("no ROM available on the slot to add a no slot clock")
|
|
||||||
}
|
|
||||||
nsc := newNoSlotClockDS1216(a, cardRom)
|
|
||||||
a.mmu.cardsROM[slot] = nsc
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddCardLogger inserts a fake card that logs accesses
|
|
||||||
func (a *Apple2) AddCardLogger(slot int) {
|
|
||||||
c := NewCardLogger()
|
|
||||||
a.insertCard(c, slot)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddCardInOut inserts a fake card that interfaces with the emulator host
|
|
||||||
func (a *Apple2) AddCardInOut(slot int) {
|
|
||||||
c := NewCardInOut()
|
|
||||||
a.insertCard(c, slot)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetKeyboardProvider attaches an external keyboard provider
|
|
||||||
func (a *Apple2) SetKeyboardProvider(kb KeyboardProvider) {
|
|
||||||
a.io.setKeyboardProvider(kb)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetSpeakerProvider attaches an external keyboard provider
|
|
||||||
func (a *Apple2) SetSpeakerProvider(s SpeakerProvider) {
|
|
||||||
a.io.setSpeakerProvider(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetJoysticksProvider attaches an external joysticks provider
|
|
||||||
func (a *Apple2) SetJoysticksProvider(j JoysticksProvider) {
|
|
||||||
a.io.setJoysticksProvider(j)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetMouseProvider attaches an external joysticks provider
|
|
||||||
func (a *Apple2) SetMouseProvider(m MouseProvider) {
|
|
||||||
a.io.setMouseProvider(m)
|
|
||||||
}
|
|
|
@ -9,17 +9,24 @@ type apple2Tester struct {
|
||||||
terminateCondition func(a *Apple2) bool
|
terminateCondition func(a *Apple2) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeApple2Tester(model string) *apple2Tester {
|
func makeApple2Tester(model string, overrides *configuration) (*apple2Tester, error) {
|
||||||
a := newApple2()
|
config, err := getConfigurationFromModel(model, overrides)
|
||||||
a.setup(0, true) // Full speed
|
if err != nil {
|
||||||
initModel(a, model, defaultInternal, defaultInternal)
|
return nil, err
|
||||||
|
}
|
||||||
a.AddLanguageCard(0)
|
config.set(confSpeed, "full")
|
||||||
|
a, err := configure(config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
var at apple2Tester
|
var at apple2Tester
|
||||||
at.a = a
|
|
||||||
a.addTracer(&at)
|
a.addTracer(&at)
|
||||||
return &at
|
return &at, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (at *apple2Tester) connect(a *Apple2) {
|
||||||
|
at.a = a
|
||||||
}
|
}
|
||||||
|
|
||||||
func (at *apple2Tester) inspect() {
|
func (at *apple2Tester) inspect() {
|
||||||
|
|
446
apple2main.go
446
apple2main.go
|
@ -1,446 +0,0 @@
|
||||||
package izapple2
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
)
|
|
||||||
|
|
||||||
const defaultInternal = "<default>"
|
|
||||||
|
|
||||||
// MainApple is a device independent main. Video, keyboard and speaker won't be defined
|
|
||||||
func MainApple() *Apple2 {
|
|
||||||
romFile := flag.String(
|
|
||||||
"rom",
|
|
||||||
defaultInternal,
|
|
||||||
"main rom file")
|
|
||||||
disk2Slot := flag.Int(
|
|
||||||
"disk2Slot",
|
|
||||||
6,
|
|
||||||
"slot for the disk driver. -1 for none.")
|
|
||||||
diskImage := flag.String(
|
|
||||||
"disk",
|
|
||||||
defaultInternal,
|
|
||||||
"file to load on the first disk drive")
|
|
||||||
diskBImage := flag.String(
|
|
||||||
"diskb",
|
|
||||||
"",
|
|
||||||
"file to load on the second disk drive")
|
|
||||||
diskCImage := flag.String(
|
|
||||||
"diskc",
|
|
||||||
"",
|
|
||||||
"file to load on the third disk drive, slot 5")
|
|
||||||
diskDImage := flag.String(
|
|
||||||
"diskd",
|
|
||||||
"",
|
|
||||||
"file to load on the fourth disk drive, slot 5")
|
|
||||||
hardDiskImage := flag.String(
|
|
||||||
"hd",
|
|
||||||
"",
|
|
||||||
"file to load on the boot hard disk (slot 7)")
|
|
||||||
hardDiskSlot := flag.Int(
|
|
||||||
"hdSlot",
|
|
||||||
-1,
|
|
||||||
"slot for the hard drive if present. -1 for none.")
|
|
||||||
fujinetSlot := flag.Int(
|
|
||||||
"fujinet",
|
|
||||||
-1,
|
|
||||||
"slot for the smatport card hosting the Fujinet. -1 for none.")
|
|
||||||
smartPortImage := flag.String(
|
|
||||||
"disk35",
|
|
||||||
"",
|
|
||||||
"file to load on the SmartPort disk (slot 5)")
|
|
||||||
cpuClock := flag.Float64(
|
|
||||||
"mhz",
|
|
||||||
CPUClockMhz,
|
|
||||||
"cpu speed in Mhz, use 0 for full speed. Use F5 to toggle.")
|
|
||||||
charRomFile := flag.String(
|
|
||||||
"charRom",
|
|
||||||
defaultInternal,
|
|
||||||
"rom file for the character generator")
|
|
||||||
languageCardSlot := flag.Int(
|
|
||||||
"languageCardSlot",
|
|
||||||
0,
|
|
||||||
"slot for the 16kb language card. -1 for none")
|
|
||||||
saturnCardSlot := flag.Int(
|
|
||||||
"saturnCardSlot",
|
|
||||||
-1,
|
|
||||||
"slot for the 256kb Saturn card. -1 for none")
|
|
||||||
vidHDCardSlot := flag.Int(
|
|
||||||
"vidHDSlot",
|
|
||||||
2,
|
|
||||||
"slot for the VidHD card, only for //e models. -1 for none")
|
|
||||||
fastChipCardSlot := flag.Int(
|
|
||||||
"fastChipSlot",
|
|
||||||
3,
|
|
||||||
"slot for the FASTChip accelerator card, -1 for none")
|
|
||||||
memoryExpansionCardSlot := flag.Int(
|
|
||||||
"memoryExpSlot",
|
|
||||||
-1,
|
|
||||||
"slot for the Memory Expansion card with 1GB. -1 for none")
|
|
||||||
parallelPrinterSlot := flag.Int(
|
|
||||||
"printer",
|
|
||||||
1,
|
|
||||||
"slot for the Parallel Printer Interface. -1 for none")
|
|
||||||
brainBoard := flag.Int(
|
|
||||||
"brainBoardSlot",
|
|
||||||
-1,
|
|
||||||
"slot for the Brain Board II. -1 for none")
|
|
||||||
ramWorksKb := flag.Int(
|
|
||||||
"ramworks",
|
|
||||||
8192,
|
|
||||||
"memory to use with RAMWorks card, 0 for no card, max is 16384")
|
|
||||||
thunderClockCardSlot := flag.Int(
|
|
||||||
"thunderClockCardSlot",
|
|
||||||
-1,
|
|
||||||
"slot for the ThunderClock Plus card. -1 for none")
|
|
||||||
consoleCardSlot := flag.Int(
|
|
||||||
"consoleCardSlot",
|
|
||||||
-1,
|
|
||||||
"slot for the host console card. -1 for none")
|
|
||||||
mouseCardSlot := flag.Int(
|
|
||||||
"mouseCardSlot",
|
|
||||||
4,
|
|
||||||
"slot for the Mouse card. -1 for none")
|
|
||||||
videxCardSlot := flag.Int(
|
|
||||||
"videxCardSlot",
|
|
||||||
3,
|
|
||||||
"slot for the Videx Videoterm 80 columns card. For pre-2e models. -1 for none")
|
|
||||||
swyftCard := flag.Bool(
|
|
||||||
"swyftCard",
|
|
||||||
false,
|
|
||||||
"activate a Swyft Card in slot 3. Load the tutorial disk if none provided")
|
|
||||||
nsc := flag.Int(
|
|
||||||
"nsc",
|
|
||||||
-1,
|
|
||||||
"add a DS1216 No-Slot-Clock on the main ROM (use 0) or a slot ROM. -1 for none")
|
|
||||||
rgbCard := flag.Bool(
|
|
||||||
"rgb",
|
|
||||||
true,
|
|
||||||
"emulate the RGB modes of the 80col RGB card for DHGR")
|
|
||||||
romX := flag.Bool(
|
|
||||||
"romx",
|
|
||||||
false,
|
|
||||||
"emulate a RomX")
|
|
||||||
fastDisk := flag.Bool(
|
|
||||||
"fastDisk",
|
|
||||||
true,
|
|
||||||
"set fast mode when the disks are spinning")
|
|
||||||
panicSS := flag.Bool(
|
|
||||||
"panicSS",
|
|
||||||
false,
|
|
||||||
"panic if a not implemented softswitch is used")
|
|
||||||
traceCPU := flag.Bool(
|
|
||||||
"traceCpu",
|
|
||||||
false,
|
|
||||||
"dump to the console the CPU execution operations")
|
|
||||||
traceSS := flag.Bool(
|
|
||||||
"traceSS",
|
|
||||||
false,
|
|
||||||
"dump to the console the sofswitches calls")
|
|
||||||
traceSSReg := flag.Bool(
|
|
||||||
"traceSSReg",
|
|
||||||
false,
|
|
||||||
"dump to the console the sofswitch registrations")
|
|
||||||
traceHD := flag.Bool(
|
|
||||||
"traceHD",
|
|
||||||
false,
|
|
||||||
"dump to the console the hd accesses")
|
|
||||||
traceSP := flag.Bool(
|
|
||||||
"traceSP",
|
|
||||||
false,
|
|
||||||
"dump to the console the smarport commands")
|
|
||||||
traceTracks := flag.Bool(
|
|
||||||
"traceTracks",
|
|
||||||
false,
|
|
||||||
"dump to the console the disk tracks changes")
|
|
||||||
model := flag.String(
|
|
||||||
"model",
|
|
||||||
"2enh",
|
|
||||||
"set base model. Models available 2plus, 2e, 2enh, base64a")
|
|
||||||
profile := flag.Bool(
|
|
||||||
"profile",
|
|
||||||
false,
|
|
||||||
"generate profile trace to analyse with pprof")
|
|
||||||
traceMLI := flag.Bool(
|
|
||||||
"traceMLI",
|
|
||||||
false,
|
|
||||||
"dump to the console the calls to ProDOS machine language interface calls to $BF00")
|
|
||||||
tracePascal := flag.Bool(
|
|
||||||
"tracePascal",
|
|
||||||
false,
|
|
||||||
"dump to the console the calls to the Apple Pascal BIOS")
|
|
||||||
forceCaps := flag.Bool(
|
|
||||||
"forceCaps",
|
|
||||||
false,
|
|
||||||
"force all letters to be uppercased (no need for caps lock!)")
|
|
||||||
sequencerDisk2 := flag.Bool(
|
|
||||||
"sequencer",
|
|
||||||
false,
|
|
||||||
"use the sequencer based Disk II card")
|
|
||||||
traceBBC := flag.Bool(
|
|
||||||
"traceBBC",
|
|
||||||
false,
|
|
||||||
"trace BBC MOS API calls used with Applecorn, skip console I/O calls")
|
|
||||||
traceBBCFull := flag.Bool(
|
|
||||||
"traceBBCFull",
|
|
||||||
false,
|
|
||||||
"trace BBC MOS API calls used with Applecorn")
|
|
||||||
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
// Process a filename with autodetection
|
|
||||||
filename := flag.Arg(0)
|
|
||||||
diskImageFinal := *diskImage
|
|
||||||
hardDiskImageFinal := *hardDiskImage
|
|
||||||
if filename != "" {
|
|
||||||
// Try loading as diskette
|
|
||||||
_, err := LoadDiskette(filename)
|
|
||||||
if err == nil {
|
|
||||||
diskImageFinal = filename
|
|
||||||
} else {
|
|
||||||
hardDiskImageFinal = filename
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resolve what is the default disk to use if not specified
|
|
||||||
if diskImageFinal == defaultInternal {
|
|
||||||
if *swyftCard {
|
|
||||||
diskImageFinal = "<internal>/SwyftWare_-_SwyftCard_Tutorial.woz"
|
|
||||||
} else {
|
|
||||||
diskImageFinal = "<internal>/dos33.dsk"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
a := newApple2()
|
|
||||||
a.setup(*cpuClock, *fastDisk)
|
|
||||||
a.io.setTrace(*traceSS)
|
|
||||||
a.io.setTraceRegistrations(*traceSSReg)
|
|
||||||
a.io.setPanicNotImplemented(*panicSS)
|
|
||||||
a.setProfiling(*profile)
|
|
||||||
a.SetForceCaps(*forceCaps)
|
|
||||||
if *traceMLI {
|
|
||||||
a.addTracer(newTraceProDOS(a))
|
|
||||||
}
|
|
||||||
if *tracePascal {
|
|
||||||
a.addTracer(newTracePascal(a))
|
|
||||||
}
|
|
||||||
if *traceBBC {
|
|
||||||
a.addTracer(newTraceApplecorn(a, true))
|
|
||||||
}
|
|
||||||
if *traceBBCFull {
|
|
||||||
a.addTracer(newTraceApplecorn(a, false))
|
|
||||||
}
|
|
||||||
|
|
||||||
initModel(a, *model, *romFile, *charRomFile)
|
|
||||||
a.cpu.SetTrace(*traceCPU)
|
|
||||||
|
|
||||||
// Disable incompatible cards
|
|
||||||
switch *model {
|
|
||||||
case "2plus":
|
|
||||||
*vidHDCardSlot = -1
|
|
||||||
case "2e":
|
|
||||||
*videxCardSlot = -1
|
|
||||||
case "2enh":
|
|
||||||
*videxCardSlot = -1
|
|
||||||
case "base64a":
|
|
||||||
*vidHDCardSlot = -1
|
|
||||||
*videxCardSlot = -1 // The videx firmware crashes the BASE64A, probably by use of ANN0
|
|
||||||
default:
|
|
||||||
panic("Model not supported")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Externsion cards
|
|
||||||
if *languageCardSlot >= 0 {
|
|
||||||
a.AddLanguageCard(*languageCardSlot)
|
|
||||||
}
|
|
||||||
if *saturnCardSlot >= 0 {
|
|
||||||
a.AddSaturnCard(*saturnCardSlot)
|
|
||||||
}
|
|
||||||
if *parallelPrinterSlot >= 0 {
|
|
||||||
a.AddParallelPrinter(*parallelPrinterSlot)
|
|
||||||
}
|
|
||||||
if *memoryExpansionCardSlot >= 0 {
|
|
||||||
a.AddMemoryExpansionCard(*memoryExpansionCardSlot)
|
|
||||||
}
|
|
||||||
if *thunderClockCardSlot > 0 {
|
|
||||||
a.AddThunderClockPlusCard(*thunderClockCardSlot)
|
|
||||||
}
|
|
||||||
if *vidHDCardSlot >= 0 {
|
|
||||||
a.AddVidHD(*vidHDCardSlot)
|
|
||||||
}
|
|
||||||
if *consoleCardSlot >= 0 {
|
|
||||||
a.AddCardInOut(*consoleCardSlot)
|
|
||||||
}
|
|
||||||
if *mouseCardSlot > 0 {
|
|
||||||
a.AddMouseCard(*mouseCardSlot)
|
|
||||||
}
|
|
||||||
if *videxCardSlot > 0 {
|
|
||||||
a.AddVidexCard(*videxCardSlot)
|
|
||||||
}
|
|
||||||
if *swyftCard {
|
|
||||||
if !a.isApple2e {
|
|
||||||
panic("SwyftCard available only on Apple IIe or better")
|
|
||||||
}
|
|
||||||
a.AddSwyftCard()
|
|
||||||
}
|
|
||||||
if *brainBoard > 0 {
|
|
||||||
a.AddBrainBoardII(*brainBoard)
|
|
||||||
}
|
|
||||||
|
|
||||||
var trackTracer trackTracer
|
|
||||||
if *traceTracks {
|
|
||||||
trackTracer = makeTrackTracerLogger()
|
|
||||||
}
|
|
||||||
|
|
||||||
if *smartPortImage != "" {
|
|
||||||
err := a.AddSmartPortDisk(5, *smartPortImage, *traceHD, *traceSP)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
} else if *diskCImage != "" || *diskDImage != "" {
|
|
||||||
if *sequencerDisk2 {
|
|
||||||
err := a.AddDisk2Sequencer(5, *diskCImage, *diskDImage, trackTracer)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err := a.AddDisk2(5, *diskCImage, *diskDImage, trackTracer)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if *fujinetSlot >= 0 {
|
|
||||||
a.AddFujinet(*fujinetSlot, *traceSP)
|
|
||||||
}
|
|
||||||
|
|
||||||
if *fastChipCardSlot >= 0 {
|
|
||||||
a.AddFastChip(*fastChipCardSlot)
|
|
||||||
}
|
|
||||||
if *disk2Slot > 0 {
|
|
||||||
if *sequencerDisk2 {
|
|
||||||
err := a.AddDisk2Sequencer(*disk2Slot, diskImageFinal, *diskBImage, trackTracer)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err := a.AddDisk2(*disk2Slot, diskImageFinal, *diskBImage, trackTracer)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if hardDiskImageFinal != "" {
|
|
||||||
if *hardDiskSlot <= 0 {
|
|
||||||
// If there is a hard disk image, but no slot assigned, use slot 7.
|
|
||||||
*hardDiskSlot = 7
|
|
||||||
}
|
|
||||||
err := a.AddSmartPortDisk(*hardDiskSlot, hardDiskImageFinal, *traceHD, *traceSP)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if *ramWorksKb != 0 {
|
|
||||||
if *ramWorksKb%64 != 0 {
|
|
||||||
panic("Ramworks size must be a multiple of 64")
|
|
||||||
}
|
|
||||||
a.AddRAMWorks(*ramWorksKb / 64)
|
|
||||||
}
|
|
||||||
|
|
||||||
if *rgbCard {
|
|
||||||
a.AddRGBCard()
|
|
||||||
}
|
|
||||||
|
|
||||||
if *nsc == 0 {
|
|
||||||
a.AddNoSlotClock()
|
|
||||||
} else if *nsc > 0 {
|
|
||||||
err := a.AddNoSlotClockInCard(*nsc)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if *romX {
|
|
||||||
err := a.AddRomX()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// a.AddCardLogger(4)
|
|
||||||
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
func initModel(a *Apple2, model string, romFile string, charRomFile string) {
|
|
||||||
var charGenMap charColumnMap
|
|
||||||
initialCharGenPage := 0
|
|
||||||
switch model {
|
|
||||||
case "2plus":
|
|
||||||
setApple2plus(a)
|
|
||||||
if romFile == defaultInternal {
|
|
||||||
romFile = "<internal>/Apple2_Plus.rom"
|
|
||||||
}
|
|
||||||
if charRomFile == defaultInternal {
|
|
||||||
charRomFile = "<internal>/Apple2rev7CharGen.rom"
|
|
||||||
}
|
|
||||||
charGenMap = charGenColumnsMap2Plus
|
|
||||||
|
|
||||||
case "2e":
|
|
||||||
setApple2e(a)
|
|
||||||
if romFile == defaultInternal {
|
|
||||||
romFile = "<internal>/Apple2e.rom"
|
|
||||||
}
|
|
||||||
if charRomFile == defaultInternal {
|
|
||||||
charRomFile = "<internal>/Apple IIe Video Unenhanced - 342-0133-A - 2732.bin"
|
|
||||||
}
|
|
||||||
charGenMap = charGenColumnsMap2e
|
|
||||||
|
|
||||||
case "2enh":
|
|
||||||
setApple2eEnhanced(a)
|
|
||||||
if romFile == defaultInternal {
|
|
||||||
romFile = "<internal>/Apple2e_Enhanced.rom"
|
|
||||||
}
|
|
||||||
if charRomFile == defaultInternal {
|
|
||||||
charRomFile = "<internal>/Apple IIe Video Enhanced - 342-0265-A - 2732.bin"
|
|
||||||
}
|
|
||||||
charGenMap = charGenColumnsMap2e
|
|
||||||
|
|
||||||
case "base64a":
|
|
||||||
setBase64a(a)
|
|
||||||
if romFile == defaultInternal {
|
|
||||||
err := loadBase64aRom(a)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
romFile = ""
|
|
||||||
}
|
|
||||||
if charRomFile == defaultInternal {
|
|
||||||
charRomFile = "<internal>/BASE64A_ROM7_CharGen.BIN"
|
|
||||||
initialCharGenPage = 1
|
|
||||||
}
|
|
||||||
charGenMap = charGenColumnsMapBase64a
|
|
||||||
|
|
||||||
default:
|
|
||||||
panic("Model not supported")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load ROM
|
|
||||||
if romFile != "" {
|
|
||||||
err := a.LoadRom(romFile)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load character generator
|
|
||||||
cg, err := newCharacterGenerator(charRomFile, charGenMap, a.isApple2e)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
cg.setPage(initialCharGenPage)
|
|
||||||
a.cg = cg
|
|
||||||
}
|
|
|
@ -2,21 +2,12 @@ package izapple2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/ivanizag/iz6502"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Copam BASE64A adaptation.
|
Copam BASE64A adaptation.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
func setBase64a(a *Apple2) {
|
|
||||||
a.Name = "Base 64A"
|
|
||||||
a.cpu = iz6502.NewNMOS6502(a.mmu)
|
|
||||||
addApple2SoftSwitches(a.io)
|
|
||||||
addBase64aSoftSwitches(a.io)
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// There are 6 ROM chips. Each can have 4Kb or 8Kb. They can fill
|
// There are 6 ROM chips. Each can have 4Kb or 8Kb. They can fill
|
||||||
// 2 or 4 banks with 2kb windows.
|
// 2 or 4 banks with 2kb windows.
|
||||||
|
|
|
@ -40,18 +40,19 @@ func (c *cardBase) reset() {
|
||||||
// nothing
|
// nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *cardBase) loadRomFromResource(resource string) {
|
func (c *cardBase) loadRomFromResource(resource string) error {
|
||||||
data, _, err := LoadResource(resource)
|
data, _, err := 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)
|
return err
|
||||||
}
|
}
|
||||||
c.loadRom(data)
|
c.loadRom(data)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *cardBase) loadRom(data []uint8) {
|
func (c *cardBase) loadRom(data []uint8) {
|
||||||
if c.a != nil {
|
if c.a != nil {
|
||||||
panic("Assert failed. Rom must be loaded before inserting the card in the slot")
|
panic("Assert failed. ROM must be loaded before inserting the card in the slot")
|
||||||
}
|
}
|
||||||
if len(data) == 0x100 {
|
if len(data) == 0x100 {
|
||||||
// Just 256 bytes in Cs00
|
// Just 256 bytes in Cs00
|
||||||
|
|
|
@ -45,25 +45,30 @@ type CardBrainBoardII struct {
|
||||||
rom []uint8
|
rom []uint8
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCardBrainBoardII creates a new CardBrainBoardII
|
func newCardBrainBoardIIBuilder() *cardBuilder {
|
||||||
func NewCardBrainBoardII() *CardBrainBoardII {
|
return &cardBuilder{
|
||||||
var c CardBrainBoardII
|
name: "Brain Board II",
|
||||||
c.name = "Brain Board II"
|
description: "Firmware card for Apple II. It has 4 banks and can be used to boot wozaniam, Integer BASIC or other çustom ROMs.",
|
||||||
|
defaultParams: &[]paramSpec{
|
||||||
|
{"rom", "ROM file to load", "<internal>/ApplesoftInteger.BIN"},
|
||||||
|
{"dip2", "Use the upper half of the ROM", "true"},
|
||||||
|
},
|
||||||
|
buildFunc: func(params map[string]string) (Card, error) {
|
||||||
|
var c CardBrainBoardII
|
||||||
|
c.highBank = false // Start with wozaniam by default
|
||||||
|
c.dip2 = paramsGetBool(params, "dip2")
|
||||||
|
|
||||||
c.highBank = false // Start with wozaniam by default
|
// The ROM has:xaa-wozaniam xab-applesoft xac-wozaniam xad-integer
|
||||||
c.dip2 = true // Use the wozaniam+integer banks
|
romFile := paramsGetPath(params, "rom")
|
||||||
|
data, _, err := LoadResource(romFile)
|
||||||
// The ROM has:xaa-wozaniam xab-applesoft xac-wozaniam xad-integer
|
if err != nil {
|
||||||
data, _, err := LoadResource("<internal>/ApplesoftInteger.BIN")
|
return nil, err
|
||||||
if err != nil {
|
}
|
||||||
// The resource should be internal and never fail
|
c.rom = data
|
||||||
panic(err)
|
c.romCxxx = &c
|
||||||
|
return &c, nil
|
||||||
|
},
|
||||||
}
|
}
|
||||||
c.rom = data
|
|
||||||
|
|
||||||
// The ROM of the card is paged as the rest of the ROMs
|
|
||||||
c.romCxxx = &c
|
|
||||||
return &c
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CardBrainBoardII) assign(a *Apple2, slot int) {
|
func (c *CardBrainBoardII) assign(a *Apple2, slot int) {
|
||||||
|
|
|
@ -0,0 +1,166 @@
|
||||||
|
package izapple2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/exp/maps"
|
||||||
|
)
|
||||||
|
|
||||||
|
type paramSpec struct {
|
||||||
|
name string
|
||||||
|
description string
|
||||||
|
defaultValue string
|
||||||
|
}
|
||||||
|
|
||||||
|
type cardBuilder struct {
|
||||||
|
name string
|
||||||
|
description string
|
||||||
|
defaultParams *[]paramSpec
|
||||||
|
requiresIIe bool
|
||||||
|
buildFunc func(params map[string]string) (Card, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
const noCardName = "empty"
|
||||||
|
|
||||||
|
var cardFactory map[string]*cardBuilder
|
||||||
|
|
||||||
|
func getCardFactory() map[string]*cardBuilder {
|
||||||
|
if cardFactory != nil {
|
||||||
|
return cardFactory
|
||||||
|
}
|
||||||
|
cardFactory = make(map[string]*cardBuilder)
|
||||||
|
cardFactory["brainboard"] = newCardBrainBoardIIBuilder()
|
||||||
|
cardFactory["diskii"] = newCardDisk2Builder()
|
||||||
|
cardFactory["diskiiseq"] = newCardDisk2SequencerBuilder()
|
||||||
|
cardFactory["fastchip"] = newCardFastChipBuilder()
|
||||||
|
cardFactory["fujinet"] = newCardSmartPortFujinetBuilder()
|
||||||
|
cardFactory["inout"] = newCardInOutBuilder()
|
||||||
|
cardFactory["language"] = newCardLanguageBuilder()
|
||||||
|
cardFactory["softswitchlogger"] = newCardLoggerBuilder()
|
||||||
|
cardFactory["memexp"] = newCardMemoryExpansionBuilder()
|
||||||
|
cardFactory["mouse"] = newCardMouseBuilder()
|
||||||
|
cardFactory["parallel"] = newCardParallelPrinterBuilder()
|
||||||
|
cardFactory["saturn"] = newCardSaturnBuilder()
|
||||||
|
cardFactory["smartport"] = newCardSmartPortStorageBuilder()
|
||||||
|
cardFactory["swyftcard"] = newCardSwyftBuilder()
|
||||||
|
cardFactory["thunderclock"] = newCardThunderClockPlusBuilder()
|
||||||
|
cardFactory["videx"] = newCardVidexBuilder()
|
||||||
|
cardFactory["vidhd"] = newCardVidHDBuilder()
|
||||||
|
return cardFactory
|
||||||
|
}
|
||||||
|
|
||||||
|
func availableCards() []string {
|
||||||
|
return maps.Keys(getCardFactory())
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupCard(a *Apple2, slot int, paramString string) (Card, error) {
|
||||||
|
paramsArgs := splitConfigurationString(paramString, ',')
|
||||||
|
|
||||||
|
cardName := paramsArgs[0]
|
||||||
|
if cardName == "" || cardName == noCardName {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
builder, ok := getCardFactory()[cardName]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("unknown card %s", cardName)
|
||||||
|
}
|
||||||
|
|
||||||
|
if builder.requiresIIe && !a.isApple2e {
|
||||||
|
return nil, fmt.Errorf("card %s requires an Apple IIe", builder.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
finalParams := make(map[string]string)
|
||||||
|
if builder.defaultParams != nil {
|
||||||
|
for _, defaultParam := range *builder.defaultParams {
|
||||||
|
finalParams[defaultParam.name] = defaultParam.defaultValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 1; i < len(paramsArgs); i++ {
|
||||||
|
paramArgSides := splitConfigurationString(paramsArgs[i], '=')
|
||||||
|
|
||||||
|
if _, ok := finalParams[paramArgSides[0]]; !ok {
|
||||||
|
return nil, fmt.Errorf("unknown parameter %s", paramArgSides[0])
|
||||||
|
}
|
||||||
|
if len(paramArgSides) > 2 {
|
||||||
|
return nil, fmt.Errorf("invalid parameter value for %s", paramArgSides[0])
|
||||||
|
}
|
||||||
|
if len(paramArgSides) == 1 {
|
||||||
|
finalParams[paramArgSides[0]] = "true"
|
||||||
|
} else {
|
||||||
|
finalParams[paramArgSides[0]] = paramArgSides[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
card, err := builder.buildFunc(finalParams)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cardBase, ok := card.(*cardBase)
|
||||||
|
if err == nil && ok {
|
||||||
|
cardBase.name = builder.name
|
||||||
|
}
|
||||||
|
|
||||||
|
card.assign(a, slot)
|
||||||
|
a.cards[slot] = card
|
||||||
|
|
||||||
|
return card, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func paramsGetBool(params map[string]string, name string) bool {
|
||||||
|
value, ok := params[name]
|
||||||
|
if !ok {
|
||||||
|
value = "false"
|
||||||
|
}
|
||||||
|
return value == "true"
|
||||||
|
}
|
||||||
|
|
||||||
|
func paramsGetString(params map[string]string, name string) string {
|
||||||
|
value, ok := params[name]
|
||||||
|
if !ok {
|
||||||
|
value = ""
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
func paramsGetPath(params map[string]string, name string) string {
|
||||||
|
value := paramsGetString(params, name)
|
||||||
|
if strings.HasPrefix(value, "\"") && strings.HasSuffix(value, "\"") {
|
||||||
|
value = value[1 : len(value)-1]
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
func paramsGetInt(params map[string]string, name string) (int, error) {
|
||||||
|
value, ok := params[name]
|
||||||
|
if !ok {
|
||||||
|
value = "0"
|
||||||
|
}
|
||||||
|
return strconv.Atoi(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func splitConfigurationString(s string, separator rune) []string {
|
||||||
|
// Split by comma, but not inside quotes
|
||||||
|
var result []string
|
||||||
|
var current string
|
||||||
|
inQuote := false
|
||||||
|
for _, c := range s {
|
||||||
|
if c == '"' {
|
||||||
|
inQuote = !inQuote
|
||||||
|
}
|
||||||
|
if c == separator && !inQuote {
|
||||||
|
result = append(result, current)
|
||||||
|
current = ""
|
||||||
|
} else {
|
||||||
|
current += string(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if current != "" {
|
||||||
|
result = append(result, current)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
56
cardDisk2.go
56
cardDisk2.go
|
@ -28,6 +28,7 @@ type CardDisk2 struct {
|
||||||
selected int // q5, Only 0 and 1 supported
|
selected int // q5, Only 0 and 1 supported
|
||||||
power bool // q4
|
power bool // q4
|
||||||
drive [2]cardDisk2Drive
|
drive [2]cardDisk2Drive
|
||||||
|
fastMode bool
|
||||||
|
|
||||||
dataLatch uint8
|
dataLatch uint8
|
||||||
q6 bool
|
q6 bool
|
||||||
|
@ -47,13 +48,42 @@ type cardDisk2Drive struct {
|
||||||
trackStep int // Stepmotor for tracks position. 4 steps per track
|
trackStep int // Stepmotor for tracks position. 4 steps per track
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCardDisk2 creates a new CardDisk2
|
func newCardDisk2Builder() *cardBuilder {
|
||||||
func NewCardDisk2(trackTracer trackTracer) *CardDisk2 {
|
return &cardBuilder{
|
||||||
var c CardDisk2
|
name: "Disk II",
|
||||||
c.name = "Disk II"
|
description: "Disk II interface card",
|
||||||
c.trackTracer = trackTracer
|
defaultParams: &[]paramSpec{
|
||||||
c.loadRomFromResource("<internal>/DISK2.rom")
|
{"disk1", "Diskette image for drive 1", ""},
|
||||||
return &c
|
{"disk2", "Diskette image for drive 2", ""},
|
||||||
|
{"tracktracer", "Trace how the disk head moves between tracks", "false"},
|
||||||
|
{"fast", "Enable CPU burst when accessing the disk", "true"},
|
||||||
|
},
|
||||||
|
buildFunc: func(params map[string]string) (Card, error) {
|
||||||
|
var c CardDisk2
|
||||||
|
c.loadRomFromResource("<internal>/DISK2.rom")
|
||||||
|
|
||||||
|
disk1 := paramsGetPath(params, "disk1")
|
||||||
|
if disk1 != "" {
|
||||||
|
err := c.drive[0].insertDiskette(disk1)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
disk2 := paramsGetPath(params, "disk2")
|
||||||
|
if disk2 != "" {
|
||||||
|
err := c.drive[1].insertDiskette(disk2)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
trackTracer := paramsGetBool(params, "tracktracer")
|
||||||
|
if trackTracer {
|
||||||
|
c.trackTracer = makeTrackTracerLogger()
|
||||||
|
}
|
||||||
|
c.fastMode = paramsGetBool(params, "fast")
|
||||||
|
return &c, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetInfo returns smartPort info
|
// GetInfo returns smartPort info
|
||||||
|
@ -80,6 +110,10 @@ func (c *CardDisk2) reset() {
|
||||||
c.q7 = false
|
c.q7 = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *CardDisk2) setTrackTracer(tt trackTracer) {
|
||||||
|
c.trackTracer = tt
|
||||||
|
}
|
||||||
|
|
||||||
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[0])
|
||||||
a.registerRemovableMediaDrive(&c.drive[1])
|
a.registerRemovableMediaDrive(&c.drive[1])
|
||||||
|
@ -151,7 +185,9 @@ func (c *CardDisk2) softSwitchQ4(value bool) {
|
||||||
if !value && c.power {
|
if !value && c.power {
|
||||||
// Turn off
|
// Turn off
|
||||||
c.power = false
|
c.power = false
|
||||||
c.a.ReleaseFastMode()
|
if c.fastMode {
|
||||||
|
c.a.ReleaseFastMode()
|
||||||
|
}
|
||||||
drive := &c.drive[c.selected]
|
drive := &c.drive[c.selected]
|
||||||
if drive.diskette != nil {
|
if drive.diskette != nil {
|
||||||
drive.diskette.PowerOff(c.a.cpu.GetCycles())
|
drive.diskette.PowerOff(c.a.cpu.GetCycles())
|
||||||
|
@ -159,7 +195,9 @@ func (c *CardDisk2) softSwitchQ4(value bool) {
|
||||||
} else if value && !c.power {
|
} else if value && !c.power {
|
||||||
// Turn on
|
// Turn on
|
||||||
c.power = true
|
c.power = true
|
||||||
c.a.RequestFastMode()
|
if c.fastMode {
|
||||||
|
c.a.RequestFastMode()
|
||||||
|
}
|
||||||
drive := &c.drive[c.selected]
|
drive := &c.drive[c.selected]
|
||||||
if drive.diskette != nil {
|
if drive.diskette != nil {
|
||||||
drive.diskette.PowerOn(c.a.cpu.GetCycles())
|
drive.diskette.PowerOn(c.a.cpu.GetCycles())
|
||||||
|
|
|
@ -37,6 +37,12 @@ type CardDisk2Sequencer struct {
|
||||||
trackTracer trackTracer
|
trackTracer trackTracer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Shared methods between both versions on the Disk II card
|
||||||
|
type cardDisk2Shared interface {
|
||||||
|
//insertDiskette(drive int, ...)
|
||||||
|
setTrackTracer(tt trackTracer)
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
disk2MotorOffDelay = uint64(2 * 1000 * 1000) // 2 Mhz cycles. Total 1 second.
|
disk2MotorOffDelay = uint64(2 * 1000 * 1000) // 2 Mhz cycles. Total 1 second.
|
||||||
disk2PulseCyles = uint8(8) // 8 cycles = 4ms * 2Mhz
|
disk2PulseCyles = uint8(8) // 8 cycles = 4ms * 2Mhz
|
||||||
|
@ -49,21 +55,49 @@ const (
|
||||||
disk2CyclestoLoseSsync = 100000
|
disk2CyclestoLoseSsync = 100000
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewCardDisk2Sequencer creates a new CardDisk2Sequencer
|
func newCardDisk2SequencerBuilder() *cardBuilder {
|
||||||
func NewCardDisk2Sequencer(trackTracer trackTracer) *CardDisk2Sequencer {
|
return &cardBuilder{
|
||||||
var c CardDisk2Sequencer
|
name: "Disk II Sequencer",
|
||||||
c.name = "Disk II"
|
description: "Disk II interface card emulating the Woz state machine",
|
||||||
c.trackTracer = trackTracer
|
defaultParams: &[]paramSpec{
|
||||||
c.loadRomFromResource("<internal>/DISK2.rom")
|
{"disk1", "Diskette image for drive 1", ""},
|
||||||
|
{"disk2", "Diskette image for drive 2", ""},
|
||||||
|
{"tracktracer", "Trace how the disk head moves between tracks", "false"},
|
||||||
|
},
|
||||||
|
buildFunc: func(params map[string]string) (Card, error) {
|
||||||
|
var c CardDisk2Sequencer
|
||||||
|
err := c.loadRomFromResource("<internal>/DISK2.rom")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
data, _, err := LoadResource("<internal>/DISK2P6.rom")
|
data, _, err := LoadResource("<internal>/DISK2P6.rom")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// The resource should be internal and never fail
|
return nil, err
|
||||||
panic(err)
|
}
|
||||||
|
c.p6ROM = data
|
||||||
|
|
||||||
|
disk1 := paramsGetString(params, "disk1")
|
||||||
|
if disk1 != "" {
|
||||||
|
err := c.drive[0].insertDiskette(disk1)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
disk2 := paramsGetString(params, "disk2")
|
||||||
|
if disk2 != "" {
|
||||||
|
err := c.drive[1].insertDiskette(disk2)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
trackTracer := paramsGetBool(params, "tracktracer")
|
||||||
|
if trackTracer {
|
||||||
|
c.trackTracer = makeTrackTracerLogger()
|
||||||
|
}
|
||||||
|
return &c, nil
|
||||||
|
},
|
||||||
}
|
}
|
||||||
c.p6ROM = data
|
|
||||||
|
|
||||||
return &c
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetInfo returns card info
|
// GetInfo returns card info
|
||||||
|
@ -79,6 +113,10 @@ func (c *CardDisk2Sequencer) reset() {
|
||||||
c.q = [8]bool{}
|
c.q = [8]bool{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *CardDisk2Sequencer) setTrackTracer(tt trackTracer) {
|
||||||
|
c.trackTracer = tt
|
||||||
|
}
|
||||||
|
|
||||||
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[0])
|
||||||
a.registerRemovableMediaDrive(&c.drive[1])
|
a.registerRemovableMediaDrive(&c.drive[1])
|
||||||
|
|
|
@ -26,11 +26,14 @@ type CardFastChip struct {
|
||||||
configRegister uint8
|
configRegister uint8
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCardFastChip creates a new CardFastChip
|
func newCardFastChipBuilder() *cardBuilder {
|
||||||
func NewCardFastChip() *CardFastChip {
|
return &cardBuilder{
|
||||||
var c CardFastChip
|
name: "FASTChip IIe Card - limited",
|
||||||
c.name = "FASTChip IIe Card - limited"
|
description: "Accelerator card for Apple IIe. Limited support.",
|
||||||
return &c
|
buildFunc: func(params map[string]string) (Card, error) {
|
||||||
|
return &CardFastChip{}, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
13
cardInOut.go
13
cardInOut.go
|
@ -24,11 +24,14 @@ type CardInOut struct {
|
||||||
reader *bufio.Reader
|
reader *bufio.Reader
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCardInOut creates CardInOut
|
func newCardInOutBuilder() *cardBuilder {
|
||||||
func NewCardInOut() *CardInOut {
|
return &cardBuilder{
|
||||||
var c CardInOut
|
name: "InOut test card",
|
||||||
c.name = "Card to test I/O"
|
description: "Card to test I/O",
|
||||||
return &c
|
buildFunc: func(params map[string]string) (Card, error) {
|
||||||
|
return &CardInOut{}, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CardInOut) assign(a *Apple2, slot int) {
|
func (c *CardInOut) assign(a *Apple2, slot int) {
|
||||||
|
|
|
@ -35,11 +35,14 @@ type CardLanguage struct {
|
||||||
altBank bool // false is bank1, true is bank2
|
altBank bool // false is bank1, true is bank2
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCardLanguage creates a new CardLanguage
|
func newCardLanguageBuilder() *cardBuilder {
|
||||||
func NewCardLanguage() *CardLanguage {
|
return &cardBuilder{
|
||||||
var c CardLanguage
|
name: "16 KB Language Card",
|
||||||
c.name = "16KB Language Card"
|
description: "Language card with 16 extra KB for the Apple ][ and ][+",
|
||||||
return &c
|
buildFunc: func(params map[string]string) (Card, error) {
|
||||||
|
return &CardLanguage{}, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
|
@ -13,11 +13,14 @@ type CardLogger struct {
|
||||||
cardBase
|
cardBase
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCardLogger creates a new VidHD card
|
func newCardLoggerBuilder() *cardBuilder {
|
||||||
func NewCardLogger() *CardLogger {
|
return &cardBuilder{
|
||||||
var c CardLogger
|
name: "Softswitch logger card",
|
||||||
c.name = "Softswitch log card"
|
description: "Card to log softswitch accesses",
|
||||||
return &c
|
buildFunc: func(params map[string]string) (Card, error) {
|
||||||
|
return &CardLogger{}, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CardLogger) assign(a *Apple2, slot int) {
|
func (c *CardLogger) assign(a *Apple2, slot int) {
|
||||||
|
|
|
@ -37,30 +37,45 @@ but it will always be “F” when you read it. If the card has more than one
|
||||||
Megabyte of RAM, the top nybble will be a meaningful part of the address.
|
Megabyte of RAM, the top nybble will be a meaningful part of the address.
|
||||||
*/
|
*/
|
||||||
const (
|
const (
|
||||||
memoryExpansionSize256 = 256 * 1024
|
memoryExpansionMask = 0x000fffff // 10 bits, 1MB
|
||||||
memoryExpansionSize512 = 512 * 1024
|
|
||||||
memoryExpansionSize768 = 768 * 1024
|
|
||||||
memoryExpansionSize1024 = 1024 * 1024
|
|
||||||
memoryExpansionMask = 0x000fffff // 10 bits, 1MB
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// CardMemoryExpansion is a Memory Expansion card
|
// CardMemoryExpansion is a Memory Expansion card
|
||||||
type CardMemoryExpansion struct {
|
type CardMemoryExpansion struct {
|
||||||
cardBase
|
cardBase
|
||||||
ram [memoryExpansionSize1024]uint8
|
ram []uint8
|
||||||
index int
|
index int
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCardMemoryExpansion creates a new VidHD card
|
func newCardMemoryExpansionBuilder() *cardBuilder {
|
||||||
func NewCardMemoryExpansion() *CardMemoryExpansion {
|
return &cardBuilder{
|
||||||
var c CardMemoryExpansion
|
name: "Memory Expansion Card",
|
||||||
c.name = "Memory Expansion Card"
|
description: "Memory expansion card. It can be configured to have 256KB, 512KB, 768KB or 1MB.",
|
||||||
c.loadRomFromResource("<internal>/MemoryExpansionCard-341-0344a.bin")
|
defaultParams: &[]paramSpec{
|
||||||
|
{"size", "RAM of the card, can be 256, 512, 768 or 1024", "1024"},
|
||||||
|
},
|
||||||
|
buildFunc: func(params map[string]string) (Card, error) {
|
||||||
|
size, err := paramsGetInt(params, "size")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if size != 256 && size != 512 && size != 768 && size != 1024 {
|
||||||
|
return nil, fmt.Errorf("invalid RAM size %v. It must be 256, 512, 768 or 1024", size)
|
||||||
|
}
|
||||||
|
|
||||||
return &c
|
var c CardMemoryExpansion
|
||||||
|
c.ram = make([]uint8, size*1024)
|
||||||
|
err = c.loadRomFromResource("<internal>/MemoryExpansionCard-341-0344a.bin")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &c, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetInfo returns smartPort info
|
// GetInfo returns card info
|
||||||
func (c *CardMemoryExpansion) GetInfo() map[string]string {
|
func (c *CardMemoryExpansion) GetInfo() map[string]string {
|
||||||
info := make(map[string]string)
|
info := make(map[string]string)
|
||||||
info["size"] = fmt.Sprintf("%vKB", len(c.ram)/1024)
|
info["size"] = fmt.Sprintf("%vKB", len(c.ram)/1024)
|
||||||
|
|
23
cardMouse.go
23
cardMouse.go
|
@ -33,14 +33,21 @@ type CardMouse struct {
|
||||||
trace bool
|
trace bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCardMouse creates a new SmartPort card
|
func newCardMouseBuilder() *cardBuilder {
|
||||||
func NewCardMouse() *CardMouse {
|
return &cardBuilder{
|
||||||
var c CardMouse
|
name: "Mouse Card",
|
||||||
c.name = "Mouse Card"
|
description: "Mouse card implementation. Does not emulate a real card, only the firmware behaviour.",
|
||||||
c.trace = false
|
defaultParams: &[]paramSpec{
|
||||||
c.maxX = 0x3ff
|
{"trace", "Trace accesses to the card", "false"},
|
||||||
c.maxY = 0x3ff
|
},
|
||||||
return &c
|
buildFunc: func(params map[string]string) (Card, error) {
|
||||||
|
return &CardMouse{
|
||||||
|
maxX: 0x3ff,
|
||||||
|
maxY: 0x3ff,
|
||||||
|
trace: paramsGetBool(params, "trace"),
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
|
@ -15,24 +15,39 @@ See:
|
||||||
// CardParallelPrinter represents a Parallel Printer Interface card
|
// CardParallelPrinter represents a Parallel Printer Interface card
|
||||||
type CardParallelPrinter struct {
|
type CardParallelPrinter struct {
|
||||||
cardBase
|
cardBase
|
||||||
file *os.File
|
file *os.File
|
||||||
|
ascii bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCardParallelPrinter creates a new CardParallelPrinter
|
func newCardParallelPrinterBuilder() *cardBuilder {
|
||||||
func NewCardParallelPrinter() *CardParallelPrinter {
|
return &cardBuilder{
|
||||||
var c CardParallelPrinter
|
name: "Parallel Printer Interface",
|
||||||
c.name = "Parallel Printer Interface"
|
description: "Card to dump to a file what would be printed to a parallel printer.",
|
||||||
c.loadRomFromResource("<internal>/Apple II Parallel Printer Interface Card ROM fixed.bin")
|
defaultParams: &[]paramSpec{
|
||||||
return &c
|
{"file", "File to store the printed code", "printer.out"},
|
||||||
|
{"ascii", "Remove the 7 bit. Useful for normal text printing, but breaks graphics printing ", "false"},
|
||||||
|
},
|
||||||
|
buildFunc: func(params map[string]string) (Card, error) {
|
||||||
|
var c CardParallelPrinter
|
||||||
|
c.ascii = paramsGetBool(params, "ascii")
|
||||||
|
filepath := paramsGetPath(params, "file")
|
||||||
|
f, err := os.OpenFile(filepath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
c.file = f
|
||||||
|
|
||||||
|
err = c.loadRomFromResource("<internal>/Apple II Parallel Printer Interface Card ROM fixed.bin")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &c, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CardParallelPrinter) assign(a *Apple2, slot int) {
|
func (c *CardParallelPrinter) assign(a *Apple2, slot int) {
|
||||||
f, err := os.OpenFile(printerFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
c.file = f
|
|
||||||
|
|
||||||
c.addCardSoftSwitchW(0, func(value uint8) {
|
c.addCardSoftSwitchW(0, func(value uint8) {
|
||||||
c.printByte(value)
|
c.printByte(value)
|
||||||
}, "PARALLELDEVW")
|
}, "PARALLELDEVW")
|
||||||
|
@ -44,11 +59,10 @@ func (c *CardParallelPrinter) assign(a *Apple2, slot int) {
|
||||||
c.cardBase.assign(a, slot)
|
c.cardBase.assign(a, slot)
|
||||||
}
|
}
|
||||||
|
|
||||||
const printerFile = "printer.out"
|
|
||||||
|
|
||||||
func (c *CardParallelPrinter) printByte(value uint8) {
|
func (c *CardParallelPrinter) printByte(value uint8) {
|
||||||
|
if c.ascii {
|
||||||
// As text the MSB has to be removed, but if done, graphics modes won't work
|
// As text the MSB has to be removed, but if done, graphics modes won't work
|
||||||
//value = value & 0x7f // Remove the MSB bit
|
value = value & 0x7f // Remove the MSB bit
|
||||||
|
}
|
||||||
c.file.Write([]byte{value})
|
c.file.Write([]byte{value})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
package izapple2
|
package izapple2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
RAMWorks style card on the Apple IIe aus slot.
|
RAMWorks style card on the Apple IIe aus slot.
|
||||||
https://patents.google.com/patent/US4601018
|
https://patents.google.com/patent/US4601018
|
||||||
|
@ -11,8 +16,16 @@ Diagnostics disks:
|
||||||
It's is like the extra 64kb on an Apple IIe 80col 64kb card, but with up to 256 banks
|
It's is like the extra 64kb on an Apple IIe 80col 64kb card, but with up to 256 banks
|
||||||
*/
|
*/
|
||||||
|
|
||||||
func setupRAMWorksCard(a *Apple2, banks int) {
|
func setupRAMWorksCard(a *Apple2, sizeArg string) error {
|
||||||
a.mmu.initExtendedRAM(banks)
|
size, err := strconv.Atoi(sizeArg)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid RamWorks card RAM size: %s", sizeArg)
|
||||||
|
}
|
||||||
|
if size%64 != 0 {
|
||||||
|
return fmt.Errorf("the Ramworks size must be a multiple of 64, %v is not", size)
|
||||||
|
}
|
||||||
|
|
||||||
|
a.mmu.initExtendedRAM(size / 64)
|
||||||
|
|
||||||
ssr := func() uint8 {
|
ssr := func() uint8 {
|
||||||
return a.mmu.extendedRAMBlock
|
return a.mmu.extendedRAMBlock
|
||||||
|
@ -31,4 +44,6 @@ func setupRAMWorksCard(a *Apple2, banks int) {
|
||||||
a.io.addSoftSwitchW(0x73, ssw, "RAMWORKSW")
|
a.io.addSoftSwitchW(0x73, ssw, "RAMWORKSW")
|
||||||
a.io.addSoftSwitchW(0x75, ssw, "RAMWORKSW")
|
a.io.addSoftSwitchW(0x75, ssw, "RAMWORKSW")
|
||||||
a.io.addSoftSwitchW(0x77, ssw, "RAMWORKSW")
|
a.io.addSoftSwitchW(0x77, ssw, "RAMWORKSW")
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,11 +16,14 @@ type CardSaturn struct {
|
||||||
activeBlock uint8
|
activeBlock uint8
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCardSaturn creates a new CardSaturn
|
func newCardSaturnBuilder() *cardBuilder {
|
||||||
func NewCardSaturn() *CardSaturn {
|
return &cardBuilder{
|
||||||
var c CardSaturn
|
name: "Saturn 128KB Ram Card",
|
||||||
c.name = "Saturn 128KB Ram Card"
|
description: "RAM card with 128Kb. It's like 8 language cards.",
|
||||||
return &c
|
buildFunc: func(params map[string]string) (Card, error) {
|
||||||
|
return &CardSaturn{}, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
|
@ -27,11 +27,64 @@ type CardSmartPort struct {
|
||||||
trace bool
|
trace bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCardSmartPort creates a new SmartPort card
|
func newCardSmartPortStorageBuilder() *cardBuilder {
|
||||||
func NewCardSmartPort() *CardSmartPort {
|
return &cardBuilder{
|
||||||
var c CardSmartPort
|
name: "SmartPort",
|
||||||
c.name = "SmartPort Card"
|
description: "SmartPort interface card",
|
||||||
return &c
|
defaultParams: &[]paramSpec{
|
||||||
|
{"image1", "Disk image for unit 1", ""},
|
||||||
|
{"image2", "Disk image for unit 2", ""},
|
||||||
|
{"image3", "Disk image for unit 3", ""},
|
||||||
|
{"image4", "Disk image for unit 4", ""},
|
||||||
|
{"image5", "Disk image for unit 5", ""},
|
||||||
|
{"image6", "Disk image for unit 6", ""},
|
||||||
|
{"image7", "Disk image for unit 7", ""},
|
||||||
|
{"image8", "Disk image for unit 8", ""},
|
||||||
|
{"tracesp", "Trace SmartPort calls", "false"},
|
||||||
|
{"tracehd", "Trace image accesses", "false"},
|
||||||
|
},
|
||||||
|
buildFunc: func(params map[string]string) (Card, error) {
|
||||||
|
var c CardSmartPort
|
||||||
|
c.trace = paramsGetBool(params, "tracesp")
|
||||||
|
traceHD := paramsGetBool(params, "tracehd")
|
||||||
|
for i := 1; i <= 8; i++ {
|
||||||
|
image := paramsGetPath(params, "image"+strconv.Itoa(i))
|
||||||
|
if image != "" {
|
||||||
|
err := c.LoadImage(image, traceHD)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &c, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newCardSmartPortFujinetBuilder() *cardBuilder {
|
||||||
|
return &cardBuilder{
|
||||||
|
name: "Fujinet",
|
||||||
|
description: "SmartPort interface card hosting the Fujinet",
|
||||||
|
defaultParams: &[]paramSpec{
|
||||||
|
{"tracesp", "Trace SmartPort calls", "false"},
|
||||||
|
{"tracenet", "Trace on the network device", "false"},
|
||||||
|
{"traceclock", "Trace on the clock device", "false"},
|
||||||
|
},
|
||||||
|
buildFunc: func(params map[string]string) (Card, error) {
|
||||||
|
var c CardSmartPort
|
||||||
|
c.trace = paramsGetBool(params, "tracesp")
|
||||||
|
|
||||||
|
net := NewSmartPortFujinetNetwork(&c)
|
||||||
|
net.trace = paramsGetBool(params, "tracenet")
|
||||||
|
c.AddDevice(net)
|
||||||
|
|
||||||
|
clock := NewSmartPortFujinetClock(&c)
|
||||||
|
clock.trace = paramsGetBool(params, "traceclock")
|
||||||
|
c.AddDevice(clock)
|
||||||
|
|
||||||
|
return &c, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetInfo returns smartPort info
|
// GetInfo returns smartPort info
|
||||||
|
@ -95,7 +148,7 @@ func (c *CardSmartPort) assign(a *Apple2, slot int) {
|
||||||
}, "HDBLOCKSLO")
|
}, "HDBLOCKSLO")
|
||||||
c.addCardSoftSwitchR(2, func() uint8 {
|
c.addCardSoftSwitchR(2, func() uint8 {
|
||||||
// Blocks available, high byte
|
// Blocks available, high byte
|
||||||
return uint8(c.hardDiskBlocks)
|
return uint8(c.hardDiskBlocks >> 8)
|
||||||
}, "HDBLOCKHI")
|
}, "HDBLOCKHI")
|
||||||
|
|
||||||
c.addCardSoftSwitchR(3, func() uint8 {
|
c.addCardSoftSwitchR(3, func() uint8 {
|
||||||
|
|
38
cardSwyft.go
38
cardSwyft.go
|
@ -20,8 +20,8 @@ ROM. This permits the SwyftCard program to take over the system at
|
||||||
power-on and run the SwyftCard program. (Please refer to the
|
power-on and run the SwyftCard program. (Please refer to the
|
||||||
schematic.)
|
schematic.)
|
||||||
|
|
||||||
The lM311 voltage comparator is connected to provide the power-on
|
The LM311 voltage comparator is connected to provide the power-on
|
||||||
reset function. When the Apple lie is first turned on, the power-on
|
reset function. When the Apple lIe is first turned on, the power-on
|
||||||
reset circuit resets the PAL, turning on the SwyftCard and disabling
|
reset circuit resets the PAL, turning on the SwyftCard and disabling
|
||||||
the Apple IIe internal ROM. The power-on reset circuit must be
|
the Apple IIe internal ROM. The power-on reset circuit must be
|
||||||
provided because the existing Apple IIe reset function is used by
|
provided because the existing Apple IIe reset function is used by
|
||||||
|
@ -53,6 +53,9 @@ Apple /Ie asserts the IINH' signal there will not be a bus contention.
|
||||||
However, there will be a bus contention on the data bus if another card
|
However, there will be a bus contention on the data bus if another card
|
||||||
attempts to control the bus while the SwyftCard is active.
|
attempts to control the bus while the SwyftCard is active.
|
||||||
|
|
||||||
|
The Cx00 rom is not used. The card is expected to be installed in
|
||||||
|
slot 3 of an Apple IIe with the 80 column firmware already present.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// CardSwyft represents a Swyft card
|
// CardSwyft represents a Swyft card
|
||||||
|
@ -62,25 +65,26 @@ type CardSwyft struct {
|
||||||
rom []uint8
|
rom []uint8
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCardSwyft creates a new CardSwyft
|
func newCardSwyftBuilder() *cardBuilder {
|
||||||
func NewCardSwyft() *CardSwyft {
|
return &cardBuilder{
|
||||||
var c CardSwyft
|
name: "SwyftCard",
|
||||||
c.name = "SwyftCard"
|
description: "Card with the ROM needed to run the Swyftcard word processing system. Must run on slot 3 only on Apple IIe.",
|
||||||
|
requiresIIe: true,
|
||||||
|
buildFunc: func(params map[string]string) (Card, error) {
|
||||||
|
var c CardSwyft
|
||||||
|
|
||||||
// The Cx00 rom is not used. The card is expected to be installed in
|
// Load main ROM replacement
|
||||||
// slot 3 of an Apple IIe with the 80 column firmware already present.
|
data, _, err := LoadResource("<internal>/SwyftCard ROM.bin")
|
||||||
return &c
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
c.rom = data
|
||||||
|
return &c, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CardSwyft) assign(a *Apple2, slot int) {
|
func (c *CardSwyft) assign(a *Apple2, slot int) {
|
||||||
// Load main ROM replacement
|
|
||||||
data, _, err := LoadResource("<internal>/SwyftCard ROM.bin")
|
|
||||||
if err != nil {
|
|
||||||
// The resource should be internal and never fail
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
c.rom = data
|
|
||||||
|
|
||||||
c.addCardSoftSwitchRW(0, func() uint8 {
|
c.addCardSoftSwitchRW(0, func() uint8 {
|
||||||
a.mmu.inhibitROM(c)
|
a.mmu.inhibitROM(c)
|
||||||
c.bank2 = false
|
c.bank2 = false
|
||||||
|
|
|
@ -6,11 +6,9 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSwyftTutorial(t *testing.T) {
|
func TestSwyftTutorial(t *testing.T) {
|
||||||
at := makeApple2Tester("2e")
|
at, err := makeApple2Tester("swyft", nil)
|
||||||
at.a.AddSwyftCard()
|
|
||||||
err := at.a.AddDisk2(6, "<internal>/SwyftWare_-_SwyftCard_Tutorial.woz", "", nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
at.terminateCondition = func(a *Apple2) bool {
|
at.terminateCondition = func(a *Apple2) bool {
|
||||||
|
|
|
@ -25,15 +25,24 @@ uPD1990AC hookup:
|
||||||
type CardThunderClockPlus struct {
|
type CardThunderClockPlus struct {
|
||||||
cardBase
|
cardBase
|
||||||
upd1990 component.MicroPD1990ac
|
upd1990 component.MicroPD1990ac
|
||||||
//component.microPD1990ac
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCardThunderClockPlus creates a new CardThunderClockPlus
|
func newCardThunderClockPlusBuilder() *cardBuilder {
|
||||||
func NewCardThunderClockPlus() *CardThunderClockPlus {
|
return &cardBuilder{
|
||||||
var c CardThunderClockPlus
|
name: "ThunderClock+ Card",
|
||||||
c.name = "ThunderClock+ Card"
|
description: "Clock card",
|
||||||
c.loadRomFromResource("<internal>/ThunderclockPlusROM.bin")
|
defaultParams: &[]paramSpec{
|
||||||
return &c
|
{"rom", "ROM file to load", "<internal>/ThunderclockPlusROM.bin"},
|
||||||
|
},
|
||||||
|
buildFunc: func(params map[string]string) (Card, error) {
|
||||||
|
var c CardThunderClockPlus
|
||||||
|
err := c.loadRomFromResource("<internal>/ThunderclockPlusROM.bin")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &c, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CardThunderClockPlus) assign(a *Apple2, slot int) {
|
func (c *CardThunderClockPlus) assign(a *Apple2, slot int) {
|
||||||
|
|
16
cardVidHD.go
16
cardVidHD.go
|
@ -13,12 +13,16 @@ type CardVidHD struct {
|
||||||
cardBase
|
cardBase
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCardVidHD creates a new VidHD card
|
func newCardVidHDBuilder() *cardBuilder {
|
||||||
func NewCardVidHD() *CardVidHD {
|
return &cardBuilder{
|
||||||
var c CardVidHD
|
name: "VidHD Card - limited",
|
||||||
c.name = "VidHD Card - limited"
|
description: "Firmware signature of the VidHD card to trick Total Replay to use the GS modes.",
|
||||||
c.loadRom(buildVidHDRom())
|
buildFunc: func(params map[string]string) (Card, error) {
|
||||||
return &c
|
var c CardVidHD
|
||||||
|
c.loadRom(buildVidHDRom())
|
||||||
|
return &c, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildVidHDRom() []uint8 {
|
func buildVidHDRom() []uint8 {
|
||||||
|
|
36
cardVidex.go
36
cardVidex.go
|
@ -30,19 +30,32 @@ type CardVidex struct {
|
||||||
charGen []uint8
|
charGen []uint8
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCardVidex creates a new CardVidex
|
func newCardVidexBuilder() *cardBuilder {
|
||||||
func NewCardVidex() *CardVidex {
|
return &cardBuilder{
|
||||||
var c CardVidex
|
name: "Videx 80 columns Card",
|
||||||
c.name = "Videx 80 col Card"
|
description: "Videx compatible 80 columns card",
|
||||||
|
defaultParams: &[]paramSpec{
|
||||||
|
{"rom", "ROM file to load", "<internal>/Videx Videoterm ROM 2.4.bin"},
|
||||||
|
{"charmap", "Character map file to load", "<internal>/80ColumnP110.BIN"},
|
||||||
|
},
|
||||||
|
buildFunc: func(params map[string]string) (Card, error) {
|
||||||
|
var c CardVidex
|
||||||
|
|
||||||
// The C800 area has ROM and RAM
|
// The C800 area has ROM and RAM
|
||||||
c.loadRomFromResource("<internal>/Videx Videoterm ROM 2.4.bin")
|
err := c.loadRomFromResource("<internal>/Videx Videoterm ROM 2.4.bin")
|
||||||
c.upperROM = c.romC8xx
|
if err != nil {
|
||||||
c.romC8xx = &c
|
return nil, err
|
||||||
|
}
|
||||||
|
c.upperROM = c.romC8xx
|
||||||
|
c.romC8xx = &c
|
||||||
|
|
||||||
// The resource should be internal and never fail
|
err = c.loadCharacterMap(paramsGetPath(params, "charmap"))
|
||||||
c.loadCharacterMap("<internal>/80ColumnP110.BIN")
|
if err != nil {
|
||||||
return &c
|
return nil, err
|
||||||
|
}
|
||||||
|
return &c, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CardVidex) loadCharacterMap(filename string) error {
|
func (c *CardVidex) loadCharacterMap(filename string) error {
|
||||||
|
@ -88,6 +101,7 @@ func (c *CardVidex) assign(a *Apple2, slot int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
c.cardBase.assign(a, slot)
|
c.cardBase.assign(a, slot)
|
||||||
|
a.softVideoSwitch = NewSoftVideoSwitch(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
const videxRomLimit = uint16(0xcc00)
|
const videxRomLimit = uint16(0xcc00)
|
||||||
|
|
|
@ -2,6 +2,7 @@ package izapple2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -33,14 +34,10 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewCharacterGenerator instantiates a new Character Generator with the rom on the file given
|
// NewCharacterGenerator instantiates a new Character Generator with the rom on the file given
|
||||||
func newCharacterGenerator(filename string, order charColumnMap, isApple2e bool) (*CharacterGenerator, error) {
|
func newCharacterGenerator(filename string, order charColumnMap, pageSize int) (*CharacterGenerator, error) {
|
||||||
var cg CharacterGenerator
|
var cg CharacterGenerator
|
||||||
cg.columnMap = order
|
cg.columnMap = order
|
||||||
cg.pageSize = charGenPageSize2Plus
|
cg.pageSize = pageSize
|
||||||
if isApple2e {
|
|
||||||
cg.pageSize = charGenPageSize2E
|
|
||||||
}
|
|
||||||
|
|
||||||
err := cg.load(filename)
|
err := cg.load(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -82,3 +79,29 @@ func (cg *CharacterGenerator) getPixel(char uint8, row int, column int) bool {
|
||||||
value := bits >> uint(bit) & 1
|
value := bits >> uint(bit) & 1
|
||||||
return value == 1
|
return value == 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setupCharactedGenerator(a *Apple2, board string, charRomFile string) error {
|
||||||
|
var charGenMap charColumnMap
|
||||||
|
initialCharGenPage := 0
|
||||||
|
pageSize := charGenPageSize2Plus
|
||||||
|
switch board {
|
||||||
|
case "2plus":
|
||||||
|
charGenMap = charGenColumnsMap2Plus
|
||||||
|
case "2e":
|
||||||
|
charGenMap = charGenColumnsMap2e
|
||||||
|
pageSize = charGenPageSize2E
|
||||||
|
case "base64a":
|
||||||
|
charGenMap = charGenColumnsMapBase64a
|
||||||
|
initialCharGenPage = 1
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("board %s not supported it must be '2plus', '2e' or 'base64a'", board)
|
||||||
|
}
|
||||||
|
|
||||||
|
cg, err := newCharacterGenerator(charRomFile, charGenMap, pageSize)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cg.setPage(initialCharGenPage)
|
||||||
|
a.cg = cg
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
name: Apple IIe
|
||||||
|
parent: _base
|
||||||
|
board: 2e
|
||||||
|
rom: <internal>/Apple2e.rom
|
||||||
|
charrom: <internal>/Apple IIe Video Unenhanced.bin
|
||||||
|
s0: language
|
||||||
|
s2: vidhd
|
||||||
|
s3: fastchip
|
||||||
|
s4: mouse
|
||||||
|
s6: diskii,disk1=<internal>/dos33.dsk
|
|
@ -0,0 +1,13 @@
|
||||||
|
name: Apple IIe
|
||||||
|
parent: _base
|
||||||
|
board: 2e
|
||||||
|
cpu: 65c02
|
||||||
|
rom: <internal>/Apple2e_Enhanced.rom
|
||||||
|
charrom: <internal>/Apple IIe Video Enhanced.bin
|
||||||
|
ramworks: 8192
|
||||||
|
nsc: main
|
||||||
|
s0: language
|
||||||
|
s2: vidhd
|
||||||
|
s3: fastchip
|
||||||
|
s4: mouse
|
||||||
|
s6: diskii,disk1=<internal>/dos33.dsk
|
|
@ -0,0 +1,8 @@
|
||||||
|
name: Apple ][+
|
||||||
|
parent: _base
|
||||||
|
board: 2plus
|
||||||
|
rom: <internal>/Apple2_Plus.rom
|
||||||
|
charrom: <internal>/Apple2rev7CharGen.rom
|
||||||
|
forceCaps: true
|
||||||
|
s0: language
|
||||||
|
s6: diskii,disk1=<internal>/dos33.dsk
|
|
@ -0,0 +1,19 @@
|
||||||
|
name: No name
|
||||||
|
cpu: 6502
|
||||||
|
speed: ntsc
|
||||||
|
profile: false
|
||||||
|
forceCaps: false
|
||||||
|
ramworks: none
|
||||||
|
nsc: none
|
||||||
|
rgb: false
|
||||||
|
romx: false
|
||||||
|
chargenmap: 2e
|
||||||
|
trace: none
|
||||||
|
s0: empty
|
||||||
|
s1: empty
|
||||||
|
s2: empty
|
||||||
|
s3: empty
|
||||||
|
s4: empty
|
||||||
|
s5: empty
|
||||||
|
s6: empty
|
||||||
|
s7: empty
|
|
@ -0,0 +1,8 @@
|
||||||
|
name: Base 64A
|
||||||
|
parent: _base
|
||||||
|
board: base64a
|
||||||
|
rom: <custom>
|
||||||
|
charrom: <internal>/BASE64A_ROM7_CharGen.BIN
|
||||||
|
s0: language
|
||||||
|
s1: parallel
|
||||||
|
s6: diskii,disk1=<internal>/dos33.dsk
|
|
@ -0,0 +1,4 @@
|
||||||
|
name: swyft
|
||||||
|
parent: 2enh
|
||||||
|
s3: swyftcard
|
||||||
|
s6: diskii,disk1=<internal>/SwyftWare_-_SwyftCard_Tutorial.woz
|
|
@ -0,0 +1,283 @@
|
||||||
|
package izapple2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const configSuffix = ".cfg"
|
||||||
|
const defaultConfiguration = "2enh"
|
||||||
|
|
||||||
|
const (
|
||||||
|
confParent = "parent"
|
||||||
|
confModel = "model"
|
||||||
|
confName = "name"
|
||||||
|
confBoard = "board"
|
||||||
|
|
||||||
|
confRom = "rom"
|
||||||
|
confCharRom = "charrom"
|
||||||
|
confCpu = "cpu"
|
||||||
|
confSpeed = "speed"
|
||||||
|
confRamworks = "ramworks"
|
||||||
|
confNsc = "nsc"
|
||||||
|
confTrace = "trace"
|
||||||
|
confProfile = "profile"
|
||||||
|
confForceCaps = "forceCaps"
|
||||||
|
confRgb = "rgb"
|
||||||
|
confRomx = "romx"
|
||||||
|
confS0 = "s0"
|
||||||
|
confS1 = "s1"
|
||||||
|
confS2 = "s2"
|
||||||
|
confS3 = "s3"
|
||||||
|
confS4 = "s4"
|
||||||
|
confS5 = "s5"
|
||||||
|
confS6 = "s6"
|
||||||
|
confS7 = "s7"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed configs/*.cfg
|
||||||
|
var configurationFiles embed.FS
|
||||||
|
|
||||||
|
type configurationModels struct {
|
||||||
|
preconfiguredConfigs map[string]*configuration
|
||||||
|
}
|
||||||
|
|
||||||
|
type configuration struct {
|
||||||
|
data map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newConfiguration() *configuration {
|
||||||
|
c := configuration{}
|
||||||
|
c.data = make(map[string]string)
|
||||||
|
return &c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *configuration) getHas(key string) (string, bool) {
|
||||||
|
key = strings.ToLower(key)
|
||||||
|
value, ok := c.data[key]
|
||||||
|
return value, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *configuration) get(key string) string {
|
||||||
|
key = strings.ToLower(key)
|
||||||
|
value, ok := c.data[key]
|
||||||
|
if !ok {
|
||||||
|
// Should not happen
|
||||||
|
panic(fmt.Errorf("key %s not found", key))
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *configuration) getFlag(key string) bool {
|
||||||
|
return c.get(key) == "true"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *configuration) set(key string, value string) {
|
||||||
|
key = strings.ToLower(key)
|
||||||
|
c.data[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
func initConfigurationModels() (*configurationModels, error) {
|
||||||
|
models := configurationModels{}
|
||||||
|
dir, err := configurationFiles.ReadDir("configs")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
models.preconfiguredConfigs = make(map[string]*configuration)
|
||||||
|
for _, file := range dir {
|
||||||
|
if file.Type().IsRegular() && strings.HasSuffix(strings.ToLower(file.Name()), configSuffix) {
|
||||||
|
content, err := configurationFiles.ReadFile("configs/" + file.Name())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
lines := strings.Split(string(content), "\n")
|
||||||
|
config := newConfiguration()
|
||||||
|
for iLine, line := range lines {
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
if line == "" || strings.HasPrefix(line, "#") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
colonPos := strings.Index(line, ":")
|
||||||
|
if colonPos < 0 {
|
||||||
|
return nil, fmt.Errorf("invalid configuration in %s:%d", file.Name(), iLine)
|
||||||
|
}
|
||||||
|
key := strings.TrimSpace(line[:colonPos])
|
||||||
|
value := strings.TrimSpace(line[colonPos+1:])
|
||||||
|
config.data[key] = value
|
||||||
|
}
|
||||||
|
name_no_ext := file.Name()[:len(file.Name())-len(configSuffix)]
|
||||||
|
models.preconfiguredConfigs[name_no_ext] = config
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check validity of base configuration
|
||||||
|
/* base, ok := configs.preconfiguredConfigs[baseConfigurationName]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("base configuration %s.cfg not found", baseConfigurationName)
|
||||||
|
}
|
||||||
|
model, ok := base[argModel]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("model not found in base configuration %s.cfg", baseConfigurationName)
|
||||||
|
}
|
||||||
|
if _, ok := configs.preconfiguredConfigs[model]; !ok {
|
||||||
|
return nil, fmt.Errorf("model %s not found and used in base configuration %s.cfg", model, baseConfigurationName)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Todo check that all configs have valid keys
|
||||||
|
|
||||||
|
return &models, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func mergeConfigs(base *configuration, addition *configuration) *configuration {
|
||||||
|
result := newConfiguration()
|
||||||
|
for k, v := range base.data {
|
||||||
|
result.set(k, v)
|
||||||
|
}
|
||||||
|
for k, v := range addition.data {
|
||||||
|
result.set(k, v)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *configurationModels) getFromModel(name string) (*configuration, error) {
|
||||||
|
name = strings.TrimSpace(name)
|
||||||
|
config, ok := c.preconfiguredConfigs[name]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("configuration %s.cfg not found", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
parentName, hasParent := config.getHas(confParent)
|
||||||
|
if !hasParent {
|
||||||
|
return config, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
parent, err := c.getFromModel(parentName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result := mergeConfigs(parent, config)
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *configurationModels) availableModels() []string {
|
||||||
|
models := make([]string, 0, len(c.preconfiguredConfigs)-1)
|
||||||
|
for name := range c.preconfiguredConfigs {
|
||||||
|
if !strings.HasPrefix(name, "_") {
|
||||||
|
models = append(models, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return models
|
||||||
|
}
|
||||||
|
|
||||||
|
func getConfigurationFromModel(model string, overrides *configuration) (*configuration, error) {
|
||||||
|
configurationModels, err := initConfigurationModels()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
configValues, err := configurationModels.getFromModel(model)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if overrides != nil {
|
||||||
|
configValues = mergeConfigs(configValues, overrides)
|
||||||
|
}
|
||||||
|
return configValues, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getConfigurationFromCommandLine() (*configuration, string, error) {
|
||||||
|
configurationModels, err := initConfigurationModels()
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
paramDescription := map[string]string{
|
||||||
|
confModel: "set base model",
|
||||||
|
confRom: "main rom file",
|
||||||
|
confCharRom: "rom file for the character generator",
|
||||||
|
confCpu: "cpu type, can be '6502' or '65c02'",
|
||||||
|
confSpeed: "cpu speed in Mhz, can be 'ntsc', 'pal', 'full' or a decimal nunmber",
|
||||||
|
confRamworks: "memory to use with RAMWorks card, max is 16384",
|
||||||
|
confNsc: "add a DS1216 No-Slot-Clock on the main ROM (use 'main') or a slot ROM",
|
||||||
|
confTrace: "trace CPU execution with one or more comma separated tracers",
|
||||||
|
confProfile: "generate profile trace to analyse with pprof",
|
||||||
|
confForceCaps: "force all letters to be uppercased (no need for caps lock!)",
|
||||||
|
confRgb: "emulate the RGB modes of the 80col RGB card for DHGR",
|
||||||
|
confRomx: "emulate a RomX",
|
||||||
|
confS0: "slot 0 configuration.",
|
||||||
|
confS1: "slot 1 configuration.",
|
||||||
|
confS2: "slot 2 configuration.",
|
||||||
|
confS3: "slot 3 configuration.",
|
||||||
|
confS4: "slot 4 configuration.",
|
||||||
|
confS5: "slot 5 configuration.",
|
||||||
|
confS6: "slot 6 configuration.",
|
||||||
|
confS7: "slot 7 configuration.",
|
||||||
|
}
|
||||||
|
|
||||||
|
stringParams := []string{
|
||||||
|
confRom, confCharRom, confCpu, confSpeed, confRamworks, confNsc, confTrace, confModel,
|
||||||
|
confS0, confS1, confS2, confS3, confS4, confS5, confS6, confS7,
|
||||||
|
}
|
||||||
|
boolParams := []string{confProfile, confForceCaps, confRgb, confRomx}
|
||||||
|
|
||||||
|
configuration, err := configurationModels.getFromModel(defaultConfiguration)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
configuration.set(confModel, defaultConfiguration)
|
||||||
|
|
||||||
|
for _, name := range stringParams {
|
||||||
|
defaultValue, ok := configuration.getHas(name)
|
||||||
|
if !ok {
|
||||||
|
return nil, "", fmt.Errorf("default value not found for %s", name)
|
||||||
|
}
|
||||||
|
flag.String(name, defaultValue, paramDescription[name])
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, name := range boolParams {
|
||||||
|
defaultValue, ok := configuration.getHas(name)
|
||||||
|
if !ok {
|
||||||
|
return nil, "", fmt.Errorf("default value not found for %s", name)
|
||||||
|
}
|
||||||
|
flag.Bool(name, defaultValue == "true", paramDescription[name])
|
||||||
|
}
|
||||||
|
|
||||||
|
flag.Usage = func() {
|
||||||
|
availableModels := strings.Join(configurationModels.availableModels(), ", ")
|
||||||
|
availableCards := strings.Join(availableCards(), ", ")
|
||||||
|
availableTracers := strings.Join(availableTracers(), ", ")
|
||||||
|
|
||||||
|
out := flag.CommandLine.Output()
|
||||||
|
fmt.Fprintf(out, "Usage: %s [file]\n", os.Args[0])
|
||||||
|
fmt.Fprintf(out, " file\n")
|
||||||
|
fmt.Fprintf(out, " path to image to use on the boot device\n")
|
||||||
|
flag.PrintDefaults()
|
||||||
|
fmt.Fprintf(out, "\nThe available pre configured models are: %s.\n", availableModels)
|
||||||
|
fmt.Fprintf(out, "The available cards are: %s.\n", availableCards)
|
||||||
|
fmt.Fprintf(out, "The available tracers are: %s.\n", availableTracers)
|
||||||
|
}
|
||||||
|
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
modelFlag := flag.Lookup(confModel)
|
||||||
|
if modelFlag != nil && strings.TrimSpace(modelFlag.Value.String()) != defaultConfiguration {
|
||||||
|
// Replace the model
|
||||||
|
configuration, err = configurationModels.getFromModel(modelFlag.Value.String())
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
flag.Visit(func(f *flag.Flag) {
|
||||||
|
configuration.set(f.Name, f.Value.String())
|
||||||
|
})
|
||||||
|
|
||||||
|
return configuration, flag.Arg(0), nil
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
package izapple2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestConfigurationModel(t *testing.T) {
|
||||||
|
|
||||||
|
t.Run("test that the default model exists", func(t *testing.T) {
|
||||||
|
models, err := initConfigurationModels()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
_, err = models.getFromModel(defaultConfiguration)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("test preconfigured models are complete", func(t *testing.T) {
|
||||||
|
models, err := initConfigurationModels()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
requiredFields := []string{
|
||||||
|
confRom, confCharRom, confCpu, confSpeed, confRamworks, confNsc,
|
||||||
|
confTrace, confProfile, confForceCaps, confRgb, confRomx,
|
||||||
|
confS0, confS1, confS2, confS3, confS4, confS5, confS6, confS7,
|
||||||
|
}
|
||||||
|
availabledModels := models.availableModels()
|
||||||
|
for _, modelName := range availabledModels {
|
||||||
|
model, err := models.getFromModel(modelName)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, field := range requiredFields {
|
||||||
|
if _, ok := model.getHas(field); !ok {
|
||||||
|
t.Errorf("missing field '%s' in the preconfigured model '%s'", field, modelName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
|
@ -5,88 +5,49 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestPlusBoots(t *testing.T) {
|
func testBoots(t *testing.T, model string, disk string, cycles uint64, banner string, prompt string) {
|
||||||
at := makeApple2Tester("2plus")
|
overrides := newConfiguration()
|
||||||
|
if disk != "" {
|
||||||
|
overrides.set(confS6, "diskii,disk1=\""+disk+"\"")
|
||||||
|
} else {
|
||||||
|
overrides.set(confS6, "empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
at, err := makeApple2Tester(model, overrides)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
at.terminateCondition = func(a *Apple2) bool {
|
at.terminateCondition = func(a *Apple2) bool {
|
||||||
return a.cpu.GetCycles() > 200_000
|
return a.cpu.GetCycles() > cycles
|
||||||
}
|
}
|
||||||
at.run()
|
at.run()
|
||||||
|
|
||||||
text := at.getText()
|
text := at.getText()
|
||||||
if !strings.Contains(text, "APPLE ][") {
|
if !strings.Contains(text, banner) {
|
||||||
t.Errorf("Expected 'APPLE ][', got '%s'", text)
|
t.Errorf("Expected '%s', got '%s'", banner, text)
|
||||||
}
|
}
|
||||||
if !strings.Contains(text, "\n]") {
|
if !strings.Contains(text, prompt) {
|
||||||
t.Errorf("Expected ] prompt, got '%s'", text)
|
t.Errorf("Expected prompt '%s', got '%s'", prompt, text)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPlusBoots(t *testing.T) {
|
||||||
|
testBoots(t, "2plus", "", 200_000, "APPLE ][", "\n]")
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test2EBoots(t *testing.T) {
|
func Test2EBoots(t *testing.T) {
|
||||||
at := makeApple2Tester("2e")
|
testBoots(t, "2e", "", 200_000, "Apple ][", "\n]")
|
||||||
at.terminateCondition = func(a *Apple2) bool {
|
|
||||||
return a.cpu.GetCycles() > 200_000
|
|
||||||
}
|
|
||||||
at.run()
|
|
||||||
|
|
||||||
text := at.getText()
|
|
||||||
if !strings.Contains(text, "Apple ][") {
|
|
||||||
t.Errorf("Expected 'Apple ][', got '%s'", text)
|
|
||||||
}
|
|
||||||
if !strings.Contains(text, "\n]") {
|
|
||||||
t.Errorf("Expected ] prompt, got '%s'", text)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test2EnhancedBoots(t *testing.T) {
|
func Test2EnhancedBoots(t *testing.T) {
|
||||||
at := makeApple2Tester("2enh")
|
testBoots(t, "2enh", "", 200_000, "Apple //e", "\n]")
|
||||||
at.terminateCondition = func(a *Apple2) bool {
|
|
||||||
return a.cpu.GetCycles() > 200_000
|
|
||||||
}
|
|
||||||
at.run()
|
|
||||||
|
|
||||||
text := at.getText()
|
|
||||||
if !strings.Contains(text, "Apple //e") {
|
|
||||||
t.Errorf("Expected 'Apple //e', got '%s'", text)
|
|
||||||
}
|
|
||||||
if !strings.Contains(text, "\n]") {
|
|
||||||
t.Errorf("Expected ] prompt, got '%s'", text)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBase64Boots(t *testing.T) {
|
func TestBase64Boots(t *testing.T) {
|
||||||
at := makeApple2Tester("base64a")
|
testBoots(t, "base64a", "", 1_000_000, "BASE 64A", "\n]")
|
||||||
at.terminateCondition = func(a *Apple2) bool {
|
|
||||||
return a.cpu.GetCycles() > 1_000_000
|
|
||||||
}
|
|
||||||
at.run()
|
|
||||||
|
|
||||||
text := at.getText()
|
|
||||||
if !strings.Contains(text, "BASE 64A") {
|
|
||||||
t.Errorf("Expected 'BASE 64A', got '%s'", text)
|
|
||||||
}
|
|
||||||
if !strings.Contains(text, "\n]") {
|
|
||||||
t.Errorf("Expected ] prompt, got '%s'", text)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPlusDOS33Boots(t *testing.T) {
|
func TestPlusDOS33Boots(t *testing.T) {
|
||||||
at := makeApple2Tester("2plus")
|
testBoots(t, "2plus", "<internal>/dos33.dsk", 100_000_000, "DOS VERSION 3.3", "\n]")
|
||||||
|
|
||||||
err := at.a.AddDisk2(6, "<internal>/dos33.dsk", "", nil)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
at.terminateCondition = func(a *Apple2) bool {
|
|
||||||
return a.cpu.GetCycles() > 100_000_000
|
|
||||||
}
|
|
||||||
at.run()
|
|
||||||
|
|
||||||
text := at.getText()
|
|
||||||
if !strings.Contains(text, "DOS VERSION 3.3") {
|
|
||||||
t.Errorf("Expected 'APPLE ][', got '%s'", text)
|
|
||||||
}
|
|
||||||
if !strings.Contains(text, "\n]") {
|
|
||||||
t.Errorf("Expected ] prompt, got '%s'", text)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,19 +5,25 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func testWoz(t *testing.T, sequencer bool, file string, expectedTracks []int, cycleLimit uint64) {
|
func testWoz(t *testing.T, sequencer bool, file string, expectedTracks []int, cycleLimit uint64) {
|
||||||
at := makeApple2Tester("2enh")
|
|
||||||
tt := makeTrackTracerSummary()
|
|
||||||
|
|
||||||
var err error
|
overrides := newConfiguration()
|
||||||
if sequencer {
|
if sequencer {
|
||||||
err = at.a.AddDisk2Sequencer(6, "woz_test_images/"+file, "", tt)
|
overrides.set(confS6, "diskiiseq,disk1=\"woz_test_images/"+file+"\"")
|
||||||
} else {
|
} else {
|
||||||
err = at.a.AddDisk2(6, "woz_test_images/"+file, "", tt)
|
overrides.set(confS6, "diskii,disk1=\"woz_test_images/"+file+"\"")
|
||||||
}
|
}
|
||||||
|
at, err := makeApple2Tester("2enh", overrides)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
diskIIcard, ok := at.a.cards[6].(cardDisk2Shared)
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("Not a disk II card")
|
||||||
|
}
|
||||||
|
tt := makeTrackTracerSummary()
|
||||||
|
diskIIcard.setTrackTracer(tt)
|
||||||
|
|
||||||
expectedLen := len(expectedTracks)
|
expectedLen := len(expectedTracks)
|
||||||
|
|
||||||
at.terminateCondition = func(a *Apple2) bool {
|
at.terminateCondition = func(a *Apple2) bool {
|
||||||
|
|
|
@ -40,7 +40,11 @@ func (s *state) DefaultTitle() string {
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var s state
|
var s state
|
||||||
s.a = izapple2.MainApple()
|
var err error
|
||||||
|
s.a, err = izapple2.CreateConfiguredApple()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error: %v\n", err)
|
||||||
|
}
|
||||||
if s.a != nil {
|
if s.a != nil {
|
||||||
if s.a.IsProfiling() {
|
if s.a.IsProfiling() {
|
||||||
// See the log with:
|
// See the log with:
|
||||||
|
|
|
@ -13,7 +13,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
a := izapple2.MainApple()
|
a, err := izapple2.CreateConfiguredApple()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error: %v\n", err)
|
||||||
|
}
|
||||||
if a != nil {
|
if a != nil {
|
||||||
if a.IsProfiling() {
|
if a.IsProfiling() {
|
||||||
// See the log with:
|
// See the log with:
|
||||||
|
|
|
@ -11,7 +11,11 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
a := apple2.MainApple()
|
a, err := apple2.CreateConfiguredApple()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error: %v\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
fe := &ansiConsoleFrontend{}
|
fe := &ansiConsoleFrontend{}
|
||||||
a.SetKeyboardProvider(fe)
|
a.SetKeyboardProvider(fe)
|
||||||
go fe.textModeGoRoutine(a)
|
go fe.textModeGoRoutine(a)
|
||||||
|
|
|
@ -14,7 +14,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
a := izapple2.MainApple()
|
a, err := izapple2.CreateConfiguredApple()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error: %v\n", err)
|
||||||
|
}
|
||||||
fe := &headLessFrontend{}
|
fe := &headLessFrontend{}
|
||||||
fe.keyChannel = make(chan uint8, 200)
|
fe.keyChannel = make(chan uint8, 200)
|
||||||
a.SetKeyboardProvider(fe)
|
a.SetKeyboardProvider(fe)
|
||||||
|
|
32
go.mod
32
go.mod
|
@ -11,20 +11,36 @@ require (
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||||
github.com/fredbi/uri v0.0.0-20181227131451-3dcfdacbaaf3 // indirect
|
github.com/fredbi/uri v0.0.0-20181227131451-3dcfdacbaaf3 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.5.1 // indirect
|
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||||
github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 // indirect
|
github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 // indirect
|
||||||
github.com/godbus/dbus/v5 v5.1.0 // indirect
|
github.com/godbus/dbus/v5 v5.1.0 // indirect
|
||||||
github.com/goki/freetype v0.0.0-20220119013949-7a161fd3728c // indirect
|
github.com/goki/freetype v0.0.0-20220119013949-7a161fd3728c // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
|
github.com/magiconair/properties v1.8.7 // indirect
|
||||||
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
|
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||||
|
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||||
|
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||||
|
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||||
|
github.com/spf13/afero v1.11.0 // indirect
|
||||||
|
github.com/spf13/cast v1.6.0 // indirect
|
||||||
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
|
github.com/spf13/viper v1.18.2 // indirect
|
||||||
github.com/srwiley/oksvg v0.0.0-20220128195007-1f435e4c2b44 // indirect
|
github.com/srwiley/oksvg v0.0.0-20220128195007-1f435e4c2b44 // indirect
|
||||||
github.com/srwiley/rasterx v0.0.0-20220128185129-2efea2b9ea41 // indirect
|
github.com/srwiley/rasterx v0.0.0-20220128185129-2efea2b9ea41 // indirect
|
||||||
github.com/stretchr/testify v1.7.1 // indirect
|
github.com/stretchr/testify v1.8.4 // indirect
|
||||||
|
github.com/subosito/gotenv v1.6.0 // indirect
|
||||||
github.com/yuin/goldmark v1.4.13 // indirect
|
github.com/yuin/goldmark v1.4.13 // indirect
|
||||||
|
go.uber.org/atomic v1.9.0 // indirect
|
||||||
|
go.uber.org/multierr v1.9.0 // indirect
|
||||||
|
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
||||||
golang.org/x/image v0.5.0 // indirect
|
golang.org/x/image v0.5.0 // indirect
|
||||||
golang.org/x/net v0.17.0 // indirect
|
golang.org/x/net v0.19.0 // indirect
|
||||||
golang.org/x/sys v0.13.0 // indirect
|
golang.org/x/sys v0.15.0 // indirect
|
||||||
golang.org/x/text v0.13.0 // indirect
|
golang.org/x/text v0.14.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.0 // indirect
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|
51
go.sum
51
go.sum
|
@ -8,11 +8,15 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||||
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/fredbi/uri v0.0.0-20181227131451-3dcfdacbaaf3 h1:FDqhDm7pcsLhhWl1QtD8vlzI4mm59llRvNzrFg6/LAA=
|
github.com/fredbi/uri v0.0.0-20181227131451-3dcfdacbaaf3 h1:FDqhDm7pcsLhhWl1QtD8vlzI4mm59llRvNzrFg6/LAA=
|
||||||
github.com/fredbi/uri v0.0.0-20181227131451-3dcfdacbaaf3/go.mod h1:CzM2G82Q9BDUvMTGHnXf/6OExw/Dz2ivDj48nVg7Lg8=
|
github.com/fredbi/uri v0.0.0-20181227131451-3dcfdacbaaf3/go.mod h1:CzM2G82Q9BDUvMTGHnXf/6OExw/Dz2ivDj48nVg7Lg8=
|
||||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||||
github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
|
github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
|
||||||
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
|
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
|
||||||
|
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||||
|
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||||
github.com/go-gl/gl v0.0.0-20210813123233-e4099ee2221f/go.mod h1:wjpnOv6ONl2SuJSxqCPVaPZibGFdSci9HFocT9qtVYM=
|
github.com/go-gl/gl v0.0.0-20210813123233-e4099ee2221f/go.mod h1:wjpnOv6ONl2SuJSxqCPVaPZibGFdSci9HFocT9qtVYM=
|
||||||
github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 h1:zDw5v7qm4yH7N8C8uWd+8Ii9rROdgWxQuGoJ9WDXxfk=
|
github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6 h1:zDw5v7qm4yH7N8C8uWd+8Ii9rROdgWxQuGoJ9WDXxfk=
|
||||||
github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw=
|
github.com/go-gl/gl v0.0.0-20211210172815-726fda9656d6/go.mod h1:9YTyiznxEY1fVinfM7RvRcjRHbw2xLBJ3AAGIT0I4Nw=
|
||||||
|
@ -26,6 +30,8 @@ github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5x
|
||||||
github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff/go.mod h1:wfqRWLHRBsRgkp5dmbG56SA0DmVtwrF5N3oPdI8t+Aw=
|
github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff/go.mod h1:wfqRWLHRBsRgkp5dmbG56SA0DmVtwrF5N3oPdI8t+Aw=
|
||||||
github.com/goki/freetype v0.0.0-20220119013949-7a161fd3728c h1:JGCm/+tJ9gC6THUxooTldS+CUDsba0qvkvU3DHklqW8=
|
github.com/goki/freetype v0.0.0-20220119013949-7a161fd3728c h1:JGCm/+tJ9gC6THUxooTldS+CUDsba0qvkvU3DHklqW8=
|
||||||
github.com/goki/freetype v0.0.0-20220119013949-7a161fd3728c/go.mod h1:wfqRWLHRBsRgkp5dmbG56SA0DmVtwrF5N3oPdI8t+Aw=
|
github.com/goki/freetype v0.0.0-20220119013949-7a161fd3728c/go.mod h1:wfqRWLHRBsRgkp5dmbG56SA0DmVtwrF5N3oPdI8t+Aw=
|
||||||
|
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||||
|
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
github.com/ivanizag/iz6502 v1.2.1 h1:nwrKzSKVhG9mO6kUE68o07aX6Qn5AuA6WFm4twCx8mA=
|
github.com/ivanizag/iz6502 v1.2.1 h1:nwrKzSKVhG9mO6kUE68o07aX6Qn5AuA6WFm4twCx8mA=
|
||||||
github.com/ivanizag/iz6502 v1.2.1/go.mod h1:h4gbw3IK6WCYawi00kBhQ4ACeQkGWgqbUeAgDaQpy6s=
|
github.com/ivanizag/iz6502 v1.2.1/go.mod h1:h4gbw3IK6WCYawi00kBhQ4ACeQkGWgqbUeAgDaQpy6s=
|
||||||
github.com/jackmordaunt/icns v0.0.0-20181231085925-4f16af745526/go.mod h1:UQkeMHVoNcyXYq9otUupF7/h/2tmHlhrS2zw7ZVvUqc=
|
github.com/jackmordaunt/icns v0.0.0-20181231085925-4f16af745526/go.mod h1:UQkeMHVoNcyXYq9otUupF7/h/2tmHlhrS2zw7ZVvUqc=
|
||||||
|
@ -34,19 +40,41 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/lucor/goinfo v0.0.0-20210802170112-c078a2b0f08b/go.mod h1:PRq09yoB+Q2OJReAmwzKivcYyremnibWGbK7WfftHzc=
|
github.com/lucor/goinfo v0.0.0-20210802170112-c078a2b0f08b/go.mod h1:PRq09yoB+Q2OJReAmwzKivcYyremnibWGbK7WfftHzc=
|
||||||
|
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||||
|
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||||
|
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||||
|
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
|
||||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/profile v1.6.0 h1:hUDfIISABYI59DyeB3OTay/HxSRwTQ8rB/H83k6r5dM=
|
github.com/pkg/profile v1.6.0 h1:hUDfIISABYI59DyeB3OTay/HxSRwTQ8rB/H83k6r5dM=
|
||||||
github.com/pkg/profile v1.6.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18=
|
github.com/pkg/profile v1.6.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||||
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
|
||||||
|
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
|
||||||
|
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
|
||||||
|
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
|
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
||||||
|
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
||||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||||
|
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
||||||
|
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
||||||
|
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
|
||||||
|
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
|
github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ=
|
||||||
|
github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk=
|
||||||
github.com/srwiley/oksvg v0.0.0-20200311192757-870daf9aa564/go.mod h1:afMbS0qvv1m5tfENCwnOdZGOF8RGR/FsZ7bvBxQGZG4=
|
github.com/srwiley/oksvg v0.0.0-20200311192757-870daf9aa564/go.mod h1:afMbS0qvv1m5tfENCwnOdZGOF8RGR/FsZ7bvBxQGZG4=
|
||||||
github.com/srwiley/oksvg v0.0.0-20220128195007-1f435e4c2b44 h1:XPYXKIuH/n5zpUoEWk2jWV/SjEMNYmqDYmTgbjmhtaI=
|
github.com/srwiley/oksvg v0.0.0-20220128195007-1f435e4c2b44 h1:XPYXKIuH/n5zpUoEWk2jWV/SjEMNYmqDYmTgbjmhtaI=
|
||||||
github.com/srwiley/oksvg v0.0.0-20220128195007-1f435e4c2b44/go.mod h1:cNQ3dwVJtS5Hmnjxy6AgTPd0Inb3pW05ftPSX7NZO7Q=
|
github.com/srwiley/oksvg v0.0.0-20220128195007-1f435e4c2b44/go.mod h1:cNQ3dwVJtS5Hmnjxy6AgTPd0Inb3pW05ftPSX7NZO7Q=
|
||||||
|
@ -54,10 +82,17 @@ github.com/srwiley/rasterx v0.0.0-20200120212402-85cb7272f5e9/go.mod h1:mvWM0+15
|
||||||
github.com/srwiley/rasterx v0.0.0-20220128185129-2efea2b9ea41 h1:YR16ysw3I1bqwtEcYV9dpvhHEe7j55hIClkLoAqY31I=
|
github.com/srwiley/rasterx v0.0.0-20220128185129-2efea2b9ea41 h1:YR16ysw3I1bqwtEcYV9dpvhHEe7j55hIClkLoAqY31I=
|
||||||
github.com/srwiley/rasterx v0.0.0-20220128185129-2efea2b9ea41/go.mod h1:nXTWP6+gD5+LUJ8krVhhoeHjvHTutPxMYl5SvkcnJNE=
|
github.com/srwiley/rasterx v0.0.0-20220128185129-2efea2b9ea41/go.mod h1:nXTWP6+gD5+LUJ8krVhhoeHjvHTutPxMYl5SvkcnJNE=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
|
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
|
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||||
|
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||||
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
||||||
github.com/veandco/go-sdl2 v0.4.17 h1:ehMJJTNjgPdVKzSG/PzoeRtRNNlrq6Vmhv8c0MGO3p8=
|
github.com/veandco/go-sdl2 v0.4.17 h1:ehMJJTNjgPdVKzSG/PzoeRtRNNlrq6Vmhv8c0MGO3p8=
|
||||||
github.com/veandco/go-sdl2 v0.4.17/go.mod h1:OROqMhHD43nT4/i9crJukyVecjPNYYuCofep6SNiAjY=
|
github.com/veandco/go-sdl2 v0.4.17/go.mod h1:OROqMhHD43nT4/i9crJukyVecjPNYYuCofep6SNiAjY=
|
||||||
|
@ -65,9 +100,15 @@ github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1
|
||||||
github.com/yuin/goldmark v1.3.8/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.3.8/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE=
|
github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE=
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
|
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
||||||
|
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||||
|
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
|
||||||
|
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
|
||||||
|
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
|
||||||
golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
golang.org/x/image v0.5.0 h1:5JMiNunQeQw++mMOz48/ISeNu3Iweh/JaZU8ZLqHRrI=
|
golang.org/x/image v0.5.0 h1:5JMiNunQeQw++mMOz48/ISeNu3Iweh/JaZU8ZLqHRrI=
|
||||||
golang.org/x/image v0.5.0/go.mod h1:FVC7BI/5Ym8R25iw5OLsgshdUBbT1h5jZTpA+mvAdZ4=
|
golang.org/x/image v0.5.0/go.mod h1:FVC7BI/5Ym8R25iw5OLsgshdUBbT1h5jZTpA+mvAdZ4=
|
||||||
|
@ -80,6 +121,8 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
|
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
|
||||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||||
|
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
|
||||||
|
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
@ -96,6 +139,8 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
||||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||||
|
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
@ -104,6 +149,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
|
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||||
|
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
|
@ -114,9 +161,13 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||||
|
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA=
|
gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA=
|
||||||
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
package izapple2
|
package izapple2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -198,3 +201,22 @@ func (nsc *noSlotClockDS1216) loadTime() {
|
||||||
|
|
||||||
nsc.timeCapture = register
|
nsc.timeCapture = register
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setupNoSlotClock(a *Apple2, arg string) error {
|
||||||
|
if arg == "main" {
|
||||||
|
nsc := newNoSlotClockDS1216(a, a.mmu.physicalROM[0])
|
||||||
|
a.mmu.physicalROM[0] = nsc
|
||||||
|
} else {
|
||||||
|
slot, err := strconv.ParseUint(arg, 10, 8)
|
||||||
|
if err != nil || slot < 1 || slot > 7 {
|
||||||
|
return errors.New("invalid slot for the no slot clock, use 'none', 'main' or a slot number from 1 to 7")
|
||||||
|
}
|
||||||
|
cardRom := a.mmu.cardsROM[slot]
|
||||||
|
if cardRom == nil {
|
||||||
|
return fmt.Errorf("no ROM available on slot %d to add a no slot clock", slot)
|
||||||
|
}
|
||||||
|
nsc := newNoSlotClockDS1216(a, cardRom)
|
||||||
|
a.mmu.cardsROM[slot] = nsc
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -34,6 +34,11 @@ func isHTTPResource(filename string) bool {
|
||||||
|
|
||||||
// LoadResource loads in memory a file from the filesystem, http or embedded
|
// LoadResource loads in memory a file from the filesystem, http or embedded
|
||||||
func LoadResource(filename string) ([]uint8, bool, error) {
|
func LoadResource(filename string) ([]uint8, bool, error) {
|
||||||
|
// Remove quotes if surrounded by them
|
||||||
|
if strings.HasPrefix(filename, "\"") && strings.HasSuffix(filename, "\"") {
|
||||||
|
filename = filename[1 : len(filename)-1]
|
||||||
|
}
|
||||||
|
|
||||||
var writeable bool
|
var writeable bool
|
||||||
var file io.Reader
|
var file io.Reader
|
||||||
if isInternalResource(filename) {
|
if isInternalResource(filename) {
|
||||||
|
|
14
romX.go
14
romX.go
|
@ -74,10 +74,10 @@ const (
|
||||||
*/
|
*/
|
||||||
)
|
)
|
||||||
|
|
||||||
func newRomX(a *Apple2, memory iz6502.Memory) (*romX, error) {
|
func newRomX(a *Apple2) (*romX, error) {
|
||||||
var rx romX
|
var rx romX
|
||||||
rx.a = a
|
rx.a = a
|
||||||
rx.memory = memory
|
rx.memory = a.mmu
|
||||||
rx.systemBank = 1
|
rx.systemBank = 1
|
||||||
rx.mainBank = 1
|
rx.mainBank = 1
|
||||||
rx.tempBank = 1
|
rx.tempBank = 1
|
||||||
|
@ -91,6 +91,8 @@ func newRomX(a *Apple2, memory iz6502.Memory) (*romX, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Intercept all memory accesses
|
||||||
|
a.cpu.SetMemory(&rx)
|
||||||
return &rx, nil
|
return &rx, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,3 +195,11 @@ func (rx *romX) interceptAccess(address uint16) (bool, uint8) {
|
||||||
|
|
||||||
return false, 0
|
return false, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setupRomX(a *Apple2) error {
|
||||||
|
_, err := newRomX(a)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -7,9 +7,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
See:
|
See:
|
||||||
https://mrob.com/pub/xapple2/colors.html
|
|
||||||
https://archive.org/details/IIgs_2523063_Master_Color_Values
|
https://mrob.com/pub/xapple2/colors.html
|
||||||
|
https://archive.org/details/IIgs_2523063_Master_Color_Values
|
||||||
*/
|
*/
|
||||||
var ntscColorMap = [16]color.Color{
|
var ntscColorMap = [16]color.Color{
|
||||||
color.RGBA{0, 0, 0, 255}, // Black
|
color.RGBA{0, 0, 0, 255}, // Black
|
||||||
|
|
|
@ -0,0 +1,192 @@
|
||||||
|
package izapple2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/ivanizag/iz6502"
|
||||||
|
)
|
||||||
|
|
||||||
|
func configure(configuration *configuration) (*Apple2, error) {
|
||||||
|
a := newApple2()
|
||||||
|
a.Name = configuration.get(confName)
|
||||||
|
|
||||||
|
// Configure the board
|
||||||
|
board := configuration.get(confBoard)
|
||||||
|
a.board = board
|
||||||
|
a.isApple2e = board == "2e"
|
||||||
|
|
||||||
|
addApple2SoftSwitches(a.io)
|
||||||
|
if a.isApple2e {
|
||||||
|
a.mmu.initExtendedRAM(1)
|
||||||
|
addApple2ESoftSwitches(a.io)
|
||||||
|
}
|
||||||
|
if board == "base64a" {
|
||||||
|
addBase64aSoftSwitches(a.io)
|
||||||
|
}
|
||||||
|
|
||||||
|
cpu := configuration.get(confCpu)
|
||||||
|
switch cpu {
|
||||||
|
case "6502":
|
||||||
|
a.cpu = iz6502.NewNMOS6502(a.mmu)
|
||||||
|
case "65c02":
|
||||||
|
a.cpu = iz6502.NewCMOS65c02(a.mmu)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := a.loadRom(configuration.get(confRom))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = setupCharactedGenerator(a, board, configuration.get(confCharRom))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
a.setProfiling(configuration.getFlag(confProfile))
|
||||||
|
a.SetForceCaps(configuration.getFlag(confForceCaps))
|
||||||
|
|
||||||
|
err = a.setClockSpeed(configuration.get(confSpeed))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add cards on the slots
|
||||||
|
for i := 0; i < 8; i++ {
|
||||||
|
cardConfig := configuration.get(fmt.Sprintf("s%v", i))
|
||||||
|
if cardConfig != "" {
|
||||||
|
_, err := setupCard(a, i, cardConfig)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add optional accesories including the aux slot
|
||||||
|
ramWorksSize := configuration.get(confRamworks)
|
||||||
|
if ramWorksSize != "" && ramWorksSize != "none" {
|
||||||
|
err = setupRAMWorksCard(a, ramWorksSize)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if configuration.getFlag(confRgb) {
|
||||||
|
setupRGBCard(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
nsc := configuration.get(confNsc)
|
||||||
|
if nsc != "none" && nsc != "" {
|
||||||
|
err = setupNoSlotClock(a, nsc)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if configuration.getFlag(confRomx) {
|
||||||
|
err := setupRomX(a)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = setupTracers(a, configuration.get(confTrace))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return a, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newApple2() *Apple2 {
|
||||||
|
var a Apple2
|
||||||
|
|
||||||
|
a.Name = "Pending"
|
||||||
|
a.mmu = newMemoryManager(&a)
|
||||||
|
a.io = newIoC0Page(&a)
|
||||||
|
a.commandChannel = make(chan command, 100)
|
||||||
|
|
||||||
|
return &a
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Apple2) setClockSpeed(speed string) error {
|
||||||
|
if speed == "full" {
|
||||||
|
a.cycleDurationNs = 0
|
||||||
|
} else if speed == "ntsc" {
|
||||||
|
a.cycleDurationNs = 1000.0 / CPUClockMhz
|
||||||
|
} else if speed == "pal" {
|
||||||
|
a.cycleDurationNs = 1000.0 / cpuClockEuroMhz
|
||||||
|
} else {
|
||||||
|
clockMhz, err := strconv.ParseFloat(speed, 64)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid clock speed: %s", speed)
|
||||||
|
}
|
||||||
|
a.cycleDurationNs = 1000.0 / clockMhz
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Apple2) setProfiling(value bool) {
|
||||||
|
a.profile = value
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetForceCaps allows the caps state to be toggled at runtime
|
||||||
|
func (a *Apple2) SetForceCaps(value bool) {
|
||||||
|
a.forceCaps = value
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
apple2RomSize = 12 * 1024
|
||||||
|
apple2eRomSize = 16 * 1024
|
||||||
|
)
|
||||||
|
|
||||||
|
func (a *Apple2) loadRom(filename string) error {
|
||||||
|
if a.board == "base64a" {
|
||||||
|
// The ROM of the base64a has several file and pages
|
||||||
|
loadBase64aRom(a)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
data, _, err := LoadResource(filename)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
size := len(data)
|
||||||
|
if size != apple2RomSize && size != apple2eRomSize {
|
||||||
|
return errors.New("rom size not supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
romBase := 0x10000 - size
|
||||||
|
a.mmu.physicalROM[0] = newMemoryRangeROM(uint16(romBase), data, "Main ROM")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateConfiguredApple is a device independent main. Video, keyboard and speaker won't be defined
|
||||||
|
func CreateConfiguredApple() (*Apple2, error) {
|
||||||
|
// Get configuration from defaults and the command line
|
||||||
|
configuration, filename, err := getConfigurationFromCommandLine()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if filename != "" {
|
||||||
|
// Try loading as diskette
|
||||||
|
_, err := LoadDiskette(filename)
|
||||||
|
isDiskette := err == nil
|
||||||
|
if isDiskette {
|
||||||
|
// Let's force a DiskII with the diskette in slot 6
|
||||||
|
configuration.set(confS6, fmt.Sprintf("diskii,disk1=\"%s\"", filename))
|
||||||
|
} else {
|
||||||
|
// Let's force a Smartport card with a block device in slot 7
|
||||||
|
configuration.set(confS7, fmt.Sprintf("smartport,image1=\"%s\"", filename))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a, err := configure(configuration)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return a, nil
|
||||||
|
}
|
|
@ -35,9 +35,8 @@ const (
|
||||||
applecornRomTitle uint16 = 0x8009
|
applecornRomTitle uint16 = 0x8009
|
||||||
)
|
)
|
||||||
|
|
||||||
func newTraceApplecorn(a *Apple2, skipConsole bool) *traceApplecorn {
|
func newTraceApplecorn(skipConsole bool) *traceApplecorn {
|
||||||
var t traceApplecorn
|
var t traceApplecorn
|
||||||
t.a = a
|
|
||||||
t.skipConsole = skipConsole
|
t.skipConsole = skipConsole
|
||||||
t.osbyteNames[0x02] = "select input device"
|
t.osbyteNames[0x02] = "select input device"
|
||||||
t.osbyteNames[0x03] = "select output device"
|
t.osbyteNames[0x03] = "select output device"
|
||||||
|
@ -57,6 +56,10 @@ func newTraceApplecorn(a *Apple2, skipConsole bool) *traceApplecorn {
|
||||||
return &t
|
return &t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *traceApplecorn) connect(a *Apple2) {
|
||||||
|
t.a = a
|
||||||
|
}
|
||||||
|
|
||||||
func (t *traceApplecorn) inspect() {
|
func (t *traceApplecorn) inspect() {
|
||||||
if !t.a.mmu.altMainRAMActiveRead {
|
if !t.a.mmu.altMainRAMActiveRead {
|
||||||
// We want to trace only the activity on the Acorn memory space
|
// We want to trace only the activity on the Acorn memory space
|
||||||
|
|
|
@ -0,0 +1,95 @@
|
||||||
|
package izapple2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"golang.org/x/exp/maps"
|
||||||
|
)
|
||||||
|
|
||||||
|
type executionTracer interface {
|
||||||
|
connect(a *Apple2)
|
||||||
|
inspect()
|
||||||
|
}
|
||||||
|
|
||||||
|
type traceBuilder struct {
|
||||||
|
name string
|
||||||
|
description string
|
||||||
|
executionTracer executionTracer
|
||||||
|
connectFunc func(a *Apple2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildTracerFactory() map[string]*traceBuilder {
|
||||||
|
tracerFactory := make(map[string]*traceBuilder)
|
||||||
|
|
||||||
|
tracerFactory["mos"] = &traceBuilder{
|
||||||
|
name: "mos",
|
||||||
|
description: "Trace MOS calls with Applecorn skipping terminal IO",
|
||||||
|
executionTracer: newTraceApplecorn(true),
|
||||||
|
}
|
||||||
|
tracerFactory["mosfull"] = &traceBuilder{
|
||||||
|
name: "mosfull",
|
||||||
|
description: "Trace MOS calls with Applecorn",
|
||||||
|
executionTracer: newTraceApplecorn(false),
|
||||||
|
}
|
||||||
|
tracerFactory["mli"] = &traceBuilder{
|
||||||
|
name: "mli",
|
||||||
|
description: "Trace ProDOS MLI calls",
|
||||||
|
executionTracer: newTraceProDOS(),
|
||||||
|
}
|
||||||
|
tracerFactory["ucsd"] = &traceBuilder{
|
||||||
|
name: "ucsd",
|
||||||
|
description: "Trace UCSD system calls",
|
||||||
|
executionTracer: newTracePascal(),
|
||||||
|
}
|
||||||
|
tracerFactory["cpu"] = &traceBuilder{
|
||||||
|
name: "cpu",
|
||||||
|
description: "Trace CPU execution",
|
||||||
|
connectFunc: func(a *Apple2) { a.cpu.SetTrace(true) },
|
||||||
|
}
|
||||||
|
tracerFactory["ss"] = &traceBuilder{
|
||||||
|
name: "ss",
|
||||||
|
description: "Trace sotfswiches calls",
|
||||||
|
connectFunc: func(a *Apple2) { a.io.setTrace(true) },
|
||||||
|
}
|
||||||
|
tracerFactory["ssreg"] = &traceBuilder{
|
||||||
|
name: "ssreg",
|
||||||
|
description: "Trace sotfswiches registrations",
|
||||||
|
connectFunc: func(a *Apple2) { a.io.setTraceRegistrations(true) },
|
||||||
|
}
|
||||||
|
tracerFactory["panicSS"] = &traceBuilder{
|
||||||
|
name: "panicSS",
|
||||||
|
description: "Panic on unimplemented softswitches",
|
||||||
|
connectFunc: func(a *Apple2) { a.io.setPanicNotImplemented(true) },
|
||||||
|
}
|
||||||
|
return tracerFactory
|
||||||
|
}
|
||||||
|
|
||||||
|
func availableTracers() []string {
|
||||||
|
return maps.Keys(buildTracerFactory())
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupTracers(a *Apple2, paramString string) error {
|
||||||
|
tracerFactory := buildTracerFactory()
|
||||||
|
tracerNames := splitConfigurationString(paramString, ',')
|
||||||
|
for _, tracer := range tracerNames {
|
||||||
|
if tracer == "none" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
builder, ok := tracerFactory[tracer]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("unknown tracer %s", tracer)
|
||||||
|
}
|
||||||
|
if builder.connectFunc != nil {
|
||||||
|
builder.connectFunc(a)
|
||||||
|
}
|
||||||
|
if builder.executionTracer != nil {
|
||||||
|
a.addTracer(builder.executionTracer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Apple2) addTracer(tracer executionTracer) {
|
||||||
|
tracer.connect(a)
|
||||||
|
a.tracers = append(a.tracers, tracer)
|
||||||
|
}
|
|
@ -12,13 +12,16 @@ const (
|
||||||
pascalJvabfoldH uint16 = 0x00ed // Points to the BIOS entry points
|
pascalJvabfoldH uint16 = 0x00ed // Points to the BIOS entry points
|
||||||
)
|
)
|
||||||
|
|
||||||
func newTracePascal(a *Apple2) *tracePascal {
|
func newTracePascal() *tracePascal {
|
||||||
var t tracePascal
|
var t tracePascal
|
||||||
t.a = a
|
|
||||||
t.skipConsole = true
|
t.skipConsole = true
|
||||||
return &t
|
return &t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *tracePascal) connect(a *Apple2) {
|
||||||
|
t.a = a
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
See:
|
See:
|
||||||
|
|
||||||
|
|
|
@ -26,13 +26,16 @@ const (
|
||||||
deviceDateTimeVector uint16 = 0xbf07 // DATETIME+1
|
deviceDateTimeVector uint16 = 0xbf07 // DATETIME+1
|
||||||
)
|
)
|
||||||
|
|
||||||
func newTraceProDOS(a *Apple2) *traceProDOS {
|
func newTraceProDOS() *traceProDOS {
|
||||||
var t traceProDOS
|
var t traceProDOS
|
||||||
t.a = a
|
|
||||||
t.deviceDrivers = make([]uint16, 0)
|
t.deviceDrivers = make([]uint16, 0)
|
||||||
return &t
|
return &t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *traceProDOS) connect(a *Apple2) {
|
||||||
|
t.a = a
|
||||||
|
}
|
||||||
|
|
||||||
func (t *traceProDOS) inspect() {
|
func (t *traceProDOS) inspect() {
|
||||||
pc, _ := t.a.cpu.GetPCAndSP()
|
pc, _ := t.a.cpu.GetPCAndSP()
|
||||||
if pc == mliAddress {
|
if pc == mliAddress {
|
||||||
|
|
Loading…
Reference in New Issue