492 lines
14 KiB
Plaintext
492 lines
14 KiB
Plaintext
|
include pmem.i
|
||
|
include cards.i
|
||
|
include start.i
|
||
|
section .text,text
|
||
|
; Initialize the virtual memory manager
|
||
|
public vmem_init
|
||
|
vmem_init:
|
||
|
movem.l d2/d3, -(a7)
|
||
|
move.w #$5, d0 ; Get the pointer to the MMU card
|
||
|
jsr find_first_card
|
||
|
move.l a0, vmem_mmu_base_addr ; Save it for later use
|
||
|
; Map 3 pages to dummy physical frames for use in accessing user page mappings
|
||
|
move.l #$FFFFC000, d0
|
||
|
move.l #3, d1
|
||
|
move.l #0, d2
|
||
|
move.l #$2, d3
|
||
|
jsr vmem_map_free_to
|
||
|
move.l a0, vmem_active_space_mapping_ptr ; Save the pointer for later use
|
||
|
; Same as above for the secondary address space
|
||
|
move.l #$FFFFC000, d0
|
||
|
move.l #3, d1
|
||
|
move.l #0, d2
|
||
|
move.l #$2, d3
|
||
|
jsr vmem_map_free_to
|
||
|
move.l a0, vmem_secondary_space_mapping_ptr ; Save the pointer for later use
|
||
|
movem.l (a7)+, d2/d3
|
||
|
; Activate the kernel address space
|
||
|
move.l #kernel_address_space, a0
|
||
|
jsr vmem_activate_addr_space
|
||
|
rts
|
||
|
|
||
|
; Clears the TLB entry of the page pointed to by a0
|
||
|
public vmem_clear_tlb_entry
|
||
|
vmem_clear_tlb_entry:
|
||
|
; Write the address to the TLB clear register of the MMU card
|
||
|
move.l vmem_mmu_base_addr, a1
|
||
|
move.l a0, ($10,a1)
|
||
|
rts
|
||
|
|
||
|
; Activates the address space pointed to by a0
|
||
|
public vmem_activate_addr_space
|
||
|
vmem_activate_addr_space:
|
||
|
movem.l a2, -(a7)
|
||
|
move.l a0, vmem_active_space_ptr ; Set the pointer to the current address space
|
||
|
move.l vmem_mmu_base_addr, a1 ; Get the MMU card base into a1
|
||
|
move.l vmem_active_space_mapping_ptr, a2 ; Load the pointer to the active space mapping pages into a2
|
||
|
move.l #2, d0 ; Loop 3 times, count -1 due to dbra looping n+1 times
|
||
|
vaas_loop:
|
||
|
move.l (a0)+, d1 ; Read the next mapping frame into d1
|
||
|
movem.l d0/d1/d2/d3/a0/a1, -(a7)
|
||
|
; Map the mapping page pointed to by a2 to the mapping frame just read into d1
|
||
|
move.l a2, a0
|
||
|
move.l d1, d0
|
||
|
move.l #1, d1
|
||
|
move.l #0, d2
|
||
|
move.l #$2, d3
|
||
|
jsr vmem_map_to
|
||
|
movem.l (a7)+, d0/d1/d2/d3/a0/a1
|
||
|
cmp.l #0, d1 ; If the mapping frame isn't 0, mark it as active
|
||
|
beq.b .1
|
||
|
ori.l #3, d1
|
||
|
.1:
|
||
|
move.l d1, (a1)+ ; Write the frame to the next quarter mapping register in the MMU
|
||
|
adda.l #$1000, a2 ; Advance to the next mapping page
|
||
|
dbra d0, vaas_loop ; Loop back if there are more frames to read
|
||
|
movem.l (a7)+, a2
|
||
|
rts
|
||
|
|
||
|
; Sets the secondary address space
|
||
|
public vmem_set_secondary_addr_space
|
||
|
vmem_set_secondary_addr_space:
|
||
|
move.l a0, vmem_secondary_space_ptr ; Set the pointer to the secondary address space
|
||
|
move.l vmem_secondary_space_mapping_ptr, a1 ; Load the pointer to the secondary space mapping pages into a1
|
||
|
move.l #2, d0 ; Loop 3 times, count -1 due to dbra looping n+1 times
|
||
|
vssas_loop:
|
||
|
move.l (a0)+, d1 ; Read the next mapping frame into d1
|
||
|
movem.l d0/d1/d2/d3/a0, -(a7)
|
||
|
; Map the mapping page pointed to by a1 to the mapping frame just read into d1
|
||
|
move.l a1, a0
|
||
|
move.l d1, d0
|
||
|
move.l #1, d1
|
||
|
move.l #0, d2
|
||
|
move.l #$2, d3
|
||
|
jsr vmem_map_to
|
||
|
movem.l (a7)+, d0/d1/d2/d3/a0
|
||
|
adda.l #$1000, a1 ; Advance to the next mapping page
|
||
|
dbra d0, vssas_loop ; Loop back if there are more frames to read
|
||
|
rts
|
||
|
|
||
|
; Get the pointer to the mapping entry for the page in a0
|
||
|
; Pointer returned in a0
|
||
|
; Address space number in d0
|
||
|
public vmem_get_map_ptr
|
||
|
vmem_get_map_ptr:
|
||
|
move.l d2, -(a7)
|
||
|
; Save the space # in d2
|
||
|
move.l d0, d2
|
||
|
; Get the quarter number * 4096 in d0 (offset of mapping page), and the page number in the quarter * 4 in d1
|
||
|
; qnum_x4k = (addr >> 10 & 0x3000)
|
||
|
; q_pnum_x4 = (addr >> 10 & 0xFFC)
|
||
|
move.l a0, d0
|
||
|
lsr.l #8, d0
|
||
|
lsr.l #2, d0
|
||
|
move.l d0, d1
|
||
|
andi.l #$3000, d0
|
||
|
andi.l #$FFC, d1
|
||
|
; If the quarter is the kernel quarter, the mapping page is fixed
|
||
|
; and we can compute the pointer w/o using the user mapping pages
|
||
|
cmp.l #$3000, d0
|
||
|
bne.b .1
|
||
|
move.l #kernel_map, a0
|
||
|
bra.b .5
|
||
|
.1:
|
||
|
; Get the correct pointers for the space #. Active space for 0, secondary space for 1
|
||
|
cmp.l #0, d2
|
||
|
bne.b .2
|
||
|
move.l vmem_active_space_mapping_ptr, a0
|
||
|
move.l vmem_active_space_ptr, a1
|
||
|
bra.b .4
|
||
|
.2:
|
||
|
cmp.l #1, d2
|
||
|
bne.b .3
|
||
|
move.l vmem_secondary_space_mapping_ptr, a0
|
||
|
move.l vmem_secondary_space_ptr, a1
|
||
|
bra.b .4
|
||
|
.3:
|
||
|
; Halt if invalid space # passed
|
||
|
stop #2700
|
||
|
.4:
|
||
|
; Add the mapping page offset to the base of the mapping pages for the space
|
||
|
adda.l d0, a0
|
||
|
; Shift the mapping page offset to get the quarter # * 4 in d0
|
||
|
lsr.l #8, d0
|
||
|
lsr.l #2, d0
|
||
|
; Get the mapping frame for that quarter
|
||
|
move.l (a1,d0), d0
|
||
|
; Return 0 if the frame is not present
|
||
|
cmp.l #0, d0
|
||
|
bne.b .5
|
||
|
move.l #0, a0
|
||
|
move.l (a7)+, d2
|
||
|
rts
|
||
|
.5:
|
||
|
; Return the mapping page address + (page # * 4)
|
||
|
lea.l (a0,d1), a0
|
||
|
move.l (a7)+, d2
|
||
|
rts
|
||
|
|
||
|
; Unmaps the virtual page at address a0
|
||
|
; Address space number in d0
|
||
|
public vmem_unmap_page
|
||
|
vmem_unmap_page:
|
||
|
movem.l d0/a0, -(a7)
|
||
|
; Clear the page's TLB entry
|
||
|
bsr.w vmem_clear_tlb_entry
|
||
|
movem.l (a7)+, d0/a0
|
||
|
; Set the page entry to 0
|
||
|
bsr.w vmem_get_map_ptr
|
||
|
move.l #0, (a0)
|
||
|
rts
|
||
|
|
||
|
; Sets the mapping for the virtual page at address a0 to d0
|
||
|
; Address space number in d1
|
||
|
vmem_set_page_mapping:
|
||
|
movem.l d2/a2, -(a7)
|
||
|
movem.l d0/d1/a0, -(a7)
|
||
|
; Clear the page's TLB entry
|
||
|
bsr.w vmem_clear_tlb_entry
|
||
|
movem.l (a7)+, d0/d1/a0
|
||
|
movem.l d0/d1/a0, -(a7)
|
||
|
; Get the mapping pointer for the passed-in page
|
||
|
move.l d1, d0
|
||
|
bsr.w vmem_get_map_ptr
|
||
|
move.l a0, a2 ; Save it in a2
|
||
|
movem.l (a7)+, d0/d1/a0
|
||
|
; If there is no pointer, a mapping frame must be allocated, else write the mapping
|
||
|
cmpa.l #0, a2
|
||
|
bne.b .4
|
||
|
move.l d0, -(a7)
|
||
|
movem.l d1/a0, -(a7)
|
||
|
jsr pmem_pop_frame ; Get a physical frame to use
|
||
|
movem.l (a7)+, d1/a0
|
||
|
; Get the passed-in page's quarter # * 4 in d2
|
||
|
move.l a0, d2
|
||
|
lsr.l #8, d2
|
||
|
lsr.l #8, d2
|
||
|
lsr.l #4, d2
|
||
|
andi.l #%1100, d2
|
||
|
; Set the new physical frame as the mapping for the passed-in page's quarter and update the specified address space
|
||
|
cmp.l #0, d1
|
||
|
bne.b .1
|
||
|
movem.l d1/a0, -(a7)
|
||
|
move.l vmem_active_space_ptr, a0
|
||
|
move.l d0, (a0,d2)
|
||
|
bsr.w vmem_activate_addr_space
|
||
|
movem.l (a7)+, d1/a0
|
||
|
bra.b .3
|
||
|
.1:
|
||
|
cmp.l #1, d1
|
||
|
bne.b .2
|
||
|
movem.l d1/a0, -(a7)
|
||
|
move.l vmem_secondary_space_ptr, a0
|
||
|
move.l d0, (a0,d2)
|
||
|
bsr.w vmem_set_secondary_addr_space
|
||
|
movem.l (a7)+, d1/a0
|
||
|
bra.b .3
|
||
|
.2:
|
||
|
; Halt if invalid space # passed
|
||
|
stop #2700
|
||
|
.3:
|
||
|
; Get the new mapping pointer
|
||
|
move.l d1, d0
|
||
|
bsr.w vmem_get_map_ptr
|
||
|
move.l a0, a2
|
||
|
move.l (a7)+, d0
|
||
|
.4:
|
||
|
move.l d0, (a2) ; Write the mapping to the mapping page
|
||
|
movem.l (a7)+, d2/a2
|
||
|
rts
|
||
|
|
||
|
; Sets the permission flags for the virtual page at address a0 to d0
|
||
|
; Address space number in d1
|
||
|
vmem_set_page_flags:
|
||
|
movem.l d2/a2, -(a7)
|
||
|
movem.l d0/d1/a0, -(a7)
|
||
|
; Clear the page's TLB entry
|
||
|
bsr.w vmem_clear_tlb_entry
|
||
|
movem.l (a7)+, d0/d1/a0
|
||
|
movem.l d0/d1/a0, -(a7)
|
||
|
move.l d1, d0
|
||
|
; Get the mapping pointer for the passed-in page
|
||
|
bsr.w vmem_get_map_ptr
|
||
|
move.l a0, a2
|
||
|
movem.l (a7)+, d0/d1/a1
|
||
|
; If there is no pointer, return
|
||
|
cmpa.l #0, a2
|
||
|
beq.b .1
|
||
|
move.l (a2), d1 ; Read the old entry
|
||
|
andi.l #(~$FFE), d1 ; Clear the permission flags on the old entry
|
||
|
or.l d0, d1 ; Set the new permission flags
|
||
|
move.l d1, (a2) ; Write the entry
|
||
|
.1:
|
||
|
movem.l (a7)+, d2/a2
|
||
|
rts
|
||
|
|
||
|
; Maps the virtual page at address a0 to the physical frame at address d0
|
||
|
; d0 must have none of its lower 12 bits set
|
||
|
; Address space number in d1
|
||
|
; Permission flags in d2
|
||
|
vmem_map_page_to:
|
||
|
; Set the passed-in permission flags and the present flag on the passed-in page number
|
||
|
or.l d2, d0
|
||
|
or.l #1, d0
|
||
|
bra.w vmem_set_page_mapping
|
||
|
|
||
|
; Maps the virtual page at address a0 to a free physical frame
|
||
|
; Address space number in d0
|
||
|
; Permission flags in d1
|
||
|
vmem_map_page:
|
||
|
; Separate the register pushes to allow popping them directly
|
||
|
; into the right registers for vmem_map_page_to's arguments
|
||
|
move.l d2, -(a7)
|
||
|
move.l a0, -(a7)
|
||
|
move.l d0, -(a7)
|
||
|
move.l d1, -(a7)
|
||
|
jsr pmem_pop_frame
|
||
|
movem.l (a7)+, d2
|
||
|
movem.l (a7)+, d1
|
||
|
movem.l (a7)+, a0
|
||
|
bsr.w vmem_map_page_to
|
||
|
move.l (a7)+, d2
|
||
|
rts
|
||
|
|
||
|
; Unmaps the range of virtual pages at address a0 with length d0
|
||
|
; Address space number in d1
|
||
|
public vmem_unmap
|
||
|
vmem_unmap:
|
||
|
subi.l #1, d0 ; Subtract 1 to account for the extra loop done by dbra
|
||
|
vmem_um_loop:
|
||
|
movem.l d0/d1/a0, -(a7)
|
||
|
move.l d1, d0
|
||
|
bsr.w vmem_unmap_page
|
||
|
movem.l (a7)+, d0/d1/a0
|
||
|
adda.l #$1000, a0
|
||
|
dbra d0, vmem_um_loop
|
||
|
rts
|
||
|
|
||
|
; Sets the permission flags of the range of virtual pages starting at address a0 with length d1 to d0
|
||
|
; Address space number in d2
|
||
|
public vmem_set_flags
|
||
|
vmem_set_flags:
|
||
|
subi.l #1, d1 ; Subtract 1 to account for the extra loop done by dbra
|
||
|
vmem_sf_loop:
|
||
|
movem.l d0/d1/a0, -(a7)
|
||
|
move.l d2, d1
|
||
|
bsr.w vmem_set_page_flags
|
||
|
movem.l (a7)+, d0/d1/a0
|
||
|
adda.l #$1000, a0
|
||
|
dbra d1, vmem_sf_loop
|
||
|
rts
|
||
|
|
||
|
; Maps the range of virtual pages starting at address a0 with length d1 to the range of physical frames starting at d0
|
||
|
; d0 must have none of its lower 12 bits set
|
||
|
; Address space number in d2
|
||
|
; Permission flags in d3
|
||
|
public vmem_map_to
|
||
|
vmem_map_to:
|
||
|
subi.l #1, d1 ; Subtract 1 to account for the extra loop done by dbra
|
||
|
vmem_mt_loop:
|
||
|
movem.l d0/d1/d2/a0, -(a7)
|
||
|
move.l d2, d1
|
||
|
move.l d3, d2
|
||
|
bsr.w vmem_map_page_to
|
||
|
movem.l (a7)+, d0/d1/d2/a0
|
||
|
adda.l #$1000, a0
|
||
|
add.l #$1000, d0
|
||
|
dbra d1, vmem_mt_loop
|
||
|
rts
|
||
|
|
||
|
; Maps the range of virtual pages starting at address a0 with length d0 to free physical frames
|
||
|
; Address space number in d1
|
||
|
; Permission flags in d2
|
||
|
public vmem_map
|
||
|
vmem_map:
|
||
|
subi.l #1, d0 ; Subtract 1 to account for the extra loop done by dbra
|
||
|
vmem_m_loop:
|
||
|
movem.l d0/d1/a0, -(a7)
|
||
|
move.l d1, d0
|
||
|
move.l d2, d1
|
||
|
bsr.w vmem_map_page
|
||
|
movem.l (a7)+, d0/d1/a0
|
||
|
adda.l #$1000, a0
|
||
|
dbra d0, vmem_m_loop
|
||
|
rts
|
||
|
|
||
|
; Maps a free range of virtual pages with length d0 to free physical frames
|
||
|
; Returns the range start in a0
|
||
|
; Address space number in d1
|
||
|
; Permission flags in d2
|
||
|
public vmem_map_free
|
||
|
vmem_map_free:
|
||
|
movem.l d0/d1/d3, -(a7)
|
||
|
move.l d1, d3
|
||
|
; Use bit 2 of the address space # to choose between getting free user or kernel
|
||
|
; pages as only 0 and 1 are valid address space #s, requiring only bit 0.
|
||
|
andi.l #$2, d3
|
||
|
beq.b .1
|
||
|
bsr.w vmem_get_free_user_pages
|
||
|
bra.b .2
|
||
|
.1:
|
||
|
bsr.b vmem_get_free_kernel_pages
|
||
|
.2:
|
||
|
movem.l (a7)+, d0/d1/d3
|
||
|
; Clear bit 2 to make the passed-in address space # valid for the rest of the VMM code
|
||
|
andi.l #$1, d1
|
||
|
move.l a0, -(a7)
|
||
|
bsr.b vmem_map
|
||
|
move.l (a7)+, a0
|
||
|
rts
|
||
|
|
||
|
; Maps a free range of virtual pages with length d1 to the range of physical frames starting at d0
|
||
|
; Returns the range start in a0
|
||
|
; Address space number in d2
|
||
|
; Permission flags in d3
|
||
|
public vmem_map_free_to
|
||
|
vmem_map_free_to:
|
||
|
movem.l d0/d1/d3, -(a7)
|
||
|
move.l d1, d0
|
||
|
move.l d2, d3
|
||
|
; Use bit 2 of the address space # to choose between getting free user or kernel
|
||
|
; pages as only 0 and 1 are valid address space #s, requiring only bit 0.
|
||
|
andi.l #$2, d3
|
||
|
beq.b .1
|
||
|
bsr.w vmem_get_free_user_pages
|
||
|
bra.b .2
|
||
|
.1:
|
||
|
bsr.b vmem_get_free_kernel_pages
|
||
|
.2:
|
||
|
movem.l (a7)+, d0/d1/d3
|
||
|
; Clear bit 2 to make the passed-in address space # valid for the rest of the VMM code
|
||
|
andi.l #$1, d2
|
||
|
move.l a0, -(a7)
|
||
|
bsr.w vmem_map_to
|
||
|
move.l (a7)+, a0
|
||
|
rts
|
||
|
|
||
|
; Copies the range of page mappings at address a0 in the primary space with length d0 to the secondary space starting at address a1
|
||
|
public vmem_copy_to_secondary
|
||
|
vmem_copy_to_secondary:
|
||
|
move.l d0, -(a7)
|
||
|
; Get the mapping pointer for the start page in the primary address space
|
||
|
move.l #0, d0
|
||
|
bsr.w vmem_get_map_ptr
|
||
|
move.l (a7)+, d0
|
||
|
subi.l #1, d0 ; Subtract 1 to account for the extra loop done by dbra
|
||
|
vmem_cts_loop:
|
||
|
; Read the next page mapping and increment the pointer
|
||
|
move.l (a0)+, d1
|
||
|
movem.l d0/a0/a1, -(a7)
|
||
|
; Set the same mapping in the secondary address space at address a1
|
||
|
move.l d1, d0
|
||
|
move.l #1, d1
|
||
|
move.l a1, a0
|
||
|
bsr.w vmem_set_page_mapping
|
||
|
movem.l (a7)+, d0/a0/a1
|
||
|
adda.l #$1000, a1
|
||
|
dbra d0, vmem_cts_loop
|
||
|
rts
|
||
|
|
||
|
; Get a range of free kernel pages with length in d0 and return its start in a0
|
||
|
public vmem_get_free_kernel_pages
|
||
|
vmem_get_free_kernel_pages:
|
||
|
move.l d2, -(a7)
|
||
|
move.l d0, d1 ; Set the remaining page # count to the length of the requested range
|
||
|
move.l #$C00000, a0 ; Put the start page of the search in a0
|
||
|
vmem_gfkp_loop:
|
||
|
movem.l d0/d1/a0, -(a7)
|
||
|
move.l #0, d0 ; Get the pointer for the current page
|
||
|
bsr.w vmem_get_map_ptr
|
||
|
; If NULL, load dummy free mapping, otherwise read page mapping into d2
|
||
|
cmpa.l #0, a0
|
||
|
beq.b .1
|
||
|
move.l (a0), d2
|
||
|
bra.b .2
|
||
|
.1:
|
||
|
move.l #0, d2
|
||
|
.2:
|
||
|
movem.l (a7)+, d0/d1/a0
|
||
|
btst #0, d2 ; If the page is not free, skip it
|
||
|
beq.b .3
|
||
|
subi.l #1, d1
|
||
|
bne.b .4 ; If we have not found a run of pages of the right length, continue to check for free pages
|
||
|
adda.l #$1000, a0 ; Start address is (current_page + $1000 - (length * $1000)), compute this in d0 and return
|
||
|
lsl.l #8, d0
|
||
|
lsl.l #4, d0
|
||
|
suba.l d0, a0
|
||
|
move.l (a7)+, d2
|
||
|
rts
|
||
|
.3:
|
||
|
move.l d0, d1 ; Page not free, reset the page # count and try again
|
||
|
.4:
|
||
|
adda.l #$1000, a0 ; Move to the next page and continue the search
|
||
|
bra.b vmem_gfkp_loop
|
||
|
|
||
|
; Get a range of free user pages with length in d0 and return its start in a0
|
||
|
public vmem_get_free_user_pages
|
||
|
vmem_get_free_user_pages:
|
||
|
move.l d2, -(a7)
|
||
|
move.l d0, d1 ; Set the remaining page # count to the length of the requested range
|
||
|
move.l #$1000, a0 ; Put the start page of the search in a0
|
||
|
vmem_gfup_loop:
|
||
|
movem.l d0/d1/a0, -(a7)
|
||
|
move.l #0, d0 ; Get the pointer for the current page
|
||
|
bsr.w vmem_get_map_ptr
|
||
|
; If NULL, load dummy free mapping, otherwise read page mapping into d2
|
||
|
cmpa.l #0, a0
|
||
|
beq.b .1
|
||
|
move.l (a0), d2
|
||
|
bra.b .2
|
||
|
.1:
|
||
|
move.l #0, d2
|
||
|
.2:
|
||
|
movem.l (a7)+, d0/d1/a0
|
||
|
btst #0, d2 ; If the page is not free, skip it
|
||
|
beq.b .3
|
||
|
subi.l #1, d1
|
||
|
bne.b .4 ; If we have not found a run of pages of the right length, continue to check for free pages
|
||
|
adda.l #$1000, a0 ; Start address is (current_page + $1000 - (length * $1000)), compute this in d0 and return
|
||
|
lsl.l #8, d0
|
||
|
lsl.l #4, d0
|
||
|
suba.l d0, a0
|
||
|
move.l (a7)+, d2
|
||
|
rts
|
||
|
.3:
|
||
|
move.l d0, d1 ; Page not free, reset the page # count and try again
|
||
|
.4:
|
||
|
adda.l #$1000, a0 ; Move to the next page and continue the search
|
||
|
bra.b vmem_gfup_loop
|
||
|
|
||
|
section .data,data
|
||
|
public kernel_address_space
|
||
|
kernel_address_space:
|
||
|
dc.l $0, $0, $0, kernel_map - $C00000
|
||
|
vmem_active_space_ptr: dc.l kernel_address_space
|
||
|
|
||
|
section .bss,bss
|
||
|
public vmem_mmu_base_addr
|
||
|
vmem_mmu_base_addr: ds.l 1
|
||
|
vmem_secondary_space_ptr: ds.l 1
|
||
|
vmem_active_space_mapping_ptr: ds.l 1
|
||
|
vmem_secondary_space_mapping_ptr: ds.l 1
|