Added comments to mmu package

This commit is contained in:
Will Angenent 2018-05-28 16:30:01 +01:00
parent b549d0e33d
commit 5e330c1373
3 changed files with 111 additions and 45 deletions

View File

@ -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
View File

@ -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))
}

View File

@ -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 {