Files
Digital-Research-Source-Code/MPM OPERATING SYSTEMS/MPM-86/MISC DRI DISKS/11/HIST.A86
Sepp J Morris 31738079c4 Upload
Digital Research
2020-11-06 18:50:37 +01:00

746 lines
14 KiB
Plaintext
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.

;
; histogram utility
; 1/14/83
;
cr equ 0dh
lf equ 0ah
cseg
public conout
public systemreset
public printm
public crlf
public blank
public collecthist
public histds
extrn parse:near
extrn parse2:near
extrn conin:near
extrn inittimer:near
extrn starttimer:near
extrn stoptimer:near
extrn printdword:near
extrn printword:near
hist:
mov ax,ds
mov ss,ax ;set up histogram stack
mov sp,offset stacktop
mov dx,offset signon
call printm ;print signon message
call getcommandline
call loadcmdfile
call setupcommandtail
call setuprange
call gettimerstartaddr
call setupbdosint
call executeuserprogram
call printhistogram
call systemreset
;
; get command line into buffer at 80h (unless it's already there)
; and put a 0 at the end of it
;
getcommandline:
cmp byte ptr .5dh,' '
jnz putzero ;already entered command line in 'hist' command
mov dx,offset comlinemessage
call printm
mov byte ptr .7fh, 7eh ;store max count
mov dx,7fh ;start of buffer for rdconbuff
call rdconbuff
call crlf
putzero:
sub bh,bh
mov bl,byte ptr .80h ;get actual count
mov byte ptr 81h[bx],0 ;store 0 at end of command line
ret
;
; load cmd file parsed from command line
; set up dma address to default to users default buffer at 80h
; set user cs, ds and es registers from base page of loaded program
;
loadcmdfile:
mov di,5ch
call parse
mov bx,65h
cmp byte ptr [bx],' '
jnz load1
mov byte ptr [bx],'C'
mov byte ptr 1[bx],'M'
mov byte ptr 2[bx],'D'
load1:
mov dx,5ch
call openfile
inc al
jz nofile
mov dx,5ch
call loadfile
mov userds,bx
mov dx,5ch
call closefile
inc al
jz closeerr
mov dx,userds
call setdmabase ;set up dma address for loaded program
mov dx,80h
call setdma
mov ax,userds
mov usercs,ax
mov useres,ax
mov es,ax ;point to user's base page
test es: byte ptr .5,1 ;check for 8080 model
jnz is8080 ;if so - cs, es = ds
mov ax,es:.3 ;get code base
mov usercs,ax
mov ax,es:.15 ;get extra base
or ax,ax ;see if there is an extra segment
jz is8080
mov useres,ax
is8080:
ret
;
nofile:
mov dx,offset nofilemessage
call printm
call systemreset
closeerr:
mov dx,offset closemessage
call printm
call systemreset
;
; copy command tail to buffer at 80h in user's base page
; parse 2 file names into 5ch and 6ch
;
setupcommandtail:
dec conptr ;point to delimiter
push conptr ;save conptr for parsing later
mov al,byte ptr .80h ;get count of command line
sub al,byte ptr conptr ;subtract chars already parsed
mov es,userds
mov di,80h ;point to user's default buffer
stos al ;store count
inc al ;to include delimiter
mov cl,al
sub ch,ch ;count in cx
s1:
call conin
stos al
dec cx
jnz s1
pop conptr ;restore conptr
mov di,5ch
call parse ;parse first filename
cmp al,0 ;check for end of command tail
jnz s2
dec conptr ;to rescan eol when parsing second filename
s2:
mov di,6ch
call parse2 ;parse second filename
mov cx,36 ;36 bytes to move in fcb
mov si,5ch
mov di,si
mov es,userds
rep movsb ;move fcb into user's base page
ret
;
; set up default range (code segment of loaded program)
; see if user wants to change to another range
;
setuprange:
mov es,userds
sub si,si ;point to base page
lods es:ax ;get low 16 bits of code segment length
mov maxoffset,ax ;store it
lods es:al ;get high 4 bits of code segment length
mov cl,12
sub ah,ah
shl ax,cl ;convert to pp's
mov maxbase,ax ;store it
lods es:ax ;get base of code segment
mov minbase,ax ;store it
add maxbase,ax ;to form total maxbase
call getrange
mov ax,minoffset
and ax,0fff0h ;mask to even paragraph
mov minoffset,ax
mov cl,4
shr ax,cl ;convert to pp's
add ax,minbase
mov minpara,ax ;store low end of range
mov ax,maxoffset
add ax,0fh ;round up
jnc sr1
add maxbase,1000h ;if wraparound occurred
sr1:
mov cl,4
shr ax,cl ;convert to pp's
add ax,maxbase
mov maxpara,ax ;store high end of range
sub ax,minpara ;range = max - min
mov bl,nbucket
dec bl
sub bh,bh
add ax,bx ;to cause round up if not even division
jnc sr15
mov ax,0ffffh ;if add caused wrap, use max range value
sr15:
sub dx,dx ;for division
mov cx,dx
mov cl,nbucket
div cx
or ax,ax
jnz sr2
inc ax ;make sure npara > 0
sr2:
mov npara,ax ;# paragraphs per bucket
; see if user wants to change range
ret
getrange:
mov dx,offset lowmessage
mov si,offset minbase
call getrangevalue
mov dx,offset highmessage
mov si,offset maxbase
call getrangevalue
ret
getrangevalue:
push si
call printm
pop si
push si
mov es,[si]
mov di,2[si]
call printdword
mov al,')' ! call conout
mov al,':' ! call conout
call blank
mov dx,offset consolebuff
call rdconbuff
mov conbuffptr,0
mov bl,conbufflen
sub bh,bh
mov conbuff[bx],0 ;insert 0 terminator
call getnum
pop si
or ah,ah
jz noinput
cmp al,':' ;base value entered?
jnz newoffset
push si
mov [si],bx
call getnum
pop si
newoffset:
cmp al,0
jnz badinput
mov 2[si],bx
noinput:
call crlf
ret
badinput:
mov dx,offset badinputmessage
call printm
call systemreset
gettimerstartaddr:
mov ax,usercs
mov startbase,ax
mov dx,offset timeronmessage
mov si,offset startbase
call getrangevalue
test startoffset,0ffffh
jz nostart
mov startflag,1
mov es,startbase
mov di,startoffset
mov ax,es:[di] ;get instruction at start address
mov startinstr,ax ;save it
; mov es: byte ptr [di],0cch ;interrupt 3 instruction
mov es: word ptr [di],04cdh ;interrupt 4 instruction
nostart:
ret
;
; save current break interrupt and replace it
; with pointer to local break interrupt handler
;
setupbreakint:
sub ax,ax
mov es,ax
mov si,010h
mov di,offset break_ip
mov ax,es:[si] ;get real break ip
mov [di],ax ;save it
mov ax,offset break_int_handler
mov es:[si],ax ;store break ip in interrupt vector
mov ax,es:2[si] ;get real break cs
mov 2[di],ax ;save it
mov ax,cs
mov es:2[si],ax ;store hist cs in interrupt vector
ret
break_int_handler:
push ds
mov ds,histds
mov sssave,ss
mov spsave,sp
mov ss,histss
mov sp,offset break_tos
push ax ! push bx ! push cx ! push dx
push bp ! push si ! push di ! push es
mov es,sssave
mov si,spsave ;point to user's stack
sub es:word ptr 2[si],2 ;dec user ip to account for INT 4
; dec es:word ptr 2[si] ;dec user ip to account for INT 3
or es:word ptr 6[si],200h ;make sure IF is on
test startflag,1
jz notstart ;jump if there was no start timer addr
mov di,es:2[si] ;get user ip
cmp di,startoffset ;see if we are at the start timer addr
jnz notstart
mov ax,es:4[si] ;get user cs
cmp ax,startbase
jnz notstart
mov es,ax
mov ax,startinstr ;get instr replaced by INT 3
mov es:[di],ax ;restore it
call starttimer
notstart:
pop es ! pop di ! pop si ! pop bp ! pop dx ! pop cx ! pop bx ! pop ax
mov ss,sssave
mov sp,spsave
pop ds
iret
;
; save current bdos interrupt and replace it
; with pointer to local bdos interrupt handler
;
setupbdosint:
sub ax,ax
mov es,ax
mov si,380h
mov di,offset bdos_ip
mov ax,es:[si] ;get real bdos ip
mov cs:[di],ax ;save it
mov ax,offset bdos_int_handler
mov es:[si],ax ;store hist ip in interrupt vector
mov ax,es:2[si] ;get real bdos cs
mov cs:2[di],ax ;save it
mov ax,cs
mov es:2[si],ax ;store hist cs in interrupt vector
ret
bdos_int_handler:
cmp cl,0
jz userdone ;done on function 0 call
jmpf dword ptr bdos_ip ;transfer control to the bdos
executeuserprogram:
call inittimer
mov histss,ss
mov histsp,sp
mov histds,ds
call setupbreakint
test startflag,1
jnz ex0
call starttimer
ex0:
mov es,useres
mov ds,userds
callf dword ptr userip
;
; arrive here upon completion of user program
; (function 0 bdos call intercepted - later add user supplied
; terminate address or timeout)
;
userdone:
call stoptimer
sub ax,ax
mov es,ax
mov di,380h
mov si,offset bdos_ip
movs ax,cs:ax
movs ax,cs:ax ;restore bdos interrupt
mov ax,histds
mov ds,ax ;restore histogram ds
mov ax,histss
mov ss,ax
mov sp,histsp ;restore histogram ss, sp
mov dx,offset donemessage
call printm
ret ;to main histogram program
bdos_ip rw 1
bdos_cs rw 1
;
; collect histogram data
; called with user ip in ax; user cs in bx
;
collecthist:
add ax,0fh ;round up to next paragraph
jnc col1 ;check for wraparound
add bx,1000h ;allow for wraparound
col1:
mov cl,4
shr ax,cl ;change ip to paragraph number
add ax,bx ;ax now has actual paragraph # of interrupt
cmp ax,minpara ;check against low end of range
jc outofrange
cmp maxpara,ax ;check against high end of range
jc outofrange
sub ax,minpara ;normalize within range
sub dx,dx ;for division
div npara ;divide by pp's per bucket to get bucket #
add ax,ax ;to index into word array
mov bx,ax
inc bucket [bx] ;increment appropriate count
outofrange:
ret
printhistogram:
call printcommandline
call findmax
mov cl,nbucket
mov bptr,0
phloop:
push cx ;save loop count
call printaddress
mov bx,bptr
add bx,bx ;index into word array
mov ax,bucket[bx]
or ax,ax
jz empty
call printbar
empty:
call crlf
pop cx ;restore loop count
dec cl
jz phdone
inc bptr
jmps phloop
phdone:
call printmax
ret
printbar: ;called with value in ax
mov bx,maxdots
dec bx
mul bx
div maxvalue
inc ax ;so anything above 0 gets at least one dot
mov cx,ax ;number of dots in cx
pb0:
mov al,'*'
push cx ;save count
call conout
pop cx
dec cx
jnz pb0
ret
printcommandline:
mov dx,offset histmessage
call printm
mov bx,81h
pcl0:
mov al,[bx]
or al,al
jz pcl1
push bx
call conout
pop bx
inc bx
jmps pcl0
pcl1:
call crlf
call crlf
ret
printaddress:
mov ax,bptr
mul npara ;get # paragraphs to add to min
mov bx,minoffset
mov cl,4
shr bx,cl ;convert min offset to paragraphs
add ax,bx ;actual address of this bucket
mov di,ax
and ax,0f000h ;mask off 64K overflow
add ax,minbase
mov es,ax
shl di,cl ;make offset bytes from paragraphs
call printdword
call blank
ret
findmax:
mov dx,0 ;max in dx
mov bx,0 ;index into bucket array
mov cl,nbucket ;count in cl
maxloop:
cmp dx,bucket[bx]
jnc max1
mov dx,bucket[bx]
max1:
inc bx
inc bx
dec cl ;decrement count
jnz maxloop
mov maxvalue,dx
ret
printmax:
mov dx,offset maxmessage
call printm
mov ax,maxvalue
call printword
call crlf
call crlf
ret
;
; return 16-bit value in bx
; delimiter in al; ah = 0 if no input
;
getnum:
sub bx,bx
mov ah,bh
get0:
call getchar
call delim
jz getret
mov cl,4
shl bx,cl
call hexcon
jc geterr
add bl,al
mov ah,1
jmps get0
getret:
ret
geterr:
mov dx,offset baddigitmessage
call printm
call systemreset
hexcon:
sub al,'0'
cmp al,10
jb hexret
add al,('0' - 'A' + 10) and 0ffh
cmp al,16
jnb hexerr
cmp al,10
jc hexerr
hexret:
clc
ret
hexerr:
stc
ret
delim:
cmp al,':'
jz d0
or al,al
d0:
ret
getchar: ;return next char from consolebuff
mov di,conbuffptr
mov al,conbuff[di]
inc conbuffptr
upper:
cmp al,'a'
jb upret
cmp al,'z'
ja upret
and al,5fh
upret:
ret
crlf:
mov al,cr
call conout
mov al,lf
call conout
ret
blank:
mov al,' '
call conout
ret
;
; OS interface routines
;
bdos: int 224
ret
systemreset:
mov cl,0
mov dl,0
jmps bdos
conout:
mov dl,al
mov cl,2
jmps bdos
printm:
mov cl,9
jmps bdos
rdconbuff:
mov cl,10
jmps bdos
openfile:
mov cl,15
jmps bdos
closefile:
mov cl,16
jmps bdos
setdma:
mov cl,26
jmps bdos
setdmabase:
mov cl,51
jmps bdos
loadfile:
mov cl,59
jmps bdos
;
; histogram data area
;
cseg
histds rw 1
userip dw 0
usercs rw 1
dseg
public userds
extrn conptr:word
signon db 'Histogram Utility Version 0.2 1/14/83',cr,lf,cr,lf,'$'
comlinemessage db 'Enter command line: $'
nofilemessage db 'No file$'
closemessage db 'Cannot close$'
histmessage db 'Histogram for: $'
donemessage db cr,lf,'User program complete',cr,lf,cr,lf,'$'
maxmessage db cr,lf,'Maximum = $'
lowmessage db 'Low address of range (default = $'
highmessage db 'High address of range (default = $'
badinputmessage db cr,lf,'Bad input$'
baddigitmessage db cr,lf,'Bad hex digit$'
timeronmessage db 'Start timer address (default = $'
bufflen equ 80
consolebuff db bufflen ;max length
conbufflen rb 1 ;actual length
conbuff rb bufflen ;buffer
conbuffptr rw 1 ;console buffer pointer
stack rw 128
stacktop rw 0
userds rw 1
useres rw 1
histss rw 1
histsp rw 1
maxnbucket equ 255 ;seems like this should be enough
bucket rw maxnbucket
nbucket db 50 ;default (for now)
npara rw 1 ;# paragraphs per bucket
minbase rw 1
minoffset rw 1
minpara rw 1 ;low end of range (pp #)
maxbase rw 1
maxoffset rw 1
maxpara rw 1 ;high end of range (pp #)
maxvalue rw 1 ;highest value in buckets
maxdots dw 64 ;# dots in largest bucket
bptr rw 1
break_ip rw 1 ;save what was there
break_cs rw 1
; for now, reserve word for INT 4
startinstr rw 1 ;instruction overwritten by INT 3
startflag db 0 ;set when user enters start timer addr
startbase dw 0
startoffset dw 0
spsave rw 1
sssave rw 1
rw 128 ;break int local stack
break_tos rw 0
end