From 948dc6aab47c1fd3e49d35e0400d1db238d89915 Mon Sep 17 00:00:00 2001 From: Ivan Izaguirre Date: Thu, 16 May 2019 22:55:19 +0200 Subject: [PATCH] Support for the 16KB language card --- apple2/apple2.go | 9 +- apple2/cardBase.go | 4 +- apple2/cardLanguage.go | 120 +++++++++++++++++++++ apple2/ioC0Page.go | 4 +- apple2/memoryManager.go | 42 +++++--- apple2/romdumps/LanguageCard_3410020F8.bin | Bin 0 -> 2048 bytes apple2/softSwitches2e.go | 24 +---- main.go | 1 + 8 files changed, 165 insertions(+), 39 deletions(-) create mode 100644 apple2/cardLanguage.go create mode 100644 apple2/romdumps/LanguageCard_3410020F8.bin diff --git a/apple2/apple2.go b/apple2/apple2.go index a0b32d9..fb5ec15 100644 --- a/apple2/apple2.go +++ b/apple2/apple2.go @@ -53,7 +53,7 @@ func NewApple2(romFile string, charRomFile string, clockMhz float64, // Set the io in 0xc000 a.io = newIoC0Page(&a) - a.mmu.setPage(0xc0, a.io) + a.mmu.setPages(0xc0, 0xc0, a.io) return &a } @@ -70,6 +70,13 @@ func (a *Apple2) AddDisk2(slot int, diskRomFile string, diskImage string) { } } +// AddLanguageCard inserts a 16Kb card +func (a *Apple2) AddLanguageCard(slot int) { + d := newCardLanguage() + d.cardBase.insert(a, slot) + d.applyState() +} + // ConfigureStdConsole uses stdin and stdout to interface with the Apple2 func (a *Apple2) ConfigureStdConsole(stdinKeyboard bool, stdoutScreen bool) { if !stdinKeyboard && !stdoutScreen { diff --git a/apple2/cardBase.go b/apple2/cardBase.go index 4a00977..21c3767 100644 --- a/apple2/cardBase.go +++ b/apple2/cardBase.go @@ -12,8 +12,8 @@ func (c *cardBase) insert(a *Apple2, slot int) { c.a = a c.slot = slot if slot != 0 && c.rom != nil { - c.rom.base = uint16(0xC000 + slot*0x100) - a.mmu.setPage(uint8(0xC0+slot), c.rom) + c.rom.base = uint16(0xc000 + slot*0x100) + a.mmu.setPagesRead(uint8(0xc0+slot), uint8(0xc0+slot), c.rom) } for i := 0; i < 0x10; i++ { diff --git a/apple2/cardLanguage.go b/apple2/cardLanguage.go new file mode 100644 index 0000000..87a11f9 --- /dev/null +++ b/apple2/cardLanguage.go @@ -0,0 +1,120 @@ +package apple2 + +/* +Language card with 16 extra kb for the Apple ][ and ][+ +Manual: http://www.applelogic.org/files/LANGCARDMAN.pdf + +The language card doesn't have ROM for Cx00. It would not +be used in slot 0 anyway. + +Note also that language cards for the Apple ][ had ROM on +board to replace the main board F8 ROM with Autostart. That +was not used/needed on the Apple ][+. As this emulates the +Apple ][+, it is not considered. For the PLus it is often +refered as Language card but it is really a 16 KB Ram card, + + +"When RAM is deselected, the ROM on the LAnguage card is selected for +the top 2K ($F800-$FFFF), and the ROM on the main board is selected +for $D000-$F7FF. + +Power on RESET initializes ROM to read mode and RAM to write mode, +and selects the second 4K bank to map $D000-$DFFF." + +*/ + +type cardLanguage struct { + cardBase + readState bool + writeState int + activeBank int + ramBankA *memoryRange // First 4kb to map in 0xD000-0xDFFF + ramBankB *memoryRange // Second 4kb to map in 0xD000-0xDFFF + ramUpper *memoryRange // Upper 8kb to map in 0xE000-0xFFFF +} + +const ( + // Write enabling requires two sofstwitch accesses + lcWriteDisabled = 0 + lcWriteHalfEnabled = 1 + lcWriteEnabled = 2 +) + +func newCardLanguage() *cardLanguage { + var c cardLanguage + c.readState = false + c.writeState = lcWriteEnabled + c.activeBank = 1 + + c.ramBankA = newMemoryRange(0xd000, make([]uint8, 0x1000)) + c.ramBankB = newMemoryRange(0xd000, make([]uint8, 0x1000)) + c.ramUpper = newMemoryRange(0xe000, make([]uint8, 0x2000)) + + for i := 0x0; i <= 0xf; i++ { + iCopy := i + c.ssr[i] = func(*ioC0Page) uint8 { + c.ssAction(iCopy) + return 0 + } + c.ssw[i] = func(*ioC0Page, uint8) { + // Writing resets write count (from A2AUDIT) + c.writeState = lcWriteDisabled + } + } + return &c +} + +func (c *cardLanguage) ssAction(ss int) { + c.activeBank = (ss >> 3) & 1 + action := ss & 0x3 + switch action { + case 0: + // RAM read, no writes + c.readState = true + c.writeState = lcWriteDisabled + case 1: + // ROM read, RAM write + c.readState = false + c.writeState++ + case 2: + // ROM read, no writes + c.readState = false + c.writeState = lcWriteDisabled + case 3: + //RAM read, RAM write + c.readState = true + c.writeState++ + } + + if c.writeState > lcWriteEnabled { + c.writeState = lcWriteEnabled + } + + c.applyState() +} + +func (c *cardLanguage) getActiveBank() *memoryRange { + if c.activeBank == 0 { + return c.ramBankA + } + return c.ramBankB +} + +func (c *cardLanguage) applyState() { + mmu := c.a.mmu + + if c.readState { + mmu.setPagesRead(0xd0, 0xdf, c.getActiveBank()) + mmu.setPagesRead(0xe0, 0xff, c.ramUpper) + } else { + mmu.setPagesRead(0xd0, 0xff, mmu.physicalROM) + } + + if c.writeState == lcWriteEnabled { + mmu.setPagesWrite(0xd0, 0xdf, c.getActiveBank()) + mmu.setPagesWrite(0xe0, 0xff, c.ramUpper) + } else { + mmu.setPagesWrite(0xd0, 0xff, nil) + } + +} diff --git a/apple2/ioC0Page.go b/apple2/ioC0Page.go index 5496643..cdce9e5 100644 --- a/apple2/ioC0Page.go +++ b/apple2/ioC0Page.go @@ -81,7 +81,7 @@ func (p *ioC0Page) setSpeakerProvider(s SpeakerProvider) { } func (p *ioC0Page) peek(address uint16) uint8 { - //fmt.Printf("Peek on $C0%02x ", address) + //fmt.Printf("Peek on $%02x\n", address) pageAddress := uint8(address) ss := p.softSwitchesR[pageAddress] if ss == nil { @@ -94,7 +94,7 @@ func (p *ioC0Page) peek(address uint16) uint8 { } func (p *ioC0Page) poke(address uint16, value uint8) { - //fmt.Printf("Poke on $C0%02x with %02x ", address, value) + //fmt.Printf("Poke on $%02x with %02x\n", address, value) pageAddress := uint8(address) ss := p.softSwitchesW[pageAddress] if ss == nil { diff --git a/apple2/memoryManager.go b/apple2/memoryManager.go index 831bdb7..6884246 100644 --- a/apple2/memoryManager.go +++ b/apple2/memoryManager.go @@ -1,6 +1,8 @@ package apple2 -import "io/ioutil" +import ( + "io/ioutil" +) // See https://fabiensanglard.net/fd_proxy/prince_of_persia/Inside%20the%20Apple%20IIe.pdf // See https://i.stack.imgur.com/yn21s.gif @@ -53,17 +55,31 @@ func (mmu *memoryManager) Poke(address uint16, value uint8) { mh.poke(address, value) } -func (mmu *memoryManager) setPage(index uint8, mh memoryHandler) { - mmu.setPageRead(index, mh) - mmu.setPageWrite(index, mh) +func (mmu *memoryManager) setPages(begin uint8, end uint8, mh memoryHandler) { + mmu.setPagesRead(begin, end, mh) + mmu.setPagesWrite(begin, end, mh) } -func (mmu *memoryManager) setPageRead(index uint8, mh memoryHandler) { - mmu.activeMemoryRead[index] = mh +func (mmu *memoryManager) setPagesRead(begin uint8, end uint8, mh memoryHandler) { + i := begin + for { + mmu.activeMemoryRead[i] = mh + if i == end { + break + } + i++ + } } -func (mmu *memoryManager) setPageWrite(index uint8, mh memoryHandler) { - mmu.activeMemoryWrite[index] = mh +func (mmu *memoryManager) setPagesWrite(begin uint8, end uint8, mh memoryHandler) { + i := begin + for { + mmu.activeMemoryWrite[i] = mh + if i == end { + break + } + i++ + } } // When 0xcfff is accessed the card expansion rom is unassigned @@ -72,9 +88,7 @@ func (mmu *memoryManager) resetSlotExpansionRoms() { // Ignore if the Apple2 shadow ROM is active return } - for i := uint8(0xc8); i < 0xd0; i++ { - mmu.setPage(i, nil) - } + mmu.setPagesRead(0xc8, 0xcf, nil) } func newMemoryManager(a *Apple2) *memoryManager { @@ -84,9 +98,7 @@ func newMemoryManager(a *Apple2) *memoryManager { // Assign RAM from 0x0000 to 0xbfff, 48kb ram := make([]uint8, 0xc000) mmu.physicalMainRAM = newMemoryRange(0, ram) - for i := 0; i < 0xc000; i = i + 0x100 { - mmu.setPage(uint8(i>>8), mmu.physicalMainRAM) - } + mmu.setPages(0x00, 0xc0, mmu.physicalMainRAM) return &mmu } @@ -119,6 +131,6 @@ func (mmu *memoryManager) loadRom(filename string) { func (mmu *memoryManager) resetRomPaging() { // Assign the first 12kb of ROM from 0xd000 to 0xfff for i := 0x0000; i < 0x3000; i = i + 0x100 { - mmu.setPageRead(uint8(0xd0+(i>>8)), mmu.physicalROM) + mmu.setPagesRead(0xd0, 0xff, mmu.physicalROM) } } diff --git a/apple2/romdumps/LanguageCard_3410020F8.bin b/apple2/romdumps/LanguageCard_3410020F8.bin new file mode 100644 index 0000000000000000000000000000000000000000..e6a4515a3e1ebdb932dd8dba97c69f12140dbd1c GIT binary patch literal 2048 zcmY*a4NM%z6`s50-#?t|H2Aj};5hF&7!C{;I?jM8#LM;4HZqE0yH-b5-R@Pis$43y zQl&V~E#8v2U1F0utx1;aEn$(*y`!cQPH~qO=HR+ah>tTzKd;F(9+E z2ZVJZ)@o*PpYMRUPez;r({bMNkT7mLRh-A|W9>V@(uwAq--&B^;yD?O zO^qbD8au_+Ua|jR?21S&_3NA5LXq2DjCm~?;k8v_7zREcXTnZ=C*wC^cVebCBK*rw zDS5&wJ36T(xavuijA9$Q97_d*C=q7IPcpq)Dm`G>=(K{28ZUijX|q z?bvcIPoOHg4`wOIk7;vOMim_gw?sP7w*OGsl%=45Ol@@+euT^rY}M|W0QkL`wU=P=rJ@}%UoTCZOp z4cgIn(I>Bd?W*LpAMRF{QN%`}q(#fIwR8)H;NBR(F5)0={l1Wwc_b$c@!)Vcz((dxS zWxWTXl@HisCz&MvVG3`~sJ{(;tg6C_IILl+ zavL5^i;BD%NYJ71+BykX2k2$1WSuP!eVM3Y=>DkXu6j{Fq4vYaYwDcU5|hd~>zq}p zn5_zJnyZqk@O0`729sTiPJ1(gqThkJTK@~oU(h}ZSfwfkThsW>w1`+QnynP{P*kem zszl_o>i>YCSeL;H=zk8@DzbYen%ghgxu*l=JnJ>hR))4@wPAW-KW|Am{w5+Y7U~OF zWpRLx%#;fW(!_jZ($XwU56N=7W?ufZ;ma95kG_9PiqP9Mrd6u2Q~s7 zAtU6A^+2v+MXZhze-VR|X?DNpj6x)ECd~(l(phVXch+*X$~lNlm}cy)#ccyL+tY-W zKMRIGR}G0-AE1Lsfy@v{29|Ou`LVu6qJ`0YDK1ce?3lJ) z{u3+$9;Jgo{ic3>Es7g6>Y!#0)9R97w*PeqnE?*t_-b0dsGSTP9&y2T+?EiL9Q54F zNUq{S4qWA0C6~QWqws5)9HPT6fHA(A7GlILthn7$i(u|D&bXKy$XO=aU-1VS)?Qj1 zQg1?AF6cRgR8cTj)%zM5{OuGQXe4tD`dd-$N%f}o4p%3&474q`f!9Y!pkdN5adpIR znrInpAMA))9+9wXpdH^x3t8^>?fM?LNbU}~+sLEfUbr2uB+FTcVc#Ob-`bF$V6Ya) z#}b^=*UUCS0`HT4y>P-c=!{wtL~MoE#PHQ;E33udB<|LVa@-+Nms;$ldOu7B zS-+(O|1qs4;c?qwS*-`qAU_Gea5cb*RzO530h8E_@1|dJb=H6$3^WQl$g?&27I_vt zlmC!)59oz#ZWu=D9C;>ilqOtf-!s0WMJ&hM?CV+}|J(JlJx(6u4&tw5@NW$Kw&80K z%Xmj@ao&K?+%f_Uo^Kj{D*Hy3*jj9@QA=E^(+Vb92M-S(iRo)vfgkm?^tDbRbEGSz zmI&{9c{LfjP2>$`^6v&C)j_G%kzb788oxg?F26GpC>Sg>=j-U7m)@RxV_dy8-T3Ov b>pv|kd-|!DUpzHGkuED+r&84E>8), mmu.physicalROMe) - } + io.apple2.mmu.setPagesRead(0xc1, 0xcf, io.apple2.mmu.physicalROMe) } func softSwitchIntCxRomOff(io *ioC0Page) { - // TODO restore all the ROM from the slot for 0xc1 to 0xc7 - mmu := io.apple2.mmu - for i := 1; i < 16; i++ { - mmu.setPage(uint8(0xc0+i), nil) - } + // TODO restore all the ROM from the slots for 0xc1 to 0xc7 + io.apple2.mmu.setPages(0xc1, 0xc7, nil) } func softSwitchSlotC3RomOn(io *ioC0Page) { - if io.isSoftSwitchActive(ioFlagIntCxRom) { - return // Ignore if allt the Apple2 shadow ROM is active - } // TODO restore the slot 3 ROM - mmu := io.apple2.mmu - mmu.setPage(0xC3, nil) + io.apple2.mmu.setPages(0xc3, 0xc3, nil) } func softSwitchSlotC3RomOff(io *ioC0Page) { - if io.isSoftSwitchActive(ioFlagIntCxRom) { - return // Ignore if alt the Apple2 shadow ROM is active - } - mmu := io.apple2.mmu - mmu.setPageRead(0xC3, mmu.physicalROMe) + io.apple2.mmu.setPagesRead(0xc3, 0xc3, io.apple2.mmu.physicalROMe) } diff --git a/main.go b/main.go index 3999ce1..a03d1aa 100644 --- a/main.go +++ b/main.go @@ -69,6 +69,7 @@ func main() { log := false a := apple2.NewApple2(*romFile, *charRomFile, *cpuClock, !*mono, *fastDisk, *panicSS) + a.AddLanguageCard(0) if *disk2Slot > 0 { a.AddDisk2(*disk2Slot, *disk2RomFile, *diskImage) }