mirror of
https://github.com/SEPPDROID/Digital-Research-Source-Code.git
synced 2025-10-23 00:14:25 +00:00
746 lines
14 KiB
Plaintext
746 lines
14 KiB
Plaintext
;
|
||
; 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
|
||
|