mirror of
https://github.com/ivanizag/izapple2.git
synced 2024-06-12 20:29:45 +00:00
Merge branch 'master' into apple2e
This commit is contained in:
commit
b1b9424aeb
|
@ -133,6 +133,8 @@ Only valid on SDL mode
|
||||||
emulate a green phosphor monitor instead of a NTSC color TV. Use F6 to toggle.
|
emulate a green phosphor monitor instead of a NTSC color TV. Use F6 to toggle.
|
||||||
-panicss
|
-panicss
|
||||||
panic if a not implemented softswitch is used
|
panic if a not implemented softswitch is used
|
||||||
|
-profile
|
||||||
|
generate profile trace to analyse with pprof
|
||||||
-rom string
|
-rom string
|
||||||
main rom file (default "<internal>/Apple2_Plus.rom")
|
main rom file (default "<internal>/Apple2_Plus.rom")
|
||||||
-saturnCardSlot int
|
-saturnCardSlot int
|
||||||
|
|
17
apple2.go
17
apple2.go
|
@ -9,6 +9,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ivanizag/apple2/core6502"
|
"github.com/ivanizag/apple2/core6502"
|
||||||
|
"github.com/pkg/profile"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Apple2 represents all the components and state of the emulated machine
|
// Apple2 represents all the components and state of the emulated machine
|
||||||
|
@ -25,6 +26,7 @@ type Apple2 struct {
|
||||||
isColor bool
|
isColor bool
|
||||||
fastMode bool
|
fastMode bool
|
||||||
fastRequestsCounter int
|
fastRequestsCounter int
|
||||||
|
profile bool
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -37,6 +39,12 @@ const maxWaitDuration = 100 * time.Millisecond
|
||||||
|
|
||||||
// Run starts the Apple2 emulation
|
// Run starts the Apple2 emulation
|
||||||
func (a *Apple2) Run() {
|
func (a *Apple2) Run() {
|
||||||
|
if a.profile {
|
||||||
|
// See the log with:
|
||||||
|
// go tool pprof --pdf ~/go/bin/apple2sdl /tmp/profile329536248/cpu.pprof > profile.pdf
|
||||||
|
defer profile.Start().Stop()
|
||||||
|
}
|
||||||
|
|
||||||
// Start the processor
|
// Start the processor
|
||||||
a.cpu.Reset()
|
a.cpu.Reset()
|
||||||
referenceTime := time.Now()
|
referenceTime := time.Now()
|
||||||
|
@ -50,6 +58,9 @@ func (a *Apple2) Run() {
|
||||||
for commandsPending {
|
for commandsPending {
|
||||||
select {
|
select {
|
||||||
case command := <-a.commandChannel:
|
case command := <-a.commandChannel:
|
||||||
|
if command == CommandKill {
|
||||||
|
return
|
||||||
|
}
|
||||||
a.executeCommand(command)
|
a.executeCommand(command)
|
||||||
default:
|
default:
|
||||||
commandsPending = false
|
commandsPending = false
|
||||||
|
@ -73,6 +84,10 @@ func (a *Apple2) Run() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *Apple2) setProfile(value bool) {
|
||||||
|
a.profile = value
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// CommandToggleSpeed toggles cpu speed between full speed and actual Apple II speed
|
// CommandToggleSpeed toggles cpu speed between full speed and actual Apple II speed
|
||||||
CommandToggleSpeed = iota + 1
|
CommandToggleSpeed = iota + 1
|
||||||
|
@ -88,6 +103,8 @@ const (
|
||||||
CommandNextCharGenPage
|
CommandNextCharGenPage
|
||||||
// CommandToggleCPUTrace toggle tracing of CPU execution
|
// CommandToggleCPUTrace toggle tracing of CPU execution
|
||||||
CommandToggleCPUTrace
|
CommandToggleCPUTrace
|
||||||
|
// CommandKill stops the cpu execution loop
|
||||||
|
CommandKill
|
||||||
)
|
)
|
||||||
|
|
||||||
// SendCommand enqueues a command to the emulator thread
|
// SendCommand enqueues a command to the emulator thread
|
||||||
|
|
|
@ -88,6 +88,11 @@ func MainApple() *Apple2 {
|
||||||
"2plus",
|
"2plus",
|
||||||
"set base model. Models available 2plus, 2e, base64a",
|
"set base model. Models available 2plus, 2e, base64a",
|
||||||
)
|
)
|
||||||
|
profile := flag.Bool(
|
||||||
|
"profile",
|
||||||
|
false,
|
||||||
|
"generate profile trace to analyse with pprof",
|
||||||
|
)
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
var a *Apple2
|
var a *Apple2
|
||||||
|
@ -138,6 +143,7 @@ func MainApple() *Apple2 {
|
||||||
a.cpu.SetTrace(*traceCPU)
|
a.cpu.SetTrace(*traceCPU)
|
||||||
a.io.setTrace(*traceSS)
|
a.io.setTrace(*traceSS)
|
||||||
a.io.setPanicNotImplemented(*panicSS)
|
a.io.setPanicNotImplemented(*panicSS)
|
||||||
|
a.setProfile(*profile)
|
||||||
|
|
||||||
// Load ROM if not loaded already
|
// Load ROM if not loaded already
|
||||||
if *romFile != "" {
|
if *romFile != "" {
|
||||||
|
|
|
@ -43,6 +43,7 @@ func SDLRun(a *apple2.Apple2) {
|
||||||
for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() {
|
for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() {
|
||||||
switch t := event.(type) {
|
switch t := event.(type) {
|
||||||
case *sdl.QuitEvent:
|
case *sdl.QuitEvent:
|
||||||
|
a.SendCommand(apple2.CommandKill)
|
||||||
running = false
|
running = false
|
||||||
case *sdl.KeyboardEvent:
|
case *sdl.KeyboardEvent:
|
||||||
//fmt.Printf("[%d ms] Keyboard\ttype:%d\tsym:%c\tmodifiers:%d\tstate:%d\trepeat:%d\n",
|
//fmt.Printf("[%d ms] Keyboard\ttype:%d\tsym:%c\tmodifiers:%d\tstate:%d\trepeat:%d\n",
|
||||||
|
|
|
@ -21,6 +21,7 @@ const (
|
||||||
sampleDurationCycles = 1000000 * apple2.CpuClockMhz / samplingHz
|
sampleDurationCycles = 1000000 * apple2.CpuClockMhz / samplingHz
|
||||||
// each sample on the sound stream is 21.31 cpu cycles approx
|
// each sample on the sound stream is 21.31 cpu cycles approx
|
||||||
maxOutOfSyncMs = 2000
|
maxOutOfSyncMs = 2000
|
||||||
|
decayLevel = 128
|
||||||
)
|
)
|
||||||
|
|
||||||
type sdlSpeaker struct {
|
type sdlSpeaker struct {
|
||||||
|
@ -28,6 +29,7 @@ type sdlSpeaker struct {
|
||||||
pendingClicks []uint64
|
pendingClicks []uint64
|
||||||
lastCycle uint64
|
lastCycle uint64
|
||||||
lastState bool
|
lastState bool
|
||||||
|
lastLevel C.Uint8
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -40,6 +42,7 @@ func newSDLSpeaker() *sdlSpeaker {
|
||||||
var s sdlSpeaker
|
var s sdlSpeaker
|
||||||
s.clickChannel = make(chan uint64, bufferSize)
|
s.clickChannel = make(chan uint64, bufferSize)
|
||||||
s.pendingClicks = make([]uint64, 0, bufferSize)
|
s.pendingClicks = make([]uint64, 0, bufferSize)
|
||||||
|
s.lastLevel = decayLevel // Mid position to avoid starting clicks.
|
||||||
return &s
|
return &s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,6 +58,7 @@ func stateToLevel(state bool) C.Uint8 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SpeakerCallback is called to get more sound buffer data
|
||||||
//export SpeakerCallback
|
//export SpeakerCallback
|
||||||
func SpeakerCallback(userdata unsafe.Pointer, stream *C.Uint8, length C.int) {
|
func SpeakerCallback(userdata unsafe.Pointer, stream *C.Uint8, length C.int) {
|
||||||
s := theSDLSpeaker
|
s := theSDLSpeaker
|
||||||
|
@ -90,7 +94,7 @@ func SpeakerCallback(userdata unsafe.Pointer, stream *C.Uint8, length C.int) {
|
||||||
|
|
||||||
// Build wave
|
// Build wave
|
||||||
var i, p int
|
var i, p int
|
||||||
level := stateToLevel(s.lastState)
|
level := s.lastLevel
|
||||||
for p = 0; p < len(s.pendingClicks); p++ {
|
for p = 0; p < len(s.pendingClicks); p++ {
|
||||||
cycle := s.pendingClicks[p]
|
cycle := s.pendingClicks[p]
|
||||||
if cycle < s.lastCycle {
|
if cycle < s.lastCycle {
|
||||||
|
@ -119,10 +123,26 @@ func SpeakerCallback(userdata unsafe.Pointer, stream *C.Uint8, length C.int) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the buffer is empty lets decay the signal
|
||||||
|
if i == 0 {
|
||||||
|
for level != decayLevel && i < bufferSize {
|
||||||
|
if i%100 == 0 {
|
||||||
|
if level > decayLevel {
|
||||||
|
level--
|
||||||
|
} else {
|
||||||
|
level++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf[i] = level
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Complete the buffer if needed
|
// Complete the buffer if needed
|
||||||
for b := i; b < bufferSize; b++ {
|
for b := i; b < bufferSize; b++ {
|
||||||
buf[b] = level
|
buf[b] = level
|
||||||
}
|
}
|
||||||
|
s.lastLevel = level
|
||||||
|
|
||||||
// Remove processed clicks, store the rest for later
|
// Remove processed clicks, store the rest for later
|
||||||
remainingClicks := len(s.pendingClicks) - p
|
remainingClicks := len(s.pendingClicks) - p
|
||||||
|
|
|
@ -33,7 +33,7 @@ const (
|
||||||
ioC8Off uint16 = 0xCFFF
|
ioC8Off uint16 = 0xCFFF
|
||||||
)
|
)
|
||||||
|
|
||||||
func (mmu *memoryManager) access(address uint16, activeMemory [256]memoryHandler) memoryHandler {
|
func (mmu *memoryManager) access(address uint16, activeMemory *[256]memoryHandler) memoryHandler {
|
||||||
if address == ioC8Off {
|
if address == ioC8Off {
|
||||||
mmu.resetSlotExpansionRoms()
|
mmu.resetSlotExpansionRoms()
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,7 @@ func (mmu *memoryManager) access(address uint16, activeMemory [256]memoryHandler
|
||||||
|
|
||||||
// Peek returns the data on the given address
|
// Peek returns the data on the given address
|
||||||
func (mmu *memoryManager) Peek(address uint16) uint8 {
|
func (mmu *memoryManager) Peek(address uint16) uint8 {
|
||||||
mh := mmu.access(address, mmu.activeMemoryRead)
|
mh := mmu.access(address, &mmu.activeMemoryRead)
|
||||||
if mh == nil {
|
if mh == nil {
|
||||||
return 0xf4 // Or some random number
|
return 0xf4 // Or some random number
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,7 @@ func (mmu *memoryManager) Peek(address uint16) uint8 {
|
||||||
|
|
||||||
// Poke sets the data at the given address
|
// Poke sets the data at the given address
|
||||||
func (mmu *memoryManager) Poke(address uint16, value uint8) {
|
func (mmu *memoryManager) Poke(address uint16, value uint8) {
|
||||||
mh := mmu.access(address, mmu.activeMemoryWrite)
|
mh := mmu.access(address, &mmu.activeMemoryWrite)
|
||||||
if mh == nil {
|
if mh == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user