;:ts=8 ; ; (C) 1986 Radical Eye Software. This code may be used and distributed ; freely, so long as this notice stays in it and the banner messages ; are unchanged except for the version notices. Written by Tomas Rokicki ; on December 19 through December 21 1986. ; ; This is the assembly language source for the second phase of the ; profiler. It contains a vertical retrace interrupt routine and a ; trap routine. ; cseg ; ; First, the interrupt routine which maintains the tick count. ; All `$123456's are stuffed with absolute addresses within the ; C routine. ; public _int_rout _int_rout add.l #$100,$123456 jmp $fc108c ; ; Now we have the trap routine; this can get entertaining. First, ; we save a bunch of registers, and then load the global vector. ; It's format (from p2.c) is: ; ; struct tstruct { ; short running ; 0 ; char *tick_add ; 2 ; char *dat_add ; 6 ; char *stack ; 10 ; char *stack_top ; 14 ; char *stack_bot ; 18 ; short error ; 22 ; char *debug_ptr ; 24 ; } trap_glob ; 28 ; ; Error bits: ; 0 Stack underflow (increase memory!) ; 1 Unlink from different routine than linked ; ; The dat_add array is organized as follows: ; struct rout_stat { ; short recursion_count ; 0 ; short data_offset ; 2 ; long invoke_count ; 4 ; long self ; 8 ; long children ; 12 ; } ; 16 ; ; The stack we create will look like this: ; struct stack_entry { ; short routine_number ; 0 ; long start_time ; 2 ; } ; 6 ; ; On every push (routine link), we push a new start_time and ; routine_number, checking for recursion. On every pop (routine ; unlink), we check that the routine we are unlinking is the ; proper one. If it isn't, we complain for now. ; Then, we calculate the elapsed time, add it to the 'child' ; field and the 'self' field. We then pop the stack, and subtract ; the elapsed time from the exposed entry (if any.) We have to ; check the stack bounds at each step, and set the error flag if ; something goes amiss. ; public _trap_rout _trap_rout movem.l regs,-(sp) move.l #$123456,a0 move.l 2(a0),a1 ; ; Get and mask out the vpos ; move.l $dff004,d1 and.l #$1ff00,d1 ; ; We loop around until we are sure the vpos didn't change ; while we were busy. This is necessary in case an interrupt ; occurs during this piece of code; we can't mask the ; interrupts since we are in user mode. ; loop1 move.l (a1),d0 move.l $dff004,d2 and.l #$1ff00,d2 cmp.l d1,d2 beq okay1 move.l d2,d1 bra loop1 ; ; Now we check it against 5. The values we use are ; 0..1 -> 254..255; 9..262 -> 0..253. No other values ; should be possible. ; okay1 lsr.l #8,d1 sub.w #2,d1 bcs over3 over2 sub.w #7,d1 over3 move.b d1,d0 ; ; Now the time is in d0. For now, we ignore it. We ; get the parameter following the trap instruction. ; move.l 2+regsize(sp),a1 move.w (a1)+,d1 ; ; Debug code; turn this off! ; ; move.l 24(a0),a2 ; move.w d1,(a2)+ ; move.l a2,24(a0) ; tst.w d1 ; ; We now return you . . . ; bmi unlink_rout bne link_rout ; ; The exit routine simply sets the done flag and then jumps into ; the normal link routine. What it should do, eventually, is ; virtually unlink everything. Especially since it will never ; exit. Yeah, that's what we do. It gets a charge of zero. ; exit_rout move.l a1,2+regsize(sp) move.w d1,(a0) lsl.w #4,d1 movem.l d1/d4,-(sp) move.l 6(a0),a1 move.l 10(a0),a2 move.w (a2),d1 ; ; Now we pop things, calculating the elapsed time. ; unstack move.l d0,d4 sub.l 2(a2),d4 ; ; We add the elapsed time to both variables of the current procedure, ; and subtract it from the self variable for the above procedure. ; add.l d4,8(a1,d1.w) add.l d4,12(a1,d1.w) move.w 6(a2),d1 ; ; This works because only if we've run off the end do we ; have a zero here. ; beq unstkd sub.l d4,8(a1,d1.w) lea 6(a2),a2 bra unstack unstkd lea 6(a2),a2 move.l a2,10(a0) movem.l (sp)+,d1/d4 ; ; Finally, we do a link-style exit. ; move.w 2(a1,d1.w),d3 movem.l (sp)+,regs move.w (sp)+,d2 move.l (sp)+,a1 move d2,sr move.l a5,-(sp) move.l a7,a5 lea (a7,d3.w),a7 jmp (a1) ; ; This is the routine used for a link instruction. We increment a ; counter at the moment; that's all. ; link_rout move.l a1,2+regsize(sp) move.w d1,(a0) link_2 lsl.w #4,d1 move.l 6(a0),a1 add.l #1,4(a1,d1.w) ; ; Now we push a new entry onto the stack. ; move.l 10(a0),a2 lea -6(a2),a2 cmp.l 18(a0),a2 bcc okay4 ; ; If there was an error, we simply overwrite ; current entry. ; lea 6(a2),a2 or.w #1,22(a0) okay4 move.l d0,2(a2) move.w d1,(a2) move.l a2,10(a0) ; ; This is the exit code. ; move.w 2(a1,d1.w),d3 movem.l (sp)+,regs move.w (sp)+,d2 move.l (sp)+,a1 move d2,sr move.l a5,-(sp) move.l a7,a5 lea (a7,d3.w),a7 jmp (a1) ; ; An unlink puts us here. For now, we simply exit; nothing fancy. ; We skip over the return address from the interrupt and just return ; directly to the calling routine. ; unlink_rout move.l a1,2+regsize(sp) ; ; We get the stack pointer, and make sure we are unlinking from ; the same routine we linked. ; lsl.w #4,d1 move.l 6(a0),a1 move.l 10(a0),a2 cmp.w (a2),d1 beq okay5 ; ; If it's not okay, we pretend it is for now, but only after ; setting the appropriate error bit. ; or.w #2,22(a0) okay5 ; ; Now we pop things, calculating the elapsed time. ; sub.l 2(a2),d0 lea 6(a2),a2 move.l a2,10(a0) ; ; We add the elapsed time to both variables of the current procedure, ; and subtract it from the self variable for the above procedure. ; add.l d0,8(a1,d1.w) add.l d0,12(a1,d1.w) move.w (a2),d1 ; ; If we are at the end, we don't do this. ; beq over7 sub.l d0,8(a1,d1.w) ; ; Exit code for unlink. ; over7 movem.l (sp)+,regs move.w (sp)+,d3 move.l (sp)+,a1 move d3,sr unlk a5 rts ; ; Registers used. ; regs reg a0-a2/d0-d1 regsize equ 20 end