CPU clock speed emulation

This commit is contained in:
Ivan Izaguirre 2019-05-04 19:49:11 +02:00
parent 240cfbae9b
commit 78ff401ff0
6 changed files with 79 additions and 28 deletions

View File

@ -51,12 +51,12 @@ func (fe *ansiConsoleFrontend) GetKey(strobed bool) (key uint8, ok bool) {
stdinReader := func(c chan uint8) {
reader := bufio.NewReader(os.Stdin)
for {
byte, err := reader.ReadByte()
key, err := reader.ReadByte()
if err != nil {
fmt.Println(err)
return
}
c <- byte
c <- key
}
}

View File

@ -4,22 +4,31 @@ import (
"bufio"
"go6502/core6502"
"os"
"time"
)
// Apple2 represents all the components and state of the emulated machine
type Apple2 struct {
cpu *core6502.State
mmu *memoryManager
io *ioC0Page
cg *CharacterGenerator
cards []cardBase
isApple2e bool
panicSS bool
activeSlot int // Slot that has the addressing 0xc800 to 0ccfff
cpu *core6502.State
mmu *memoryManager
io *ioC0Page
cg *CharacterGenerator
cards []cardBase
isApple2e bool
panicSS bool
activeSlot int // Slot that has the addressing 0xc800 to 0ccfff
commandChannel chan int
cycleDurationNs float64 // Inverse of the cpu clock in Ghz
}
const (
// CpuClockMhz is the actual Apple II clock speed
CpuClockMhz = 14.318 / 14
cpuClockEuroMhz = 14.238 / 14
)
// NewApple2 instantiates an apple2
func NewApple2(romFile string, charRomFile string, panicSS bool) *Apple2 {
func NewApple2(romFile string, charRomFile string, clockMhz float64, panicSS bool) *Apple2 {
var a Apple2
a.mmu = newMemoryManager(&a)
a.cpu = core6502.NewNMOS6502(a.mmu)
@ -27,9 +36,17 @@ func NewApple2(romFile string, charRomFile string, panicSS bool) *Apple2 {
if charRomFile != "" {
a.cg = NewCharacterGenerator(charRomFile)
}
a.mmu.resetPaging()
a.mmu.resetRomPaging()
a.commandChannel = make(chan int, 100)
a.panicSS = panicSS
if clockMhz <= 0 {
// Full speed
a.cycleDurationNs = 0
} else {
a.cycleDurationNs = 1000.0 / clockMhz
}
// Set the io in 0xc000
a.io = newIoC0Page(&a)
a.mmu.setPage(0xc0, a.io)
@ -65,18 +82,50 @@ func (a *Apple2) ConfigureStdConsole(stdinKeyboard bool, stdoutScreen bool) {
}
}
// SetKeyboardProvider attaches an external keyboard provider
func (a *Apple2) SetKeyboardProvider(kb KeyboardProvider) {
a.io.setKeyboardProvider(kb)
}
// SendCommand enqueues a command to the emulator thread
func (a *Apple2) SendCommand(command int) {
a.commandChannel <- command
}
func (a *Apple2) executeCommand(command int) {
//TODO
}
// Run starts the Apple2 emulation
func (a *Apple2) Run(log bool) {
// Start the processor
a.cpu.Reset()
startTime := time.Now()
for {
// Run a 6502 step
a.cpu.ExecuteInstruction(log)
}
}
// SetKeyboardProvider attaches an external keyboard provider
func (a *Apple2) SetKeyboardProvider(kb KeyboardProvider) {
a.io.setKeyboardProvider(kb)
// Execute meta commands
commandsPending := true
for commandsPending {
select {
case command := <-a.commandChannel:
a.executeCommand(command)
default:
commandsPending = false
}
}
if a.cycleDurationNs != 0 {
// Wait until next 6502 step has to run
clockDuration := time.Since(startTime)
simulatedDurationNs := time.Duration(float64(a.cpu.GetCycles()) * a.cycleDurationNs)
waitDuration := simulatedDurationNs - clockDuration
if waitDuration > 0 {
time.Sleep(waitDuration)
}
}
}
}
// LoadRom loads a binary file to the top of the memory.

View File

@ -98,7 +98,7 @@ func newMemoryManager(a *Apple2) *memoryManager {
return &mmu
}
func (mmu *memoryManager) resetPaging() {
func (mmu *memoryManager) resetRomPaging() {
// Assign the first 12kb of ROM from 0xd000 to 0xfff
for i := 0xd0; i <= 0xff; i++ {
mmu.setPage(uint8(i), &(mmu.physicalROM[i-0xd0]))

View File

@ -68,7 +68,7 @@ func SDLRun(a *apple2.Apple2) {
surface.Free()
texture.Destroy()
}
sdl.Delay(1000 / 60)
sdl.Delay(1000 / 30)
}
}

View File

@ -69,9 +69,15 @@ func (s *State) ExecuteInstruction(log bool) {
// Reset resets the processor state. Moves the program counter to the vector in 0cfffc.
func (s *State) Reset() {
startAddress := getWord(s.mem, vectorReset)
s.cycles = 0
s.reg.setPC(startAddress)
}
// GetCycles returns the count of CPU cycles since last reset.
func (s *State) GetCycles() int64 {
return s.cycles
}
func lineString(line []uint8, opcode opcode) string {
t := opcode.name
switch opcode.addressMode {

14
main.go
View File

@ -19,6 +19,10 @@ func main() {
"disk",
"../dos33.dsk",
"file to load on the first disk drive")
cpuClock := flag.Float64(
"mhz",
apple2.CpuClockMhz,
"cpu speed in Mhz, use 0 for full speed")
charRomFile := flag.String(
"charRom",
"apple2/romdumps/Apple2rev7CharGen.rom",
@ -42,14 +46,6 @@ func main() {
)
flag.Parse()
//romFile := "apple2/romdumps/Apple2.rom"
//romFile := "apple2/romdumps/Apple2_Plus.rom"
//romFile := "apple2/romdumps/Apple2e.rom"
//disk2RomFile := "apple2/romdumps/DISK2.rom"
//diskImage := "../dos33.dsk"
//diskImage := "../Apex II - Apple II Diagnostic (v4.7-1986).DSK"
//diskImage := "../A2Diag.v4.1.SDK"
if *dumpChars {
cg := apple2.NewCharacterGenerator(*charRomFile)
cg.Dump()
@ -57,7 +53,7 @@ func main() {
}
log := false
a := apple2.NewApple2(*romFile, *charRomFile, *panicSS)
a := apple2.NewApple2(*romFile, *charRomFile, *cpuClock, *panicSS)
a.AddDisk2(*disk2RomFile, *diskImage)
if *useSdl {
a.ConfigureStdConsole(false, *stdoutScreen)