home *** CD-ROM | disk | FTP | other *** search
Text File | 1994-01-28 | 52.5 KB | 1,668 lines |
- *************************************************************************************
- *
- * AppVM ® (Application VIRTUAL MEMORY) © Copyright LVA March 1992
- * ------- --------------------------
- *
- * Written by Laurence Vanhelsuwé.
- *
- *
- * AppVM ® is a stop-gap solution for application writers who need UNIX-style
- * Demand Paged Virtual Memory.
- * This program extends the functionality of Exec's AllocMem() and FreeMem() and
- * AmigaDOS's Read() and Write().
- * Only Processes can use VM, not Tasks !
- *
- * This implementation behaves like an exclusive resource like PAR: or SER: as
- * opposed to a shared resource. A single Process at a time can allocate (and use)
- * areas of memory that are larger than physical RAM memory as if that memory was
- * physically present.
- * As soon as this unique Process (referred to in the source as the "client") frees
- * all its VM, another client can immediately start using the resource again.
- *
- * This program is called Application VM because the system (Exec, Intuition)
- * is never allowed to handle VM pointers.
- * Only applications are able to get VM and use it "internally", i.e. they are not
- * allowed to pass a VM ptr to the system (via a call to DoIO() for example).
- * (More precisely, bus error exceptions due to the dereferencing of VM ptrs are
- * only handled correctly if the code trapping is in the scope of the client Process.)
- *
- * There's one important exception to this rule:
- * Exec's AllocMem() is the source of VM ptrs and FreeMem(), Read() and Write() can
- * all handle VM ptrs.
- * This should be enough for any application (who gets the status of "client") to
- * process huge amounts of data and load and/or save into/from VM.
- *
- * History
- * -------
- * 24/MAR/92: started code (goal: getting the Amiga to use my new MMU tree)
- * 25/MAR/92: (goal: getting instructions which use VM to restart)
- * 26/MAR/92: (goal: getting a client Process to use service)
- * 27/MAR/92: (goal: full service incl. Read()/Write() )
- * 29/APR/92: fixed double Close() bug in case disk full
- *
- *************************************************************************************
-
- include std
- include hardware/custom.i
- include exec/vmemory.i ;include our new VM include!
-
-
- ;-----------------------------------------------
- ; Program Global Constants
- ;-----------------------------------------------
-
- ; This version allows a maximum VM request of 256Mb (if you've got that much spare
- ; on your hard disk). The code can handle more though. **!! (How much, 2 Gb ?)
- ; The absolute maximum in any case is 2 Gigabytes since VM addresses start at
- ; $80000000.
-
- MAX_VM_MEGS equ 256 ;256Mb max LA range
-
- ; Since we're only using a single B-level table for the VM logical addresses (which
- ; have bit31=1), I can only map a maximum VM range of 256Mb.
- ; That should be enough since you also need that much space on your hard disk!
-
- KILOBYTE equ 1024
- MEGABYTE equ KILOBYTE*KILOBYTE
-
- PAGE_SHIFT equ 15 ;2^15 = 32K
- PAGE_SIZE equ 1<<PAGE_SHIFT ;one PAGE is 32K big
-
- ; Pages are this large because:
- ;
- ; 1) AmigaDOS is very efficient at transfering large chuncks from/to disk.
- ; 2) Bus exception errors become less frequent.
- ; 4) The D-level table shrinks significantly.
- ; 3) The free pages bitmap shrinks too.
-
-
- VM_START equ $80000000 ;all VM addresses have b31=1
-
- ; What this program does is to create a new MMU table which CONTAINS the standard
- ; 2-level (A,B) Exec MMU table and a new branch for VM addresses.
- ; The standard table uses TIA=8 and TIB=6 with a table A index LIMIT of 16.
- ; This means that the standard table provides translation for the very first 256Mb
- ; of address space in the Amiga. Everything above it is off-limit (BUS ERROR).
-
- ; What AppVM does is to "shift down" this tree and turn it into a branch of a bigger
- ; tree using the following LA field subdivisions:
-
- ; TABLE ENTRIES PER ENTRY
- ; ----- ------- ---------
-
- TIA equ 4 ; 4Gb 16 = 256 Mb
- TIB equ 4 ;256Mb 16 = 16 Mb
- TIC equ 6 ; 16Mb 64 = 256 K
- TID equ 3 ;256K 8 = 32 K PAGES
-
- ; Check whether chosen logical address breakdown fields are legal.
-
- TC_CHECK equ 32-(TIA+TIB+TIC+TID+PAGE_SHIFT)
-
- IFNE TC_CHECK
- blabla ; ** WARNING ** SELECTED LA INDEX BREAK-DOWN IS INVALID
- END
- ENDC
-
- ; The new TIB,TIC values have to be derived from the TIA,TIB values of the original
- ; translation tree. TIC has to be the old TIB. TIB Can be 4 instead the original
- ; TIA's 8 because the original A-table is LIMITED to the first 16 indices.
- ; This is necessary so that I can retain the existing MMU translation tree as a
- ; branch of my bigger tree (which is now a 4-level tree).
-
-
- *####################################################################################
- *
- * RESULTING MMU TRANSLATION TREE STRUCTURE:
- * =========================================
- *
- * A-table (one for ENTIRE SYSTEM)
- *
- * ____________
- * 0 |_1st 256Mb|------> STANDARD Exec MMU table (becomes levels B and C)
- * 1 | |
- * 2 | |
- * 3 | |
- * 4 | |
- * 5 | |
- * 6 | |
- * 7 | |
- * 8 |_8th 256Mb|------> VM B-table
- * 9 | | ____________
- * A | | | 1st 16Mb |------> 1st VM C-table
- * B | | | 2nd 16Mb | ______________
- * C | | | 3rd 16Mb | | 1st 256K |-----> 1st VM D-table
- * D | | | 4th 16Mb | | 2nd 256K | _______________
- * E | | | 5th 16Mb | | | | 1st 32K PAGE |
- * F | | | 6th 16Mb | : : | 2nd 32K PAGE |
- * ------------ | 7th 16Mb | | 3rd 32K PAGE |
- * All other ranges | 8th 16Mb | 64 entries | 4th 32K PAGE |
- * are INVALID. | 9th 16Mb | | 5th 32K PAGE |
- * |10th 16Mb | : : | 6th 32K PAGE |
- * |11th 16Mb | | | | 7th 32K PAGE |
- * |12th 16Mb | | 63rd 256K | | 8th 32K PAGE |
- * |13th 16Mb | | 64th 256K | ----------------
- * |14th 16Mb | --------------
- * |15th 16Mb |
- * |16th 16Mb |
- * ------------
- *####################################################################################
-
- A_TSIZE equ (1<<TIA)*4 ;64 ;size of one table at each level
- B_TSIZE equ (1<<TIB)*4 ;64 ;(using short descriptors)
- C_TSIZE equ (1<<TIC)*4 ;256
- D_TSIZE equ (1<<TID)*4 ;32
-
- C_TABLES equ 1<<TIB ;number of C tables depends on B level
- D_TABLES equ 1<<(TIB+TIC) ;1024 of them ! (8192 descriptors)
-
- TREE_SIZE equ A_TSIZE+B_TSIZE+(C_TABLES*C_TSIZE)+(D_TABLES*D_TSIZE)
- ; 64 + 64 + 4096 + 32768
-
- SHORT_INVALID equ $BAD00000 ;DT bits = 00 = INVALID
- ;this descriptor HAS to have b31=1 (quick test)
-
- PTR_MASK equ $FFFFFF00 ;low descriptor byte is not part of ptr
-
- MODIFIED_PAGE equ 4 ;bit set if page has been written to
- ACCESSED_PAGE equ 3 ;bit set if page has been accessed (R/W)
-
- DT_INVALID equ $0
- DT_PAGE equ $1
- DT_VALID_4BYTE equ $2 ;table pointer points to short table
- DT_VALID_8BYTE equ $3 ;table pointer points to short table
-
- TC_ENABLE equ 1<<31
- TC_PSIZE equ (%1111)<<20 ;select 32K pages
- TC_TIA equ TIA<<12
- TC_TIB equ TIB<<8
- TC_TIC equ TIC<<4
- TC_TID equ TID
-
- NEW_TC equ TC_ENABLE+TC_PSIZE+TC_TIA+TC_TIB+TC_TIC+TC_TID
-
- _intena equ $dff000+intena ;for DISABLE/ENABLE
-
- ;##########################################################################
-
- START_APPVM: move.l sp,stack_level ;store original sp
- move.l a0,arg_line ;store raw arguments ptr
-
- bsr init_all ;init vars, open DOS, stdout
- beq bail_out ;if couldn't open something: exit now
-
- move.l 4.w,a6 ;check to see that this machine is
- move.w AttnFlags(a6),d0 ;at least 030 based coz I need MMU
- btst #AFB_68030,d0 ;(**!! SIMPLISTIC TEST: tst mmu config!)
- beq need_MMU
-
- bsr parse_args ;get arguments (VM size, swapfile name)
-
- cmp.l #$00FFFFFF,$8 ;has other copy of APPVM already been
- bhi already_resident ;started ? (**!! SIMPLISTIC TEST)
-
- bsr create_swapfile ;create swapfile on disk
-
- bsr init_VM_pool ;VM logical address range management
- beq no_VM_init
-
- bsr install_patches ;modify system to handle VM accesses
-
- lea VM_ready,a0 ;"Virtual Memory Manager 0.9 Ready"
- bsr print_error
-
- move.l 4.w,a6 ;using exec library
- move.l #SIGBREAKF_CTRL_F,d0 ;Wait() until explicitly killed
- EXEC Wait ;using BREAK PROCESS x F
-
- ; At this stage this Process falls asleep but its code can temporarily come to life
- ; when there are bus errors or when other Processes call AllocMem() or FreeMem().
-
- bail_out bsr unpatch_all ;return Alloc/FreeMem to normal.
-
- lea VM_off,a0 ;"Virtual Memory Manager no longer active"
- bsr print_error
-
- move.l 4.w,a6 ;using Exec
-
- move.l vm_tree,d0 ;did we ever allocate the VM tree ?
- beq no_tree
- move.l d0,a1
- move.l #TREE_SIZE,d0
- EXEC FreeMem
-
- no_tree move.l vm_bitmap,d0 ;dealloc pages bitmap
- beq no_bitmap
- move.l d0,a1
- move.l bitmap_size,d0
- EXEC FreeMem
-
- no_bitmap bsr close_all ;used libraries, handles
-
- END_APPVM: move.l stack_level,sp ;restore sp to original
- rts
-
- ;##########################################################################
-
- ;-----------------------------------------------
- ; ERROR PRINTING ROUTINES
- ;-----------------------------------------------
- no_table lea no_table_str,a0
- bsr print_error
- bra bail_out
- ;-----------------------------------------------
- give_example lea give_example_str,a0
- bsr print_error
- bra bail_out
- ;-----------------------------------------------
- too_much_vm lea too_much_vm_str,a0
- bsr print_error
- bra bail_out
- ;-----------------------------------------------
- not_enough_vm lea not_enough_vm_str,a0
- bsr print_error
- bra bail_out
- ;-----------------------------------------------
- no_VM_init lea no_VM_init_str,a0
- bsr print_error
- bra bail_out
- ;-----------------------------------------------
- unable_to_create lea unable_to_create_str,a0
- bsr print_error
- bra bail_out
- ;-----------------------------------------------
- disk_full lea disk_full_str,a0
- bsr print_error
- bra bail_out
- ;-----------------------------------------------
- already_resident lea resident_str,a0
- bsr print_error
- bra bail_out
- ;-----------------------------------------------
- need_MMU lea need_MMU_str,a0
- bsr print_error
- bra bail_out
-
- ;-----------------------------------------------
- ; Check user arguments.
- ;
- ; Template: VMMEGS/A,SWAPFILE/K
- ;-----------------------------------------------
-
- parse_args move.l #default_swfn,swap_filename ;set default filename
-
- move.l #APPVM_template,d1
- move.l #appvm_results,d2
- bsr ReadArgs ;parse args according to template
- beq give_example
-
- move.l vm_megs,a0 ;get pointer to REQUIRED VM size arg
- bsr DEC_TO_BIN ;convert from string to binary
- cmp.l #MAX_VM_MEGS,d1 ;reasonable size request ?
- bhi too_much_vm
-
- moveq #20,d0 ;turn into bytes (2^20 = 1Meg)
- lsl.l d0,d1
- move.l d1,vm_bytes ;remember size of VM to manage
- beq not_enough_vm
-
- rts
- ;-----------------------------------------------
- ; Create a "swap partition on hard disk".
- ; This is actually just any old AmigaDOS file on any device.
- ;-----------------------------------------------
-
- create_swapfile move.l DOS_LIB_PTR,a6 ;using DOS..
-
- move.l swap_filename,d1
- move.l #MODE_OLDFILE,d2 ;assume file already exists..
- DOS Open ;open for R/W accesses
- move.l d1,swap_fhandle
- bne set_size
-
- move.l swap_filename,d1 ;if not then create fresh.
- move.l #MODE_NEWFILE,d2
- DOS Open
- move.l d1,swap_fhandle
- beq unable_to_create ;exit if that didn't work either!
- ;---------------
- set_size move.l vm_bytes,d2 ;force existing file to be large
- move.l #OFFSET_BEGINNING,d3 ;enough for our VM range
- DOS SetFileSize ;(shrink or stretch)
- cmp.l d2,d0
- bne no_stretch ;did sizing work ?
-
- rts
- ;-----------------------------------------------
- ; Couldn't create such a large file on disk.
- ; Close file and Delete it before reporting failure.
- ;-----------------------------------------------
- no_stretch move.l swap_fhandle,d1 ;free lock on file
- DOS Close
-
- move.l swap_filename,d1
- DOS DeleteFile ;remove it (otherwise disk 100% full)
-
- clr.l swap_fhandle ;tell cleanup() we've done it.
- bra disk_full
-
- ;-----------------------------------------------------------------------------------
- ; Allocate a fresh RAM page so that a new VM page can be enabled and loaded
- ; from disk.
- ; If no external RAM is available, then go through D-table descriptors and
- ; find any page which hasn't been modified yet and rob it to satisfy our need.
- ; If all pages have been modified, then take the first page and swap it out
- ; to disk (last resort).
- ;
- ; This routine is the USER mode part of the exception handler.
- ;
- ; naughty_vm_ptr -> VM address that caused the bus error exception
- ;-----------------------------------------------------------------------------------
-
- add_VM_page bsr age_pages ;**!! should be in IRQ
-
- move.l naughty_vm_ptr,d0
- and.l #~(PAGE_SIZE-1),d0
- move.l d0,naughty_vm_ptr ;calc aligned VM PAGE address
-
- move.l #PAGE_SIZE,d7 ;get RAM aligned to a page boundary
- move.l #PAGE_SIZE,d0 ;size of allocation
- bsr alloc_aligned ;get fresh page
- move.l d0,d7 ;store addr in parm for enable_page
- beq running_low ;if succeeded,
- ;---------------
- localize_page move.l naughty_vm_ptr,a2 ;-> VM addr at which
- move.l d7,a3 ;this RAM page should appear.
- bsr load_page ;fill page from copy on swapdevice
- bsr enable_page ;and change MMU table to enable page
-
- return_to_excep lea bus_error_tail,a5 ;-> continuation point of exc. handler
- move.l 4.w,a6
- EXEC Supervisor ;finish off and never come back here
-
- ; -- WE NEVER RETURN HERE --
-
-
- ************************************************************************************
-
- ;----------------------------------------------------------------------
- ; Can't allocate a page from RAM, so steal a page from MMU tree itself.
- ; There's always at least one mapped page in the tree. **!!
- ;----------------------------------------------------------------------
-
- running_low move.l D_tables,a0 ;-> list of page descriptors
-
- moveq #0,d0
- bset #TIB+TIC+TID,d0 ;total # of descriptors in D-tables
-
- find_unmodified move.l (a0)+,d7 ;get a page descriptor
-
- bclr #0,d7 ;is this a valid page ?
- beq bad_page ;yes,
-
- move.l d7,d6 ;remember any valid page for worse case
- move.l a0,d5 ;and remember associated VM address !
-
- bclr #ACCESSED_PAGE,d7 ;is this an old page ?
- bne bad_page ;yep,
-
- bclr #MODIFIED_PAGE,d7 ;is this page still virgin ?
- beq rob_page ;no,
-
- bad_page sub.l #1,d0 ;more descriptors available ?
- bne find_unmodified ;no, use last valid page regardless then
-
- and.l #PTR_MASK,d6 ;isolate robbed dirty page address
-
- move.l d5,d0 ;descr address in D-table (+4)
-
- sub.l #4,d0 ;undo descriptor scan postincrement
- sub.l D_tables,d0 ;= VM page # (*4)
- moveq #PAGE_SHIFT-2,d1 ;(-2 because already multiplied by 4)
- lsl.l d1,d0 ;VM byte offset
- add.l #VM_START,d0 ;-> valid VM address
-
- move.l d0,a2
- move.l d6,a3 ;before robbing dirty page: save it to
- bsr save_page ;swapdevice
-
- move.l d6,d7 ;and now drop through to rob page.
- move.l d5,a0 ;restore D-table ptr
-
- ;---------------
-
- rob_page move.l #SHORT_INVALID,-(a0) ;grab page to enable it somewhere else
- and.l #PTR_MASK,d7 ;isolate RAM page address
- bra localize_page ;poof! you're used for other VM page
- ;-----------------------------------------------
- ; Map RAM page at VM address "naughty_vm_ptr"
- ;
- ; D7 -> PHYSICAL RAM PAGE
- ;-----------------------------------------------
- enable_page or.w #DT_PAGE,d7 ;turn address into descriptor
-
- move.l naughty_vm_ptr,d0
- bclr #31,d0 ;change VM ptr into slot offset
- moveq #PAGE_SHIFT,d1
- lsr.l d1,d0 ;d1 = slot number
- move.l D_tables,a0 ;change MMU table to map new page
- move.l d7,(a0,d0.l*4) ;enable new page
- rts
- ;-----------------------------------------------
- ; Age pages.
- ; Using a low priority clock interrupt, go clear the ACCESSED bits in a block
- ; of page descriptors. Do so in a circular buffer fashion.
- ;-----------------------------------------------
- age_pages move.l D_tables,a0 ;-> base of D-level tables
- move.l ager,d0 ;-> circular descr index
- move.w #4096-1,d1 ;age N descriptors at a time **!!
- move.l #(1<<(TIB+TIC+TID))-1,d2 ;circular buffer index mask
-
- age_em bclr #ACCESSED_PAGE,3(a0,d0.l*4) ;to "age"
- add.l #1,d0
- and.l d2,d0 ;keep index inside circular buffer
- dbra d1,age_em
-
- move.l d0,ager
- rts
- ;-----------------------------------------------
- ; Copy a page from VM to disk.
- ; A2 -> VM address
- ; A3 -> Source Machine address
- ;-----------------------------------------------
- save_page move.l DOS_LIB_PTR,a6 ;using DOS...
-
- move.l swap_fhandle,d1 ;seek to correct page in file
- move.l a2,d2
- bclr #31,d2 ;(turn VM ptr into file offset)
- move.l #OFFSET_BEGINNING,d3
- DOS Seek
-
- move.l swap_fhandle,d1
- move.l a3,d2 ;write page back to disk
- move.l #PAGE_SIZE,d3
- DOS Write
- rts
- ;-----------------------------------------------
- ; Copy a page from disk to VM.
- ;
- ; A2 -> VM Address
- ; A3 -> Dest Machine address
- ;-----------------------------------------------
- load_page movem.l d2/d3,-(sp) ;save non-scratch regs
-
- move.l DOS_LIB_PTR,a6 ;using DOS...
-
- move.l swap_fhandle,d1 ;seek to correct page in file
- move.l a2,d2
- bclr #31,d2 ;(turn VM ptr into file offset)
- move.l #OFFSET_BEGINNING,d3
- DOS Seek
-
- move.l swap_fhandle,d1
- move.l a3,d2 ;read page from disk
- move.l #PAGE_SIZE,d3
- DOS Read
-
- movem.l (sp)+,d2/d3 ;restore non-scratch regs
- rts
- ;-----------------------------------------------
- ; Using a block of memory aligned to a 16-byte boundary, initialize it to
- ; hold the COMPLETE translation tree for all VM addresses in the range
- ; of the requested VM size.
- ; This table will become a BRANCH off the A-level table to handle
- ; VM addresses only. VM Addresses all have bit 31 SET (e.g $80000000).
- ;-----------------------------------------------
-
- gen_VM_tree moveq #16,d7 ;get RAM aligned to 16-byte boundary
- move.l #TREE_SIZE,d0 ;A+B+C+D
- bsr alloc_aligned
- move.l d0,vm_tree ;store address of our future MMU table
- beq no_table
- ;---------------
- move.l d0,a0 ;-> available aligned RAM to fill
- move.l d0,a1 ;copy
-
- moveq #0,d0
- bset #TIA,d0 ;build the one and only A-level table
- gen_A_table move.l #SHORT_INVALID,(a0)+
- sub.l #1,d0
- bne gen_A_table
-
- move.l a0,B_table ;remember ptr to VM B-table
-
- move.l a0,d0
- or.w #DT_VALID_4BYTE,d0
- move.l d0,8*4(a1) ;let table A point to my VM tree
- ;---------------
- lea B_TSIZE(a0),a1 ;-> first C-level table
- move.l a1,d0
- or.w #DT_VALID_4BYTE,d0
- move.l d0,a1 ;descriptor to store in B table
-
- moveq #0,d0
- bset #TIB,d0 ;build the one and only B-level table
- gen_B_table move.l a1,(a0)+ ;store table descriptors to the 16
- add.l #C_TSIZE,a1 ;C-level tables
- sub.l #1,d0
- bne gen_B_table
- ;---------------
- move.l a0,C_tables
-
- moveq #0,d0
- bset #TIB+TIC,d0 ;construct 16 C-level tables
- gen_C_tables move.l a1,(a0)+ ;store descriptors to D-level tables
- add.l #D_TSIZE,a1
- sub.l #1,d0
- bne gen_C_tables ; (64 entries per table) * 16 tables
-
- ;-------------------------------------
- ; The D-tables don't contain further table pointers
- ; but either PAGE descriptors or INVALID descriptors.
- ;-------------------------------------
-
- move.l a0,D_tables ;remember where D-level tables are
-
- moveq #0,d0
- bset #TIB+TIC+TID,d0 ;construct 16*64 D-level tables
- gen_D_tables move.l #SHORT_INVALID,(a0)+
- sub.l #1,d0
- bne gen_D_tables ; (8 entries per table)
-
- rts
- ;-----------------------------------------------
- ; Modify MMU registers CRP and TC to make MMU use my new tree.
- ;-----------------------------------------------
-
- use_new_tree move.l 4.w,a6 ;using exec library
-
- ;can't have any interrupts using untranslated ROM addresses !
-
- DISABLE
-
- lea new_MMU_table,a5 ;-> MMU surgery routine
- EXEC Supervisor
-
- ENABLE ;re-enable IRQs, multi-tasking etc..
- rts
- ;-----------------------------------------------
- ; Read current MMU configuration and store values.
- ; Construct new MMU register values and poke them (enabling new tree).
- ;-----------------------------------------------
- new_MMU_table move.l vm_tree,a0 ;-> A-table
-
- lea old_crp_room,a1 ;-> room to store original CRP
- lea new_crp_room,a2 ;-> room to construct new CRP
- lea old_tc,a3 ;-> spot to save current TC in
-
- pmove.d CRP,(a1) ;get current CRP
- pmove.l TC,(a3) ;get current TC
-
- move.l 4(a1),d0 ;get translation table address
- or.w #DT_VALID_4BYTE,d0 ;turn into a valid table descriptor
- move.l d0,(a0) ;store in new A-table
-
- lea tc_long,a3
- clr.l (a3)
- pmove.l (a3),TC ;disable MMU translation
-
- ; The ATC just got flushed.
- ; At this point the next PC won't be translated by the MMU
- ; but will be used "raw" by the Amiga bus sub-system.
-
- move.l #$7FFF<<16+DT_VALID_4BYTE,d0 ;no LIMIT for A-table index
- move.l d0,(a2)
- move.l a0,4(a2) ;construct new CRP
- pmove.d (a2),CRP ;point MMU to our new tree
-
- move.l #NEW_TC,(a3)
- pmove.l (a3),TC ;re-enable MMU with new table
- rte
-
- **********************************************************************************
- **********************************************************************************
- * This is the core of the program: the 68030 BUS ERROR handler.
- * It allows non-existent VM addresses to be used by trapping the generated
- * bus errors and modifying the MMU tables and swapping pages in & out of
- * physical RAM from/to disk.
- **********************************************************************************
- **********************************************************************************
-
- REGS_BLOCK equ (8+8)*4 ;64 bytes for MOVEM.L block
-
- ;-----------------------------------------------
- bus_err_handler tst.l client_Process ;if we haven't got a VM user...
- beq normal_bus_err ;then use normal Exec handler
- ;---------------
- movem.l a0/a1,-(sp) ;push some regs
- move.l 4.w,a0 ;-> Exec library
-
- move.l client_Process,a1 ;this Process MUST be our client...
- cmp.l ThisTask(a0),a1
- bne pop_n_normal ;if not let Task handle excep. itself
-
- movem.l (sp)+,a0/a1 ;restore temp regs
- ;---------------
- movem.l d0-d7/a0-a6,-(sp) ;don't touch ANY user regs!
- move.l USP,a0
- move.l a0,-(sp) ;note Task's user SP !
-
- lea REGS_BLOCK(sp),a0 ;-> Bus error exception stack frame
- move.l $10(a0),naughty_vm_ptr ;get VM ptr that caused exception
-
- ; lea $200,a1 ;-> area to dump stack frame in
- ; moveq #$60-1,d0
- ;copy_frame move.b (a0)+,(a1)+
- ; dbra d0,copy_frame
-
- move.w #$F00,$dff180 ;show sign of exception occurring
- move.w #$FFF,$dff180 ;show sign of exception occurring
- move.w #$FFF,$dff180 ;show sign of exception occurring
- move.w #$FFF,$dff180 ;show sign of exception occurring
- move.w #$F00,$dff180 ;show sign of exception occurring
- move.w #$AAA,$dff180
-
- ; Create an RTE frame that will make a Hyperjump to USER code to do all
- ; the neccessary page swapping (loading/saving) and then eventually
- ; switch back to SUPER mode to pop all original regs back and restart the
- ; broken instruction.
-
- ;note level of SSP before exiting. If SSP isn't the same when returning: PANIC !
-
- move.l sp,continue_ssp
-
- move.w #$0010,-(sp) ;TYPE+VEC ;Illegal Inst. frame
- move.l #add_VM_page,-(sp) ;PC ;
- move.w #$0000,-(sp) ;SR ;TT=S=0 and IL = 0 !!
- rte
-
- ; At this point the CPU whizzes off to "add_VM_page" and will eventually
- ; return to finish off this exception at "bus_error_tail".
-
- ;-----------------------------------------------
- pop_n_normal movem.l (sp)+,a0/a1 ;if not: restore regs, go to Exec
-
- normal_bus_err jmp $F80000 ;**!! SELF-MODIFIED
-
-
- ;------------------------------------------------------------------------------------
- ; We're nearing the end of our BUS ERROR exception handler.
- ; The user code responsible for doing disk accesses has jumped to here via
- ; a Supervisor() call.
- ; That's just to get us back into supervisor mode, so discard Supervisor RTE
- ; frame.
- ;
- ; **!! Normally, when Tasks call the Supervisor() function they ALWAYS find the
- ; SSP pointing to the same spot. The entire VM system relies on this, therefore
- ; to avoid crashing due to another Task modifying the level of the supervisor stack
- ; we save and restore the SSP, this means that any other Task in the system which
- ; messes around with the supervisor stack will get hit by my SSP resets.
- ;------------------------------------------------------------------------------------
-
- bus_error_tail move.l continue_ssp,a7 ;discard Supervisor() stack frame
- ;(in theory nothing more)
-
- ; At this point the supervisor stack is back in the state we
- ; left it in when switching from SUPER to USER mode with the RTE above.
-
- move.l (sp)+,a0
- move.l a0,USP ;restore user SP
- movem.l (sp)+,d0-d7/a0-a6 ;restore regs
- rte ;restart instruction !
-
- ;-----------------------------------------------
- ; Allocate and initialize the bitmap which represents the VM pages pool.
- ; The VM Pool represents allocated or free VM logical address ranges, it
- ; does NOT represent real allocated memory in any way.
- ;-----------------------------------------------
- init_VM_pool move.l vm_bytes,d0 ;get requested VM size (1Mb multiples)
-
- moveq #PAGE_SHIFT,d1 ;turn into # of pages
- lsr.l d1,d0
- move.l d0,vm_total_pages ;initialize available VM pages
- move.l d0,vm_pages ;remember maximum total (constant)
-
- lsr.l #3,d0 ;= # of bytes for bitmap
- move.l d0,bitmap_size ;remember size of bitmap for dealloc
-
- move.l #MEMF_CLEAR,d1
- move.l 4.w,a6
- EXEC AllocMem
- move.l d0,vm_bitmap
- rts ;return EQ/NE (FAIL/OK)
- ;-----------------------------------------------
- ; Attempt to allocate a VM block of a given size.
- ;
- ; D0 = VM block size. (will be rounded up to an integral # of pages)
- ;-----------------------------------------------
- alloc_VM movem.l d2-d7/a2-a6,-(sp) ;protect non-scratch
-
- add.l #PAGE_SIZE-1,d0 ;round up to int # of pages
- and.l #~(PAGE_SIZE-1),d0
-
- move.l d0,d7 ;remember rounded blocksize
-
- moveq #PAGE_SHIFT,d1
- lsr.l d1,d0 ;calc # of pages
-
- cmp.l vm_total_pages,d0 ;if request is more than available
- bhi too_big ;VM : sod off.
-
- move.l vm_bitmap,a0 ;-> cached bitmap ptr
-
- move.l d0,d4 ;# of free pages (=bits) to find
- moveq #0,d5 ;starting looking from bit #0
- move.l vm_pages,d6
- sub.l d4,d6 ;max number of "window" positions
- move.l #VM_START,a5 ;scanning from start of VM...
- ;---------------
- check_window move.l d5,d0 ;is first window bit 0 ?
- bsr check_bit
- bne slide_along
-
- move.l d5,d2 ;yes, check entire window
- move.l d4,d3 ;(pass starting bitno & window length)
- bsr check_empty_fld ;enough contiguous 0s in a row ?
- beq alloc_window ;yep, go set them and return VMPTR
-
- slide_along add.l #1,d5 ;no, check next possible window
- add.l #PAGE_SIZE,a5 ;in parallel track VMPTR
- sub.l #1,d6 ;more pages available ?
- bne check_window
-
- too_big moveq #0,d0 ;unable to allocate VM block!
- bra done_alloc
- ;---------------
- alloc_window move.l a5,a1 ;a1-> VM start address (& 7fffffff)
- sub.l #VM_START,a1
-
- move.l d7,d0
- bsr alloc_pages ;go set bits
-
- move.l a5,d0 ;return VM address
-
- done_alloc movem.l (sp)+,d2-d7/a2-a6 ;restore non-scratch registers
- rts
- ;-----------------------------------------------
- ; Check if VM bitmap bitstring starting at bit position P has N clr bits
- ; A0 -> bitmap
- ; D2 = starting bit #
- ; D3 = # of bits that we want
- ; RETURNS : EQ if bitfield is long enough USES: D0/D1/D2/D3
- ;-----------------------------------------------
- check_empty_fld sub.w #1,d3 ;-1 DBRA
-
- check_match move.l d2,d0
- add.l #1,d2
- bsr check_bit
- dbne d3,check_match ;**!! 65536 pages limit
- rts
- ;-----------------------------------------------
- ; D0 = bit # to check in bitmap USES: D0/D1
- ; A0 -> bitmap
- ; RETURNS: EQ/NE for 0/1 bit
- ;-----------------------------------------------
- check_bit moveq #7,d1
- and.w d0,d1
- not.w d1 ;d1= bit # in byte
- lsr.l #3,d0 ;d0= byte #
- btst d1,0(a0,d0.l)
- rts
- ;-----------------------------------------------
- ; Free a previously allocated VM block.
- ; A1 -> VM address
- ; D0 = block size
- ;-----------------------------------------------
- free_VM movem.l d2-d7/a2-a6,-(SP)
-
- sub.l #VM_START,a1 ;any ptr has to be a VM ptr !!
- bcc its_a_VM_ptr
-
- moveq #-1,d0 ;if top bit was not set: return error
- bra bad_VMPTR
-
- its_a_VM_ptr add.l #PAGE_SIZE-1,d0 ;round up to int # of pages
- and.l #~(PAGE_SIZE-1),d0
-
- moveq #PAGE_SHIFT,d1
- lsr.l d1,d0 ;# of page bits to clear
- add.l d0,vm_total_pages ;track how much gets freed
-
- move.l a1,d1 ;-> VM address
- moveq #PAGE_SHIFT,d2
- lsr.l d2,d1 ;first page bit # to clear
-
- move.l D_tables,a3 ;-> base of all D-level page descr
- lea 0(a3,d1.l*4),a3 ;-> 1st page descr of block to free
-
- moveq #7,d2
- and.w d1,d2
- eor.w #7,d2 ;bit # in first byte
-
- lsr.l #3,d1 ;= bitmap byte offset
- move.l vm_bitmap,a2 ;-> base of bitmap
- add.l d1,a2 ;-> byte in bitmap
-
- move.l 4.w,a6 ;in case we need to FreeMem() pages
- move.l #SHORT_INVALID,d7 ;cached INVALID descriptor
- move.l #PTR_MASK,d6 ;mask to isolate ptr from descriptor
-
- move.l d0,d3 ;# of pages to free
- ;---------------
- dealloc_pages move.l (a3),d4 ;get current page descriptor
- bclr #0,d4
- beq was_invalid ;if descriptor contains real page ptr
-
- and.l d6,d4 ;mask out any non-ptr bits
- move.l d4,a1 ;-> RAM
- move.l #PAGE_SIZE,d0 ;free this RAM page as we go
- EXEC FreeMem ;**!! RECURSION
-
- was_invalid move.l d7,(a3)+ ;invalidate descriptor in MMU table
- bclr d2,(a2) ;deallocate page bit in bitmap
-
- sub.w #1,d2 ;dec bitnumber
- bpl in_byte ;if wraps from 0 to -1
-
- moveq #7,d2 ;reset bit # to 7
- add.l #1,a2 ;and goto next byte in bitmap
-
- in_byte sub.l #1,d3 ;more pages to free ?
- bne dealloc_pages
- ;---------------
- moveq #0,d0 ;return OK.
- bad_VMPTR movem.l (sp)+,d2-d7/a2-a6
- rts
- ;-----------------------------------------------
- ; Allocate a page range that we've identified as being free.
- ; A1 -> VM address (without bit31 set)
- ; D0 = block size
- ;-----------------------------------------------
- alloc_pages moveq #PAGE_SHIFT,d1
- lsr.l d1,d0 ;# of page bits to clear
- sub.l d0,vm_total_pages ;track how many free pages left
-
- move.l a1,d1
- moveq #PAGE_SHIFT,d2
- lsr.l d2,d1 ;first page bit # to clear
-
- moveq #7,d2
- and.w d1,d2
- eor.w #7,d2 ;first bit in byte
-
- lsr.l #3,d1 ;= bitmap byte offset
- move.l vm_bitmap,a0
- add.l d1,a0 ;-> byte in bitmap
-
- set_page_bits bset d2,(a0) ;allocate page
-
- sub.w #1,d2 ;dec bitnumber
- bpl in_byte2 ;if wraps from 0 to -1
-
- moveq #7,d2 ;reset bit # to 7
- add.l #1,a0 ;and goto next byte in bitmap
-
- in_byte2 sub.l #1,d0 ;more bits to set ?
- bne set_page_bits
- rts
- ;-----------------------------------------------
- ; Modify system structures to handle new use of VM pointers
- ; by a single Process.
- ;-----------------------------------------------
- install_patches bsr gen_VM_tree ;gen MMU translation tree for VM LAs
- bsr use_new_tree ;change MMU to use new, bigger tree
-
- st hard_patched ;MMU has been reconfigured !!
-
- move.l 4.w,a6 ;using Exec...
- FORBID
-
- bsr patch_Exec ;patch into AllocMem(), FreeMem()
- bsr patch_DOS ;patch into Read(), Write()
-
- move.l $8,normal_bus_err+2 ;our handler passes exception on if
- move.l #bus_err_handler,$8 ;can't handle it
-
- move.l 4.w,a6 ;using Exec...
- PERMIT
-
- st soft_patched ;SYSTEM HAS BEEN MODIFIED !!
- rts
- ;-----------------------------------------------
- ; Unpatch all VM handling stuff from system.
- ; (If patches ever installed)
- ;-----------------------------------------------
- unpatch_all move.l 4.w,a6 ;using Exec...
-
- tst.b soft_patched ;did we patch it all up ?
- beq no_soft_patches
-
- FORBID ;stop others from accessing system in inconsistent state.
-
- move.l a6,a1
- lea _LVOAllocMem,a0 ;library entry number
- move.l normal_alloc+2,d0 ;restore usual vector
- EXEC SetFunction
-
- move.l a6,a1
- lea _LVOFreeMem,a0
- move.l normal_free+2,d0
- EXEC SetFunction
-
- move.l DOS_LIB_PTR,a1
- lea _LVORead,a0
- move.l normal_Read+2,d0
- EXEC SetFunction
-
- move.l DOS_LIB_PTR,a1
- lea _LVOWrite,a0
- move.l normal_Write+2,d0
- EXEC SetFunction
-
- move.l normal_bus_err+2,$8 ;restore std Exec exception vector
-
- move.l 4.w,a6
- PERMIT ;OK: everything's clean again.
-
- ;---------------
- no_soft_patches tst.b hard_patched ;is VM MMU table active ?
- req
-
- DISABLE
- lea reset_regs,a5 ;MMU changes in supervisor mode
- EXEC Supervisor ;do it..
- ENABLE
- rts
- ;---------------------------------------
- ; Reset MMU registers to their original values before APPVM.
- ;---------------------------------------
- reset_regs lea tc_long,a0 ;-> room to construct TC register
- clr.l (a0)
- pmove.l (a0),TC ;disable MMU
-
- lea old_crp_room,a0
- lea old_tc,a1
-
- pmove.d (a0),CRP ;restore original CRP pointer
- pmove.l (a1),TC ;re-enable MMU as it was originally
- rte
-
- ;-----------------------------------------------
- ; Modify the Exec AllocMem() and FreeMem() calls to handle MEMF_VM.
- ;-----------------------------------------------
- patch_Exec move.l 4.w,a6
-
- move.l a6,a1 ;modify exec.library itself
- lea _LVOAllocMem,a0 ;library entry number
- move.l #new_AllocMem,d0 ;-> new routine for this entry
- EXEC SetFunction ;change library and
- move.l d0,normal_alloc+2 ;copy usual vector into my jump
-
- move.l a6,a1
- lea _LVOFreeMem,a0
- move.l #new_FreeMem,d0
- EXEC SetFunction ;same for FreeMem
- move.l d0,normal_free+2
- rts
- ;-----------------------------------------------
- ; Modify the DOS Read() and Write() calls to handle reads and writes from/to VM.
- ;-----------------------------------------------
- patch_DOS move.l DOS_LIB_PTR,a1 ;modify dos.library itself
- lea _LVORead,a0
- move.l #new_Read,d0
- EXEC SetFunction ;install new Read
- move.l d0,normal_Read+2
-
- move.l DOS_LIB_PTR,a1
- lea _LVOWrite,a0
- move.l #new_Write,d0
- EXEC SetFunction ;install new AllocMem
- move.l d0,normal_Write+2
- rts
- ;-----------------------------------------------
- ; Some **!! SELF-MODIFIED code follows here ...
-
- normal_alloc jmp $F80000 ;execute standard AllocMem()
- normal_free jmp $F80000 ;execute standard FreeMem()
- normal_Read jmp $F80000 ;execute standard Read()
- normal_Write jmp $F80000 ;execute standard Write()
-
- ;-----------------------------------------------
- ; This is our new AllocMem() function.
- ; If the MEMF_VM bit is clear, then just execute normal AllocMem().
- ;
- ; First of all we try to allocate a physical RAM page because the exception handler
- ; assumes that there is always at least one mapped page available to steal if
- ; physical RAM levels are low.
- ; If this succeeds then we check if we've got enough VM left for this request.
- ; If we have then the page gets mapped otherwise the page gets freed again.
- ;
- ; D0 = VM blocksize
- ; D1 = MEMF_VM
- ;-----------------------------------------------
- new_AllocMem tst.l d1 ;btst #MEMB_VM,D1
- bpl normal_alloc ;if Process wants some VM,
-
- tst.l client_Process ;check if we should know this Process.
- bne check_client ;if 1st time call, then
-
- move.l ThisTask(a6),client_Process ;have this Task as our client
- clr.l client_usage ;initialize its VM usage counter
- bra get_vm
-
- check_client move.l client_Process,a0 ;get current client addr
- cmp.l ThisTask(a6),a0 ;"Are you the same one, Oh Task ?"
- bne bad_client
- ;---------------
- get_vm movem.l d7/a2-a3,-(sp) ;we're a system routine now !!
- move.l d0,request_size ;remember size of requested block
-
- move.l #PAGE_SIZE,d7 ;get RAM aligned to a page boundary
- move.l #PAGE_SIZE,d0 ;size of allocation
- bsr alloc_aligned ;get fresh page
- move.l d0,d7 ;store addr in parm for enable_page
- beq no_initial_page ;if succeeded,
-
- move.l request_size,d0
- bsr alloc_VM ;handle VM allocation request
- move.l d0,naughty_vm_ptr ;if VM allocation fails, then
- bne got_vm
-
- move.l d7,a1 ;free RAM page and fail totally
- move.l #PAGE_SIZE,d0
- EXEC FreeMem
- bra no_initial_page
- ;---------------
- got_vm move.l d0,a2 ;-> VM addr at which
- move.l d7,a3 ;RAM page should appear.
- bsr load_page ;fill page from copy on swapdevice
- bsr enable_page ;and change MMU table to enable page
-
- move.l 4.w,a6 ;restore EXEC ptr in A6
-
- move.l request_size,d0 ;track client's VM usage so we can
- add.l d0,client_usage ;free client when freeing all.
-
- move.l naughty_vm_ptr,d0 ;return VM ptr
- movem.l (sp)+,d7/a2-a3
- rts
-
- ;---------------
- no_initial_page movem.l (sp)+,d7/a2-a3 ;restore non-scratch register
-
- bad_client moveq #0,d0 ;sorry, VM used by other Process
- rts
- ;-----------------------------------------------
- ; This is our new FreeMem() function.
- ; If the address of the returned block is not a VM address, then just
- ; execute normal FreeMem().
- ;
- ; D0 = VM blocksize
- ; A1 -> VM Block
- ;-----------------------------------------------
- new_FreeMem move.l a1,test_ptr ;test if ptr is a VMPTR
- bpl normal_free ;yes,
-
- tst.l client_Process ;Process HAS to be our client.
- beq VM_FreeMem_err ;impossible not to know him.
-
- move.l client_Process,a0 ;get current client addr
- cmp.l ThisTask(a6),a0 ;"Are you the same one, Oh Task ?"
- bne VM_FreeMem_err
-
- sub.l d0,client_usage ;when client FreeVM()s all
- bne holding_VM ;open door to new client
- clr.l client_Process ;client has no more VM: goodbye!
-
- holding_VM bsr free_VM
-
- VM_FreeMem_err rts
-
- ;-----------------------------------------------
- ; This is our new AmigaDOS Read() function.
- ; If the address of the buffer is not a VM address, then just
- ; execute normal Read().
- ;
- ; D1 = filehandle
- ; D2 = buffer (possibly VM ptr)
- ; D3 = size of buffer to fill
- ;-----------------------------------------------
- new_Read tst.l d2 ;test if ptr is a VMPTR
- bpl normal_Read ;yes,
-
- move.l client_Process,a0 ;get current client addr (even if NULL)
- move.l 4.w,a1
- cmp.l ThisTask(a1),a0 ;"Are you the same one, Oh Task ?"
- bne alien_uses_VM
-
- movem.l d2-d7/a2-a6,-(sp) ;protect all non-scratch regs !
-
- move.l DOS_LIB_PTR,a6 ;in case Task is naughty !
- move.l d1,a5 ;filehandle
- move.l d2,d4 ;VM destination
- move.l d3,d5 ;# of bytes to transfer
- bsr VM_READ
-
- move.l d7,d0 ;return # of bytes successfully read
-
- movem.l (sp)+,d2-d7/a2-a6
- rts
-
- alien_uses_VM moveq #0,d0 ;no bytes read: ERROR.
- rts
-
- ;-----------------------------------------------
- ; This is our new AmigaDOS Write() function.
- ; If the address of the buffer is not a VM address, then just
- ; execute normal Write().
- ;
- ; D1 = filehandle
- ; D2 = buffer (possibly VM ptr)
- ; D3 = size of buffer to save
- ;-----------------------------------------------
- new_Write tst.l d2 ;test if ptr is a VMPTR
- bpl normal_Write ;yes,
-
- move.l client_Process,a0 ;get current client addr
- move.l 4.w,a1
- cmp.l ThisTask(a1),a0 ;"Are you the same one, Oh Task ?"
- bne alien_uses_VM
-
- movem.l d2-d7/a2-a6,-(sp) ;protect all non-scratch regs !
-
- move.l DOS_LIB_PTR,a6 ;in case Task is naughty !
- move.l d1,a5 ;filehandle
- move.l d2,d4 ;VM destination
- move.l d3,d5 ;# of bytes to transfer
- bsr VM_WRITE
-
- movem.l (sp)+,d2-d7/a2-a6
- move.l d3,d0 ;return # of bytes successfully write
- rts
-
- ;-----------------------------------------------------------------------------------
- ; Read N bytes from any file into VM.
- ; Parts of the read going to VM which isn't currently mapped are transferred
- ; straight to the swapfile.
- ; Worst case: No mapped VM exists and we have to do a file_to_file copy.
- ; Best case: All VM is mapped to RAM and we just fill in all these pages.
- ;
- ; A5 -> filehandle
- ; D4 = VM destination
- ; D5 = bytes to read
- ; A6 = DOS_LIB_PTR
- ;
- ; RETURN: bytes read in D7
- ;-----------------------------------------------------------------------------------
-
- VM_READ moveq #0,d7 ;0 bytes read so far..
-
- bclr #31,d4 ;VMPTR to byte offset
- move.l d4,d0 ; = byte offset
-
- moveq #PAGE_SHIFT,d1
- lsr.l d1,d0 ; = starting page #
-
- move.l D_tables,a4 ;-> base of D-table page descr.
- lea 0(a4,d0.l*4),a4 ;-> first page descr of destination
-
- move.l #PAGE_SIZE-1,d0 ;is there an un-aligned portion
- and.l d4,d0 ;to read at all ?
- beq no_head
- ;---------------
- move.l #PAGE_SIZE,d1
- sub.l d0,d1 ;max # of head bytes to read
-
- cmp.l d5,d1 ;is # of bytes smaller than this ?
- bcs head_num_ok ;yes,
- move.l d5,d1 ;then read that few instead
-
- head_num_ok sub.l d1,d5 ;decr total to read left
-
- move.l (a4)+,d2 ;get page descriptor
- bmi external_page0
-
- and.l #PTR_MASK,d2 ;turn into valid machine ptr
-
- add.l d0,d2 ;point to correct read spot
-
- move.l d1,d3 ;Read() N bytes
- move.l a5,d1 ;from user file
- DOS Read ;straight into physical RAM
- add.l d0,d7 ;track bytes successfully read
-
- cmp.l d3,d0 ;if we read less than requested, then
- beq align_offset ;we've reached EOF, so exit here
- rts
- ;-----
- external_page0 move.l a5,a2 ;from user file
- move.l swap_fhandle,a3 ;to swapfile
- move.l d4,d2 ;at VM offset
- move.l d1,d3 ;copy N bytes
- bsr file_to_file
- add.l d0,d7
-
- cmp.l d3,d0
- beq align_offset
- rts
-
- align_offset add.l #PAGE_SIZE-1,d4
- and.l #~(PAGE_SIZE-1),d4 ;align VM offset to page boundary
- ;---------------
- no_head tst.l d5 ;any more bytes to read ?
- beq VM_read_done
-
- move.l #~(PAGE_SIZE-1),d6 ;any integral # of pages to read ?
- and.l d5,d6
- beq no_body
-
- moveq #PAGE_SHIFT,d1
- lsr.l d1,d6 ;# of whole pages to "load"
-
- read_pages move.l (a4)+,d2 ;get page descriptor
- bmi external_page1 ;if page is mapped,
- ;---------------
- and.l #PTR_MASK,d2 ;turn descr into valid machine ptr
-
- move.l #PAGE_SIZE,d3 ;Read() N bytes
- move.l a5,d1 ;from user file
- DOS Read ;straight into physical RAM
- add.l d0,d7 ;track bytes successfully read
-
- cmp.l d3,d0 ;if we read less than requested, then
- beq rd_next_page ;we've reached EOF, so exit here
- rts
-
- external_page1 move.l a5,a2 ;from user file
- move.l swap_fhandle,a3 ;to swapfile
- move.l d4,d2 ;at VM offset
- move.l #PAGE_SIZE,d3 ;copy exactly one page
- bsr file_to_file
- add.l d0,d7
-
- cmp.l d3,d0
- beq rd_next_page
- rts
-
- rd_next_page add.l #PAGE_SIZE,d4 ;keep track of VM read offset
- sub.l #PAGE_SIZE,d5 ;track how much left to read
- sub.l #1,d6 ;more pages to read ?
- bne read_pages
- ;---------------
- no_body tst.l d5 ;any more bytes to read ?
- beq VM_read_done
-
- move.l (a4)+,d2 ;get page descriptor
- bmi external_page2
-
- and.l #PTR_MASK,d2 ;turn descr into valid machine ptr
-
- move.l d5,d3 ;Read() last N bytes
- move.l a5,d1 ;from user file
- DOS Read ;straight into physical RAM
- add.l d0,d7 ;track bytes successfully read
- rts
-
- external_page2 move.l a5,a2 ;from user file
- move.l swap_fhandle,a3 ;to swapfile
- move.l d4,d2 ;at VM offset
- move.l d5,d3 ;copy remaining bytes
- bsr file_to_file
- add.l d0,d7
-
- VM_read_done rts
-
- ;-----------------------------------------------------------------------------------
- ; Write N bytes from VM to any file.
- ; Parts of the write from VM which aren't currently mapped are treated as
- ; no-ops (the page/bytes should already be on the disk !).
- ; Best case: No mapped VM exists, so we don't do anything.
- ; Worse case: All VM is mapped to RAM and we have to write all these pages.
- ;
- ; A5 -> filehandle
- ; D4 = VM source
- ; D5 = bytes to write
- ; A6 = DOS_LIB_PTR
- ;-----------------------------------------------------------------------------------
-
- VM_WRITE: bclr #31,d4 ;VMPTR to byte offset
- move.l d4,d7 ; = byte offset
-
- moveq #PAGE_SHIFT,d0
- lsr.l d0,d7 ; = starting page #
-
- move.l D_tables,a4 ;-> base of D-table page descr.
- lea 0(a4,d7.l*4),a4 ;-> first page descr of destination
-
- move.l #PAGE_SIZE-1,d0 ;is there an un-aligned portion
- and.l d4,d0 ;to read at all ?
- beq no_head_wr
- ;---------------
- move.l #PAGE_SIZE,d1
- sub.l d0,d1 ;max # of head bytes to read
-
- cmp.l d5,d1 ;is # of bytes smaller than this ?
- bcs head_num_ok2 ;yes,
- move.l d5,d1 ;then read that few instead
-
- head_num_ok2 sub.l d1,d5 ;decr total to read left
-
- move.l (a4)+,d2 ;get page descriptor
- bmi align_offset2 ;only if page is valid
-
- and.l #PTR_MASK,d2 ;turn into valid machine ptr
-
- add.l d0,d2 ;point to correct read spot
-
- move.l d1,d3 ;Write() N bytes
- move.l a5,d1 ;to user file
- DOS Write ;straight from physical RAM
- ;-----
- align_offset2 add.l #PAGE_SIZE-1,d4
- and.l #~(PAGE_SIZE-1),d4 ;align VM offset to page boundary
- ;---------------
- no_head_wr tst.l d5 ;any more bytes to write ?
- beq VM_write_done
-
- move.l #~(PAGE_SIZE-1),d6 ;any integral # of pages to write ?
- and.l d5,d6
- beq no_body2
-
- moveq #PAGE_SHIFT,d1
- lsr.l d1,d6 ;# of whole pages to "load"
-
- write_pages move.l (a4)+,d2 ;get page descriptor
- bmi wr_next_page ;only if page is mapped,
- ;---------------
- and.l #PTR_MASK,d2 ;turn descr into valid machine ptr
-
- move.l #PAGE_SIZE,d3 ;Write() N bytes
- move.l a5,d1 ;to user file
- DOS Write ;straight from physical RAM
-
- wr_next_page add.l #PAGE_SIZE,d4 ;keep track of VM read offset
- sub.l #PAGE_SIZE,d5 ;track how much left to read
- sub.l #1,d6 ;more pages to write ?
- bne write_pages
- ;---------------
- no_body2 tst.l d5 ;any more bytes to read ?
- beq VM_write_done
-
- move.l (a4)+,d2 ;get page descriptor
- bmi VM_write_done ;only if page is mapped,
-
- and.l #PTR_MASK,d2 ;turn descr into valid machine ptr
-
- move.l d5,d3 ;Read() last N bytes
- move.l a5,d1 ;from user file
- DOS Write ;straight into physical RAM
-
- VM_write_done rts
-
- ;-----------------------------------------------------------------------------------
- ; Copy N bytes from src file to another at a specific dest position.
- ;
- ; A2 -> Source FileHandle (fh1) (file ptr position IMPLICIT)
- ; A3 -> Dest FileHandle (fh2) (file ptr position EXPLICIT)
- ;
- ; D2 = Position to Seek to in Destination File
- ; D3 = total # of bytes to copy
- ;
- ; INTERNALLY: D7 = original buff size
- ; D4 -> chunk buffer
- ; D5 = chunk buffer size
- ;
- ; RETURN: D0 = number of bytes really transferred
- ;-----------------------------------------------------------------------------------
-
- file_to_file movem.l d2-d7/a2-a6,-(sp) ;protect non-scratch registers
-
- move.l d2,d0 ;check that copy operation
- add.l d3,d0 ;fits entirely inside swapfile!
- cmp.l vm_bytes,d0 ;**!! SHOULD NEVER OCCUR
- bhi copy_impossible ;yes, it won't overflow swapfile so
- ;---------------
- move.l 4.w,a6 ;using Exec..
-
- move.l d3,d7 ;remember original size
- bra try_largest ;try to get largest poss. copy buffer
- ;---------------
- buf_too_big lsr.l #1,d3 ;attempt block half as big then...
-
- try_largest move.l d3,d0 ;attempt buffer of this size
- move.l #MEMF_PUBLIC,d1 ;FAST RAM please ! (if possible)
- EXEC AllocMem
- move.l d0,d4 ;did allocation succeed ?
- beq buf_too_big ;OK we've got a block of size N
- ;---------------
- move.l d3,d5 ;remember chunk size
-
- moveq #0,d6 ;no bytes transferred so far.
-
- move.l DOS_LIB_PTR,a6 ;using DOS...
-
- move.l a3,d1 ;set fileptr of destination file
- ; ; ;D2 = original argument !
- move.l #OFFSET_BEGINNING,d3
- DOS Seek ;dest fileptr to start of copy pos
-
- move.l d4,d2 ;set TO/FROM buffer ptr (constant)
- move.l d5,d3 ;set transfer size (constant)
- ;---------------
- copy_chunk move.l a2,d1 ;Read() a chunk of huge block into
- DOS Read ;copy work buffer
- add.l d0,d6 ;track # of bytes transferred
-
- move.l a3,d1 ;Write() a chunk of huge block out
- move.l d0,d3 ;or less if premature EOF encountered
- beq ff_transf_done
- DOS Write ;to destination file
-
- sub.l d5,d7 ;track howmany bytes copied so far
- cmp.l d5,d7 ;can we do another chunk copy ?
- bhi copy_chunk
- ;---------------
- move.l d7,d3
- beq perfect_fit ;if any extra bytes to do,
-
- move.l a2,d1 ;copy left overs too..
- DOS Read ;read in MOD chunksize bytes
- add.l d0,d6 ;track how many bytes transferred
-
- move.l a3,d1
- move.l d0,d3
- DOS Write ;and copy them out again
- ;---------------
- perfect_fit move.l 4.w,a6 ;return temporary copy buffer!
- move.l d4,a1
- move.l d5,d0
- EXEC FreeMem
-
- ff_transf_done move.l d6,d0 ;return # of bytes actually copied
- movem.l (sp)+,d2-d7/a2-a6
- rts
-
- copy_impossible moveq #0,d0 ;return FAIL. Not enough VM.
- movem.l (sp)+,d2-d7/a2-a6
- rts
-
- ;-----------------------------------------------
- ; Open libraries, console handle, init defaults
- ;-----------------------------------------------
- init_all clr.l client_Process ;no Task using our VM yet..
- clr.l ager ;reset descriptor ageing index
-
- sf soft_patched ;don't touch system when "unpatching"!
- sf hard_patched ;don't stuff invalid values in MMU
-
- move.l 4.w,a6 ;using Exec...
-
- lea dosname,a1
- moveq #0,d0
- EXEC OpenLibrary ;open DOS
- move.l d0,DOS_LIB_PTR
- beq END_APPVM
-
- move.l d0,a6
- DOS Output ;get stdout handle
- move.l d0,stdout
- rts
- ;-----------------------------------------------
- ; Free libraries, handles...
- ;-----------------------------------------------
- close_all move.l DOS_LIB_PTR,d0 ;did we ever get DOS ?
- beq no_dos ;yes,
-
- move.l d0,a6 ;then close swapfile first
- move.l swap_fhandle,d1 ;if we ever got that file
- beq no_swaphandle
- DOS Close
-
- no_swaphandle move.l a6,a1 ;then close library itself
-
- move.l 4.w,a6
- EXEC CloseLibrary
- ;---------------
- no_dos
- dummy rts
-
- ;-----------------------------------------------
- ; D0 = size of block to get
- ; D7 = block should be aligned to ... byte boundary (powers of two!)
- ;
- ; This is done by first attempting to allocate a block that is guaranteed
- ; to CONTAIN a block with the correct alignment, noting that base address,
- ; then doing an AllocAbs() of the aligned block inside that block.
- ;-----------------------------------------------
-
- alloc_aligned movem.l d2-d7/a2-a6,-(sp)
-
- move.l 4.w,a6 ;using Exec
- FORBID ;disable allocs between Free & AllocAbs
-
- move.l d0,d6 ;remember original requested size
-
- add.l d7,d0 ;incr. blocksize to get a size which
- move.l d0,d3 ;guarantees a contained aligned base
-
- move.l #MEMF_PUBLIC,d1 ;attempt to get this block which
- EXEC AllocMem ;encloses the block we really want.
- move.l d0,d2
- beq fail_alloc
- ;---------------
- move.l d3,d0 ;ok, now release this "test" block
- move.l d2,a1
- EXEC FreeMem ;and from its base address
-
- sub.l #1,d7 ;calc address of aligned block
- add.l d7,d2
- not.l d7
- and.l d7,d2 ;-> aligned ptr
- move.l d2,a1
- move.l d6,d0
- EXEC AllocAbs ;in theory this call cannot fail !
- move.l d0,d2
- PERMIT
- move.l d2,d0 ;return ptr or NULL
- movem.l (sp)+,d2-d7/a2-a6
- rts
-
- fail_alloc PERMIT
- moveq #0,d0 ;aligned allocation FAILED
- movem.l (sp)+,d2-d7/a2-a6
- rts
- ;-----------------------------------------------
- include "SRC:UTILS/ReadArgs.s"
- include "SRC:MODULES/LIB1/DEC_TO_BIN"
-
- ;-----------------------------------------------
- ; Program Strings, Constants
- ;-----------------------------------------------
-
- no_table_str dc.b "Couldn't allocate new MMU translation tables!",0
- give_example_str dc.b "Example: APPVM 20 SWAPFILE DEVS:swapfile",0
- too_much_vm_str dc.b "Kindly request less than 256 Megabytes for VM management.",0
- not_enough_vm_str dc.b "Kindly request at least 1 Megabyte.",0
- no_VM_init_str dc.b "Not enough memory to initialise VM pool.",0
- unable_to_create_str dc.b "Could not create swapfile.",0
- disk_full_str dc.b "Not enough room on disk for swapfile.",0
- resident_str dc.b "APPVM is already active.",0
- need_MMU_str dc.b "APPVM requires an MMU to enable Virtual Memory.",0
-
- dosname DOSNAME
-
- default_swfn dc.b "Work:T/SWAPFILE",0
-
- vm_node_name dc.b "VM_NODE",0
-
- APPVM_template dc.b "VMMEGS/A,SWAPFILE/K",0
-
- VM_ready dc.b LF,"Virtual Memory Manager 0.9 "
- dc.b ESC,"[7m" ;HIGHLIGHT ON
- dc.b "Ready"
- dc.b ESC,"[0m" ;HIGHLIGHT OFF
- dc.b LF
- dc.b "Written by Laurence Vanhelsuwé © March 1992",0
-
- VM_off dc.b LF,"Virtual Memory Manager no longer active.",LF,0
-
- appVM_version dc.b "$VER: APPVM 0.9 ©LVA (Alpha release) 27/MAR/92",0
- dc.b "Copyright material, see user license for details.",0
-
- ;-----------------------------------------------
- ;Program variables, pointers, flags
- ;-----------------------------------------------
-
- arg_line ds.l 1 ;-> raw arguments
- stack_level ds.l 1 ;program launch SP
-
- DOS_LIB_PTR ds.l 1 ;-> opened dos.library
- stdout ds.l 1 ;-> console file
-
- swap_fhandle ds.l 1 ;file handle for swapfile
-
- continue_ssp ds.l 1 ;exception handler SSP level (SSP must remain same)
-
- client_Process ds.l 1 ;-> NULL or ONLY Task/Process which is using VM
- client_usage ds.l 1 ; # of VM bytes client is using at this moment.
-
- test_ptr ds.l 1 ;a LONG to test ptr in A reg without using regs
- naughty_vm_ptr ds.l 1 ;VMPTR that caused bus error exception
- request_size ds.l 1 ;for AllocVM() to remember # of bytes
-
- vm_tree ds.l 1 ;-> root of tree (A-level table)
-
- B_table ds.l 1 ;-> one and only VM B-table
- C_tables ds.l 1
- D_tables ds.l 1 ;-> ptr to INVALID descriptors and valid PAGE descr.
- ager ds.l 1 ;index into descriptors
-
- old_crp_room ds.l 2 ;room to store current 64-bit CRP register
- old_tc ds.l 1 ;same for TC
-
- new_crp_room ds.l 2 ;room to store future 64-bit CRP register
- tc_long ds.l 1
-
- ; VM Pool management variables
- ; ----------------------------
- vm_bytes ds.l 1 ;size of VM in bytes (rounded)
- vm_total_pages ds.l 1 ;total amount of VM pages remaining
- vm_pages ds.l 1 ;total amount of VM pages (constant **!!)
- vm_bitmap ds.l 1 ;-> allocated pages bitmap
- bitmap_size ds.l 1 ;size of above
- pages_mapped ds.l 1 **!!
-
- ; ReadArgs() results array
- ; ------------------------
- EVEN
- appvm_results
- vm_megs ds.l 1
- swap_filename ds.l 1
-
- ; Flags
- ; -----
- soft_patched ds.b 1 ;tell unpatch_all to undo work..
- hard_patched ds.b 1 ;restore old MMU configuration
-
- END
-