; Copyright 1996 Acorn Computers Ltd ; ; Licensed under the Apache License, Version 2.0 (the "License"); ; you may not use this file except in compliance with the License. ; You may obtain a copy of the License at ; ; http://www.apache.org/licenses/LICENSE-2.0 ; ; Unless required by applicable law or agreed to in writing, software ; distributed under the License is distributed on an "AS IS" BASIS, ; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ; See the License for the specific language governing permissions and ; limitations under the License. ; ; > MEMC2 ; MEMC interface file - MEMC2 version ; Created by TMD 10-Aug-90 PhysRAML2PT * &02000000 + (512+64)*1024 ; Synonyms VInit * MEMC2Address + MEMC2_VINITe VStart * MEMC2Address + MEMC2_VSTRTe VEnd * MEMC2Address + MEMC2_VENDe CInit * MEMC2Address + MEMC2_CINIT ; ***************************************************************************** ; ; SetDAG - Program DMA address generator R1 with physical address R0 ; ; in: r0 = physical address ; r1 = index of DMA address generator to program, as defined in vdudecl ; ; out: All registers preserved, operation ignored if illegal ; SetDAG ENTRY "r0,r1" CMP r1, #MEMCDAG_MaxReason EXIT HI ADR r14, DAGAddressTable LDR r14, [r14, r1, LSL #2] ; load base address in MEMC2 MOV r1, r0, LSR #16 ; r1 is top 16 bits EOR r0, r0, r1, LSL #16 ; and r0 is bottom 16 bits BIC r0, r0, #&0F ; bits 0..3 must be clear BIC r1, r1, #&F000 ; and bits 28..31 must be clear STMIA r14, {r0, r1} ; atomic update (we believe) EXIT GBLA DAGIndex DAGIndex SETA 0 MACRO DAGTab $reason, $address ASSERT ($reason)=DAGIndex & $address DAGIndex SETA DAGIndex + 1 MEND DAGAddressTable DAGTab MEMCDAG_VInit, VInit DAGTab MEMCDAG_VStart, VStart DAGTab MEMCDAG_VEnd, VEnd DAGTab MEMCDAG_CInit, CInit ; **************** CAM manipulation utility routines *********************************** ; ************************************************************************************** ; ; BangCamUpdate - Update CAM entry and soft copy ; ; This part of the routine has to do more work on MEMC2 ; ; First look in the CamEntries table to find the logical address L this physical page is ; currently allocated to. Then check in the Level 2 page tables to see if page L is currently ; at page R2. If it is, then map page L to be inaccessible, otherwise leave page L alone. ; Then map logical page R3 to physical page R2. ; ; in: r2 = physical page number ; r3 = logical address ; r9 = current MEMC1 control register (irrelevant on MEMC2) ; r11 = PPL ; ; out: r0, r1, r4, r6 corrupted ; r2, r3, r5, r7-r12 preserved ; ; NB Use of stack is allowed in this routine BangCamUpdate ROUT MOV r1, #0 LDR r1, [r1, #CamEntriesPointer] LDR r0, [r1, r2, LSL #2] ; r0 = current logaddress + PPL for phys page r2 ORR r4, r3, r11, LSL #28 ; new entry for CamEntries STR r4, [r1, r2, LSL #2] ; update BIC r0, r0, #&F0000000 ; just get logical address LDR r1, =PhysRAML2PT ; point to page tables LDR r4, [r1, r0, LSR #11] ; get physical page + PPL for this logical page TEQ r2, r4, LSR #3 ; see if still there BNE %FT10 ; if not there, then just put in new page Push "r3, r14" MOV r3, r0 ; map out old page at this logical address MOV r0, #0 ; physical page 0 but PPL(MEMC2)=0 ie no access, not even for me! BL BangL2PT ; map page out Pull "r3, r14" 10 ; and drop thru to ... ; ************************************************************************************** ; ; BangCam - Update CAM entry, but not soft copy ; ; This routine maps a physical page to a given logical address ; For MEMC2, I assume that the physical page was previously not mapped ; anywhere else - on MEMC1 it would automatically unmap any logical ; address that the physical page was previously at, but on MEMC2 it won't ; ; in: r2 = physical page number ; r3 = logical address ; r9 = current MEMC1 control register (irrelevant on MEMC2) ; r11 = PPL ; ; out: r0, r1, r4, r6 corrupted ; r2, r3, r5, r7-r12 preserved ; ; NB Can't use stack - there might not be one! BangCam ADR r0, PPLTrans ; translate MEMC1 PPL to MEMC2 PPL LDRB r0, [r0, r11] ORR r0, r0, r2, LSL #3 ; value to store in level 2 page table ; is PPL :OR: (phys page number << 3) LDR r1, =PhysRAML2PT ; point to level 2 page tables BangL2PT ; internal entry point used only by BangCamUpdate BICS r4, r3, #(3 :SHL: 11) ; ensure going to be on word boundary (EQ => logical page zero) STR r0, [r1, r4, LSR #11] ; update level 2 page table MOV r6, #MEMC2Address STREQ r0, [r6, #MEMC2_SuperPageZero] ; if logical page 0 then update special entry MOV r0, #0 ; now flush the TLB STR r0, [r6, #MEMC2_Flush] MOV pc, lr PPLTrans = 6 ; R any W any = 3 ; R any W sup = 2 ; R sup W sup = 2 ; R sup W sup PageSizes & 4*1024 ; 0 is 4K & 8*1024 ; 4 is 8K & 16*1024 ; 8 is 16 & 32*1024 ; C is 32 PageShifts = 12, 13, 0, 14 ; 1 2 3 4 = 0, 0, 0, 15 ; 5 6 7 8 ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ; SWI OS_UpdateMEMC: Read/write MEMC1 control register SSETMEMC ROUT AND r10, r0, r1 MOV r12, #0 TEQP pc, #SVC_mode+I_bit+F_bit LDR r0, [r12, #MEMC_CR_SoftCopy] ; return old value BIC r11, r0, r1 ORR r11, r11, R10 BIC r11, r11, #&FF000000 BIC r11, r11, #&00F00000 ORR r11, r11, #MEMCADR STR r11, [r12, #MEMC_CR_SoftCopy] ; We now have to mimic the relevant bits of the MEMC1 control register ; ; bits 0,1 => unused ; bits 2,3 => page size, irrelevant since always 8K ; bits 4,5 => low ROM access time, irrelevant since this has to be fixed ; bits 6,7 => hi ROM access time, -----------------""------------------ ; bits 8,9 => DRAM refresh control, irrelevant (refresh must always be on) ; bit 10 => Video/cursor DMA enable, corresponds to bit venbe of VATT ; (and possibly bit venbo of IATT for interlaced displays) ; Unfortunately VATT (and IATT) is a write-only register. Later on ; we might have a soft copy of these, but for now just write whole ; register. ; bit 11 => Sound DMA enable, ignore for now ; bit 12 => OS mode, ignore ; Program all of VATT ; ; vdis = 0 (don't disable DMA after one buffer) ; venbe = (bit 10 of r11) ; vrnw = 1 (read from RAM, not write) ; vmske = 0 (no interrupts at end of buffer) MOV r12, # (0 * VATT_vdis) + (0 * VATT_venbe) + (1 * VATT_vrnw) + (0 * VATT_vmske) TST r11, # (1 :SHL: 10) ORRNE r12, r12, # VATT_venbe MOV r10, #MEMC2Address STR r12, [r10, #MEMC2_VATT] TEQP pc, #SVC_mode+I_bit ExitSWIHandler ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ; ; ClearPhysRAM - Routine to clear "all" memory ; ; While this routine is running, keyboard IRQs may happen. For this reason ; it avoids LogRAM 0..31 (where hardware IRQ vector is) and PhysRAM ; 0..31 where the IRQ workspace is. ; ; On MEMC2 it also has to avoid the pages where the level 2 page tables are ; ; r7 contains memory speed and must be preserved ; r8 contains page size and must be preserved ; r9 contains MEMC control register and must be preserved ; ClearPhysRAM ROUT MOV r0, #0 MOV r1, #0 MOV r2, #0 MOV r3, #0 MOV r11, #0 MOV r4, #PhysRam CMP r13, #512*1024 ADDEQ r10, r4, #(512-64)*1024 ; get address that's logram 0 ADDNE r10, r4, #512*1024 ADD r13, r13, #PhysRam ; end of memory ADD r12, r4, #PhysRAML2PT-PhysRam ADD r4, r4, #4*8 ; skip minimal startup workspace 10 CMP r4, r10 ADDEQ r4, r4, #4*8 ; skip physram that's logram 0 CMP r4, r12 ADDEQ r4, r4, #32*1024 ; skip 32K of L2PT STMNEIA r4!, {r0-r3} CMP r4, r13 BNE %BT10 SUB r13, r13, #PhysRam LDR r0, =OsbyteVars + :INDEX: LastBREAK MOV r1, #&80 STRB r1, [r0] ; flag the fact that RAM cleared MOV pc, lr ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ; ; InitMEMC - Initialise memory controller ; InitMEMC ROUT MOV r0, # MEMC2Address MOV r1, # Control_romfast STR r1, [r0, #MEMC2_Control] ; make ROMS go fast ADR r1, ClockTimingTable ; initialise CLK block LDMIA r1, {r2-r5} ADD r1, r0, #MEMC2_clkj0 STMIA r1, {r2-r5} ADR r1, DRAMTimingTable ; initialise DRAM block LDMIA r1, {r2-r8} ADD r1, r0, #MEMC2_rwt0 STMIA r1, {r2-r8} LDR r1, rtype_value STR r1, [r0, #MEMC2_rtype] LDR r1, refr_value STR r1, [r0, #MEMC2_refr] MOV r1, # (PhysRAML2PT-PhysRam)/(8*1024) ; level 1 entry (paged) for 0..16M MOV r2, # (PhysRAML2PT+8*1024-PhysRam)/(8*1024) ; level 1 entry (paged) for 16M..32M MOV r3, # Prot_UrwSRW+(L1D_RAM :SHL: 3)+(L1D_RAM :SHL: 5)+(L1D_RAM :SHL: 7)+(L1D_RAM :SHL: 9)+(0 :SHL: 11) ; level 1 entry (direct) for 32M..48M (all RAM, base 0) LDR r4, = Prot_URwSRW+(L1D_IO :SHL: 3)+(L1D_PROG :SHL: 5)+(L1D_ROM :SHL: 7)+(L1D_ROM :SHL: 9)+(0 :SHL: 11) ; level 1 entry (direct) for 48M..64M (IO,PROG,ROM,ROM) ADD r5, r0, #MEMC2_Level1+L1_Paged+L1_Sec0 ADD r6, r5, #4*2*16 ; cycle thru all 4 bus masters and (USR,SPV) ADD r7, r5, #(L1_Direct+L1_Sec2)-(L1_Paged+L1_Sec0) 10 STMIA r5, {r1,r2} ; set up the paged sections 0,1 STMIA r7, {r3,r4} ; set up the direct sections 2,3 ADD r5, r5, #16 ADD r7, r7, #16 TEQ r5, r6 ; have we got to the end ? BNE %BT10 ; Now turn on the translation, but don't set up the level 2 page tables until later, ; when we know the DRAM multiplex option LDR r1, = Control_ton + Control_l1on + Control_romfast STR r1, [r0, #MEMC2_Control] ; now set up VINC MOV r1, #&10 ; low bits MOV r2, #&00 ; high bits ADD r3, r0, #MEMC2_VINC STMIA r3, {r1,r2} MOV pc, lr ClockTimingTable J0 & &F200 ; 3 / 2 J1 & &F200 ; 3 / 2 RSPEED & &1C00 ; 3 / 3 ISPEED & &B800 ; 2.5 / 2.5 DRAMTimingTable RWT0 & &2A320 RRD0 & &2AC80 RSQ0 & &0B200 B0Dummy & 0 RWT1 & &2A320 RRD1 & &2AC80 RSQ1 & &0B200 rtype_value & &3333 ; Bank Type [x,bank,s1,s0] * 4 ; set largest memory type by default refr_value & &1086 ; Enable refresh, refresh length = 10 hclk ticks, refresh period = 12us ; -> MemSize ; (non-destructive) algorithm to determine MEMC RAM configuration ; ; Dave Flynn and Alasdair Thomas ; 17-March-87 ; ; Spooling checkered by NRaine and SSwales ! ; 8MByte check bodged in by APT ; ; NOTE: Routines MemSize and TimeCPU are called by the power-on test software, ; so their specifications MUST not change. ; ; Set MEMC for 32-k page then analyse signature of possible ; external RAM configurations... ; The configurations are: ; ; Ram Size Page Size Configuration (Phys RAM) Signature ;-------------------------------------------------------------------- ; 16MByte 32k 4*32*1Mx1 A13,A20,A21,A22,A23,A23.5 distinct ; 16MByte 32k 16*8*256kx4 A13,A20,A21,A22,A23,A23.5 distinct ; ; 12MByte 32k 3*32*1Mx1 A13,A20,A21,A22,A23 OK, A23.5 fail ; 12MByte 32k 12*8*256kx4 A13,A20,A21,A22,A23 OK, A23.5 fail ; ; 8MByte 32k 2*32*1Mx1 A13,A20,A21,A22 distinct, A23 fail ; 8MByte 32k 8*8*256kx4 A13,A20,A21,A22 distinct, A23 fail ; ; 4Mbyte 32k 32*1Mx1 A13,A21,A20 distinct, A22,A23 fail ; 4Mbyte 32k 4*8*256kx4 A13,A21,A20 distinct, A22,A23 fail ; ; 2Mbyte 32k expandable 2*8*256kx4 A13,A20 distinct, A21 fails ; 2Mbyte ??? 16k fixed 2*8*256kx4 A13,A21 distinct, A20 fails ; ; 1Mbyte 8k 32*256kx1 A13,A20 fail, A19,A18,A12 distinct ; 1Mbyte 8k 8*256kx1 A13,A20 fail, A19,A18,A12 distinct ; 1Mbyte 8k 4*8*64kx4 A13,A20 fail, A19,A18,A12 distinct ; ; 512Kbyte 8k expandable 2*8*64kx4 A13,A20,A19 fail, A12,A18 distinct ; 512Kbyte 4k fixed 2*8*64kx4 A13,A20,A12 fail, A19,A18 distinct ; ; 256Kbyte 4K 8*64kx4 A13,A20,A12,A18 fail, A21,A19 ok ; 256Kbyte 4K 32*64kx1 A13,A20,A12,A18 fail, A21,A19 ok ; Z_Flag * &40000000 ; MemSize routine... enter with 32K pagesize set ; R0 returns page size ; R1 returns memory size ; R2 returns value set in MEMC ; uses R3-R7 MemSize ROUT [ {TRUE} ; now work on different configurations, but only bank zero MOV r7, lr ; first find out appropriate rtype value ; initial routine has set DRAM type to 3 MOV r0, #PhysRam ADD r1, r0, #A9 ; if A9 ghosts BL DistinctAddresses MOVNE r0, #2_00 ; then type 00 BNE %FT10 ADD r1, r0, #A11 ; else if A11 ghosts BL DistinctAddresses MOVNE r0, #2_01 ; then type 01 BNE %FT10 ADD r1, r0, #A12 ; else if A12 ghosts BL DistinctAddresses MOVNE r0, #2_01 ; then type 01 MOVEQ r0, #2_11 ; else type 11 10 LDR r1, rtype_value BIC r1, r1, #2_11 ORR r1, r1, r0 MOV r0, #MEMC2Address STR r1, [r0, #MEMC2_rtype] ; having set up the DRAM multiplexing correctly, we can now zap the L2PT ; to no access for any page LDR r1, =PhysRAML2PT ADD r2, r1, #2*8*1024 ; two L2PT tables at the moment MOV r3, #0 ; page 0, no access MOV r4, #0 MOV r5, #0 MOV r6, #0 15 STMIA r1!,{r3-r6} TEQ r1, r2 BNE %BT15 STR r3, [r0, #MEMC2_SuperPageZero] ; don't forget super page zero ; now find out the memory size MOV r0, #PhysRam MOV r6, #256*1024 20 ADD r1, r0, r6 BL DistinctAddresses ; try next address line BNE %FT30 ; if ghosts or not there then finish MOV r6, r6, LSL #1 CMP r6, #16*1024*1024 ; give up if we've got 16MBytes or more BCC %BT20 30 MOV r1, r6 LDR r2, ResetMemC_Value BIC r2, r2, #&C ORR r2, r2, #Page8K MOV r0, #8*1024 ; fixed 8K page size MOV pc, r7 | MOV r7, lr MOV r0, #PhysRam ADD r1, r0, #A13 BL DistinctAddresses BNE %10 ADD r1, r0, #A21 BL DistinctAddresses MOVNE r0, #Page32K MOVNE r1, #2048*1024 BNE MemSizeDone MOV r0, #PhysRam ADD r1, r0, #4*1024*1024 BL DistinctAddresses MOVNE r0, #Page32K MOVNE r1, #4*1024*1024 BNE MemSizeDone MOV r0, #PhysRam ADD r1, r0, #8*1024*1024 BL DistinctAddresses MOVNE r0, #Page32K MOVNE r1, #8*1024*1024 BNE MemSizeDone MOV r0, #PhysRam ADD r1, r0, #12*1024*1024 BL DistinctAddresses MOV r0, #Page32K MOVNE r1, #12*1024*1024 MOVEQ r1, #16*1024*1024 B MemSizeDone 10 ADD r1, r0, #A20 BL DistinctAddresses BNE %20 MOV r0, #Page16K MOV r1, #2048*1024 B MemSizeDone 20 ADD r1, r0, #A19 BL DistinctAddresses BEQ %30 MOV r0, #Page8K MOV r1, #512*1024 B MemSizeDone 30 ADD r1, r0, #A18 BL DistinctAddresses BEQ %40 MOV r0, #Page4K MOV r1, #256*1024 B MemSizeDone 40 ADD r1, r0, #A12 BL DistinctAddresses BEQ %50 MOV r0, #Page4K MOV r1, #512*1024 B MemSizeDone 50 MOV r0, #Page8K MOV r1, #1024*1024 MemSizeDone LDR r2, ResetMemC_Value BIC r2, r2, #&C ORR r2, r2, r0 STR r2, [r2] ; set MEMC to right state MOV pc, r7 ] ; DistinctAddresses routine... ; r0,r1 are the addresses to check ; uses r2-5 ; writes interleaved patterns (to prevent dynamic storage...) ; checks writing every bit low and high... ; return Z-flag set if distinct DistinctAddresses ROUT LDR r2, [r0] ; preserve LDR r3, [r1] LDR r4, Pattern STR r4, [r0] ; mark first MOV r5, r4, ROR #16 STR r5, [r1] ; mark second LDR r5, [r0] CMP r5, r4 ; check first BNE %10 ; exit with Z clear LDR r5, [r1] ; check second CMP r5, r4, ROR #16 ; clear Z if not same BNE %10 ; now check inverse bit writes STR r4, [r1] ; mark second MOV r5, r4, ROR #16 STR r5, [r0] ; mark first LDR r5, [r1] CMP r5, r4 ; check second BNE %10 ; exit with Z clear LDR r5, [r0] ; check first CMP r5, r4, ROR #16 ; clear Z if not same 10 STR r3, [r1] ; restore STR r2, [r0] ORREQ lr, lr, #Z_Flag BICNE lr, lr, #Z_Flag MOVS pc, lr Pattern & &AAFF5500 ; shiftable bit check pattern ; init state with masked out page size ResetMemC_Value & &E010C :OR: MEMCADR ; slugged ROMs + flyback refresh only + 32K page ; Constants ; A0 * 1 :SHL: 00 A1 * 1 :SHL: 01 A2 * 1 :SHL: 02 A3 * 1 :SHL: 03 A4 * 1 :SHL: 04 A5 * 1 :SHL: 05 A6 * 1 :SHL: 06 A7 * 1 :SHL: 07 A8 * 1 :SHL: 08 A9 * 1 :SHL: 09 A10 * 1 :SHL: 10 A11 * 1 :SHL: 11 A12 * 1 :SHL: 12 A13 * 1 :SHL: 13 A14 * 1 :SHL: 14 A15 * 1 :SHL: 15 A16 * 1 :SHL: 16 A17 * 1 :SHL: 17 A18 * 1 :SHL: 18 A19 * 1 :SHL: 19 A20 * 1 :SHL: 20 A21 * 1 :SHL: 21 A22 * 1 :SHL: 22 A23 * 1 :SHL: 23 A24 * 1 :SHL: 24 A25 * 1 :SHL: 25 A26 * 1 :SHL: 26 A27 * 1 :SHL: 27 A28 * 1 :SHL: 28 A29 * 1 :SHL: 29 A30 * 1 :SHL: 30 A31 * 1 :SHL: 31 Page32K * &C ; in MEMC control reg patterns... Page16K * &8 Page8K * &4 Page4K * &0 ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ; In r0-r6 trashable ; r9 = Current MEMC CR ; Out r9 MEMC value with slowest ROM speed, correct pagesize ; r7 processor speed in kHz, bit 16 => can do STM to I/O (ie MEMC1a, MEMC2), bit 17 => MEMC2 ncpuloops * 1024 ; don't go longer than 4ms without refresh ! nmulloops * 128 TimeCPU ROUT [ {TRUE} ; fudge it for now LDR r7, =6000 + (3 :SHL: 16) ; pretend 6MHz system, and MEMC2 MOV pc, lr | BIC r9, r9, #3 :SHL: 8 STR r9, [r9] ; turn off refresh for a bit ; Time CPU/Memory speed LDR r1, =&7FFE ; 32K @ 2MHz = ~16ms limit MOV r3, #IOC MOV r0, r1, LSR #8 STRB r1, [r3, #Timer1LL] STRB r0, [r3, #Timer1LH] LDR r0, =ncpuloops STRB r0, [r3, #Timer1GO] ; start the timer NOW B %FT10 ; Looks superfluous, but is required ; to get ncpuloops pipeline breaks 10 SUBS r0, r0, #1 ; 1S BNE %BT10 ; 1N + 2S STRB r0, [r3, #Timer1LR] ; latch count NOW LDRB r2, [r3, #Timer1CL] LDRB r0, [r3, #Timer1CH] ADD r2, r2, r0, LSL #8 ; count after looping is ... SUB r2, r1, r2 ; decrements ! MOV r2, r2, LSR #1 ; IOC clock decrements at 2MHz ; Time CPU/MEMC Multiply time MOV r4, #-1 ; Gives worst case MUL MOV r0, r1, LSR #8 STRB r1, [r3, #Timer1LL] STRB r0, [r3, #Timer1LH] LDR r0, =nmulloops STRB r0, [r3, #Timer1GO] ; start the timer NOW B %FT20 ; Looks superfluous, but is required ; to get nmulloops pipeline breaks 20 MUL r5, r4, r4 ; 1S + 16I MUL r5, r4, r4 ; 1S + 16I SUBS r0, r0, #1 ; 1S BNE %BT20 ; 1N + 2S STRB r0, [r3, #Timer1LR] ; latch count NOW LDRB r4, [r3, #Timer1CL] LDRB r0, [r3, #Timer1CH] ADD r4, r4, r0, LSL #8 ; count after looping is ... SUB r4, r1, r4 ; decrements ! MOV r4, r4, LSR #1 ; IOC clock decrements at 2MHz ORR r9, r9, #1 :SHL: 8 ; set refresh on flyback STR r9, [r9] ; restore MEMC state a.s.a.p. ; In ROM - each cpu loop took 4R cycles @ 8/f*500ns/cycle LDR r0, =4*(8*500/1000)*ncpuloops*1000 DivRem r7, r0, r2, r1 ; r2 preserved MOV r0, #&80 ; At 8 MHz and below, run fast ROMs LDR r1, =8050 ; Over 8 MHz, need medium ROMs CMP r7, r1 MOVHI r0, #&40 LDR r1, =13000 ; Over 13 MHz, need slowest ROMs CMP r7, r1 MOVHI r0, #&00 ORR r9, r9, r0 STR r9, [r9] ; Set ROM speed appropriately ASSERT ncpuloops = 8*nmulloops ; for given ratio cutoff <------------ MOV r4, r4, LSL #10 ; *1024 to get resolution on divide DivRem r0, r4, r2, r1 LDR r1, =1100 ; Cutoff point; MEMC1 longer than this CMP r0, r1 ORRLO r7, r7, #1 :SHL: 16 ; Note MEMC1a prescence MOV pc, lr ; Typical figures give (in ROM at 8MHz): ; MEMC1 2048 CPU, 2432 MEMC -> MUL ratio 1216 ; MEMC1a 2048 864 432 ] ; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ END