MS-DOS/v2.0/source/PROFIL.ASM
2018-09-21 17:53:34 -07:00

705 lines
21 KiB
NASM
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

TITLE PROFIL - MS-DOS Profile program
;Profiler for MS-DOS 1.25 2.00
;
; Lots of stuff stolen from debug.
; User provides # of paragraphs per bucket, program is cut up accordingly.
; User also specifies clock interval
;System calls
PRINTBUF EQU 9
SETDMA EQU 26
CREATE EQU 22
OPEN EQU 15
CLOSE EQU 16
GETBUF EQU 10
BLKWRT EQU 40
BLKRD EQU 39
OUTCH EQU 2
SETBASE EQU 38
FCB EQU 5CH
BUFLEN EQU 80
; FCB offsets
RR EQU 33
RECLEN EQU 14
FILELEN EQU 16
;Segments in load order
CODE SEGMENT PUBLIC
CODE ENDS
DATA SEGMENT BYTE
DATA ENDS
INIT SEGMENT BYTE
INIT ENDS
DG GROUP CODE,DATA,INIT
;The data segment
DATA SEGMENT BYTE
ORG 0
ENDMES DB 13,10,"Program terminated normally",13,10,"$"
ABORTMES DB 13,10,"Program aborted",13,10,"$"
TOOBIG DB "Program too big",13,10,"$"
EXEBAD DB "EXE file bad",13,10,"$"
OUT_FCB LABEL WORD
DB 0
OUTNAME DB " PRF"
DB 30 DUP(0)
DB 80H DUP(?)
STACK LABEL WORD
BYTEBUF DB BUFLEN DUP(?) ;Processed input queue
AXSAVE DW ? ;See interrupt routine
BXSAVE DW ? ; " " "
PROG_AREA DW ? ;Segment of program start
;EXE file header
RUNVAR LABEL WORD
RELPT DW ?
LASTP LABEL WORD
RELSEG DW ?
PSIZE LABEL WORD
PAGES DW ?
RELCNT DW ?
HEADSIZ DW ?
DW ?
LOADLOW DW ?
PROG_SS LABEL WORD ;Program stack seg
INITSS DW ?
PROG_SP LABEL WORD ;Program SP
INITSP DW ?
DW ?
PROG_ENTRY EQU THIS DWORD
PROG_RA LABEL WORD ;Program start offset
INITIP DW ?
PROG_SA LABEL WORD ;Program start segment (may be different from PROG_AREA)
INITCS DW ?
RELTAB DW ?
RUNVARSIZ EQU $-RUNVAR
EXEFILE DB 0 ;Flag to indicate EXE file
DRV_VALID DW ? ;Init for AX register
OUTPUT_DATA LABEL WORD ;Start of the profile data
CLOCK_GRAIN DW ? ;Clock interval micro-seconds
BUCKET_NUM DW ? ;Number of buckets
BUCKET_SIZE DW ? ;Paragraphs per bucket
PROG_LOW_PA DW ? ;Start of program (PARA #)
PROG_HIGH_PA DW ? ;End of program (PARA #)
DOS_PA DW ? ;IO-DOS PARA boundry
HIT_IO DW 0 ;IO bucket
HIT_DOS DW 0 ;DOS bucket
HIT_HIGH DW 0 ;Above Program bucket
NUM_DATA_WORDS EQU ($-OUTPUT_DATA)/2 ;Number of word items
BUCKET LABEL WORD ;Bucket count area
;The following data will be overwritten when the buckets are initialized
LINEBUF DB BUFLEN,1,0DH ;Raw input buffer
DB BUFLEN DUP(?)
NOFILE DB "File not found",13,10,"$"
OUTERR DB "Cannot open output file",13,10,"$"
GRAIN_PROMPT DB "Sample time (micro-sec) >= 60 ? ","$"
SIZE_PROMPT DB "Number of paragraphs (16 bytes) per bucket? ","$"
PARAM_PROMPT DB "Parameters to program? ","$"
DATA ENDS
;The resident code portion
CODE SEGMENT PUBLIC
ASSUME CS:DG,DS:DG,ES:DG,SS:DG
;The clock interrupt routine
PUBLIC CLK_INTER
;Stuff provided by external clock handler routine
EXTRN CLOCKON:NEAR,CLOCKOFF:NEAR,LEAVE_INT:NEAR
ORG 100H
START:
CLD
MOV SP,OFFSET DG:STACK ;Use internal stack
CALL SETUP
;The following setup stuff cannot be done in SETUP because we're probably
; overwritting the INIT area
MOV DX,[PROG_AREA]
MOV AH,SETBASE
INT 21H ;Set base for program
MOV ES,[PROG_AREA]
PUSH SI ;Points to BYTEBUF
MOV DI,81H ;Set unformatted params
COMTAIL:
LODSB
STOSB
CMP AL,13
JNZ COMTAIL
SUB DI,82H ;Figure length
XCHG AX,DI
MOV BYTE PTR ES:[80H],AL
POP SI
MOV DI,FCB ;First param
MOV AX,2901H
INT 21H
MOV BYTE PTR [DRV_VALID],AL
MOV AX,2901H
MOV DI,6CH ;Second param
INT 21H
MOV BYTE PTR [DRV_VALID+1],AL
MOV AX,ES ;Prog segment to AX
MOV DX,[PROG_RA] ;Offset
CMP [EXEFILE],1
JZ EXELOAD ;EXE file
JMP BINFIL ;Regular file (.COM)
EXELOAD:
MOV AX,[HEADSIZ] ;Size of header in paragraphs
ADD AX,31
MOV CL,4
ROL AX,CL ;Size in bytes
MOV BX,AX
AND AX,0FE00H
AND BX,0FH
MOV WORD PTR DS:[FCB+RR],AX ;Position in file of program
MOV WORD PTR DS:[FCB+RR+2],BX ;Record size
MOV DX,[PAGES] ;Size in 512 byte blocks
DEC DX
XCHG DH,DL
ROL DX,1
MOV DI,DX
MOV SI,DX
AND DI,0FE00H
AND SI,1FFH
SUB DI,AX
SBB SI,BX
MOV AX,[LASTP]
OR AX,AX
JNZ PARTP
MOV AX,200H
PARTP:
ADD DI,AX
ADC SI,0
MOV AX,DI
ADD AX,15
AND AL,0F0H
OR AX,SI
MOV CL,4
ROR AX,CL
XCHG AX,CX
MOV BX,[PROG_AREA]
ADD BX,10H
MOV AX,WORD PTR DS:[2]
SUB AX,CX
MOV DX,OFFSET DG:TOOBIG
JB ERROR
CMP BX,AX
JA ERROR
CMP [LOADLOW],-1
JNZ LOADEXE
XCHG AX,BX
LOADEXE:
MOV BP,AX
XOR DX,DX
CALL READ
JC HAVEXE
BADEXE:
MOV DX,OFFSET DG:EXEBAD
ERROR:
MOV AH,PRINTBUF ;Print the message in DX
INT 21H
INT 20H ;Exit
HAVEXE:
MOV AX,[RELTAB] ;Get position of relocation table
MOV WORD PTR DS:[FCB+RR],AX
MOV WORD PTR DS:[FCB+RR+2],0
MOV DX,OFFSET DG:RELPT ;Four byte buffer
MOV AH,SETDMA
INT 21H
CMP [RELCNT],0
JZ NOREL
RELOC:
MOV AH,BLKRD
MOV DX,FCB
MOV CX,4
INT 21H ;Read in one relocation pointer
OR AL,AL
JNZ BADEXE
MOV DI,[RELPT] ;Pointer offset
MOV AX,[RELSEG] ;pointer segment
ADD AX,BP ;Bias with actual load segment
MOV ES,AX
ADD ES:[DI],BP ;Relocate
DEC [RELCNT]
JNZ RELOC
NOREL:
ADD [INITSS],BP
ADD [INITCS],BP
JMP SHORT PROGGO
BINFIL:
MOV WORD PTR DS:[FCB+RECLEN],1
MOV SI,-1
MOV DI,SI
CALL READ
MOV ES,[PROG_SA] ;Prog segment to ES
MOV AX,WORD PTR ES:[6]
MOV [PROG_SP],AX ;Default SP for non EXE files
DEC AH
MOV WORD PTR ES:[6],AX ;Fix size
PROGGO:
PUSH DS
MOV AX,[PROG_AREA]
MOV DS,AX
MOV DX,80H
MOV AH,SETDMA
INT 21H ;Set default disk transfer address
POP DS
MOV BX,[BUCKET_NUM]
SHL BX,1 ;Mult by 2 to get #bytes in bucket area
CLEAR:
MOV BUCKET[BX],0 ;Zero counts
SUB BX,2
JGE CLEAR
MOV DX,[CLOCK_GRAIN]
PUSH DS
POP ES
CLI ;Don't collect data yet
CALL CLOCKON ;Set the interrupt
MOV SI,[PROG_RA]
MOV DI,[PROG_AREA]
MOV BX,[PROG_SS]
MOV CX,[PROG_SP]
MOV AX,[DRV_VALID]
MOV DX,[PROG_SA]
MOV SS,BX
MOV SP,CX
XOR CX,CX
PUSH CX ;0 on prog stack
PUSH DX
PUSH SI
MOV DS,DI ;Set up segments
MOV ES,DI
STI ;Start collecting data
XXX PROC FAR
RET ;Hop to program
XXX ENDP
READ:
; AX:DX is disk transfer address (segment:offset)
; SI:DI is 32 bit length
RDLOOP:
MOV BX,DX
AND DX,000FH
MOV CL,4
SHR BX,CL
ADD AX,BX
PUSH AX
PUSH DX
PUSH DS
MOV DS,AX
MOV AH,SETDMA
INT 21H
POP DS
MOV DX,FCB
MOV CX,0FFF0H ;Keep request in segment
OR SI,SI ;Need > 64K?
JNZ BIGRD
MOV CX,DI ;Limit to amount requested
BIGRD:
MOV AH,BLKRD
INT 21H
SUB DI,CX ;Subtract off amount done
SBB SI,0 ;Ripple carry
CMP AL,1 ;EOF?
POP DX
POP AX ;Restore transfer address
JZ RET10
ADD DX,CX ;Bump transfer address by last read
MOV BX,SI
OR BX,DI ;Finished with request
JNZ RDLOOP
RET10: STC
RET
;Return here on termination or abort
TERMINATE:
CLI ;Stop collecting data
MOV DX,OFFSET DG:ENDMES
JMP SHORT WRITEOUT
ABORT:
CLI ;Stop collecting data
MOV DX,OFFSET DG:ABORTMES
WRITEOUT:
MOV AX,CS
MOV DS,AX
MOV SS,AX
MOV SP,OFFSET DG:STACK ;Use internal stack
PUSH DX
CALL CLOCKOFF ;Restore original clock routine
STI ;Back to normal clock
POP DX
MOV AH,PRINTBUF
INT 21H ;Apropriate termination message
MOV [OUT_FCB+14],2 ;Word size records
MOV DX,OFFSET DG:OUTPUT_DATA
MOV AH,SETDMA
INT 21H ;Set the transfer address
MOV CX,NUM_DATA_WORDS
ADD CX,[BUCKET_NUM]
MOV DX,OFFSET DG:OUT_FCB
MOV AH,BLKWRT
INT 21H ;Write out data
MOV DX,OFFSET DG:OUT_FCB
MOV AH,CLOSE
INT 21H
INT 20H ;Exit
;The clock interrupt routine
CLK_INTER PROC NEAR
CLI
PUSH DS
PUSH CS
POP DS ;Get profile segment
MOV [AXSAVE],AX
MOV [BXSAVE],BX
POP AX ;old DS
MOV BX,OFFSET DG:LEAVE_INT
PUSH BX
PUSH AX
PUSH ES
PUSH [AXSAVE]
PUSH [BXSAVE]
PUSH CX
PUSH DX
;Stack looks like this
;
; +18 OLDFLAGS
; +16 OLDCS
; +14 OLDIP
; +12 RETURN TO LEAVE_INT
; +10 OLDDS
; +8 OLDES
; +6 OLDAX
; +4 OLDBX
; +2 OLDCX
;SP-> OLDDX
MOV BX,SP
LES BX,DWORD PTR SS:[BX+14] ;Get CS:IP
MOV AX,BX
MOV CL,4
SHR AX,CL
MOV CX,ES
ADD AX,CX ;Paragraph of CS:IP
CMP AX,[DOS_PA] ;Below DOS?
JB IOHIT
CMP AX,[PROG_LOW_PA] ;Below program?
JB DOSHIT
CMP AX,[PROG_HIGH_PA] ;Above program?
JAE MISSH
SUB AX,[PROG_LOW_PA] ;Paragraph offset
XOR DX,DX
DIV [BUCKET_SIZE]
MOV BX,AX
SHL BX,1 ;Mult by 2 to get byte offset
INC BUCKET[BX]
JMP SHORT DONE
IOHIT:
INC [HIT_IO]
JMP SHORT DONE
DOSHIT:
INC [HIT_DOS]
JMP SHORT DONE
MISSH:
INC [HIT_HIGH]
DONE:
POP DX
POP CX
POP BX
POP AX
POP ES
POP DS
STI
RET ;To LEAVE_INT
CLK_INTER ENDP
CODE ENDS
;The init segment contains code to process input parameters
; It will be blasted as soon as the program to be run is read in
; And/or the bucket area is initialized
INIT SEGMENT BYTE
ORG 0
SETUP:
MOV DX,FCB
MOV AH,OPEN
INT 21H ;Open program file
AND AL,AL
JZ OPENOK
MOV DX,OFFSET DG:NOFILE
JMP ERROR
OPENOK:
XOR BX,BX
MOV WORD PTR DS:[FCB+RR],BX
MOV WORD PTR DS:[FCB+RR+2],BX ;RR to 0
MOV SI,FCB
MOV DI,OFFSET DG:OUT_FCB
MOV CX,4
REP MOVSW
MOVSB ;Transfer drive spec and file to output
MOV DX,OFFSET DG:OUT_FCB
MOV AH,CREATE
INT 21H ;Try to create the output file
AND AL,AL
JZ GETSIZE
MOV DX,OFFSET DG:OUTERR
JMP ERROR
GETSIZE: ;Get bucket size
MOV DX,OFFSET DG:SIZE_PROMPT
MOV AH,PRINTBUF
INT 21H
CALL INBUF
CALL SCANB
JZ GETSIZE ;SCANB went to CR
XOR BX,BX
INC BX ;Size >=1
CALL GETNUM
JC GETSIZE ;Bad number
MOV [BUCKET_SIZE],DX
CMP WORD PTR DS:[FCB+9],5800H+"E" ;"EX"
JNZ NOTEXE
CMP BYTE PTR DS:[FCB+11],"E"
JNZ NOTEXE
LOADEXEHEAD: ;Load the EXE header
MOV [EXEFILE],1
MOV DX,OFFSET DG:RUNVAR ;Read header in here
MOV AH,SETDMA
INT 21H
MOV CX,RUNVARSIZ
MOV DX,FCB
MOV WORD PTR DS:[FCB+RECLEN],1
OR AL,AL
MOV AH,BLKRD
INT 21H
CMP [RELPT],5A4DH ;Magic number
JZ EXEOK
JMP BADEXE
EXEOK:
MOV AX,[PAGES] ;Size of file in 512 byte blocks
MOV CL,5
SHL AX,CL ;Size in paragraphs
JMP SHORT SETBUCKET
NOTEXE:
MOV AX,WORD PTR DS:[FCB+FILELEN]
MOV DX,WORD PTR DS:[FCB+FILELEN+2] ;Size of file in bytes DX:AX
ADD AX,15
ADC DX,0 ;Round to PARA
MOV CL,4
SHR AX,CL
AND AX,0FFFH
MOV CL,12
SHL DX,CL
AND DX,0F000H
OR AX,DX ;Size in paragraphs to AX
MOV [PROG_RA],100H ;Default offset
SETBUCKET:
PUSH AX ;Save size
XOR DX,DX
DIV [BUCKET_SIZE]
INC AX ;Round up
MOV [BUCKET_NUM],AX
MOV BX,OFFSET DG:BUCKET
SHL AX,1 ;Number of bytes in bucket area
ADD AX,BX ;Size of profil in bytes
ADD AX,15 ;Round up to PARA boundry
MOV CL,4
SHR AX,CL ;Number of paragraphs in profil
INC AX ;Insurance
MOV BX,CS
ADD AX,BX
MOV [PROG_AREA],AX
CMP [EXEFILE],1
JZ SETBOUNDS
MOV AX,[PROG_AREA] ;Set up .COM segments
MOV [PROG_SS],AX
MOV [PROG_SA],AX
SETBOUNDS: ;Set the sample window
MOV BX,10H ;Get start offset
ADD BX,[PROG_AREA] ;PARA # of start
MOV [PROG_LOW_PA],BX
POP AX ;Recall size of PROG in paragraphs
ADD BX,AX
MOV [PROG_HIGH_PA],BX
SETDOS:
XOR DX,DX
MOV ES,DX ;look in interrupt area
MOV DX,WORD PTR ES:[82H] ;From int #20
MOV [DOS_PA],DX
PUSH DS
POP ES
GETGRAIN: ;Get sample interval
MOV DX,OFFSET DG:GRAIN_PROMPT
MOV AH,PRINTBUF
INT 21H
CALL INBUF
CALL SCANB
JZ GETGRAIN ;SCANB went to CR
MOV BX,60 ;Grain >=60
CALL GETNUM
JC GETGRAIN ;Bad number
MOV [CLOCK_GRAIN],DX
MOV DX,OFFSET DG:PARAM_PROMPT
MOV AH,PRINTBUF
INT 21H
CALL INBUF ;Get program parameters
MOV AX,2522H ;Set vector 22H
MOV DX,OFFSET DG:TERMINATE
INT 21H
MOV AL,23H ;Set vector 23H
MOV DX,OFFSET DG:ABORT
INT 21H
RET ;Back to resident code
GETNUM: ;Get a number, DS:SI points to buffer, carry set if bad
XOR DX,DX
MOV CL,0
LODSB
NUMLP:
SUB AL,"0"
JB NUMCHK
CMP AL,9
JA NUMCHK
CMP DX,6553
JAE BADNUM
MOV CL,1
PUSH BX
MOV BX,DX
SHL DX,1
SHL DX,1
ADD DX,BX
SHL DX,1
CBW
POP BX
ADD DX,AX
LODSB
JMP NUMLP
NUMCHK:
CMP CL,0
JZ BADNUM
CMP BX,DX
JA BADNUM
CLC
RET
BADNUM:
STC
RET
INBUF: ;Read in from console, SI points to start on exit
MOV AH,GETBUF
MOV DX,OFFSET DG:LINEBUF
INT 21H
MOV SI,2 + OFFSET DG:LINEBUF
MOV DI,OFFSET DG:BYTEBUF
CASECHK:
LODSB
CMP AL,'a'
JB NOCONV
CMP AL,'z'
JA NOCONV
ADD AL,"A"-"a" ;Convert to upper case
NOCONV:
STOSB
CMP AL,13
JZ INDONE
CMP AL,'"'
JNZ QUOTSCAN
CMP AL,"'"
JNZ CASECHK
QUOTSCAN:
MOV AH,AL
KILLSTR:
LODSB
STOSB
CMP AL,13
JZ INDONE
CMP AL,AH
JNZ KILLSTR
JMP SHORT CASECHK
INDONE:
MOV SI,OFFSET DG:BYTEBUF
;Output CR/LF
CRLF:
MOV AL,13
CALL OUT
MOV AL,10
OUT:
PUSH AX
PUSH DX
AND AL,7FH
XCHG AX,DX
MOV AH,OUTCH
INT 21H
POP DX
POP AX
RET
SCANB: ;Scan to first non-blank
PUSH AX
SCANNEXT:
LODSB
CMP AL," "
JZ SCANNEXT
CMP AL,9
JZ SCANNEXT
DEC SI
POP AX
EOLCHK:
CMP BYTE PTR[SI],13
RET
INIT ENDS
END START