📄 shexcept.src
字号:
add #1, r1
mov.l r1, @r2
.aendi
tlb45: rte
nop
.nopool
; Sh3 chip bug work around. When an RTS at a page boundary causes an address
; error (PSL return) and delay slot fetch causes a TLB miss, the CPU incorrectly
; reports the TEA as the address error value instead of the TLB miss address.
;
; (r1) = TEA
; (r4) = ptr to current thread
tlb55:
.aif SH_CPU eq h'40
mov r1, r0
mov #-26, r3
shld r3, r0
and #h'3f, r0
mov #h'38, r3
cmp/eq r0, r3
bf tlb56
mov #h'E0000000, r0
sub r0, r1
mov #h'fff00000, r0
and r0, r1
mov #_dwStoreQueueBase, r0
mov @r0, r0
add r1, r0
bra tlb40
nop
tlb56:
.aendi
cmp/eq r4, r1
bf tlb60 ; TEA != ptr to current thread, fault
stc SPC, r1 ; (r1) = SHDB address of RTS
bra tlb10 ; continue with correct TEA
add #8, r1 ; put into the next page
; Invalid virtual address or this thread's key does not allow access to the
; memory block. Jump to the general exception handler to process the fault.
tlb60:
bra TLBMissError
nop
.nopool
.PAGE
ALTERNATE_ENTRY _LoadKPage
; Load User KPage entry into TLB.
mov #KPAGE_PTEL, r1
mov #SH3CTL_BASE, r3
mov @(MMUPTEH,r3), r0 ; (r0) = ASID in LSByte
mov #USER_KPAGE, r4
and #h'FF, r0 ; (r0) = ASID
or r4, r0 ; (r0) = User KPage | ASID
shll r1
mov.l r0, @(MMUPTEH,r3)
shlr r1 ; clear hit bit of entry
mov.l r1, @(MMUPTEL,r3)
ldtlb
nop
nop
rts
nop
.nopool
.PAGE
ALTERNATE_ENTRY _VerifyAccess
; VerifyAccess(PVOID pvAddr, DWORD dwFlags, ACCESSKEY aky)
; - verify access to a pointer
;
; VerifyAccess checks to see if the current thread has access to
; the given address. If bWrite is TRUE, both read & write access are
; verified.
;
; Entry (r4) = virtual address to verify
; (r5) = type of access to verify
; (r6) = access key to use for validation
; Return TRUE - access is OK
; FALSE - if access is not valid
; Uses r0, r1, r2, r3
mov #SH3CTL_BASE, r0
mov.l @(MMUTTB,r0), r0 ; (r0) = ptr to SectionTable array
cmp/pz r4
bf vfy60 ; address >2GB, out of SectionTable bounds
mov r4, r1
shlr8 r1
mov r1, r2
shlr16 r2 ; (r2) = TEA >> 24
shlr r2 ; (r2) = section table index
shll2 r2
mov.l @(r2,r0), r0 ; (r0) = ptr to section
mov r1, r2
mov #BLOCK_MASK, r3
shlr8 r1
and r3, r1 ; (r1) = block index
shll2 r1
mov.l @(r1,r0), r1 ; (r1) = ptr to MEMBLOCK structure
cmp/pz r1
bt/s vfy35 ; unmapped block
mov r2, r0
mov.l @(mb_lock,r1), r2 ; (r2) = block's access lock
add #mb_pages, r1 ; (r1) = ptr to array of TLB entries
.aif VA_PAGE eq 12
shlr2 r0 ; (r0) = TEA >> 10
.aendi
and #PAGE_MASK4, r0 ; (r0) = page index * 4
mov.l @(r0,r1), r0 ; (r0) = TLB entry
tst r2, r6 ; (T) = 1 iff access is allowed
bt vfy50 ; thread cannot access this block
tst #1, r0
bt vfy50 ; invalid entry
.aif VA_PAGE eq 12
mov #h'fff, r1
.aelse
mov #h'3ff, r1
.aendi
vfy20: mov #VERIFY_WRITE_FLAG, r2
tst r2, r5
bf vfy40 ; need to validate write permission
vfy30: and r1, r4 ; (r4) = offset w/in page
not r1, r1 ; (r1) = PFN mask
and r1, r0 ; (r0) = physical page address
mov #h'80000000, r1 ; (r1) = unmapped region base
or r4, r0 ; (r0) = physical byte address
rts
or r1, r0 ; (r0) = unmapped byte address
.nopool
; Unmapped virtual address. If the address is the kernel data page, load a
; shared TLB entry to map that page. Otherwise, jump to the general exception
; handler to process the fault.
;
; (r0) = Address >> 8
vfy35: shlr2 r0 ; (r0) = Address >> 10
cmp/eq #USER_KPAGE_SHR10, r0
mov #KPAGE_PTEL, r0
mov #h'3ff, r1 ; (r1) = page offset mask (KPage is always 1K)
bt vfy20 ; the address is the User KPage
;** Fall through to vfy40. The write verify will fail.
; Verify write access.
;
; (r0) = page table entry
vfy40: tst #h'20, r0
bf vfy30 ; access allowed
;** Fall through into vfy50 to return failure.
; Invalid virtual address or this thread's key does not allow access to the
; memory block. Return 0 to indicate an invalid address.
vfy50: rts
mov #0, r0
.nopool
; Address is in the kernel's memory space. Check the caller indicated that
; kernel access was OK and that the address is not within the kernel mapped
; address space from 0xC0000000 to 0xE0000000.
;
; (r4) = address to verify
vfy60: mov #h'C0000000, r3
mov #VERIFY_KERNEL_OK, r2
tst r2, r5
bf vfy61 ; access OK - flag was set
stc r4_bank, r5 ; (r5) = pCurThd
add #THREAD_CONTEXT_OFFSET+CtxR2, r5
mov.l @(CtxPsr-CtxR2, r5), r5
shll r5
shll r5 ; 'T' bit = Kernel mode status
bf vfy50 ; access not allowed - not in kernel mode
vfy61: cmp/hs r3, r4 ; 'T' set if Addr >= 0xC0000000
mov #h'E0000000, r2
bf vfy62 ; access OK
cmp/hs r2, r4 ; 'T' set if Addr >= 0xE0000000
bf vfy50 ; don't allow access to kernel mapped space
vfy62: rts
mov r4, r0 ; access OK, return pointer unchanged
.endf
.PAGE
LEAF_ENTRY _ZeroPage
; void ZeroPage(void *vpPage)
;
; Entry (r4) = (vpPage) = ptr to address of page to zero
; Return none
; Uses r0, r1, r4
mov #1 << (VA_PAGE-4), r1 ; (r1) = # of 16 bytes units to zero
mov #0, r0
zp10: mov.l r0, @r4
mov.l r0, @(4,r4)
mov.l r0, @(8,r4)
mov.l r0, @(12,r4)
dt r1 ; (r1) = # of units left
bf/s zp10
add #16, r4 ; (r4) = next 16 byte unit
rts
nop
.endf
.PAGE
LEAF_ENTRY _UnusedHandler
rts
mov #SYSINTR_NOP, r0 ; ignore the interrupt
.endf
.PAGE
.org h'600
; An interrupt exception has occured. Dispatch to an interrupt handler based
; upon the higest unmasked pending interrupt bit which is set.
;
; Bank 1 registers are pre-loaded with the following values:
;
; (r4) - ptr to current thread
; (r5) - ptr to context save area
; (r7) - SH3CTL_BASE (used to access MMU & exception data)
LEAF_ENTRY InterruptExceptionHandler
mov.l @(INTEVT,r7), r6
mov #ExceptionTable, r0
mov r6, r2
shlr2 r2
shlr r2 ; (r1) = exception code >> 3
mov.l @(r0,r2), r1 ; (r1) = interrupt handler function
shlr2 r2
mov #_IntrPrio, r0
add #-16, r2
mov.b @(r0,r2), r3
mov #_KData+bResched, r2
mov.b @(1,r2), r0 ; (r0) = kernel reentrancy flag
dt r0 ; decrement for each entry
bf/s ieh01 ; nested exception
mov.b r0, @(1,r2) ; save reentrancy level
mov r15, r2
mov #_KStack, r15 ; switch to kernel's stack
mov.l r2, @-r15 ; save stack pointer
ieh01:
.aif CELOG eq h'01
;
; NOTE : To make things relatively consistent, I'm going to make
; the registers R0-R3, R6 available for use. On entry to CeLogInterruptSHx
; R0 will contain the value to be logged (cNest + SYSINTR value)
;
; I'm assuming that at this point the only register of these that
; I need to preserve is R1, R3, and PR
;
sts PR, @-r15 ; save current return address
mov.l r0, @-r15 ; save registers
mov.l r1, @-r15 ; save registers
mov.l r2, @-r15 ; save registers
mov.l r3, @-r15 ; save registers
mov.l r6, @-r15 ; save registers
mov #_CeLogInterruptSHx, r2
mov #h'80000000, r3 ; r3 = mark as ISR entry
jsr @r2 ; CeLogInterruptSHx(dwLogValue)
mov r3, r0 ; delay slot: r0 = r3
mov.l @r15+, r6 ; Restore register
mov.l @r15+, r3 ; Restore register
mov.l @r15+, r2 ; Restore register
mov.l @r15+, r1 ; Restore register
mov.l @r15+, r0 ; Restore register
lds @r15+, PR ; Restore return address
.aendi
sts PR, @-r15
stc SPC, @-r15
stc SSR, @-r15
stc r0_user, @-r15
stc r1_user, @-r15
stc r2_user, @-r15
stc r3_user, @-r15
stc r6_user, @-r15
ldc r1, r1_user
mov #PR_B0_IE, r2
shll2 r3
shll2 r3
or r3, r2
ldc r2, SR ; bank 0, lower prio interrupts masked
jsr @r1 ; invoke interrupt service routine
nop
mov #PR_B1_BK, r2
ldc r2, SR
stc r0_user, r3
ldc @r15+, r6_user
ldc @r15+, r3_user
ldc @r15+, r2_user
ldc @r15+, r1_user
ldc @r15+, r0_user
ldc @r15+, SSR
ldc @r15+, SPC
lds @r15+, PR
.aif CELOG eq h'01
;
; NOTE : To make things relatively consistent, I'm going to make
; the registers R0-R3, R6 available for use. On entry to CeLogInterruptSHx
; R0 will contain the value to be logged (cNest + SYSINTR value)
;
; I'm assuming that at this point the only register of these that
; I need to preserve is R3 (SYSINTR value) and PR (return address)
;
sts PR, @-r15 ; save current return address
mov.l r3, @-r15 ; save registers
mov #_KData+cNest, r1
mov.b @r1, r1 ; r1 = nest level (0, -1, -2, etc)
neg r1, r1 ; r1 = nest level (0, 1, 2, etc)
shll16 r1 ; r1 <<= 16
mov #_CeLogInterruptSHx, r2
or r1, r3 ; r3 = (-cNest << 16) | SYSINTR_val
jsr @r2 ; CeLogInterruptSHx(dwLogValue)
mov r3, r0
mov.l @r15+, r3 ; Restore register
lds @r15+, PR ; Restore return address
.aendi
mov #_KData+bResched, r2
mov.b @(1,r2), r0
add #1, r0
mov.b r0, @(1,r2)
cmp/eq #1, r0
bf ieh02
mov @r15, r15
ieh02:
mov r3, r0
mov #SH3CTL_BASE, r7
cmp/eq #SYSINTR_NOP, r0
bt ieh50 ; no additional processing needed
cmp/eq #SYSINTR_BREAK, r0
bt ieh60 ; external break button pushed
add #-SYSINTR_DEVICES, r0
cmp/pz r0
bf ieh40
mov #_KData+PendEvents, r1 ; r1 = &PendEvents
mov #1, r3
mov.l @r1, r2 ; r2 = PendEvents
shld r0, r3 ; r3 = bit signifying current interrupt
or r3, r2 ; r2 = all interrupts so far (bitmask)
mov.l r2, @r1 ; store PendEvents
ieh40: mov #_KData+bResched, r1
mov.b @(1,r1), r0 ; cNest
mov #1, r2
cmp/eq #1, r0
bf ieh49
bra InterruptResched ; jump to general exception handler
mov.w r2, @r1
.nopool
; Check for interlocked API in progress. The interlocked apis are all located
; at the end of the user kernel page. If the PC is less than INTERLOCKED_END and
; greater than INTERLOCKED_START, then the PC is adjusted to restart the routine.
;
; in register bank 1.
ieh49: mov.b r2, @r1 ; set bResched flag
ieh50: mov #INTERLOCKED_END, r1 ; (r1) = end of interlocked api block
stc SPC, r0 ; (r0) = interrupted PC
cmp/hi r0, r1 ; 'T' = r1 > r0
bt ieh70 ; PC < INTERLOCKED_END
ieh55: rte
nop
.nopool
; External break button. Trap into the debugger.
ieh60: mov #16, r0
mov r0, @(TRPA,r7) ; set trapa code to 16 (trapa #4)
mov #h'160, r0 ; (r0) = trapa expevt value
bra TLBMissError
mov r0, @(EXPEVT,r7) ; set event code
.nopool
; PC is below the end of the INTERLOCKED APIs, check lower bound and adjust the PC
; to restart the routine.
;
; (r0) = interrupted PC
ieh70: mov #INTERLOCKED_START, r1 ; (r1) = start of interlocked api block
tst #1,r0
bf ieh55 ; PC is odd, don't change it
cmp/hi r0, r1 ; 'T' = r1 > r0
bt ieh55 ; out of range, no backup
mov #-8, r1
and r1, r0 ; (old PC) &= ~7
ldc r0, SPC ; update PC
.aif INTRLOCK_LEDS eq 1
mov #h'AA001010, r2 ; (r2) = LED address
mov.l @r2, r0
add #1,r0
mov.l r0, @r2 ; put it in lights
.aendi
rte
nop
.endf
NESTED_ENTRY xKCall
;++
; The following code is never executed. Its purpose is to support unwinding
; through the call to the exception dispatcher.
;--
mov.l r15, @(0,r15) ; caller's stack pointer
sts.l PR, @-r15 ; return address
add #-16, r15 ; argument save area
PROLOG_END
ALTERNATE_ENTRY _KCall
; KCall - call kernel function
;
; KCall invokes a kernel function in a non-preemtable state by incrementing
; the kernel nest level and switching onto a kernel stack.
;
; While in a preemtible state, the thread's register save area is
; volatile. On the way in, nothing can be saved into the thread
; structure until KNest is set and on the way out anything needed from the
; thread structure must be loaded before restoring KNest.
;
; The sequence of stack switching must be handled carefully because
; whenever KNest != 1, the general exception handler assumes that the kernel
; stack is current and will not switch stacks. On the way in, we must switch
; to the kernel stack before setting KNest but not use it until after KNest
; is set. On the way out, we must reset KNest before restoring the thread's
; stack pointer.
;
; Entry (r4) = ptr to function to call
; (r5) = first function arg
; (r6) = second fucntion arg
; (r7) = third function arg
; Exit (r0) = function return value
; Uses r0-r7
mov #_KData+bResched, r1
mov r4, r3 ; (r3) = ptr to function to call
mov.b @(1,r1), r0 ; (r0) = kernel nest level
mov r5, r4 ; ripple args down
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -