Digital Research
This commit is contained in:
2020-11-06 18:50:37 +01:00
parent 621ed8ccaf
commit 31738079c4
8481 changed files with 1888323 additions and 0 deletions

View File

@@ -0,0 +1,746 @@
;
; 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