Added comments to mmu package
This commit is contained in:
parent
b549d0e33d
commit
5e330c1373
|
@ -898,7 +898,7 @@ func SetColdStartReset() {
|
|||
|
||||
// Reset sets the CPU and memory states so that a next call to cpu.Run() calls the firmware reset code
|
||||
func Reset() {
|
||||
mmu.InitROM()
|
||||
mmu.InitROM() // Set upper memory area for reading from ROM
|
||||
mmu.InitRAM()
|
||||
|
||||
bootVector := 0xfffc
|
||||
|
|
118
mmu/io.go
118
mmu/io.go
|
@ -93,6 +93,13 @@ const (
|
|||
S6Q7H = 0xC0EF // write
|
||||
)
|
||||
|
||||
// VideoState has 3 booleans which determine the video configuration:
|
||||
// TextMode HiresMode Mixed
|
||||
// text 1 0 N/A
|
||||
// lores + text 0 0 1
|
||||
// lores 0 0 0
|
||||
// hires N/A 1 0
|
||||
// hires + text N/A 1 1
|
||||
var VideoState struct {
|
||||
TextMode bool
|
||||
HiresMode bool
|
||||
|
@ -113,6 +120,7 @@ func InitIO() {
|
|||
system.DriveState.Q6 = false
|
||||
system.DriveState.Q7 = false
|
||||
|
||||
// Initialize video
|
||||
VideoState.TextMode = true
|
||||
VideoState.HiresMode = false
|
||||
VideoState.Mixed = false
|
||||
|
@ -120,12 +128,8 @@ func InitIO() {
|
|||
disk.InitDiskImage()
|
||||
}
|
||||
|
||||
func driveIsreadSequencing() bool {
|
||||
return (!system.DriveState.Q6) && (!system.DriveState.Q7)
|
||||
}
|
||||
|
||||
// Handle soft switch addresses where both a read and a write has a side
|
||||
// effect and the return value is meaningless
|
||||
// Handle soft switch addresses between $c000-$c0ff where both a read and a write has a side
|
||||
// effect. Returns true if the read/write has been handled.
|
||||
func readWrite(address uint16, isRead bool) bool {
|
||||
lsb := address & 0xff
|
||||
if lsb >= 0x80 && lsb < 0x90 {
|
||||
|
@ -140,48 +144,56 @@ func readWrite(address uint16, isRead bool) bool {
|
|||
case SETAUXRD:
|
||||
SetFakeAuxMemoryRead(true)
|
||||
return true
|
||||
|
||||
case CLRAUXWR:
|
||||
SetFakeAuxMemoryWrite(false)
|
||||
return true
|
||||
case SETAUXWR:
|
||||
SetFakeAuxMemoryWrite(true)
|
||||
return true
|
||||
|
||||
case CLRAUXZP:
|
||||
SetFakeAltZP(false)
|
||||
return true
|
||||
case SETAUXZP:
|
||||
SetFakeAltZP(true)
|
||||
return true
|
||||
|
||||
case CLR80VID:
|
||||
SetCol80(false)
|
||||
return true
|
||||
case SET80VID:
|
||||
SetCol80(true)
|
||||
return true
|
||||
|
||||
case TXTPAGE1:
|
||||
SetPage2(false)
|
||||
return true
|
||||
case TXTPAGE2:
|
||||
SetPage2(true)
|
||||
return true
|
||||
|
||||
case CLRTEXT:
|
||||
VideoState.TextMode = false
|
||||
return true
|
||||
case SETTEXT:
|
||||
VideoState.TextMode = true
|
||||
return true
|
||||
|
||||
case CLRMIXED:
|
||||
VideoState.Mixed = false
|
||||
return true
|
||||
case SETMIXED:
|
||||
VideoState.Mixed = true
|
||||
return true
|
||||
|
||||
case CLRHIRES:
|
||||
VideoState.HiresMode = false
|
||||
return true
|
||||
case SETHIRES:
|
||||
VideoState.HiresMode = true
|
||||
return true
|
||||
|
||||
case CLR80COL:
|
||||
if !isRead {
|
||||
SetStore80(false)
|
||||
|
@ -192,6 +204,7 @@ func readWrite(address uint16, isRead bool) bool {
|
|||
case SET80COL:
|
||||
SetStore80(true)
|
||||
return true
|
||||
|
||||
case STATEREG:
|
||||
// Ignore not implemented memory management reg
|
||||
return true
|
||||
|
@ -200,37 +213,40 @@ func readWrite(address uint16, isRead bool) bool {
|
|||
case S6CLRDRVP0, S6SETDRVP0, S6CLRDRVP1, S6SETDRVP1, S6CLRDRVP2, S6SETDRVP2, S6CLRDRVP3, S6SETDRVP3:
|
||||
magnet := (address - S6CLRDRVP0) / 2
|
||||
on := ((address - S6CLRDRVP0) % 2) == 1
|
||||
|
||||
if on {
|
||||
system.DriveState.Phases |= (1 << magnet)
|
||||
|
||||
// Move head if a neighboring magnet is on and all others are off
|
||||
direction := int8(0)
|
||||
if (system.DriveState.Phases & (1 << uint8((system.DriveState.Phase+1)&3))) != 0 {
|
||||
direction += 1
|
||||
}
|
||||
if (system.DriveState.Phases & (1 << uint8((system.DriveState.Phase+3)&3))) != 0 {
|
||||
direction -= 1
|
||||
}
|
||||
|
||||
if direction != 0 {
|
||||
system.DriveState.Phase += direction
|
||||
|
||||
if system.DriveState.Phase < 0 {
|
||||
system.DriveState.Phase = 0
|
||||
}
|
||||
if system.DriveState.Phase == 80 {
|
||||
system.DriveState.Phase = 79
|
||||
}
|
||||
|
||||
disk.MakeTrackData(uint8(system.DriveState.Phase))
|
||||
|
||||
if audio.ClickWhenDriveHeadMoves {
|
||||
audio.Click()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if !on {
|
||||
// Turn off the magnet in Phases
|
||||
system.DriveState.Phases &= ^(1 << magnet)
|
||||
return true
|
||||
}
|
||||
|
||||
// Implicit else, a magnet has been switched on
|
||||
system.DriveState.Phases |= (1 << magnet)
|
||||
|
||||
// Move head if a neighboring magnet is on and all others are off
|
||||
direction := int8(0)
|
||||
if (system.DriveState.Phases & (1 << uint8((system.DriveState.Phase+1)&3))) != 0 {
|
||||
direction += 1
|
||||
}
|
||||
if (system.DriveState.Phases & (1 << uint8((system.DriveState.Phase+3)&3))) != 0 {
|
||||
direction -= 1
|
||||
}
|
||||
|
||||
// Move the head
|
||||
if direction != 0 {
|
||||
system.DriveState.Phase += direction
|
||||
|
||||
if system.DriveState.Phase < 0 {
|
||||
system.DriveState.Phase = 0
|
||||
}
|
||||
if system.DriveState.Phase == 80 {
|
||||
system.DriveState.Phase = 79
|
||||
}
|
||||
|
||||
disk.MakeTrackData(uint8(system.DriveState.Phase))
|
||||
|
||||
if audio.ClickWhenDriveHeadMoves {
|
||||
audio.Click()
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
|
@ -241,12 +257,14 @@ func readWrite(address uint16, isRead bool) bool {
|
|||
case S6MOTORON:
|
||||
system.DriveState.Spinning = true
|
||||
return true
|
||||
|
||||
case S6SELDRV1:
|
||||
system.DriveState.Drive = 1
|
||||
return true
|
||||
case S6SELDRV2:
|
||||
system.DriveState.Drive = 2
|
||||
return true
|
||||
|
||||
case S6Q6L:
|
||||
if !isRead {
|
||||
system.DriveState.Q6 = false
|
||||
|
@ -259,6 +277,7 @@ func readWrite(address uint16, isRead bool) bool {
|
|||
return true
|
||||
}
|
||||
return false
|
||||
|
||||
case S6Q7L:
|
||||
system.DriveState.Q7 = false
|
||||
return true
|
||||
|
@ -271,12 +290,15 @@ func readWrite(address uint16, isRead bool) bool {
|
|||
}
|
||||
}
|
||||
|
||||
// ReadIO does a read in the $c000-$c0ff area
|
||||
func ReadIO(address uint16) uint8 {
|
||||
// Try the generic readWrite and return if it has handled the read
|
||||
if readWrite(address, true) {
|
||||
return 0
|
||||
}
|
||||
|
||||
switch address {
|
||||
|
||||
case KEYBOARD, STROBE:
|
||||
keyBoardData, strobe := keyboard.Read()
|
||||
if address == KEYBOARD {
|
||||
|
@ -285,15 +307,18 @@ func ReadIO(address uint16) uint8 {
|
|||
keyboard.ResetStrobe()
|
||||
return strobe
|
||||
}
|
||||
|
||||
case RDRAMRD, RDRAMWR, RDAUXZP:
|
||||
panic("Read/write aux memory not implemented")
|
||||
return 0x0d
|
||||
|
||||
case RDCXROM:
|
||||
if UsingExternalSlotRom {
|
||||
return 0x8d
|
||||
} else {
|
||||
return 0x0d
|
||||
}
|
||||
|
||||
case RD80VID:
|
||||
// using 80-column display mode not implemented
|
||||
return 0x0d
|
||||
|
@ -308,24 +333,33 @@ func ReadIO(address uint16) uint8 {
|
|||
// 4-bit annunciator inputs
|
||||
case SETAN0, CLRAN0, SETAN1, CLRAN1, SETAN2, CLRAN2, SETAN3, CLRAN3:
|
||||
// Annunciators not implemented
|
||||
|
||||
case OPNAPPLE:
|
||||
// Open apple key not implemented
|
||||
return 0
|
||||
|
||||
case CLSAPPLE:
|
||||
// Closed apple key not implemented
|
||||
|
||||
case RD80COL:
|
||||
if Store80 {
|
||||
return 0x8d
|
||||
} else {
|
||||
return 0x0d
|
||||
}
|
||||
|
||||
case RDALTCH:
|
||||
// RDALTCH not implemented
|
||||
// RDALTCH not implemented, but it's also used, so don't fail on it.
|
||||
return 0x0d
|
||||
|
||||
case SPEAKER:
|
||||
audio.Click()
|
||||
return 0
|
||||
|
||||
case S6Q6L:
|
||||
// A read from disk
|
||||
return disk.ReadTrackData()
|
||||
|
||||
default:
|
||||
panic(fmt.Sprintf("TODO read %04x\n", address))
|
||||
}
|
||||
|
@ -333,31 +367,41 @@ func ReadIO(address uint16) uint8 {
|
|||
return 0
|
||||
}
|
||||
|
||||
// ReadIO does a write in the $c000-$c0ff area
|
||||
func WriteIO(address uint16, value uint8) {
|
||||
// Try the generic readWrite and return if it has handled the write
|
||||
if readWrite(address, false) {
|
||||
return
|
||||
}
|
||||
|
||||
switch address {
|
||||
|
||||
case STROBE:
|
||||
keyboard.ResetStrobe()
|
||||
|
||||
case CLRCXROM:
|
||||
MapFirstHalfOfIO()
|
||||
case SETCXROM:
|
||||
MapSecondHalfOfIO()
|
||||
|
||||
case CLRALTCH:
|
||||
return
|
||||
case SETALTCH:
|
||||
panic("SETALTCH not implemented")
|
||||
|
||||
case CLR80COL:
|
||||
// CLR80COL not implemented
|
||||
return
|
||||
|
||||
case CLRC3ROM:
|
||||
// CLRC3ROM not implemented
|
||||
case SETC3ROM:
|
||||
// SETC3ROM not implemented
|
||||
|
||||
case S6Q6H:
|
||||
// A write to disk
|
||||
disk.WriteTrackData(value)
|
||||
|
||||
default:
|
||||
panic(fmt.Sprintf("TODO write %04x\n", address))
|
||||
}
|
||||
|
|
36
mmu/mmu.go
36
mmu/mmu.go
|
@ -7,16 +7,18 @@ import (
|
|||
"github.com/freewilll/apple2/system"
|
||||
)
|
||||
|
||||
const RomPath = "apple2e.rom"
|
||||
const StackPage = 1
|
||||
const RomPath = "apple2e.rom" // So far only one ROM is supported and it's loaded at startup
|
||||
const StackPage = 1 // The 6502 stack is at 0x100
|
||||
|
||||
// PhysicalMemory contains all the unmapped memory, ROM and RAM
|
||||
var PhysicalMemory struct {
|
||||
MainMemory [0x10000]uint8
|
||||
UpperROM [0x3000]uint8
|
||||
RomC1 [0x1000]uint8
|
||||
RomC2 [0x1000]uint8
|
||||
MainMemory [0x10000]uint8 // Main RAM
|
||||
UpperROM [0x3000]uint8 // $c000-$ffff ROM area
|
||||
RomC1 [0x1000]uint8 // First half of IO ROM
|
||||
RomC2 [0x1000]uint8 // Second half of IO ROM
|
||||
}
|
||||
|
||||
// Page tables for read & write
|
||||
var ReadPageTable [0x100][]uint8
|
||||
var WritePageTable [0x100][]uint8
|
||||
|
||||
|
@ -35,6 +37,7 @@ var (
|
|||
Page2 bool // Main memory Page2 is selected
|
||||
)
|
||||
|
||||
// Make page tables for current RAM, ROM and IO configuration
|
||||
func ApplyMemoryConfiguration() {
|
||||
// Map main RAM for read/write
|
||||
for i := 0x0; i < 0xc0; i++ {
|
||||
|
@ -102,7 +105,7 @@ func MapSecondHalfOfIO() {
|
|||
ApplyMemoryConfiguration()
|
||||
}
|
||||
|
||||
// emptySlot zeroes all RAM for a slot
|
||||
// emptySlot zeroes all RAM for a slot, effectively disabling the slot
|
||||
func emptySlot(slot int) {
|
||||
for i := slot * 0x100; i < (slot+1)*0x100; i++ {
|
||||
PhysicalMemory.RomC1[i] = 0
|
||||
|
@ -134,6 +137,7 @@ func InitApple2eROM() {
|
|||
InitROM() // Map 0xd000-0xffff for reading
|
||||
}
|
||||
|
||||
// Set upper memory area for reading from ROM
|
||||
func InitROM() {
|
||||
UpperReadMappedToROM = true
|
||||
ApplyMemoryConfiguration()
|
||||
|
@ -149,31 +153,39 @@ func SetUpperRamReadOnly(value bool) {
|
|||
ApplyMemoryConfiguration()
|
||||
}
|
||||
|
||||
// Set d000 bank to map to $c000 or $d000 in the physical memory
|
||||
func SetD000Bank(value int) {
|
||||
D000Bank = value
|
||||
ApplyMemoryConfiguration()
|
||||
}
|
||||
|
||||
// Aux memory hasn't been implemented. If aux memory is selected, and a read
|
||||
// is attempted, then nonsense must be returned.
|
||||
func SetFakeAuxMemoryRead(value bool) {
|
||||
FakeAuxMemoryRead = value
|
||||
ApplyMemoryConfiguration()
|
||||
}
|
||||
|
||||
// Aux memory hasn't been implemented. If aux memory is selected, and a write
|
||||
// is attempted, then it must be ignored.
|
||||
func SetFakeAuxMemoryWrite(value bool) {
|
||||
FakeAuxMemoryWrite = value
|
||||
ApplyMemoryConfiguration()
|
||||
}
|
||||
|
||||
// Alternate zero page isn't implemented
|
||||
func SetFakeAltZP(value bool) {
|
||||
FakeAltZP = value
|
||||
ApplyMemoryConfiguration()
|
||||
}
|
||||
|
||||
// 80 column card isn't implemented
|
||||
func SetCol80(value bool) {
|
||||
Col80 = value
|
||||
// No changes are needed when this is toggled
|
||||
}
|
||||
|
||||
// Page switching is only implemented for the main memory
|
||||
func SetPage2(value bool) {
|
||||
// If the 80 column card is enabled, then this toggles aux memory
|
||||
// Otherwise, page1/page2 is toggled in the main memory
|
||||
|
@ -185,12 +197,14 @@ func SetPage2(value bool) {
|
|||
}
|
||||
}
|
||||
|
||||
// 80 column card isn't implemented
|
||||
func SetStore80(value bool) {
|
||||
Store80 = value
|
||||
FakePage2 = value
|
||||
ApplyMemoryConfiguration()
|
||||
}
|
||||
|
||||
// InitRAM sets all default RAM memory settings and resets the page tables
|
||||
func InitRAM() {
|
||||
UpperRamReadOnly = false
|
||||
D000Bank = 2
|
||||
|
@ -209,6 +223,7 @@ func WipeRAM() {
|
|||
}
|
||||
}
|
||||
|
||||
// SetMemoryMode is used to set UpperRamReadOnly, UpperReadMappedToROM and D000Bank number
|
||||
func SetMemoryMode(mode uint8) {
|
||||
// mode corresponds to a read/write to $c080 with
|
||||
// $c080 mode=$00
|
||||
|
@ -236,6 +251,7 @@ func SetMemoryMode(mode uint8) {
|
|||
ApplyMemoryConfiguration()
|
||||
}
|
||||
|
||||
// ReadMemory reads the ROM or RAM page table
|
||||
func ReadMemory(address uint16) uint8 {
|
||||
if (address >= 0xc000) && (address < 0xc100) {
|
||||
return ReadIO(address)
|
||||
|
@ -257,15 +273,18 @@ func ReadMemory(address uint16) uint8 {
|
|||
}
|
||||
}
|
||||
|
||||
// Implicit else, we're reading the main non-IO RAM
|
||||
return ReadPageTable[address>>8][address&0xff]
|
||||
}
|
||||
|
||||
// ReadMemory writes to the ROM or RAM page table
|
||||
func WriteMemory(address uint16, value uint8) {
|
||||
if (address >= 0xc000) && (address < 0xc100) {
|
||||
WriteIO(address, value)
|
||||
return
|
||||
}
|
||||
|
||||
// Magic routine to trigger an interrupt, used in the CPU interrupt tests
|
||||
if system.RunningInterruptTests && address == 0xbffc {
|
||||
oldValue := ReadMemory(address)
|
||||
system.WriteInterruptTestOpenCollector(address, oldValue, value)
|
||||
|
@ -284,11 +303,14 @@ func WriteMemory(address uint16, value uint8) {
|
|||
}
|
||||
|
||||
memory := WritePageTable[address>>8]
|
||||
|
||||
// If memory is nil, then it's read only. The write is ignored.
|
||||
if memory != nil {
|
||||
memory[uint8(address&0xff)] = value
|
||||
}
|
||||
|
||||
// If doing CPU functional tests, 0x200 has the test number in it. A write to
|
||||
// it means a test passed or the tests are complete.
|
||||
if system.RunningFunctionalTests && address == 0x200 {
|
||||
testNumber := ReadMemory(0x200)
|
||||
if testNumber == 0xf0 {
|
||||
|
|
Loading…
Reference in New Issue