Added files

This commit is contained in:
Broken Pipe
2026-01-04 13:54:33 +10:00
parent 435fe91271
commit 62b6d9772b
57 changed files with 9501 additions and 3 deletions
BIN
View File
Binary file not shown.
+501
View File
@@ -0,0 +1,501 @@
TITLE HRDDRV.SYS for the ALTOS ACS-86C.
; Hard Disk Drive for Version 2.x of MSDOS.
; Constants for commands in Altos ROM.
ROM_CONSTA EQU 01 ;Return status AL of console selected in CX.
ROM_CONIN EQU 02 ;Get char. from console in CX to AL
ROM_CONOUT EQU 03 ;Write char. in DL to console in CX.
ROM_PMSG EQU 07 ;Write string ES:DX to console in CX.
ROM_DISKIO EQU 08 ;Perform disk I/O from IOPB in ES:CX.
ROM_INIT EQU 10 ;Returns boot console and top memory ES:DX.
CODE SEGMENT
ASSUME CS:CODE,DS:CODE,ES:CODE,SS:CODE
ORG 0 ;Starts at an offset of zero.
PAGE
SUBTTL Device driver tables.
;-----------------------------------------------+
; DWORD pointer to next device | 1 word offset.
; (-1,-1 if last device) | 1 word segement.
;-----------------------------------------------+
; Device attribute WORD ; 1 word.
; Bit 15 = 1 for chacter devices. ;
; 0 for Block devices. ;
; ;
; Charcter devices. (Bit 15=1) ;
; Bit 0 = 1 current sti device. ;
; Bit 1 = 1 current sto device. ;
; Bit 2 = 1 current NUL device. ;
; Bit 3 = 1 current Clock device. ;
; ;
; Bit 13 = 1 for non IBM machines. ;
; 0 for IBM machines only. ;
; Bit 14 = 1 IOCTL control bit. ;
;-----------------------------------------------+
; Device strategy pointer. ; 1 word offset.
;-----------------------------------------------+
; Device interrupt pointer. ; 1 word offset.
;-----------------------------------------------+
; Device name field. ; 8 bytes.
; Character devices are any valid name ;
; left justified, in a space filled ;
; field. ;
; Block devices contain # of units in ;
; the first byte. ;
;-----------------------------------------------+
DSKDEV: ;Header for hard disk driver.
DW -1,-1 ;Last device
DW 2000H ;Is a block device
DW STRATEGY
DW DSK_INT
MEMMAX DB 1 ;Number of Units
PAGE
SUBTTL Dispatch tables for each device.
DSK_TBL:DW DSK_INI ;0 - Initialize Driver.
DW MEDIAC ;1 - Return current media code.
DW GET_BPB ;2 - Get Bios Parameter Block.
DW CMDERR ;3 - Reserved. (currently returns error)
DW DSK_RED ;4 - Block read.
DW BUS_EXIT ;5 - (Not used, return busy flag)
DW EXIT ;6 - Return status. (Not used)
DW EXIT ;7 - Flush input buffer. (Not used.)
DW DSK_WRT ;8 - Block write.
DW DSK_WRV ;9 - Block write with verify.
DW EXIT ;10 - Return output status.
DW EXIT ;11 - Flush output buffer. (Not used.)
DW EXIT ;12 - IO Control.
PAGE
SUBTTL Strategy and Software Interrupt routines.
;Define offsets for io data packet
IODAT STRUC
CMDLEN DB ? ;LENGTH OF THIS COMMAND
UNIT DB ? ;SUB UNIT SPECIFIER
CMD DB ? ;COMMAND CODE
STATUS DW ? ;STATUS
DB 8 DUP (?)
MEDIA DB ? ;MEDIA DESCRIPTOR
TRANS DD ? ;TRANSFER ADDRESS
COUNT DW ? ;COUNT OF BLOCKS OR CHARACTERS
START DW ? ;FIRST BLOCK TO TRANSFER
IODAT ENDS
PTRSAV DD 0 ;Strategy pointer save.
;
; Simplistic Strategy routine for non-multi-Tasking system.
;
; Currently just saves I/O packet pointers in PTRSAV for
; later processing by the individual interrupt routines.
;
STRATP PROC FAR
STRATEGY:
MOV WORD PTR CS:[PTRSAV],BX
MOV WORD PTR CS:[PTRSAV+2],ES
RET
STRATP ENDP
;
; Ram memory driver interrupt routine for processing I/O packets.
;
DSK_INT:
PUSH SI ;Save SI from caller.
MOV SI,OFFSET DSK_TBL
;
; Common program for handling the simplistic I/O packet
; processing scheme in MSDOS 2.0
;
ENTRY: PUSH AX ;Save all nessacary registers.
PUSH CX
PUSH DX
PUSH DI
PUSH BP
PUSH DS
PUSH ES
PUSH BX
LDS BX,CS:[PTRSAV] ;Retrieve pointer to I/O Packet.
MOV AL,[BX.UNIT] ;AL = Unit code.
MOV AH,[BX.MEDIA] ;AH = Media descriptor.
MOV CX,[BX.COUNT] ;CX = Contains byte/sector count.
MOV DX,[BX.START] ;DX = Starting Logical sector.
XCHG DI,AX ;Save Unit and Media Temporarily.
MOV AL,[BX.CMD] ;Retrieve Command type. (1 => 11)
XOR AH,AH ;Clear upper half of AX for calculation.
ADD SI,AX ;Compute entry pointer in dispatch table.
ADD SI,AX
CMP AL,11 ;Verify that not more than 11 commands.
JA CMDERR ;Ah, well, error out.
XCHG AX,DI
LES DI,[BX.TRANS] ;DI contains addess of Transfer address.
;ES contains segment.
PUSH CS
POP DS ;Data segment same as Code segment.
JMP [SI] ;Perform I/O packet command.
PAGE
SUBTTL Common error and exit points.
BUS_EXIT: ;Device busy exit.
MOV AH,00000011B ;Set busy and done bits.
JMP SHORT EXIT1
CMDERR: MOV AL,3 ;Set unknown command error #.
;
; Common error processing routine.
; AL contains actual error code.
;
; Error # 0 = Write Protect violation.
; 1 = Unkown unit.
; 2 = Drive not ready.
; 3 = Unknown command in I/O packet.
; 4 = CRC error.
; 5 = Bad drive request structure length.
; 6 = Seek error.
; 7 = Unknown media discovered.
; 8 = Sector not found.
; 9 = Printer out of paper.
; 10 = Write fault.
; 11 = Read fault.
; 12 = General failure.
;
ERR_EXIT:
MOV AH,10000001B ;Set error and done bits.
STC ;Set carry bit also.
JMP SHORT EXIT1 ;Quick way out.
EXITP PROC FAR ;Normal exit for device drivers.
EXIT: MOV AH,00000001B ;Set done bit for MSDOS.
EXIT1: LDS BX,CS:[PTRSAV]
MOV [BX.STATUS],AX ;Save operation compete and status.
POP BX ;Restore registers.
POP ES
POP DS
POP BP
POP DI
POP DX
POP CX
POP AX
POP SI
RET ;RESTORE REGS AND RETURN
EXITP ENDP
PAGE
subttl Hard Disk drive control.
;
; Read command = 09 hex.
; Write command = 02 hex.
; Seek command = 10 hex.
; Recal command = 20 hex.
; Rezero command = 40 hex.
; Reset command = 80 hex.
;
; Busy = 01 hex.
; Operation Complete = 02 hex.
; Bad Sector = 04 hex.
; Record Not found = 08 hex.
; CRC error = 10 hex.
; (not used) = 20 hex.
; Write fault = 40 hex.
; Drive Ready = 80 hex.
;
hd_read equ 09h
hd_writ equ 02h
hd_wmsk equ 5dh
hd_rmsk equ 9ch
page
SUBTTL Altos monitor ram and 8089 IOPB structures.
;
; Structure to reference 8089 and ROM command table.
;
SIOPB STRUC
DB 4 DUP (?) ;Monitor Use Only
OPCODE DB ? ;I/O operation code.
DRIVE DB ? ;Logical drive spec.
TRACK DW ? ;Logical track number.
HEAD DB ? ;Logical head number.
SECTOR DB ? ;Logical sector to start with.
SCOUNT DB ? ;Number of logical sectors in buffer.
RETCODE DB ? ;Error code after masking.
RETMASK DB ? ;Error mask.
RETRIES DB ? ;Number of retries before error exit.
DMAOFF DW ? ;Buffer offset address.
DMASEG DW ? ;Buffer segment.
SECLENG DW ? ;Sector Length.
DB 6 DUP (?) ;8089 use only.
SIOPB ENDS
IOPB SIOPB <,0,0,0,0,0,0,0,0,0,0,0,0,>
PAGE
SUBTTL Common Drive parameter block definitions on Altos.
DBP STRUC
JMPNEAR DB 3 DUP (?) ;Jmp Near xxxx for boot.
NAMEVER DB 8 DUP (?) ;Name / Version of OS.
;------- Start of Drive Parameter Block.
SECSIZE DW ? ;Sector size in bytes. (dpb)
ALLOC DB ? ;Number of sectors per alloc. block. (dpb)
RESSEC DW ? ;Reserved sectors. (dpb)
FATS DB ? ;Number of FAT's. (dpb)
MAXDIR DW ? ;Number of root directory entries. (dpb)
SECTORS DW ? ;Number of sectors per diskette. (dpb)
MEDIAID DB ? ;Media byte ID. (dpb)
FATSEC DW ? ;Number of FAT Sectors. (dpb)
;------- End of Drive Parameter Block.
SECTRK DW ? ;Number of Sectors per track.
HEADS DW ? ;Number of heads per cylinder.
HIDDEN DW ? ;Number of hidden sectors.
DBP ENDS
HDDRIVE DBP <,,512,4,0,2,256,4000,0F5H,3,12,4,0>
INI_TAB DW OFFSET HDDRIVE.SECSIZE
PAGE
SUBTTL Media check routine
;
; Media check routine.
; On entry:
; AL = memory driver unit number.
; AH = media byte
; On exit:
;
; [MEDIA FLAG] = -1 (FF hex) if disk is changed.
; [MEDIA FLAG] = 0 if don't know.
; [MEDIA FLAG] = 1 if not changed.
;
MEDIAC: LDS BX,CS:[PTRSAV]
MOV BYTE PTR [BX.TRANS],1
JMP EXIT
PAGE
SUBTTL Build and return Bios Parameter Block for a diskette.
;
; Build Bios Parameter Blocks.
;
; On entry: ES:BX contains the address of a scratch sector buffer.
; AL = Unit number.
; AH = Current media byte.
;
; On exit: Return a DWORD pointer to the associated BPB
; in the Request packet.
;
GET_BPB:
MOV SI,OFFSET HDDRIVE+11
LDS BX,CS:[PTRSAV]
MOV WORD PTR [BX.COUNT],SI
MOV WORD PTR [BX.COUNT+2],CS
JMP EXIT
PAGE
SUBTTL MSDOS 2.x Disk I/O drivers.
;
; Disk READ/WRITE functions.
;
; On entry:
; AL = Disk I/O driver number
; AH = Media byte.
; ES = Disk transfer segment.
; DI = Disk transfer offset in ES.
; CX = Number of sectors to transfer
; DX = Logical starting sector.
;
; On exit:
; Normal exit through common exit routine.
;
; Abnormal exit through common error routine.
;
DSK_RED:
MOV AH,HD_READ
JMP SHORT DSK_COM
DSK_WRV:
DSK_WRT:
MOV AH,HD_WRIT
DSK_COM:
MOV SI,OFFSET HDDRIVE ;Keeps code size down.
MOV [IOPB.DMASEG],ES
MOV [IOPB.DMAOFF],DI
MOV DI,[SI.SECSIZE]
MOV [IOPB.SECLENG],DI
MOV [IOPB.RETRIES],1
MOV [IOPB.RETMASK],05DH ;Error return mask.
MOV [IOPB.OPCODE],AH
MOV [IOPB.DRIVE],4 ;Drive 4 is only available.
ADD DX,[SI.HIDDEN] ;Account for invisible sectors.
MOV BP,CX ;Save number of sectors to R/W
DSK_IO1:
PUSH DX ;Save starting sector.
MOV AX,DX
MOV DX,0 ;32 bit divide coming up.
MOV CX,[SI.SECTRK]
DIV CX ;Get track+head and start sector.
MOV [IOPB.SECTOR],DL ;Starting sector.
MOV BL,DL ;Save starting sector for later.
MOV DX,0
MOV CX,[SI.HEADS]
DIV CX ;Compute head we are on.
MOV [IOPB.HEAD],DL
MOV [IOPB.TRACK],AX ;Track to read/write.
MOV AX,[SI.SECTRK] ;Now see how many sectors
INC AL ; we can burst read.
SUB AL,BL ;BL is the starting sector.
MOV AH,0
POP DX ;Retrieve logical sector start.
CMP AX,BP ;See if on last partial track+head.
JG DSK_IO2 ;Yes, on last track+head.
SUB BP,AX ;No, update number of sectors left.
ADD DX,AX ;Update next starting sector.
JMP SHORT DSK_IO3
DSK_IO2:MOV AX,BP ;Only read enough of sector
MOV BP,0 ;to finish buffer and clear # left.
DSK_IO3:MOV [IOPB.SCOUNT],AL
MOV DI,AX ;Save number sectors for later.
MOV BX,ROM_DISKIO
MOV CX,OFFSET IOPB
PUSH CS
POP ES
CALL ROM_CALL ;Do disk operation.
MOV AL,[IOPB.RETCODE] ;Get error code.
OR AL,AL
JNZ DERROR
MOV AX,DI ;Retrieve number of sectors read.
MOV CX,[SI.SECSIZE] ;Number of bytes per sector.
PUSH DX
MUL CX
POP DX
TEST AL,0FH ;Make sure no strange sizes.
JNZ SERR1
MOV CL,4
SHR AX,CL ;Convert number of bytes to para.
ADD AX,[IOPB.DMASEG]
MOV [IOPB.DMASEG],AX
OR BP,BP
JNZ DSK_IO1 ;Still more to do.
MOV AL,0
JMP EXIT ;All done.
SERR1: MOV AL,12
JMP ERR_EXIT
PAGE
SUBTTL Disk Error processing.
;
; Disk error routine.
;
DERROR:
LDS BX,CS:[PTRSAV]
MOV [BX.COUNT],0
PUSH CS
POP DS
MOV BL,-1
MOV AH,AL
MOV BH,14 ;Lenght of table.
MOV SI,OFFSET DERRTAB
DERROR2:INC BL ;Increment to next error code.
LODS BYTE PTR CS:[SI]
CMP AH,AL ;See if error code matches disk status.
JZ DERROR3 ;Got the right error, exit.
DEC BH
JNZ DERROR2 ;Keep checking table.
MOV BL,12 ;Set general type of error.
DERROR3:MOV AL,BL ;Now we've got the code.
JMP ERR_EXIT
DERRTAB DB 00H ; 0. Write protect error
DB 00H ; 1. Unknown unit.
DB 00H ; 2. Not ready error.
DB 00H ; 3. Unknown command.
DB 10H ; 4. CRC error
DB 00H ; 5. Bad drive request.
DB 00H ; 6. Seek error
DB 00H ; 7. Unknown media.
DB 08H ; 8. Sector not found
DB 00H ; 9. (Not used.)
DB 40H ;10. Write fault.
DB 04H ;11. Read fault.
DB 01H ;12. General type of failure.
PAGE
SUBTTL Common ROM call routine.
;
; Save all registers except CX, BX and AX.
ROMRTN DD 0FE000000H ;Main ROM entry point.
ROM_CALL:
PUSH DI
PUSH SI
PUSH BP
PUSH DX
PUSH ES
CALL CS:DWORD PTR [ROMRTN]
POP ES
POP DX
POP BP
POP SI
POP DI
RET
PAGE
SUBTTL Hard Disk Drive initalization routine.
DSK_INI:
LDS BX,CS:[PTRSAV]
MOV BYTE PTR [BX.MEDIA],1
MOV WORD PTR [BX.TRANS],OFFSET DSK_INI
MOV WORD PTR [BX.TRANS+2],CS
MOV WORD PTR [BX.COUNT],OFFSET INI_TAB
MOV WORD PTR [BX.COUNT+2],CS
JMP EXIT
CODE ENDS
END

