URL
https://opencores.org/ocsvn/rtf65002/rtf65002/trunk
Subversion Repositories rtf65002
[/] [rtf65002/] [trunk/] [software/] [asm/] [memory.asm] - Rev 40
Compare with Previous | Blame | View Log
; ============================================================================; __; \\__/ o\ (C) 2013, 2014 Robert Finch, Stratford; \ __ / All rights reserved.; \/_// robfinch<remove>@opencores.org; ||;;; This source file is free software: you can redistribute it and/or modify; it under the terms of the GNU Lesser General Public License as published; by the Free Software Foundation, either version 3 of the License, or; (at your option) any later version.;; This source file is distributed in the hope that it will be useful,; but WITHOUT ANY WARRANTY; without even the implied warranty of; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the; GNU General Public License for more details.;; You should have received a copy of the GNU General Public License; along with this program. If not, see <http://www.gnu.org/licenses/>.;; ============================================================================;MAX_VIRTUAL_PAGE EQU 320MAX_PHYSICAL_PAGE EQU 2048INV_PAGE EQU 000 ; page number to use for invalid entries;------------------------------------------------------------------------------; InitMMU;; Initialize the 64 maps of the MMU.; Initially all the maps are set the same:; Virtual Page Physical Page; 000-319 000 (invalid page marker); 320-511 1856-2047; Note that there are only 512 virtual pages per map, and 2048 real; physical pages of memory. This limits maps to 32MB.; This range includes the BIOS assigned stacks for the tasks and tasks; virtual video buffers.; Note that physical pages 0 to 1855 are not mapped, but do exist. They may; be mapped into a task's address space as required.; If changing the maps the last 192 pages (12MB) of the map should always point; to the BIOS area. Don't change map entries 320-511 or the system may; crash. The last 192 pages map the virtual memory to the same physical; addresses so that the physical and virtual address are the same.; If the rts at the end of this routine works, then memory was mapped; successfully.;; System Memory Map (Physical Addresses); Page; 0000 BASIC ROM, scratch memory ( 1 page global); 0001-0063 unassigned (4MB - 63 pages); 0064-0191 Bitmap video memory (8 MB - 128 pages); 0192-0336 DOS usage, disk cache etc. (9.4MB - 145 pages); 0337-1855 Heap space (99MB - 1519 pages); 1856-1983 Virtual Screen buffers (8MB - 128 pages); 1984-2047 BIOS/OS area (4MB - 64 pages); 2032-2047 Stacks area (1MB - 16 pages); 65535 BIOS ROM (64kB - 1 Page global); 261952-262015 I/O area (4MB - 64 pages global);------------------------------------------------------------------------------align 8public InitMMU:lda #1sta MMU_KVMMU+1deasta MMU_KVMMUimmu1:sta MMU_AKEY ; set access key for mapldx #0immu2:; set the first 320 pages to invalid page marker; set the last 192 pages to physical page 1856-2047ld r4,#INV_PAGEcpx #320blo immu3ld r4,r2add r4,r4,#1536 ; 1856-320immu3:st r4,MMU,xinxcpx #512bne immu2inacmp #64 ; 64 MMU mapsbne immu1stz MMU_OKEY ; set operating key to map #0lda #2sta MMU_FUSE ; set fuse to 2 clocks before mapping startsnopnop;------------------------------------------------------------------------------; Note that when switching the memory map, the stack address should not change; as the virtual address was mapped to the physical one.;------------------------------------------------------------------------------;align 8public EnableMMUMapping:phalda RunningTCB ; no need to enable mapping for Monitor/Debugger joblda TCB_hJCB,r1cmp #2blo dmm2lda #12 ; is there even an MMU present ?bmt CONFIGRECbeq emm1lda RunningTCBlda TCB_hJCB,r1sta MMU_OKEY ; select the mmu map for the joblda #2sta MMU_FUSE ; set fuse to 2 clocks before mapping startslda #1sta MMU_MAPEN ; set MMU_MAPEN = 1emm1:plartspublic DisableMMUMapping:phadmm2:lda #12 ; is there even an MMU present ?bmt CONFIGRECbeq dmm1stz MMU_MAPENdmm1:plarts;------------------------------------------------------------------------------;------------------------------------------------------------------------------;SetAKEYForCurrentJob:phajsr GetPtrCurrentJCBlda JCB_Map,r1sta MMU_AKEYplarts;------------------------------------------------------------------------------;------------------------------------------------------------------------------;align 8public MemInit:lda #1 ; initialize memory semaphoresta mem_semalda #1519sta nPagesFree; Initialize the allocated page map to zero.lda #64 ; 64*32 = 2048 bitsldx #0ldy #PageMapstos; Mark the last 192 pages as used (by the OS); 6-32 bit wordslda #-1sta PageMap+58sta PageMap+59sta PageMap+60sta PageMap+61sta PageMap+62sta PageMap+63; Mark page #0 usedlda #1sta PageMap; Mark 64-336 used (DOS)lda #64meminit1:bms PageMapinacmp #336blo meminit1rts;------------------------------------------------------------------------------; Allocate a memory page from the available memory pool.; Returns a pointer to the page in memory. The address returned is the; virtual memory address.;; Returns:; r1 = 0 if no more memory is available or max mapped capacity is reached.; r1 = virtual address of allocated memory page;------------------------------------------------------------------------------;align 8public AllocMemPage:phxphy; Search the page bitmap for a free memory page.lda #0ldx #MAX_PHYSICAL_PAGEspl mem_sema + 1amp2:bmt PageMapbeq amp1 ; found a free page ?inadexbne amp2; Here all memory pages are already in use. No more memmory is available.stz mem_sema + 1plyplxlda #0rts; Here we found an unallocated memory page. Next find a spot in the MMU; map to place the page.amp1:; Find unallocated map slot in the MMUjsr SetAKEYForCurrentJobldx #0amp4:ldy MMU,xcpy #INV_PAGEbeq amp3inxcpx #MAX_VIRTUAL_PAGEbne amp4; Here we searched the entire MMU slots and none were availablestz mem_sema + 1plyplxlda #0 ; return NULL pointerrts; Here we have both an available page, and available map slot.amp3:bms PageMap ; mark page as allocatedsta MMU,x ; put the page# into the map slotasl r1,r2,#14 ; pages are 16kW in size (compute virtual address)dec nPagesFreestz mem_sema + 1plyplxrts;------------------------------------------------------------------------------; Parameters:; r1 = size of allocation in words; Returns:; r1 = word pointer to memory; No MMU;------------------------------------------------------------------------------;align 8public AllocMemPages:phpphxphypush r4seiamp5:taylsr r3,r3,#14 ; convert amount to #pagesiny ; round upcpy nPagesFreebhi amp11tyx ; x = request size in pages; Search for enough free pages to satisfy the requestlda #0amp7:bmt PageMap ; test for a free pagebne amp6 ; not a free pagecpx #1 ; did we find enough free pages ?bls amp8dexamp6: ; keep checking for next free pageinacmp #1855 ; did we hit end of map ?bhi amp11 ; can't allocate enough memorybra amp7 ; go back and test for another free page; Insufficient memory, return NULL pointeramp11:lda #0pop r4plyplxplprts; Mark pages as allocatedamp8:tyx ; x= #pages to allocatecpx #1bne amp9txa ; flag indicates last pagebra amp10amp9:lda #0 ; flag indicates middle pageamp10:jsr AllocMemPage ; allocate first pageld r4,r1 ; save virtual address of first page allocateddexbeq amp14amp13:cpx #1bne amp15txabra amp12amp15:lda #0amp12:jsr AllocMemPagedexbne amp13amp14:ld r1,r4 ; r1 = first virtual addresspop r4plyplxplprts;------------------------------------------------------------------------------; FreeMemPage:;; Free a single page of memory. This is an internal function called by; FreeMemPages(). Normally FreeMemPages() will be called to free up the; entire run of pages. This function both unmarks the memory page in the; page bitmap and invalidates the page in the MMU.;; Parameters:; r1 = virtual memory address;------------------------------------------------------------------------------;align 8FreeMemPage:phaphpphxsei; First mark the page as available in the virtual page map.phalsr r1,r1,#14and #$1ff ; 512 virtual pages maxldx RunningTCBldx TCB_mmu_map,x ; x = map #asl r2,r2,#4 ; 16 words per mapbmc VPM_bitmap_b0,x ; clear both bitsbmc VPM_bitmap_b1,xpla; Mark the page available in the physical page mapphajsr VirtToPhys ; convert to a physical addresslsr r1,r1,#14and #$7ff ; 2048 physical pages maxbmc PageMappla; Now mark the MMU slot as emptylsr r1,r1,#14 ; / 16kW r1 = page # nowand #$1ff ; 512 pages maxjsr SetAKEYForCurrentJobtaxlda #INV_PAGEsta MMU,xinc nPagesFreeplxplpplarts;------------------------------------------------------------------------------; FreeMemPages:;; Free up multiple pages of memory. The pages freed are a consecutive; run of pages. A double-bit bitmap is used to identify where the run of; pages ends. Bit code 00 indicates a unallocated page, 01 indicates an; allocated page somewhere in the run, and 11 indicates the end of a run; of allocated pages.;; Parameters:; r1 = pointer to memory;------------------------------------------------------------------------------;align 8public FreeMemPages:cmp #0x3fff ; test for a proper pointerbls fmp5pha; Turn the memory pointer into a bit indexlsr r1,r1,#14 ; / 16kW acc = virtual page #cmp #MAX_VIRTUAL_PAGE ; make sure index is sensiblebhs fmp4phxspl mem_sema + 1ldx RunningTCBldx TCB_mmu_map,xasl r2,r2,#4fmp2:bmt VPM_bitmap_b1,x ; Test to see if end of allocationbne fmp3asl r1,r1,#14 ; acc = virtual addressjsr FreeMemPage ;lsr r1,r1,#14 ; acc = virtual page # againinacmp #MAX_VIRTUAL_PAGE ; last 192 pages aren't freeableblo fmp2fmp3; Clear the last bitasl r1,r1,#14 ; acc = virtual addressjsr FreeMemPage ;lsr r1,r1,#14 ; acc = virtual page # againbmc VPM_bitmap_b1,xstz mem_sema + 1plxfmp4:plafmp5:rts;------------------------------------------------------------------------------; Convert a virtual address to a physical address.; Parameters:; r1 = virtual address to translate; Returns:; r1 = physical address;------------------------------------------------------------------------------;align 8public VirtToPhys:cmp #$3FFF ; page #0 is physical page #0bls vtp2cmp #$01FFFFFF ; outside of managed address bounds (ROM / IO)bhi vtp2phxldx CONFIGREC ; check if there is an MMU presentbit r2,#4096 ; if not, then virtual and physical addressesbeq vtp3 ; will matchphytay ; save original addressand r3,r3,#$FF803FFF ; mask off MMU managed address bitsjsr SetAKEYForCurrentJoblsr r2,r1,#14 ; convert to MMU indexand r2,r2,#511 ; 512 mmu pageslda MMU,x ; a = physical page#beq vtp1 ; zero = invalid address translationasl r1,r1,#14 ; *16kWor r1,r1,r3 ; put back unmanaged address bitsvtp1:plyvtp3:plxvtp2:rts;------------------------------------------------------------------------------; PhysToVirt;; Convert a physical address to a virtual address. A little more complex; than converting virtual to physical addresses as the MMU map table must; be searched for the physical page.;; Parameters:; r1 = physical address to translate; Returns:; r1 = virtual address;------------------------------------------------------------------------------;align 8public PhysToVirt:cmp #$3FFF ; first check for direct translationsbls ptv3 ; outside of the MMU managed rangecmp #$01FFFFFFbhi ptv3phxldx CONFIGREC ; check if there is an MMU presentbit r2,#4096 ; if not, then virtual and physical addressesbeq ptv4 ; will matchphyjsr SetAKEYForCurrentJobtayand r3,r3,#$FF803FFF ; mask off MMU managed address bitslsr r1,r1,#14 ; /16k to get indexand r1,r1,#$7ff ; 2048 pages maxldx #0ptv2:cmp MMU,xbeq ptv1inxcpx #512bne ptv2; Return NULL pointer if address translation failsplyplxlda #0rtsptv1:asl r1,r2,#14 ; * 16kor r1,r1,r3 ; put back unmanaged address bitsplyptv4:plxptv3:rts; ============================================================================; Heap related functions.;; The heap is managed as a doublely linked list of memory blocks.; ============================================================================align 8public InitHeap:lda RunningTCBldx TCB_HeapStart,r1ldy TCB_HeapEnd,r1lda #$4D454D20sta MEM_CHK,xsta MEM_FLAG,xlda #$6D656D20 ; mark the last block as allocatedsta MEM_CHK,ysta MEM_FLAG,ylda #0sta MEM_PREV,x ; prev of first MEMHDRsty MEM_NEXT,xsta MEM_NEXT,ystx MEM_PREV,yrts;------------------------------------------------------------------------------; Allocate memory from the heap.; Each task has it's own memory heap.;------------------------------------------------------------------------------align 8public MemAlloc:phxphypush r4ldx RunningTCBldx TCB_HeapStart,xmema4:ldy MEM_FLAG,x ; Check the flag word to see if this block is availablecpy #$4D454D20bne mema1 ; block not available, go to next blockld r4,MEM_NEXT,x ; compute the size of this blocksub r4,r4,r2sub r4,r4,#4 ; minus size of block headercmp r1,r4 ; is the block large enough ?bmi mema2 ; if yes, go allocatemema1:ldx MEM_NEXT,x ; go to the next blockbeq mema3 ; if no more blocks, out of memory errorbra mema4mema2:ldy #$6D656D20sty MEM_FLAG,xsub r4,r4,r1cmp r4,#4 ; is the block large enough to splitbpl memaSplittxaadd #4 ; point to payload areapop r4plyplxrtsmema3: ; insufficient memorypop r4plyplxlda #0rtsmemaSplit:add r4,r1,r2add r4,#4ldy #$4D454D20sty (r4)sty MEM_FLAG,r4stx MEM_PREV,r4ldy MEM_NEXT,xsty MEM_NEXT,r4st r4,MEM_PREV,yld r1,r4add #4pop r4plyplxrts;------------------------------------------------------------------------------; Free previously allocated memory. Recombine with next and previous blocks; if they are free as well.;------------------------------------------------------------------------------align 8public MemFree:cmp #4 ; null pointer ?blo memf2phxphysub #4 ; backup to header arealdx MEM_FLAG,r1cpx #$6D656D20 ; is the block allocated ?bne memf1ldx #$4D454D20stx MEM_FLAG,r1 ; mark block as freeldx MEM_PREV,r1 ; is the previous block free ?beq memf3 ; no previous blockldy MEM_FLAG,xcpy #$4D454D20bne memf3 ; the previous block is not freeldy MEM_NEXT,r1sty MEM_NEXT,xbeq memf1 ; no next blockstx MEM_PREV,ymemf3:ldy MEM_NEXT,r1ldx MEM_FLAG,ycpx #$4D454D20bne memf1 ; next block not freeldx MEM_PREV,r1stx MEM_PREV,ybeq memf1 ; no previous blocksty MEM_NEXT,xmemf1:plyplxmemf2:rts;------------------------------------------------------------------------------; Report the amount of system memory free. Counts up the number of; unallocated pages in the page bitmap.;------------------------------------------------------------------------------;public ReportMemFree:jsr CRLFlda #' 'jsr DisplayCharlda #0tayrmf2:bmt PageMapbne rmf1inyrmf1:inacmp #2048blo rmf2tyaasl r1,r1,#14 ; 16kW per bitldx #5jsr PRTNUMlea r1,msgMemFreejsr DisplayStringBrtsmsgMemFree:db " words free",CR,LF,0;==============================================================================; Memory Management routines follow.;==============================================================================;------------------------------------------------------------------------------; brk; Establish a new program break;; Parameters:; r1 = new program break address;------------------------------------------------------------------------------;public _brk:phxpush r4push r5push r6ldx RunningTCBld r4,TCB_ASID,xst r4,MMU_AKEYld r4,TCB_npages,xlsr r1,r1,#14add r1,r1,#1cmp r1,r4beq brk6 ; allocation isn't changingblo brk1 ; reducing allocation; Here we're increasing the amount of memory allocated to the program.;cmp r1,#320 ; max 320 RAM pagesbhi brk2sub r1,r1,r4 ; number of new pagescmp r1,mem_pages_free ; are there enough free pages ?bhi brk2ld r5,mem_pages_freesub r5,r5,r1st r5,mem_pages_freeld r6,r1 ; r6 = number of pages to allocateadd r1,r1,r4 ; get back value of addresssta TCB_npages,xlda #0brk5:bmt PageMap ; test if page is freebne brk4 ; no, go for next pagebms PageMap ; allocate the pagesta MMU,r4 ; store the page number in the MMU tableadd r4,#1 ; move to next MMU entrysub r6,#1 ; decrement count of neededbeq brk6 ; we're done if count = 0brk4:inacmp #2048blo brk5; Here there was an OS or hardware error; According to mem_pages_free there should have been enough free pages; to fulfill the request. Something is corrupt.;; Here we are reducing the program break, which means freeing up pages of; memory.brk1:sta TCB_npages,xadd r5,r1,#1 ; move to page after last pagebrk7:cmp r5,r4 ; are we done freeing pages ?bhi brk6lda MMU,r5 ; get the page to freebmc PageMap ; free the pageinc mem_pages_freeadd r5,#1bra brk7; Successful returnbrk6:pop r6pop r5pop r4plxlda #0rts; Return insufficient memory error;brk2:lda #E_NoMemsta TCB_errno,xpop r6pop r5pop r4plxlda #-1rts;------------------------------------------------------------------------------; Parameters:; r1 = change in memory allocation;------------------------------------------------------------------------------public _sbrk:phxpush r4push r5ldx RunningTCBld r4,TCB_npages,x ; get the current memory allocationcmp r1,#0 ; zero difference = get old brk addressbeq sbrk2asl r5,r4,#14 ; convert to wordsadd r1,r1,r5 ; +/- amountjsr _brkcmp r1,#-1bne sbrk2; Failure return, return -1;pop r5pop r4plxrts; Successful return, return the old break address;sbrk2:ld r1,r4asl r1,r1,#14pop r5pop r4plxrts
