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 // Reset sets the CPU and memory states so that a next call to cpu.Run() calls the firmware reset code
func Reset() { func Reset() {
mmu.InitROM() mmu.InitROM() // Set upper memory area for reading from ROM
mmu.InitRAM() mmu.InitRAM()
bootVector := 0xfffc bootVector := 0xfffc

118
mmu/io.go
View File

@ -93,6 +93,13 @@ const (
S6Q7H = 0xC0EF // write 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 { var VideoState struct {
TextMode bool TextMode bool
HiresMode bool HiresMode bool
@ -113,6 +120,7 @@ func InitIO() {
system.DriveState.Q6 = false system.DriveState.Q6 = false
system.DriveState.Q7 = false system.DriveState.Q7 = false
// Initialize video
VideoState.TextMode = true VideoState.TextMode = true
VideoState.HiresMode = false VideoState.HiresMode = false
VideoState.Mixed = false VideoState.Mixed = false
@ -120,12 +128,8 @@ func InitIO() {
disk.InitDiskImage() disk.InitDiskImage()
} }
func driveIsreadSequencing() bool { // Handle soft switch addresses between $c000-$c0ff where both a read and a write has a side
return (!system.DriveState.Q6) && (!system.DriveState.Q7) // effect. Returns true if the read/write has been handled.
}
// Handle soft switch addresses where both a read and a write has a side
// effect and the return value is meaningless
func readWrite(address uint16, isRead bool) bool { func readWrite(address uint16, isRead bool) bool {
lsb := address & 0xff lsb := address & 0xff
if lsb >= 0x80 && lsb < 0x90 { if lsb >= 0x80 && lsb < 0x90 {
@ -140,48 +144,56 @@ func readWrite(address uint16, isRead bool) bool {
case SETAUXRD: case SETAUXRD:
SetFakeAuxMemoryRead(true) SetFakeAuxMemoryRead(true)
return true return true
case CLRAUXWR: case CLRAUXWR:
SetFakeAuxMemoryWrite(false) SetFakeAuxMemoryWrite(false)
return true return true
case SETAUXWR: case SETAUXWR:
SetFakeAuxMemoryWrite(true) SetFakeAuxMemoryWrite(true)
return true return true
case CLRAUXZP: case CLRAUXZP:
SetFakeAltZP(false) SetFakeAltZP(false)
return true return true
case SETAUXZP: case SETAUXZP:
SetFakeAltZP(true) SetFakeAltZP(true)
return true return true
case CLR80VID: case CLR80VID:
SetCol80(false) SetCol80(false)
return true return true
case SET80VID: case SET80VID:
SetCol80(true) SetCol80(true)
return true return true
case TXTPAGE1: case TXTPAGE1:
SetPage2(false) SetPage2(false)
return true return true
case TXTPAGE2: case TXTPAGE2:
SetPage2(true) SetPage2(true)
return true return true
case CLRTEXT: case CLRTEXT:
VideoState.TextMode = false VideoState.TextMode = false
return true return true
case SETTEXT: case SETTEXT:
VideoState.TextMode = true VideoState.TextMode = true
return true return true
case CLRMIXED: case CLRMIXED:
VideoState.Mixed = false VideoState.Mixed = false
return true return true
case SETMIXED: case SETMIXED:
VideoState.Mixed = true VideoState.Mixed = true
return true return true
case CLRHIRES: case CLRHIRES:
VideoState.HiresMode = false VideoState.HiresMode = false
return true return true
case SETHIRES: case SETHIRES:
VideoState.HiresMode = true VideoState.HiresMode = true
return true return true
case CLR80COL: case CLR80COL:
if !isRead { if !isRead {
SetStore80(false) SetStore80(false)
@ -192,6 +204,7 @@ func readWrite(address uint16, isRead bool) bool {
case SET80COL: case SET80COL:
SetStore80(true) SetStore80(true)
return true return true
case STATEREG: case STATEREG:
// Ignore not implemented memory management reg // Ignore not implemented memory management reg
return true return true
@ -200,37 +213,40 @@ func readWrite(address uint16, isRead bool) bool {
case S6CLRDRVP0, S6SETDRVP0, S6CLRDRVP1, S6SETDRVP1, S6CLRDRVP2, S6SETDRVP2, S6CLRDRVP3, S6SETDRVP3: case S6CLRDRVP0, S6SETDRVP0, S6CLRDRVP1, S6SETDRVP1, S6CLRDRVP2, S6SETDRVP2, S6CLRDRVP3, S6SETDRVP3:
magnet := (address - S6CLRDRVP0) / 2 magnet := (address - S6CLRDRVP0) / 2
on := ((address - S6CLRDRVP0) % 2) == 1 on := ((address - S6CLRDRVP0) % 2) == 1
if !on {
if on { // Turn off the magnet in Phases
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 {
system.DriveState.Phases &= ^(1 << magnet) 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 return true
@ -241,12 +257,14 @@ func readWrite(address uint16, isRead bool) bool {
case S6MOTORON: case S6MOTORON:
system.DriveState.Spinning = true system.DriveState.Spinning = true
return true return true
case S6SELDRV1: case S6SELDRV1:
system.DriveState.Drive = 1 system.DriveState.Drive = 1
return true return true
case S6SELDRV2: case S6SELDRV2:
system.DriveState.Drive = 2 system.DriveState.Drive = 2
return true return true
case S6Q6L: case S6Q6L:
if !isRead { if !isRead {
system.DriveState.Q6 = false system.DriveState.Q6 = false
@ -259,6 +277,7 @@ func readWrite(address uint16, isRead bool) bool {
return true return true
} }
return false return false
case S6Q7L: case S6Q7L:
system.DriveState.Q7 = false system.DriveState.Q7 = false
return true 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 { func ReadIO(address uint16) uint8 {
// Try the generic readWrite and return if it has handled the read
if readWrite(address, true) { if readWrite(address, true) {
return 0 return 0
} }
switch address { switch address {
case KEYBOARD, STROBE: case KEYBOARD, STROBE:
keyBoardData, strobe := keyboard.Read() keyBoardData, strobe := keyboard.Read()
if address == KEYBOARD { if address == KEYBOARD {
@ -285,15 +307,18 @@ func ReadIO(address uint16) uint8 {
keyboard.ResetStrobe() keyboard.ResetStrobe()
return strobe return strobe
} }
case RDRAMRD, RDRAMWR, RDAUXZP: case RDRAMRD, RDRAMWR, RDAUXZP:
panic("Read/write aux memory not implemented") panic("Read/write aux memory not implemented")
return 0x0d return 0x0d
case RDCXROM: case RDCXROM:
if UsingExternalSlotRom { if UsingExternalSlotRom {
return 0x8d return 0x8d
} else { } else {
return 0x0d return 0x0d
} }
case RD80VID: case RD80VID:
// using 80-column display mode not implemented // using 80-column display mode not implemented
return 0x0d return 0x0d
@ -308,24 +333,33 @@ func ReadIO(address uint16) uint8 {
// 4-bit annunciator inputs // 4-bit annunciator inputs
case SETAN0, CLRAN0, SETAN1, CLRAN1, SETAN2, CLRAN2, SETAN3, CLRAN3: case SETAN0, CLRAN0, SETAN1, CLRAN1, SETAN2, CLRAN2, SETAN3, CLRAN3:
// Annunciators not implemented // Annunciators not implemented
case OPNAPPLE: case OPNAPPLE:
// Open apple key not implemented // Open apple key not implemented
return 0 return 0
case CLSAPPLE: case CLSAPPLE:
// Closed apple key not implemented // Closed apple key not implemented
case RD80COL: case RD80COL:
if Store80 { if Store80 {
return 0x8d return 0x8d
} else { } else {
return 0x0d return 0x0d
} }
case RDALTCH: case RDALTCH:
// RDALTCH not implemented // RDALTCH not implemented, but it's also used, so don't fail on it.
return 0x0d return 0x0d
case SPEAKER: case SPEAKER:
audio.Click() audio.Click()
return 0
case S6Q6L: case S6Q6L:
// A read from disk
return disk.ReadTrackData() return disk.ReadTrackData()
default: default:
panic(fmt.Sprintf("TODO read %04x\n", address)) panic(fmt.Sprintf("TODO read %04x\n", address))
} }
@ -333,31 +367,41 @@ func ReadIO(address uint16) uint8 {
return 0 return 0
} }
// ReadIO does a write in the $c000-$c0ff area
func WriteIO(address uint16, value uint8) { func WriteIO(address uint16, value uint8) {
// Try the generic readWrite and return if it has handled the write
if readWrite(address, false) { if readWrite(address, false) {
return return
} }
switch address { switch address {
case STROBE: case STROBE:
keyboard.ResetStrobe() keyboard.ResetStrobe()
case CLRCXROM: case CLRCXROM:
MapFirstHalfOfIO() MapFirstHalfOfIO()
case SETCXROM: case SETCXROM:
MapSecondHalfOfIO() MapSecondHalfOfIO()
case CLRALTCH: case CLRALTCH:
return return
case SETALTCH: case SETALTCH:
panic("SETALTCH not implemented") panic("SETALTCH not implemented")
case CLR80COL: case CLR80COL:
// CLR80COL not implemented // CLR80COL not implemented
return return
case CLRC3ROM: case CLRC3ROM:
// CLRC3ROM not implemented // CLRC3ROM not implemented
case SETC3ROM: case SETC3ROM:
// SETC3ROM not implemented // SETC3ROM not implemented
case S6Q6H: case S6Q6H:
// A write to disk
disk.WriteTrackData(value) disk.WriteTrackData(value)
default: default:
panic(fmt.Sprintf("TODO write %04x\n", address)) panic(fmt.Sprintf("TODO write %04x\n", address))
} }

View File

@ -7,16 +7,18 @@ import (
"github.com/freewilll/apple2/system" "github.com/freewilll/apple2/system"
) )
const RomPath = "apple2e.rom" const RomPath = "apple2e.rom" // So far only one ROM is supported and it's loaded at startup
const StackPage = 1 const StackPage = 1 // The 6502 stack is at 0x100
// PhysicalMemory contains all the unmapped memory, ROM and RAM
var PhysicalMemory struct { var PhysicalMemory struct {
MainMemory [0x10000]uint8 MainMemory [0x10000]uint8 // Main RAM
UpperROM [0x3000]uint8 UpperROM [0x3000]uint8 // $c000-$ffff ROM area
RomC1 [0x1000]uint8 RomC1 [0x1000]uint8 // First half of IO ROM
RomC2 [0x1000]uint8 RomC2 [0x1000]uint8 // Second half of IO ROM
} }
// Page tables for read & write
var ReadPageTable [0x100][]uint8 var ReadPageTable [0x100][]uint8
var WritePageTable [0x100][]uint8 var WritePageTable [0x100][]uint8
@ -35,6 +37,7 @@ var (
Page2 bool // Main memory Page2 is selected Page2 bool // Main memory Page2 is selected
) )
// Make page tables for current RAM, ROM and IO configuration
func ApplyMemoryConfiguration() { func ApplyMemoryConfiguration() {
// Map main RAM for read/write // Map main RAM for read/write
for i := 0x0; i < 0xc0; i++ { for i := 0x0; i < 0xc0; i++ {
@ -102,7 +105,7 @@ func MapSecondHalfOfIO() {
ApplyMemoryConfiguration() ApplyMemoryConfiguration()
} }
// emptySlot zeroes all RAM for a slot // emptySlot zeroes all RAM for a slot, effectively disabling the slot
func emptySlot(slot int) { func emptySlot(slot int) {
for i := slot * 0x100; i < (slot+1)*0x100; i++ { for i := slot * 0x100; i < (slot+1)*0x100; i++ {
PhysicalMemory.RomC1[i] = 0 PhysicalMemory.RomC1[i] = 0
@ -134,6 +137,7 @@ func InitApple2eROM() {
InitROM() // Map 0xd000-0xffff for reading InitROM() // Map 0xd000-0xffff for reading
} }
// Set upper memory area for reading from ROM
func InitROM() { func InitROM() {
UpperReadMappedToROM = true UpperReadMappedToROM = true
ApplyMemoryConfiguration() ApplyMemoryConfiguration()
@ -149,31 +153,39 @@ func SetUpperRamReadOnly(value bool) {
ApplyMemoryConfiguration() ApplyMemoryConfiguration()
} }
// Set d000 bank to map to $c000 or $d000 in the physical memory
func SetD000Bank(value int) { func SetD000Bank(value int) {
D000Bank = value D000Bank = value
ApplyMemoryConfiguration() 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) { func SetFakeAuxMemoryRead(value bool) {
FakeAuxMemoryRead = value FakeAuxMemoryRead = value
ApplyMemoryConfiguration() 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) { func SetFakeAuxMemoryWrite(value bool) {
FakeAuxMemoryWrite = value FakeAuxMemoryWrite = value
ApplyMemoryConfiguration() ApplyMemoryConfiguration()
} }
// Alternate zero page isn't implemented
func SetFakeAltZP(value bool) { func SetFakeAltZP(value bool) {
FakeAltZP = value FakeAltZP = value
ApplyMemoryConfiguration() ApplyMemoryConfiguration()
} }
// 80 column card isn't implemented
func SetCol80(value bool) { func SetCol80(value bool) {
Col80 = value Col80 = value
// No changes are needed when this is toggled // No changes are needed when this is toggled
} }
// Page switching is only implemented for the main memory
func SetPage2(value bool) { func SetPage2(value bool) {
// If the 80 column card is enabled, then this toggles aux memory // If the 80 column card is enabled, then this toggles aux memory
// Otherwise, page1/page2 is toggled in the main 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) { func SetStore80(value bool) {
Store80 = value Store80 = value
FakePage2 = value FakePage2 = value
ApplyMemoryConfiguration() ApplyMemoryConfiguration()
} }
// InitRAM sets all default RAM memory settings and resets the page tables
func InitRAM() { func InitRAM() {
UpperRamReadOnly = false UpperRamReadOnly = false
D000Bank = 2 D000Bank = 2
@ -209,6 +223,7 @@ func WipeRAM() {
} }
} }
// SetMemoryMode is used to set UpperRamReadOnly, UpperReadMappedToROM and D000Bank number
func SetMemoryMode(mode uint8) { func SetMemoryMode(mode uint8) {
// mode corresponds to a read/write to $c080 with // mode corresponds to a read/write to $c080 with
// $c080 mode=$00 // $c080 mode=$00
@ -236,6 +251,7 @@ func SetMemoryMode(mode uint8) {
ApplyMemoryConfiguration() ApplyMemoryConfiguration()
} }
// ReadMemory reads the ROM or RAM page table
func ReadMemory(address uint16) uint8 { func ReadMemory(address uint16) uint8 {
if (address >= 0xc000) && (address < 0xc100) { if (address >= 0xc000) && (address < 0xc100) {
return ReadIO(address) 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] return ReadPageTable[address>>8][address&0xff]
} }
// ReadMemory writes to the ROM or RAM page table
func WriteMemory(address uint16, value uint8) { func WriteMemory(address uint16, value uint8) {
if (address >= 0xc000) && (address < 0xc100) { if (address >= 0xc000) && (address < 0xc100) {
WriteIO(address, value) WriteIO(address, value)
return return
} }
// Magic routine to trigger an interrupt, used in the CPU interrupt tests
if system.RunningInterruptTests && address == 0xbffc { if system.RunningInterruptTests && address == 0xbffc {
oldValue := ReadMemory(address) oldValue := ReadMemory(address)
system.WriteInterruptTestOpenCollector(address, oldValue, value) system.WriteInterruptTestOpenCollector(address, oldValue, value)
@ -284,11 +303,14 @@ func WriteMemory(address uint16, value uint8) {
} }
memory := WritePageTable[address>>8] memory := WritePageTable[address>>8]
// If memory is nil, then it's read only. The write is ignored. // If memory is nil, then it's read only. The write is ignored.
if memory != nil { if memory != nil {
memory[uint8(address&0xff)] = value 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 { if system.RunningFunctionalTests && address == 0x200 {
testNumber := ReadMemory(0x200) testNumber := ReadMemory(0x200)
if testNumber == 0xf0 { if testNumber == 0xf0 {