BIN
View File
Binary file not shown.
+705
View File
@@ -0,0 +1,705 @@
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
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+35
View File
@@ -0,0 +1,35 @@
PROHST is a preliminary version of a utility to process the profile
file produced by the PROFIL utility of MSDOS.
Those of you familiar with MS-Pascal or MS-Fortran will have little
difficulty in understanding how the parameters work. There are three,
the .PRF filename, an optional histogram file (default extension .HST,
default name same as the .PRF file) and an optional link map. If the
link map was produced with the line number options PROHST will try
and relate buckets to line numbers. Otherwise, it will relate it to
module offsets. If you specify no map file (the default), addresses
relative to the start of the program will be used. The default extension
for the map file is .MAP.
a:prohst f;
this will produce a histogram for the file f.prf in f.hst and no map file
will be assumed.
a:prohst f,,;
this will produce a histogram for f.prf in f.hst and expects a f.map file.
a:prohst f,g,k
this produces a histogram for f.prf in g.hst and expects a map file k.map.
Note that if you select the map option with line numbers, the program will
appear to be looping. Never fear, go and have lunch or some other time
consuming pastime, and you will be suprised how long it took to produce
such a small file. Also, some of the line number/bucket correspondances are
not what they might be. Future version shoudl fix this. If you make a better
version, be sure to let me have a copy.
David Jones.

