CPU clock speed emulation
This commit is contained in:
parent
240cfbae9b
commit
78ff401ff0
|
@ -51,12 +51,12 @@ func (fe *ansiConsoleFrontend) GetKey(strobed bool) (key uint8, ok bool) {
|
||||||
stdinReader := func(c chan uint8) {
|
stdinReader := func(c chan uint8) {
|
||||||
reader := bufio.NewReader(os.Stdin)
|
reader := bufio.NewReader(os.Stdin)
|
||||||
for {
|
for {
|
||||||
byte, err := reader.ReadByte()
|
key, err := reader.ReadByte()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c <- byte
|
c <- key
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,22 +4,31 @@ import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"go6502/core6502"
|
"go6502/core6502"
|
||||||
"os"
|
"os"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 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 {
|
||||||
cpu *core6502.State
|
cpu *core6502.State
|
||||||
mmu *memoryManager
|
mmu *memoryManager
|
||||||
io *ioC0Page
|
io *ioC0Page
|
||||||
cg *CharacterGenerator
|
cg *CharacterGenerator
|
||||||
cards []cardBase
|
cards []cardBase
|
||||||
isApple2e bool
|
isApple2e bool
|
||||||
panicSS bool
|
panicSS bool
|
||||||
activeSlot int // Slot that has the addressing 0xc800 to 0ccfff
|
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
|
// 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
|
var a Apple2
|
||||||
a.mmu = newMemoryManager(&a)
|
a.mmu = newMemoryManager(&a)
|
||||||
a.cpu = core6502.NewNMOS6502(a.mmu)
|
a.cpu = core6502.NewNMOS6502(a.mmu)
|
||||||
|
@ -27,9 +36,17 @@ func NewApple2(romFile string, charRomFile string, panicSS bool) *Apple2 {
|
||||||
if charRomFile != "" {
|
if charRomFile != "" {
|
||||||
a.cg = NewCharacterGenerator(charRomFile)
|
a.cg = NewCharacterGenerator(charRomFile)
|
||||||
}
|
}
|
||||||
a.mmu.resetPaging()
|
a.mmu.resetRomPaging()
|
||||||
|
a.commandChannel = make(chan int, 100)
|
||||||
a.panicSS = panicSS
|
a.panicSS = panicSS
|
||||||
|
|
||||||
|
if clockMhz <= 0 {
|
||||||
|
// Full speed
|
||||||
|
a.cycleDurationNs = 0
|
||||||
|
} else {
|
||||||
|
a.cycleDurationNs = 1000.0 / clockMhz
|
||||||
|
}
|
||||||
|
|
||||||
// Set the io in 0xc000
|
// Set the io in 0xc000
|
||||||
a.io = newIoC0Page(&a)
|
a.io = newIoC0Page(&a)
|
||||||
a.mmu.setPage(0xc0, a.io)
|
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
|
// Run starts the Apple2 emulation
|
||||||
func (a *Apple2) Run(log bool) {
|
func (a *Apple2) Run(log bool) {
|
||||||
// Start the processor
|
// Start the processor
|
||||||
a.cpu.Reset()
|
a.cpu.Reset()
|
||||||
|
startTime := time.Now()
|
||||||
for {
|
for {
|
||||||
|
// Run a 6502 step
|
||||||
a.cpu.ExecuteInstruction(log)
|
a.cpu.ExecuteInstruction(log)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetKeyboardProvider attaches an external keyboard provider
|
// Execute meta commands
|
||||||
func (a *Apple2) SetKeyboardProvider(kb KeyboardProvider) {
|
commandsPending := true
|
||||||
a.io.setKeyboardProvider(kb)
|
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.
|
// LoadRom loads a binary file to the top of the memory.
|
||||||
|
|
|
@ -98,7 +98,7 @@ func newMemoryManager(a *Apple2) *memoryManager {
|
||||||
return &mmu
|
return &mmu
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mmu *memoryManager) resetPaging() {
|
func (mmu *memoryManager) resetRomPaging() {
|
||||||
// Assign the first 12kb of ROM from 0xd000 to 0xfff
|
// Assign the first 12kb of ROM from 0xd000 to 0xfff
|
||||||
for i := 0xd0; i <= 0xff; i++ {
|
for i := 0xd0; i <= 0xff; i++ {
|
||||||
mmu.setPage(uint8(i), &(mmu.physicalROM[i-0xd0]))
|
mmu.setPage(uint8(i), &(mmu.physicalROM[i-0xd0]))
|
||||||
|
|
|
@ -68,7 +68,7 @@ func SDLRun(a *apple2.Apple2) {
|
||||||
surface.Free()
|
surface.Free()
|
||||||
texture.Destroy()
|
texture.Destroy()
|
||||||
}
|
}
|
||||||
sdl.Delay(1000 / 60)
|
sdl.Delay(1000 / 30)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,9 +69,15 @@ func (s *State) ExecuteInstruction(log bool) {
|
||||||
// Reset resets the processor state. Moves the program counter to the vector in 0cfffc.
|
// Reset resets the processor state. Moves the program counter to the vector in 0cfffc.
|
||||||
func (s *State) Reset() {
|
func (s *State) Reset() {
|
||||||
startAddress := getWord(s.mem, vectorReset)
|
startAddress := getWord(s.mem, vectorReset)
|
||||||
|
s.cycles = 0
|
||||||
s.reg.setPC(startAddress)
|
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 {
|
func lineString(line []uint8, opcode opcode) string {
|
||||||
t := opcode.name
|
t := opcode.name
|
||||||
switch opcode.addressMode {
|
switch opcode.addressMode {
|
||||||
|
|
14
main.go
14
main.go
|
@ -19,6 +19,10 @@ func main() {
|
||||||
"disk",
|
"disk",
|
||||||
"../dos33.dsk",
|
"../dos33.dsk",
|
||||||
"file to load on the first disk drive")
|
"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(
|
charRomFile := flag.String(
|
||||||
"charRom",
|
"charRom",
|
||||||
"apple2/romdumps/Apple2rev7CharGen.rom",
|
"apple2/romdumps/Apple2rev7CharGen.rom",
|
||||||
|
@ -42,14 +46,6 @@ func main() {
|
||||||
)
|
)
|
||||||
flag.Parse()
|
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 {
|
if *dumpChars {
|
||||||
cg := apple2.NewCharacterGenerator(*charRomFile)
|
cg := apple2.NewCharacterGenerator(*charRomFile)
|
||||||
cg.Dump()
|
cg.Dump()
|
||||||
|
@ -57,7 +53,7 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
log := false
|
log := false
|
||||||
a := apple2.NewApple2(*romFile, *charRomFile, *panicSS)
|
a := apple2.NewApple2(*romFile, *charRomFile, *cpuClock, *panicSS)
|
||||||
a.AddDisk2(*disk2RomFile, *diskImage)
|
a.AddDisk2(*disk2RomFile, *diskImage)
|
||||||
if *useSdl {
|
if *useSdl {
|
||||||
a.ConfigureStdConsole(false, *stdoutScreen)
|
a.ConfigureStdConsole(false, *stdoutScreen)
|
||||||
|
|
Loading…
Reference in New Issue