From bd1274448d52c0492c4227782a836c3472ed94b5 Mon Sep 17 00:00:00 2001 From: Martin Haye Date: Sat, 6 Jun 2015 09:25:04 -0700 Subject: [PATCH] Moving memory manager up into Language Card RAM. --- Platform/Apple/virtual/src/core/mem.s | 569 ++++++++++++++------------ 1 file changed, 296 insertions(+), 273 deletions(-) diff --git a/Platform/Apple/virtual/src/core/mem.s b/Platform/Apple/virtual/src/core/mem.s index 669e367c..c42d6d8d 100644 --- a/Platform/Apple/virtual/src/core/mem.s +++ b/Platform/Apple/virtual/src/core/mem.s @@ -4,7 +4,7 @@ ; ; See detailed description in mem.i - * = $800 +* = $2000 ; PLASMA loader loads us initially at $2000 ; Use hi-bit ASCII for Apple II !convtab "../include/hiBitAscii.ct" @@ -13,13 +13,14 @@ !source "../include/global.i" !source "../include/mem.i" !source "../include/plasma.i" +!source "../include/debug.i" ; Constants MAX_SEGS = 96 DO_COMP_CHECKSUMS = 0 ; during compression debugging DEBUG_DECOMP = 0 -DEBUG = 0 +DEBUG = 1 SANITY_CHECK = 0 ; also prints out request data ; Zero page temporary variables @@ -46,12 +47,94 @@ headerBuf = $4C00 ; len $1400 prodosMemMap = $BF58 ;------------------------------------------------------------------------------ -; Initial vectors - these have to start at $800 -codeBegin: - clc - bcc locationCheck - jmp main_dispatch - jmp aux_dispatch +; Relocate all the pieces to their correct locations +relocate: +; first our lo memory piece goes to $800 (two pages should be plenty) + ldy #0 +- lda loMemBegin,y + sta $800,y + lda loMemBegin+$100,y + sta $900,y + iny + bne - +; copy the ProDOS code from main memory to aux + bit setLcRW+lcBank1 ; only copy bank 1, because bank 2 is PLASMA runtime + bit setLcRW+lcBank1 ; write to it + ldy #0 + ldx #$D0 +.pglup stx .ld+2 + stx .st+2 +.bylup sta clrAuxZP ; get byte from main LC +.ld lda $D000,y + sta setAuxZP ; temporarily turn on aux LC +.st sta $D000,y + iny + bne .bylup + inx ; all pages until we hit $00 + bne .pglup + sta clrAuxZP ; ...back to main LC +; patch into the main ProDOS MLI entry point + lda #$4C ; jmp + sta $BFBB + lda #enterProDOS1 + sta $BFBD +; patch into the interrupt handler + lda #$4C ; jmp + sta $BFEB + lda #enterProDOS2 + sta $BFED +; patch into the shared MLI/IRQ exit routine + lda #$4C ; jmp + sta $BFA0 + lda #exitProDOS + sta $BFA2 +; now blow away the main RAM LC area as a check + ldx #$D0 + lda #0 + tay +.clrlup stx .st2+2 +.st2 sta $D000,Y + iny + bne .st2 + inx + cpx #$F8 + bne .clrlup +; it's very convenient to have the monitor in the LC for debugging + bit setLcWr+lcBank1 ; read from ROM, write to LC RAM +.cpmon stx .ld3+2 + stx .st3+2 +.ld3 lda $F800,Y +.st3 sta $F800,Y + iny + bne .ld3 + inx + bne .cpmon +; Place the bulk of the memory manager code into the newly cleared LC + ldx #>hiMemBegin +.cpmm stx .ld4+2 +.ld4 lda hiMemBegin,y +.st4 sta $D000,y + iny + bne .ld4 + inc .st4+2 + inx + cpx #>(hiMemEnd+$100) + bne .cpmm +; Ready to actually init the memory manager in its final location. + ; fall through to j_init... + +;------------------------------------------------------------------------------ +; Vectors and debug support code - these go in low memory at $800 +loMemBegin: !pseudopc $800 { + jmp j_init + jmp j_main_dispatch + jmp j_aux_dispatch jmp __asmPlasm ; Vectors for debug macros @@ -65,46 +148,131 @@ codeBegin: jmp __crout jmp __waitKey -;------------------------------------------------------------------------------ -; Relocation code -locationCheck: - jsr monrts - tsx - lda $100,x - cmp #>locationCheck - bne + - jmp init -+ sta pSrc+1 - lda #>* - sta pDst+1 - ldy #0 - sty pSrc - sty pDst - ldx #>(tableEnd-codeBegin+$100) -- lda (pSrc),y - sta (pDst),y - iny - bne - - inc pSrc+1 - inc pDst+1 - dex - bne - - jmp codeBegin +j_init: + bit setLcRW+lcBank1 ; switch in mem mgr + bit setLcRW+lcBank1 + jsr init + bit setLcRW+lcBank2 ; back to PLASMA + rts + +j_main_dispatch: + bit setLcRW+lcBank1 ; switch in mem mgr + bit setLcRW+lcBank1 + jsr main_dispatch + bit setLcRW+lcBank2 ; back to PLASMA + rts + +j_aux_dispatch: + bit setLcRW+lcBank1 ; switch in mem mgr + bit setLcRW+lcBank1 + jsr aux_dispatch + bit setLcRW+lcBank2 ; back to PLASMA + rts ;------------------------------------------------------------------------------ -; Variables -targetAddr: !word 0 -unusedSeg: !byte 0 -scanStart: !byte 0, 1 ; main, aux -segNum: !byte 0 -nextLdVec: jmp diskLoader -curPartition: !byte 0 -partFileRef: !byte 0 -fixupHint: !word 0 +; Normal entry point for ProDOS MLI calls. This patches the code at $BFBB. +enterProDOS1: !zone + pla ; saved A reg + sta .ld2+1 + pla ; lo byte of ret addr + sta .ld1+1 + pla ; hi byte of ret addr + sta setAuxZP ; switch to aux stack/ZP/LC + pha ; hi byte of ret addr +.ld1 lda #11 ; self-modified earlier + pha ; lo byte of ret addr +.ld2 lda #11 ; saved A reg + pha + lda $E000 ; this is what the original code at $BFBB did + jmp $BFBE ; jump back in where ProDOS enter left off ;------------------------------------------------------------------------------ -!source "../include/debug.i" +; Entry point for ProDOS interrupt handler. This patches the code at $BFEB. +enterProDOS2: !zone + pla ; saved P reg + sta .ld2+1 + pla ; ret addr lo + sta .ld1+1 + pla ; ret addr hi + sta setAuxZP ; switch to aux stack/ZP/LC + pha +.ld1 lda #11 ; self-modified earlier + pha +.ld2 lda #11 ; ditto + pha + bit $C08B ; this is what the original code at $BFEB did + jmp $BFEE ; back to where ProDOS left off +;------------------------------------------------------------------------------ +; Shared exit point for ProDOS MLI and IRQ handlers. This patches the code +; at $BFA0. +exitProDOS: !zone + pla ; saved A reg + sta .ld3+1 + pla ; P-reg for RTI + sta .ld2+1 + pla ; hi byte of ret addr + sta .ld1+1 + pla ; lo byte of ret addr + sta clrAuxZP ; back to main stack/ZP/LC + pha ; lo byte of ret addr +.ld1 lda #11 ; self-modified earlier + pha ; hi byte of ret addr +.ld2 lda #11 ; ditto + pha ; P-reg for RTI +.ld3 lda #11 ; self-modified earlier (the saved A reg) + ; Note! We leave LC bank 1 enabled, since that's where the memory + ; manager lives, and it's the only code that calls ProDOS. + rti ; RTI pops P-reg and *exact* return addr (not adding 1) + +;------------------------------------------------------------------------------ +; Utility routine for convenient assembly routines in PLASMA code. +; Params: Y=number of parameters passed from PLASMA routine +; 1. Save PLASMA's X register index to evalStk +; 2. Verify X register is in the range 0-$10 +; 3. Load the *last* parameter into A=lo, Y=hi +; 4. Run the calling routine (X still points into evalStk for add'l params if needed) +; 5. Restore PLASMA's X register, and advance it over the parameter(s) +; 6. Store A=lo/Y=hi into PLASMA return value +; 7. Return to PLASMA +__asmPlasm: !zone + pla ; save address of calling routine, so we can call it + clc + adc #1 + sta .jsr+1 + pla + adc #0 + sta .jsr+2 + ; adjust PLASMA stack pointer to skip over params + dey + sty tmp + txa + cpx #$11 + bcs .badx ; X must be in range 0..$10 +.add adc tmp ; carry cleared by cpx above + pha ; and save that + cmp #$11 ; again, X must be in range 0..$10 + bcs .badx + lda evalStkL,x ; get last param to A=lo + ldy evalStkH,x ; ...Y=hi +.jsr jsr $1111 ; call the routine to do work + sta tmp ; stash return value lo + pla + tax ; restore adjusted PLASMA stack pointer + lda tmp + sta evalStkL,x ; store return value + tya + sta evalStkH,x + rts ; and return to PLASMA interpreter +.badx ; X reg ran outside valid range. Print and abort. + +prStr : !text $8D,"X=",0 + +prX + ldx #<+ + ldy #>+ + jmp fatalError ++ !text $8D, "PLASMA x-reg out of range", 0 + +;------------------------------------------------------------------------------ ; Debug code to support macros ; Fetch a byte pointed to by the first entry on the stack, and advance that entry. @@ -216,6 +384,66 @@ __waitKey: !zone { jmp iorest } +!macro callMLI cmd, parms { + lda #cmd + ldx #parms + jsr _callMLI +} + +; Call MLI from main memory rather than LC, since it lives in aux LC. +_callMLI: sta .cmd + stx .params + sty .params+1 + jsr mli +.cmd !byte 0 +.params !word 0 + rts + +; Out ProDOS param blocks can't be in LC ram +openParams: !byte 3 ; param count + !word filename ; pointer to file name + !word fileBuf ; pointer to buffer +openFileRef: !byte 0 ; returned file number +filename: !byte 15 ; length + !raw "/LL/GAME.PART." ; TODO: Figure out how to avoid specifying full path. "raw" for ProDOS + ; If I leave it out, ProDOS complains with error $40. +partNumChar: !raw "x" ; "x" replaced by partition number + +readParams: !byte 4 ; param count +readFileRef: !byte 0 ; file ref to read +readAddr: !word 0 +readLen: !word 0 +readGot: !word 0 + +setMarkParams: !byte 2 ; param count +setMarkFileRef: !byte 0 ; file reference to set mark in +setMarkPos: !byte 0 ; mark position (3 byte integer) + !byte 0 + !byte 0 + +closeParams: !byte 1 ; param count +closeFileRef: !byte 0 ; file ref to close + +paramsEnd = * +} ; end of !pseodupc $800 +loMemEnd = * + +;------------------------------------------------------------------------------ +; The remainder of the code gets relocated up into the Language Card, bank 1. +hiMemBegin: !pseudopc $D000 { + +;------------------------------------------------------------------------------ +; Variables +targetAddr: !word 0 +unusedSeg: !byte 0 +scanStart: !byte 0, 1 ; main, aux +segNum: !byte 0 +nextLdVec: jmp diskLoader +curPartition: !byte 0 +partFileRef: !byte 0 +fixupHint: !word 0 + ;------------------------------------------------------------------------------ grabSegment: !zone ; Input: None @@ -541,8 +769,6 @@ init: !zone ; put something interesting on the screen :) jsr home +prStr : !text "Welcome to Mythos.",0 -; relocate ProDOS to the aux LC bank - jsr moveProDOS ; close all files lda #0 jsr closeFile @@ -623,9 +849,9 @@ init: !zone sty tSegAdrHi+3 dey sty tSegAdrHi+7 - lda #tableEnd + lda #>paramsEnd sta tSegAdrHi+4 lda #$40 sta tSegAdrHi+5 @@ -650,6 +876,10 @@ init: !zone lda #$20 sta framePtr+1 ; because sanity check verifies it's not $BE or $BF } + + jsr printMem + brk + ldx #0 ldy #2 ; 2 pages lda #REQUEST_MEMORY @@ -691,132 +921,6 @@ init: !zone ldx #$10 ; initial eval stack index .gomod: jmp $1111 ; jump to module for further bootstrapping -;------------------------------------------------------------------------------ -moveProDOS: !zone -; only do this once - lda $BFBB - cmp #$4C - bne + - rts -+ -; copy the ProDOS code from main memory to aux - ldy #0 - ldx #$D0 - bit setLcRW+lcBank1 ; turn on language card - bit setLcRW+lcBank1 ; for writing -.pglup stx .ld+2 - stx .st+2 -.bylup sta clrAuxZP -.ld lda $D000,Y - sta setAuxZP -.st sta $D000,Y - iny - bne .bylup - inx - bne .pglup - sta clrAuxZP -; patch into the main ProDOS MLI entry point - lda #$4C ; jmp - sta $BFBB - lda #enterProDOS1 - sta $BFBD -; patch into the interrupt handler - lda #$4C ; jmp - sta $BFEB - lda #enterProDOS2 - sta $BFED -; patch into the shared MLI/IRQ exit routine - lda #$4C ; jmp - sta $BFA0 - lda #exitProDOS - sta $BFA2 -; now blow away the main LC area as a check - bit setLcWr+lcBank1 ; only clear bank 1, because bank 2 is PLASMA runtime - bit setLcWr+lcBank1 ; write to it - ldx #$D0 - lda #0 - tay -.clrlup stx .st2+2 -.st2 sta $D000,Y - iny - bne .st2 - inx - cpx #$F8 - bne .clrlup -; it's very convenient to have the monitor in the LC -.cpmon stx .ld3+2 - stx .st3+2 -.ld3 lda $F800,Y -.st3 sta $F800,Y - iny - bne .ld3 - inx - bne .cpmon -; all done -.done rts - -;------------------------------------------------------------------------------ -; Normal entry point for ProDOS MLI calls. This patches the code at $BFBB. -enterProDOS1: !zone - pla ; saved A reg - sta .ld2+1 - pla ; lo byte of ret addr - sta .ld1+1 - pla ; hi byte of ret addr - sta setAuxZP ; switch to aux stack/ZP/LC - pha ; hi byte of ret addr -.ld1 lda #11 ; self-modified earlier - pha ; lo byte of ret addr -.ld2 lda #11 ; saved A reg - pha - lda $E000 ; this is what the original code at $BFBB did - jmp $BFBE ; jump back in where ProDOS enter left off - -;------------------------------------------------------------------------------ -; Entry point for ProDOS interrupt handler. This patches the code at $BFEB. -enterProDOS2: !zone - pla ; saved P reg - sta .ld2+1 - pla ; ret addr lo - sta .ld1+1 - pla ; ret addr hi - sta setAuxZP ; switch to aux stack/ZP/LC - pha -.ld1 lda #11 ; self-modified earlier - pha -.ld2 lda #11 ; ditto - pha - bit $C08B ; this is what the original code at $BFEB did - jmp $BFEE ; back to where ProDOS left off - -;------------------------------------------------------------------------------ -; Shared exit point for ProDOS MLI and IRQ handlers. This patches the code -; at $BFA0. -exitProDOS: !zone - pla ; saved A reg - sta .ld3+1 - pla ; P-reg for RTI - sta .ld2+1 - pla ; hi byte of ret addr - sta .ld1+1 - pla ; lo byte of ret addr - sta clrAuxZP ; back to main stack/ZP/LC - bit setLcRW+lcBank2 ; switch in PLASMA - bit setLcRW+lcBank2 ; for write as well as read - pha ; lo byte of ret addr -.ld1 lda #11 ; self-modified earlier - pha ; hi byte of ret addr -.ld2 lda #11 ; ditto - pha ; P-reg for RTI -.ld3 lda #11 ; self-modified earlier (the saved A reg) - rti ; RTI pops P-reg and *exact* return addr (not adding 1) - ;------------------------------------------------------------------------------ !if DEBUG { printMem: !zone @@ -1332,14 +1436,12 @@ openPartition: !zone lda curPartition clc adc #'0' ; assume partition numbers range from 0..9 for now - sta .partNumChar + sta partNumChar ; open the file - jsr mli - !byte MLI_OPEN - !word .openParams + +callMLI MLI_OPEN, openParams bcs prodosError ; grab the file number, since we're going to keep it open - lda .openFileRef + lda openFileRef sta partFileRef sta readFileRef ; Read the first two bytes, which tells us how long the header is. @@ -1361,21 +1463,6 @@ openPartition: !zone sta readAddr jmp readToMain ; finish by reading the rest of the header -.openParams: !byte 3 ; number of params - !word .filename ; pointer to file name - !word fileBuf ; pointer to buffer -.openFileRef: !byte 0 ; returned file number -.filename: !byte 15 ; length - !raw "/LL/GAME.PART." ; TODO: Figure out how to avoid specifying full path. "raw" for ProDOS - ; If I leave it out, ProDOS complains with error $40. -.partNumChar: !raw "x" ; "x" replaced by partition number - -readParams: !byte 4 ; number of params -readFileRef: !byte 0 -readAddr: !word 0 -readLen: !word 0 -readGot: !word 0 - ;------------------------------------------------------------------------------ prodosError: !zone pha @@ -1500,14 +1587,14 @@ disk_finishLoad: !zone lda partFileRef ; see if we actually queued anything (and opened the file) bne + ; non-zero means we have work to do rts ; nothing to do - return immediately -+ sta .setMarkFileRef ; copy the file ref number to our MLI param blocks ++ sta setMarkFileRef ; copy the file ref number to our MLI param blocks sta readFileRef lda headerBuf ; grab # header bytes - sta .setMarkPos ; set to start reading at first non-header byte in file + sta setMarkPos ; set to start reading at first non-header byte in file lda headerBuf+1 ; hi byte too - sta .setMarkPos+1 + sta setMarkPos+1 lda #0 - sta .setMarkPos+2 + sta setMarkPos+2 sta .nFixups jsr setupDecomp ; one-time init for decompression code jsr startHeaderScan ; start scanning the partition header @@ -1573,9 +1660,7 @@ disk_finishLoad: !zone lda tSegAdrHi,x ; hi byte too sta pDst+1 !if DEBUG { jsr .debug2 } - jsr mli ; move the file pointer to the current block - !byte MLI_SET_MARK - !word .setMarkParams + +callMLI MLI_SET_MARK, setMarkParams ; move the file pointer to the current block bcs .prodosErr !if DEBUG >= 2 { +prStr : !text "Deco.",0 } jsr lz4Decompress ; decompress (or copy if uncompressed) @@ -1583,18 +1668,18 @@ disk_finishLoad: !zone .resume ldy .ysave .next lda (pTmp),y ; lo byte of length clc - adc .setMarkPos ; advance mark position exactly that far - sta .setMarkPos + adc setMarkPos ; advance mark position exactly that far + sta setMarkPos iny lda (pTmp),y ; hi byte of length bpl + ; if hi bit is clear, resource is uncompressed iny ; skip compressed size iny and #$7F ; mask off the flag -+ adc .setMarkPos+1 ; bump the high byte of the file mark pos - sta .setMarkPos+1 ++ adc setMarkPos+1 ; bump the high byte of the file mark pos + sta setMarkPos+1 bcc + - inc .setMarkPos+2 ; account for partitions > 64K + inc setMarkPos+2 ; account for partitions > 64K + iny ; increment to next entry bpl + ; if Y index is is small, no need to adjust jsr adjYpTmp ; adjust pTmp and Y to make it small again @@ -1604,11 +1689,6 @@ disk_finishLoad: !zone .addrErr: jmp invalAddr -.setMarkParams: !byte 2 ; param count -.setMarkFileRef:!byte 0 ; file reference -.setMarkPos: !byte 0 ; mark position (3 byte integer) - !byte 0 - !byte 0 .ysave: !byte 0 .nFixups: !byte 0 @@ -1652,24 +1732,16 @@ adjYpTmp: !zone ;------------------------------------------------------------------------------ closeFile: !zone - sta .closeFileRef - jsr mli ; now that we're done loading, we can close the partition file - !byte MLI_CLOSE - !word .closeParams + sta closeFileRef + +callMLI MLI_CLOSE, closeParams bcs .prodosErr rts .prodosErr: jmp prodosError -.closeParams: - !byte 1 ; param count -.closeFileRef: - !byte 0 ; file ref to close ;------------------------------------------------------------------------------ readToMain: !zone - jsr mli - !byte MLI_READ - !word readParams + +callMLI MLI_READ, readParams bcs .err rts .err: jmp prodosError @@ -2187,58 +2259,6 @@ doAllFixups: !zone .mainBase !word 0 .auxBase !word 0 -;------------------------------------------------------------------------------ -; Utility routine for convenient assembly routines in PLASMA code. -; Params: Y=number of parameters passed from PLASMA routine -; 1. Save PLASMA's X register index to evalStk -; 2. Verify X register is in the range 0-$10 -; 3. Switch to ROM -; 4. Load the *last* parameter into A=lo, Y=hi -; 5. Run the calling routine (X still points into evalStk for add'l params if needed) -; 6. Switch back to LC RAM -; 7. Restore PLASMA's X register, and advance it over the parameter(s) -; 8. Store A=lo/Y=hi into PLASMA return value -; 9. Return to PLASMA -__asmPlasm: !zone - pla ; save address of calling routine, so we can call it - clc - adc #1 - sta .jsr+1 - pla - adc #0 - sta .jsr+2 - ; adjust PLASMA stack pointer to skip over params - dey - sty tmp - txa - cpx #$11 - bcs .badx ; X must be in range 0..$10 -.add adc tmp ; carry cleared by cpx above - pha ; and save that - cmp #$11 ; again, X must be in range 0..$10 - bcs .badx - lda evalStkL,x ; get last param to A=lo - ldy evalStkH,x ; ...Y=hi -.jsr jsr $1111 ; call the routine to do work - sta tmp ; stash return value lo - pla - tax ; restore adjusted PLASMA stack pointer - lda tmp - sta evalStkL,x ; store return value - tya - sta evalStkH,x - rts ; and return to PLASMA interpreter -.badx jsr crout ; X reg ran outside valid range. Print and abort. - lda #'X' - jsr cout - txa - jsr prbyte - jsr crout - ldx #<+ - ldy #>+ - jmp fatalError -+ !text $8D, "PLASMA x-reg out of range", 0 - ;------------------------------------------------------------------------------ ; Segment tables @@ -2253,3 +2273,6 @@ tSegAdrHi = * : !fill MAX_SEGS ;------------------------------------------------------------------------------ ; Marker for end of the tables, so we can compute its length tableEnd = * + +} ; end of !pseudopc $D000 +hiMemEnd = * \ No newline at end of file