+403
View File
@@ -0,0 +1,403 @@
PROGRAM prohst(input,output);
{$debug- $line- $symtab+}
{**********************************************************************}
{* *}
{* prohst *}
{* *}
{* This program produces a histogram from the profile file produced *}
{* by the MS-DOS profile utility. It optionally reads the map file *}
{* generated when the program being profiled was linked, and writes *}
{* either the module address or, if available, the line number as *}
{* a prefix to the line of the graph which describes a particular *}
{* bucket. *}
{* *}
{* After using filbm (derived from the Pascal and Fortran front end *}
{* command scanner) to parse its parameters, prohst opens the map *}
{* file if specified, searches for the heading line, and then reads *}
{* the lines giving the names and positions of the modules. It builds *}
{* a linked list of module names and start addresses. *}
{* *}
{* It then reads the bucket file header and and bucket array elements *}
{* into a variable created on the heap. It simultaneously calculates *}
{* a normalization factor. It writes the profile listing header and *}
{* starts to write the profile lines. For each bucket, the address *}
{* is calculated. The first entry in the address/name linked list *}
{* is the lowest addressed module. This is initially the 'current' *}
{* module. The bucket address is compared with the current module *}
{* address. When it becomes the greater, the module name is written *}
{* to the listing and the next entry in the address/name list becomes *}
{* the current module. If line numbers are available, the bucket *}
{* address is also compared to the current line/address. This is *}
{* read and calculated directly from the file. Since there may be *}
{* more than one line per bucket, several entries may be read until *}
{* the addresses compare within the span of addresses encompassed by *}
{* a bucket (its 'width'). Note that the idiosyncracies of Pascal i/o *}
{* make it necessary to continually check for the end of the map file *}
{* and the complexity of this code is mainly due to an attempt to *}
{* make it reasonably resilient to changes in the format of the map *}
{* file. *}
{* *}
{**********************************************************************}
CONST
max_file = 32;
TYPE
filenam = LSTRING (max_file);
sets = SET OF 0..31;
address_pointer = ^address_record;
address_record = RECORD
next: address_pointer;
name: STRING (15);
address: WORD;
END;
VAR
i: INTEGER;
bucket: FILE OF WORD;
hist: TEXT;
map: TEXT;
first_address,
this_address: address_pointer;
current_base: WORD;
bucket_name,
hist_name,
map_name: filenam;
switches: sets;
line: LSTRING (100);
map_avail: BOOLEAN;
line_nos_avail: BOOLEAN;
norm: REAL;
per_cent: INTEGER;
real_bucket,
norm_bucket: REAL;
cum_per_cent,
real_per_cent: REAL;
bucket_num,
clock_grain,
bucket_size,
prog_low_pa,
prog_high_pa,
dos_pa,
hit_io,
hit_dos,
hit_high: WORD;
seg,
offset,
parcel: WORD;
address: WORD;
new_line_no,
line_no: WORD;
dummy : LSTRING (8);
name: LSTRING (20);
line_no_part: LSTRING (17);
start: LSTRING (6);
buckets: ^SUPER ARRAY [1 .. *] OF REAL;
this_bucket: WORD;
LABEL 1;
PROCEDURE filbm (VAR prffil, hstfil, mapfil: filenam;
VAR switches: sets); EXTERN;
FUNCTION realword (w: WORD): REAL;
BEGIN
IF ORD (w) < 0 THEN BEGIN
realword := FLOAT (maxint) + FLOAT (ORD (w - maxint));
END
ELSE BEGIN
realword := FLOAT (ORD(w));
END {IF};
END {realword};
PROCEDURE skip_spaces;
BEGIN
WHILE NOT eof(map) AND THEN map^ = ' ' DO BEGIN
get (map);
END {WHILE};
END {skip_spaces};
FUNCTION hex_char (ch: CHAR): WORD;
BEGIN
IF ch >= '0' AND THEN ch <= '9' THEN BEGIN
hex_char := WRD (ch) - WRD ('0');
END
ELSE IF ch >= 'A' AND THEN ch <= 'F' THEN BEGIN
hex_char := WRD (ch) - WRD ('A') + 10;
END
ELSE BEGIN
WRITELN ('Invalid hex character');
hex_char := 0;
END {IF};
END {hex_char};
FUNCTION read_hex (i :WORD): WORD;
VAR
hex_val: WORD;
BEGIN
skip_spaces;
hex_val := 0;
WHILE NOT eof (map) AND THEN i <> 0 DO BEGIN
hex_val := hex_val * 16 + hex_char (map^);
GET (map);
i := i - 1;
END {WHILE};
read_hex := hex_val;
END {read_hex};
FUNCTION read_h: WORD;
BEGIN
read_h := read_hex (4);
get (map);
get (map);
END;
FUNCTION read_word: WORD;
VAR
int_value: WORD;
BEGIN
int_value := 0;
IF NOT EOF (map) THEN BEGIN
READ (map, int_value);
END {IF};
read_word := int_value;
END {read_word};
FUNCTION map_digit: BOOLEAN;
BEGIN
map_digit := (map^ >= '0') OR (map^ <= '9');
END {map_digit};
BEGIN {prohst}
writeln (output, ' Profile Histogram Utility - Version 1.0');
writeln (output);
writeln (output, ' Copyright - Microsoft, 1983');
start := ' ';
filbm (bucket_name, hist_name, map_name, switches);
IF 31 IN switches THEN BEGIN
ABORT ('Map file must not be terminal', 0, 0);
END {IF};
IF NOT (28 IN switches) THEN BEGIN
ABORT ('No histogram file specified', 0, 0);
END {IF};
ASSIGN (bucket, bucket_name);
reset (bucket);
ASSIGN (hist, hist_name);
rewrite (hist);
map_avail := 29 IN switches;
line_nos_avail := FALSE;
IF map_avail THEN BEGIN
ASSIGN (map, map_name);
RESET (map);
WHILE NOT EOF (map) AND THEN start <> ' Start' DO BEGIN
READLN (map, start);
END {WHILE};
NEW (first_address);
this_address := NIL;
WHILE NOT EOF(map) DO BEGIN
READLN (map, line);
IF line.len < 6 OR ELSE line [2] < '0' OR ELSE
line [2] > '9' THEN BEGIN
BREAK;
END {IF};
IF this_address <> NIL THEN BEGIN
NEW (this_address^.next);
this_address := this_address^.next;
END
ELSE BEGIN
this_address := first_address;
END {IF};
this_address^.next := NIL;
this_address^.address := (hex_char (line [2]) * 4096) +
(hex_char (line [3]) * 256) +
(hex_char (line [4]) * 16) +
hex_char (line [5]);
FOR i := 1 TO 15 DO BEGIN
this_address^.name [i] := line [22 + i];
END {FOR};
END {WHILE};
WHILE NOT EOF (map) DO BEGIN
READLN (map, line_no_part);
IF line_no_part = 'Line numbers for ' THEN BEGIN
line_nos_avail := TRUE;
BREAK;
END {IF};
END {WHILE};
END {IF};
read (bucket, clock_grain, bucket_num, bucket_size,
prog_low_pa, prog_high_pa, dos_pa, hit_io, hit_dos, hit_high);
NEW (buckets,ORD (bucket_num));
norm := 0.0;
norm_bucket := 0.0;
FOR i := 1 TO ORD (bucket_num) DO BEGIN
read (bucket, this_bucket);
real_bucket := realword (this_bucket);
IF real_bucket > norm_bucket THEN BEGIN
norm_bucket := real_bucket;
END {IF};
norm := norm + real_bucket;
buckets^[i] := real_bucket;
END {FOR};
norm_bucket := 45.0/norm_bucket;
norm := 100.0/norm;
WRITELN (hist, 'Microsoft Profiler Output Listing');
WRITELN (hist);
WRITELN (hist, ORD (bucket_num):6, bucket_size:4,'-byte buckets.');
WRITELN (hist);
WRITELN (hist, 'Profile taken between ', prog_low_pa*16::16,
' and ', prog_high_pa*16::16, '.');
WRITELN (hist);
WRITELN (hist, 'DOS program address:', dos_pa::16);
WRITELN (hist);
WRITELN (hist, 'Number of hits in DOS: ', hit_dos:5,
' or ', realword (hit_dos) * norm:4:1, '%.');
WRITELN (hist, 'Number of hits in I/O: ', hit_io:5,
' or ', realword (hit_io) * norm:4:1, '%.');
WRITELN (hist, 'Number of hits high : ', hit_high:5,
' or ', realword (hit_high) * norm:4:1, '%.');
WRITELN (hist);
WRITELN (hist, ' Hits Addr. Line/ Cumul. % 0.0 ',
' ',
1.0/norm:1:1);
WRITELN (hist, ' Offset +----------------',
'----------------------------');
WRITELN (hist, name);
i := 0;
parcel := 0;
current_base := 0;
line_no := 0;
new_line_no := 0;
cum_per_cent := 0.0;
WHILE i < ORD (bucket_num) DO BEGIN
i := i + 1;
IF buckets^[i] < 0.9 THEN BEGIN
WRITELN (hist);
REPEAT
i := i + 1;
UNTIL (i = ORD (bucket_num)) OR ELSE buckets^[i] > 0.0;
END {IF};
address := bucket_size * (WRD (i) - 1);
WHILE map_avail AND THEN
address >= first_address^.address DO BEGIN
WRITELN (hist, ' ', first_address^.name);
current_base := first_address^.address;
first_address := first_address^.next;
END {WHILE};
WHILE line_nos_avail AND THEN NOT eof (map) AND THEN
address >= parcel DO BEGIN
skip_spaces;
WHILE (map^ < '0') OR (map^ > '9') DO BEGIN
IF EOF (map) THEN BEGIN
goto 1;
END {IF};
READLN (map);
skip_spaces;
END {WHILE};
line_no := new_line_no;
new_line_no := read_word;
seg := read_hex (4);
IF EOF (map) THEN BEGIN
GOTO 1;
END {IF};
IF map^ <> ':' THEN BEGIN
WRITELN ('Invalid map file');
END {IF};
get (map);
IF EOF (map) THEN BEGIN
GOTO 1;
END {IF};
offset := read_hex (3) + WRD (hex_char (map^) > 0);
get (map);
IF map^ <> 'H' THEN BEGIN
WRITELN ('Invalid map file');
END {IF};
IF EOF (map) THEN BEGIN
GOTO 1;
END {IF};
get (map);
parcel := seg + offset;
END {WHILE};
1: real_per_cent := buckets^[i] * norm;
cum_per_cent := cum_per_cent + real_per_cent;
per_cent := ROUND ( buckets^[i] * norm_bucket);
WRITE (hist, buckets^ [i]:6:0, ' ',
address*16:6:16);
IF line_no <> 0 THEN BEGIN
WRITE (hist, line_no:6);
line_no := 0;
END
ELSE IF map_avail AND THEN first_address <> NIL THEN BEGIN
WRITE (hist, ' #', address - first_address^.address:4:16);
END
ELSE BEGIN
WRITE (hist, ' ');
END {IF};
WRITELN (hist, ' ', cum_per_cent:5:1, ' ', real_per_cent:4:1, ' |',
'*': per_cent);
END {WHILE};
WRITELN (hist, ' +-----------------',
'------------------');
END.
+1377
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.
Binary file not shown.