mirror of
https://github.com/ivanizag/izapple2.git
synced 2025-02-03 02:31:22 +00:00
Support DMA requests from expansion cards
This commit is contained in:
parent
1cbb97d55f
commit
9cf24ab904
@ -25,6 +25,10 @@ type Apple2 struct {
|
||||
isFourColors bool // An Apple II without the 6 color mod
|
||||
commandChannel chan command
|
||||
|
||||
dmaActive bool
|
||||
dmaSlot int
|
||||
|
||||
cycles uint64
|
||||
cycleDurationNs float64 // Current speed. Inverse of the cpu clock in Ghz
|
||||
fastRequestsCounter int32
|
||||
cycleBreakpoint uint64
|
||||
@ -32,6 +36,7 @@ type Apple2 struct {
|
||||
profile bool
|
||||
showSpeed bool
|
||||
paused bool
|
||||
cpuTrace bool
|
||||
forceCaps bool
|
||||
removableMediaDrives []drive
|
||||
}
|
||||
@ -67,7 +72,7 @@ func (a *Apple2) IsPaused() bool {
|
||||
}
|
||||
|
||||
func (a *Apple2) GetCycles() uint64 {
|
||||
return a.cpu.GetCycles()
|
||||
return a.cycles
|
||||
}
|
||||
|
||||
// SetCycleBreakpoint sets a cycle number to pause the emulator. 0 to disable
|
||||
|
39
apple2Run.go
39
apple2Run.go
@ -25,6 +25,8 @@ func (a *Apple2) Run() {
|
||||
func (a *Apple2) Start(paused bool) {
|
||||
// Start the processor
|
||||
a.cpu.Reset()
|
||||
a.cycles = a.cpu.GetCycles()
|
||||
|
||||
referenceTime := time.Now()
|
||||
speedReferenceTime := referenceTime
|
||||
speedReferenceCycles := uint64(0)
|
||||
@ -34,19 +36,29 @@ func (a *Apple2) Start(paused bool) {
|
||||
for {
|
||||
// Run 6502 steps
|
||||
if !a.paused {
|
||||
for i := 0; i < cpuSpinLoops; i++ {
|
||||
// Conditional tracing
|
||||
// pc, _ := a.cpu.GetPCAndSP()
|
||||
// a.cpu.SetTrace(pc >= 0xc700 && pc < 0xc800)
|
||||
if !a.dmaActive {
|
||||
spinStartCycles := a.cpu.GetCycles()
|
||||
for i := 0; i < cpuSpinLoops && !a.dmaActive; i++ {
|
||||
// Conditional tracing
|
||||
// pc, _ := a.cpu.GetPCAndSP()
|
||||
// a.cpu.SetTrace(pc >= 0xc700 && pc < 0xc800)
|
||||
|
||||
// Execution
|
||||
a.cpu.ExecuteInstruction()
|
||||
// Execution
|
||||
a.cpu.ExecuteInstruction()
|
||||
|
||||
// Special tracing
|
||||
a.executionTrace()
|
||||
// Special tracing
|
||||
a.executionTrace()
|
||||
}
|
||||
a.cycles += a.cpu.GetCycles() - spinStartCycles
|
||||
} else {
|
||||
card := a.cards[a.dmaSlot]
|
||||
for i := 0; i < cpuSpinLoops && a.dmaActive; i++ {
|
||||
card.runDMACycle()
|
||||
a.cycles++
|
||||
}
|
||||
}
|
||||
|
||||
if a.cycleBreakpoint != 0 && a.cpu.GetCycles() >= a.cycleBreakpoint {
|
||||
if a.cycleBreakpoint != 0 && a.cycles >= a.cycleBreakpoint {
|
||||
a.breakPoint = true
|
||||
a.cycleBreakpoint = 0
|
||||
a.paused = true
|
||||
@ -89,7 +101,7 @@ func (a *Apple2) Start(paused bool) {
|
||||
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)
|
||||
simulatedDuration := time.Duration(float64(a.cycles) * 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
|
||||
@ -101,15 +113,14 @@ func (a *Apple2) Start(paused bool) {
|
||||
}
|
||||
}
|
||||
|
||||
if a.showSpeed && a.cpu.GetCycles()-speedReferenceCycles > 1000000 {
|
||||
if a.showSpeed && a.cycles-speedReferenceCycles > 1000000 {
|
||||
// Calculate speed in MHz every million cycles
|
||||
newTime := time.Now()
|
||||
newCycles := a.cpu.GetCycles()
|
||||
elapsedCycles := float64(newCycles - speedReferenceCycles)
|
||||
elapsedCycles := float64(a.cycles - speedReferenceCycles)
|
||||
freq := 1000.0 * elapsedCycles / float64(newTime.Sub(speedReferenceTime).Nanoseconds())
|
||||
fmt.Printf("Freq: %f Mhz\n", freq)
|
||||
speedReferenceTime = newTime
|
||||
speedReferenceCycles = newCycles
|
||||
speedReferenceCycles = a.cycles
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ func (at *apple2Tester) getText(textMode testTextModeFunc) string {
|
||||
/*
|
||||
func buildTerminateConditionCycles(cycles uint64) terminateConditionFunc {
|
||||
return func(a *Apple2) bool {
|
||||
return a.cpu.GetCycles() > cycles
|
||||
return a.cycles() > cycles
|
||||
}
|
||||
}
|
||||
*/
|
||||
@ -85,7 +85,7 @@ func buildTerminateConditionTexts(needles []string, textMode testTextModeFunc, t
|
||||
lastCheck := uint64(0)
|
||||
found := false
|
||||
return func(a *Apple2) bool {
|
||||
cycles := a.cpu.GetCycles()
|
||||
cycles := a.GetCycles()
|
||||
if cycles > timeoutCycles {
|
||||
return true
|
||||
}
|
||||
|
@ -85,7 +85,7 @@ func addApple2SoftSwitches(io *ioC0Page) {
|
||||
func buildNotImplementedSoftSwitchR(io *ioC0Page) softSwitchR {
|
||||
return func() uint8 {
|
||||
// Return random info. Some games (Serpentine) used CASSETTE and get stuck if not changing.
|
||||
return uint8(io.apple2.cpu.GetCycles())
|
||||
return uint8(io.apple2.GetCycles())
|
||||
}
|
||||
}
|
||||
|
||||
@ -121,7 +121,7 @@ func getSoftSwitch(io *ioC0Page, ioFlag uint8, isSet bool) softSwitchR {
|
||||
func buildSpeakerSoftSwitch(io *ioC0Page) softSwitchR {
|
||||
return func() uint8 {
|
||||
if io.speaker != nil {
|
||||
io.speaker.Click(io.apple2.cpu.GetCycles())
|
||||
io.speaker.Click(io.apple2.GetCycles())
|
||||
}
|
||||
return 0
|
||||
}
|
||||
@ -179,7 +179,7 @@ func buildPaddleSoftSwitch(io *ioC0Page, i int) softSwitchR {
|
||||
return 255 // Capacitors never discharge if there is not joystick
|
||||
}
|
||||
cyclesNeeded := uint64(reading) * paddleToCyclesFactor
|
||||
cyclesElapsed := io.apple2.cpu.GetCycles() - io.paddlesStrobeCycle
|
||||
cyclesElapsed := io.apple2.GetCycles() - io.paddlesStrobeCycle
|
||||
if cyclesElapsed < cyclesNeeded {
|
||||
// The capacitor is not charged yet
|
||||
return 128
|
||||
@ -191,7 +191,7 @@ func buildPaddleSoftSwitch(io *ioC0Page, i int) softSwitchR {
|
||||
func buildStrobePaddlesSoftSwitch(io *ioC0Page) softSwitchR {
|
||||
return func() uint8 {
|
||||
// On the real machine this discharges the capacitors.
|
||||
io.paddlesStrobeCycle = io.apple2.cpu.GetCycles()
|
||||
io.paddlesStrobeCycle = io.apple2.GetCycles()
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ func addApple2ESoftSwitches(io *ioC0Page) {
|
||||
// 12480 cycles drawing lines, VERTBLANK = $00
|
||||
// 4550 cycles doing the return to position (0,0), VERTBLANK = $80
|
||||
// Vert blank takes 12480 cycles every page redraw
|
||||
cycles := io.apple2.cpu.GetCycles() % screenDrawCycles
|
||||
cycles := io.apple2.GetCycles() % screenDrawCycles
|
||||
if cycles <= screenVertBlankingCycles {
|
||||
return ssOn
|
||||
}
|
||||
|
17
cardBase.go
17
cardBase.go
@ -9,6 +9,7 @@ type Card interface {
|
||||
loadRom(data []uint8, layout cardRomLayout) error
|
||||
assign(a *Apple2, slot int)
|
||||
reset()
|
||||
runDMACycle()
|
||||
|
||||
setName(name string)
|
||||
setDebug(debug bool)
|
||||
@ -154,6 +155,22 @@ func (c *cardBase) assign(a *Apple2, slot int) {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *cardBase) runDMACycle() {
|
||||
// No DMA
|
||||
}
|
||||
|
||||
func (c *cardBase) activateDMA() {
|
||||
if c.a.dmaActive {
|
||||
panic("DMA chain not supported")
|
||||
}
|
||||
c.a.dmaActive = true
|
||||
c.a.dmaSlot = c.slot
|
||||
}
|
||||
|
||||
func (c *cardBase) deactivateDMA() {
|
||||
c.a.dmaActive = false
|
||||
}
|
||||
|
||||
func (c *cardBase) addCardSoftSwitchR(address uint8, ss softSwitchR, name string) {
|
||||
c._ssr[address] = ss
|
||||
c._ssrName[address] = name
|
||||
|
@ -25,7 +25,7 @@ func TestBrainBoardCardWozaniam(t *testing.T) {
|
||||
at := buildBrainBoardTester(t, "brainboard,switch=up")
|
||||
|
||||
at.terminateCondition = func(a *Apple2) bool {
|
||||
return a.cpu.GetCycles() > 10_000_000
|
||||
return a.GetCycles() > 10_000_000
|
||||
}
|
||||
at.run()
|
||||
|
||||
|
14
cardDisk2.go
14
cardDisk2.go
@ -212,7 +212,7 @@ func (c *CardDisk2) softSwitchQ4(value bool) {
|
||||
}
|
||||
drive := &c.drive[c.selected]
|
||||
if drive.diskette != nil {
|
||||
drive.diskette.PowerOff(c.a.cpu.GetCycles())
|
||||
drive.diskette.PowerOff(c.a.GetCycles())
|
||||
}
|
||||
} else if value && !c.power {
|
||||
// Turn on
|
||||
@ -222,7 +222,7 @@ func (c *CardDisk2) softSwitchQ4(value bool) {
|
||||
}
|
||||
drive := &c.drive[c.selected]
|
||||
if drive.diskette != nil {
|
||||
drive.diskette.PowerOn(c.a.cpu.GetCycles())
|
||||
drive.diskette.PowerOn(c.a.GetCycles())
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -231,10 +231,10 @@ func (c *CardDisk2) softSwitchQ5(selected int) {
|
||||
if c.power && c.selected != selected {
|
||||
// Selected changed with power on, power goes to the other disk
|
||||
if c.drive[c.selected].diskette != nil {
|
||||
c.drive[c.selected].diskette.PowerOff(c.a.cpu.GetCycles())
|
||||
c.drive[c.selected].diskette.PowerOff(c.a.GetCycles())
|
||||
}
|
||||
if c.drive[selected].diskette != nil {
|
||||
c.drive[selected].diskette.PowerOn(c.a.cpu.GetCycles())
|
||||
c.drive[selected].diskette.PowerOn(c.a.GetCycles())
|
||||
}
|
||||
}
|
||||
|
||||
@ -271,9 +271,9 @@ func (c *CardDisk2) processQ6Q7(in uint8) {
|
||||
}
|
||||
if !c.q6 { // shift
|
||||
if !c.q7 { // Q6L-Q7L: Read
|
||||
c.dataLatch = d.diskette.Read(d.trackStep, c.a.cpu.GetCycles())
|
||||
c.dataLatch = d.diskette.Read(d.trackStep, c.a.GetCycles())
|
||||
} else { // Q6L-Q7H: Write the dataLatch value to disk. Shift data out
|
||||
d.diskette.Write(d.trackStep, c.dataLatch, c.a.cpu.GetCycles())
|
||||
d.diskette.Write(d.trackStep, c.dataLatch, c.a.GetCycles())
|
||||
}
|
||||
} else { // load
|
||||
if !c.q7 { // Q6H-Q7L: Sense write protect / prewrite state
|
||||
@ -286,7 +286,7 @@ func (c *CardDisk2) processQ6Q7(in uint8) {
|
||||
|
||||
/*
|
||||
if c.dataLatch >= 0x80 {
|
||||
fmt.Printf("Datalach: 0x%.2x in cycle %v\n", c.dataLatch, c.a.cpu.GetCycles())
|
||||
fmt.Printf("Datalach: 0x%.2x in cycle %v\n", c.dataLatch, c.a.GetCycles())
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
@ -166,7 +166,7 @@ func (c *CardDisk2Sequencer) assign(a *Apple2, slot int) {
|
||||
}
|
||||
|
||||
func (c *CardDisk2Sequencer) catchUp(data uint8) {
|
||||
currentCycle := c.a.cpu.GetCycles() << 1 // Disk2 cycles are x2 cpu cycle
|
||||
currentCycle := c.a.GetCycles() << 1 // Disk2 cycles are x2 cpu cycle
|
||||
|
||||
motorOn := c.step(data, true)
|
||||
if motorOn && currentCycle > c.lastCycle+disk2CyclestoLoseSsync {
|
||||
|
@ -84,7 +84,8 @@ func (a *Apple2) executeCommand(command command) {
|
||||
a.cg.nextPage()
|
||||
fmt.Printf("Chargen page %v\n", a.cg.page)
|
||||
case CommandToggleCPUTrace:
|
||||
a.cpu.SetTrace(!a.cpu.GetTrace())
|
||||
a.cpuTrace = !a.cpuTrace
|
||||
a.cpu.SetTrace(a.cpuTrace)
|
||||
case CommandReset:
|
||||
a.reset()
|
||||
case CommandComplex:
|
||||
|
@ -30,7 +30,7 @@ func testWoz(t *testing.T, sequencer bool, file string, expectedTracks []int, cy
|
||||
tracksMayMatch := len(tt.quarterTracks) >= expectedLen &&
|
||||
tt.quarterTracks[expectedLen-1] == expectedTracks[expectedLen-1]
|
||||
|
||||
return tracksMayMatch || a.cpu.GetCycles() > cycleLimit
|
||||
return tracksMayMatch || a.GetCycles() > cycleLimit
|
||||
}
|
||||
at.run()
|
||||
|
||||
@ -38,7 +38,7 @@ func testWoz(t *testing.T, sequencer bool, file string, expectedTracks []int, cy
|
||||
t.Errorf("Quarter tracks, expected %#v, got %#v", expectedTracks, tt.quarterTracks)
|
||||
}
|
||||
|
||||
// t.Errorf("Cycles: %d vs %d", at.a.cpu.GetCycles(), cycleLimit)
|
||||
// t.Errorf("Cycles: %d vs %d", at.a.GetCycles(), cycleLimit)
|
||||
}
|
||||
|
||||
const (
|
||||
|
@ -27,7 +27,7 @@ type memoryManager struct {
|
||||
physicalExtAltRAM []*memoryRange // 0xd000 to 0xdfff, 4Kb. Up to 256 banks.
|
||||
|
||||
// Configuration switches, Language cards
|
||||
lcSelectedBlock uint8 // Language card block selected. Usually, allways 0. But Saturn has 8
|
||||
lcSelectedBlock uint8 // Language card block selected. Usually, always 0. But Saturn has 8
|
||||
lcActiveRead bool // Upper RAM active for read
|
||||
lcActiveWrite bool // Upper RAM active for write
|
||||
lcAltBank bool // Alternate
|
||||
|
@ -52,7 +52,10 @@ func getTracerFactory() map[string]*traceBuilder {
|
||||
tracerFactory["cpu"] = &traceBuilder{
|
||||
name: "cpu",
|
||||
description: "Trace CPU execution",
|
||||
connectFunc: func(a *Apple2) { a.cpu.SetTrace(true) },
|
||||
connectFunc: func(a *Apple2) {
|
||||
a.cpuTrace = true
|
||||
a.cpu.SetTrace(true)
|
||||
},
|
||||
}
|
||||
tracerFactory["ss"] = &traceBuilder{
|
||||
name: "ss",
|
||||
|
@ -57,11 +57,9 @@ func (t *traceProDOS) inspect() {
|
||||
t.dumpMLICall()
|
||||
t.refreshDeviceDrives()
|
||||
t.callPending = true
|
||||
// t.a.cpu.SetTrace(true)
|
||||
} else if t.callPending && pc == t.returnAddress {
|
||||
t.dumpMLIReturn()
|
||||
t.callPending = false
|
||||
// t.a.cpu.SetTrace(false)
|
||||
} else if pc == biAddress {
|
||||
t.dumpBIExec()
|
||||
} else if /*t.callPending &&*/ t.isDriverAddress(pc) {
|
||||
@ -189,8 +187,6 @@ func (t *traceProDOS) dumpMLIReturn() {
|
||||
default:
|
||||
fmt.Printf("Ok\n")
|
||||
}
|
||||
|
||||
t.a.cpu.SetTrace(false)
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user