2019-02-16 19:15:41 +00:00
|
|
|
package apple2
|
|
|
|
|
2019-03-02 17:33:50 +00:00
|
|
|
import (
|
|
|
|
"bufio"
|
|
|
|
"go6502/core6502"
|
|
|
|
"os"
|
|
|
|
)
|
2019-02-16 19:15:41 +00:00
|
|
|
|
2019-03-02 19:41:25 +00:00
|
|
|
// Apple2 represents all the components and state of the emulated machine
|
2019-03-02 17:33:50 +00:00
|
|
|
type Apple2 struct {
|
|
|
|
cpu *core6502.State
|
|
|
|
mmu *memoryManager
|
2019-03-02 19:41:25 +00:00
|
|
|
io *ioC0Page
|
|
|
|
cards []cardBase
|
2019-03-02 17:33:50 +00:00
|
|
|
isApple2e bool
|
2019-03-02 19:41:25 +00:00
|
|
|
activeSlot int // Slot that has the addressing 0xc800 to 0ccfff
|
2019-03-02 17:33:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewApple2 instantiates an apple2
|
|
|
|
func NewApple2(romFile string) *Apple2 {
|
|
|
|
var a Apple2
|
|
|
|
a.mmu = newMemoryManager(&a)
|
|
|
|
a.cpu = core6502.NewNMOS6502(a.mmu)
|
|
|
|
a.loadRom(romFile)
|
|
|
|
a.mmu.resetPaging()
|
|
|
|
|
|
|
|
// Set the io in 0xc000
|
2019-03-02 19:41:25 +00:00
|
|
|
a.io = newIoC0Page(&a)
|
|
|
|
a.mmu.setPage(0xc0, a.io)
|
2019-03-02 17:33:50 +00:00
|
|
|
|
|
|
|
return &a
|
|
|
|
}
|
2019-02-28 22:54:38 +00:00
|
|
|
|
2019-03-02 19:41:25 +00:00
|
|
|
// AddDisk2 insterts a DiskII controller on slot 6
|
2019-03-04 23:00:12 +00:00
|
|
|
func (a *Apple2) AddDisk2(diskRomFile string, diskImage string) {
|
2019-03-02 19:41:25 +00:00
|
|
|
d := newCardDisk2(diskRomFile)
|
|
|
|
d.cardBase.insert(a, 6)
|
2019-03-04 23:00:12 +00:00
|
|
|
|
|
|
|
if diskImage != "" {
|
2019-03-10 23:12:34 +00:00
|
|
|
diskette := loadDisquette(diskImage)
|
|
|
|
//diskette.saveNib(diskImage + "bak")
|
|
|
|
d.drive[0].insertDiskette(diskette)
|
2019-03-04 23:00:12 +00:00
|
|
|
}
|
2019-03-02 19:41:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Run starts the Apple2 emulation
|
2019-04-13 18:29:31 +00:00
|
|
|
func (a *Apple2) Run(log bool, consoleKeyboard bool) {
|
2019-03-02 17:33:50 +00:00
|
|
|
// Init frontend
|
|
|
|
fe := newAnsiConsoleFrontend(a)
|
2019-04-13 18:29:31 +00:00
|
|
|
if consoleKeyboard {
|
|
|
|
a.io.setKeyboardProvider(fe)
|
|
|
|
}
|
2019-03-02 19:41:25 +00:00
|
|
|
if !log {
|
|
|
|
go fe.textModeGoRoutine()
|
|
|
|
}
|
2019-02-20 22:51:47 +00:00
|
|
|
|
2019-02-16 19:15:41 +00:00
|
|
|
// Start the processor
|
2019-03-02 17:33:50 +00:00
|
|
|
a.cpu.Reset()
|
2019-02-17 23:00:39 +00:00
|
|
|
for {
|
2019-03-02 17:33:50 +00:00
|
|
|
a.cpu.ExecuteInstruction(log)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-13 18:29:31 +00:00
|
|
|
// SetKeyboardProvider attaches an external keyboard provider
|
|
|
|
func (a *Apple2) SetKeyboardProvider(kb KeyboardProvider) {
|
|
|
|
a.io.setKeyboardProvider(kb)
|
|
|
|
}
|
|
|
|
|
2019-03-02 17:33:50 +00:00
|
|
|
// LoadRom loads a binary file to the top of the memory.
|
|
|
|
const (
|
|
|
|
apple2RomSize = 12 * 1024
|
|
|
|
apple2eRomSize = 16 * 1024
|
|
|
|
)
|
|
|
|
|
|
|
|
func (a *Apple2) loadRom(filename string) {
|
|
|
|
f, err := os.Open(filename)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
stats, statsErr := f.Stat()
|
|
|
|
if statsErr != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
size := stats.Size()
|
|
|
|
if size != apple2RomSize && size != apple2eRomSize {
|
|
|
|
panic("Rom size not supported")
|
|
|
|
}
|
|
|
|
bytes := make([]byte, size)
|
|
|
|
buf := bufio.NewReader(f)
|
|
|
|
buf.Read(bytes)
|
|
|
|
|
|
|
|
romStart := 0
|
|
|
|
if size == apple2eRomSize {
|
|
|
|
// The extra 4kb ROM is first in the rom file.
|
|
|
|
// It starts with 256 unused bytes not mapped to 0xc000.
|
|
|
|
a.isApple2e = true
|
|
|
|
extraRomSize := apple2eRomSize - apple2RomSize
|
|
|
|
a.mmu.physicalROMe = make([]romPage, extraRomSize>>8)
|
|
|
|
for i := 0; i < extraRomSize; i++ {
|
|
|
|
a.mmu.physicalROMe[i>>8].burn(uint8(i), bytes[i])
|
|
|
|
}
|
|
|
|
romStart = extraRomSize
|
|
|
|
}
|
|
|
|
|
|
|
|
a.mmu.physicalROM = make([]romPage, apple2RomSize>>8)
|
|
|
|
for i := 0; i < apple2RomSize; i++ {
|
|
|
|
a.mmu.physicalROM[i>>8].burn(uint8(i), bytes[i+romStart])
|
2019-02-16 19:15:41 +00:00
|
|
|
}
|
|
|
|
}
|