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

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

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


View File

@@ -0,0 +1,254 @@
eol equ 0
cseg
public parse
public parse2
public conin
public printdword
public printword
public printbyte
extrn systemreset:near
extrn printm:near
extrn crlf:near
extrn conout:near
extrn blank:near
;
; **************************************
; * *
; * file name parsing routines *
; * *
; **************************************
;
parse: ;parse into fcb whose offset is in [di]
push ds
pop es ;set es=cs
push di ;save fcb address
sub al,al
mov cx,36 ;fcblen
rep stos al ;initialize fcb to 0
pop di ;restore fcb address
;
parse2: ;enter here to parse without clearing
;assumes es = cs from parse:
mov fcbadr,di ;save fcb address
inc di ;point to first byte of filename
call setupdisk ;check for d: and set drive byte
mov cx,8
call fillfield ;first item was disk, now get filename
mov cx,3 ;length of file type
cmp lastchar,'.'
jz filltype
call fillbl ;fill type with blanks if no '.'
jmps parseret
filltype:
call fillfield ;if '.', fill field from console buff
parseret:
mov al,lastchar
ret ;with last char scanned in [al]
parseerr:
mov dx,offset parsemessage
call printm
call systemreset
;
deblank:
call conin
mov lastchar,al
cmp al,' '
jz deblank ;deblank input
ret
;
setupdisk: ;set byte 0 of fcb according to char in fcb (1)
call deblank
cmp al,eol
jz s1 ;can't be drive, decrement conptr to rescan
call conin
cmp al,':'
jnz s0 ;not a drive, subtract 2 from conptr to rescan
mov al,lastchar ;get drive char
sub al,'A'-1
dec di ;point to fcb (0)
stos al ;store drive byte
ret
s0:
dec conptr
s1:
dec conptr
ret
;
pdelim: ;check char in [al] for delimiter; return ZF if so.
mov di,offset delims
mov cx,ndelims
repnz scas al ;look in table
ret
;
fillfield: ;count in [cx], dest ptr in [di]
call conin
mov lastchar,al ;save last char scanned
cmp al,'*'
jz parseerr
cmp al,'?'
jz parseerr
push di
push cx
call pdelim
pop cx
pop di
jz fillbl ;if delimiter, fill field with ' '
jcxz parseerr ;error if count exceeded
stos al ;store char in fcb
dec cx ;decrement count
jmps fillfield
fillbl:
mov al,' '
fillx:
jcxz filldone
rep stos al ;store '?' or ' '
filldone:
ret
;
;
printseginfo: ;print name, start and end address of segment whose
;number is in [al] if length is non-zero.
mov es,userds
mov cl,al ;save seg number
mov bl,6
mul bl ;6 bytes per segment in base page
mov si,ax ;si now points to entry in base page
lods es:ax ;get low 16 bits of length
mov bx,ax ;save in bx
lods es:al ;get high nibble of length
mov dl,al ;save it
mov ah,0
or ax,bx ;test for zero length
jz psiret ;if zero, no display
lods es:ax ;get base
push bx ;save low (length)
push dx ;save high (length)
push ax ;save base
mov al,cl ;get seg number
mov bl,3 ;3 bytes per segment name
mul bl ;calculate offset into table
add ax,offset segnames ;ax now points to segment name
push ax ;save it
call crlf
pop si
call printm ;print segment name
call blank
pop es ;get base
push es ;save base
mov di,0
call printdword ;print start address
call blank
pop bx ;get base
pop ax ;get high (len)
mov cl,12
shl ax,cl ;move ls nibble of al to ms nibble of ah
add ax,bx ;add ms nibble of length to base
mov es,ax
pop di ;get low (len)
call printdword ;print end address
psiret:
ret
;
ifcb:
push conptr ;save input pointer
mov di,5ch
call parse
cmp al,eol
jnz i0 ;only one filename
dec conptr ;to rescan eol and blank second filename in fcb
i0:
mov di,6ch
call parse2 ;parse second filename
push ds
pop es ;point to SID's ds
pop conptr ;restore input pointer
mov di,81h ;move command tail to [es]:[di]
sub cx,cx ;zero count
i1:
call conin ;get char from command tail
cmp al,eol ;end of command tail?
jz i2
stos al ;store in user's base page
inc cx ;increment count
jmps i1 ;loop until eol
i2:
mov al,0
stos al ;store 0 at end of string
mov es:.80h,cl ;store count at start of buffer
mov si,5ch
mov di,si
mov es,userds
add cx,38 ;total bytes to move = # in command + 36 (fcb)
; +2 (0 at end of command and count byte)
rep movs al,al ;move fcb from sid86 basepage to user's basepage
idone:
ret
conin:
mov si,81h
add si,conptr
lods al
inc conptr
upper: cmp al,'a' ;less than 'a'
jb upret ;or
cmp al,'z' ;greater than 'z'
ja upret ;then no change
and al,5fh ;else convert to uc
upret: ret
printdword: ;print double word as ssss:oooo
; called with:
; es = segment
; di = offset
push di
mov ax,es
call printword ;print segment
mov al,':'
call conout
pop ax
;
printword: ;print value in [ax] as 4 hex digits
push ax
mov al,ah
call printbyte
pop ax
;
printbyte: ;print value in [al] as 2 hex digits
push ax
mov cl,4
shr al,cl ;shift al right 4 places
call printnibble ;output upper nibble
pop ax ;restore al (now we do lower nibble)
;
printnibble: ;print value in low 4 bits of [al] as a hex digit
and al,0fh ;mask upper 4 bits
add al,90h
daa
adc al,40h
daa
call conout
ret
dseg
public conptr
extrn userds:word
conptr dw 0
lastchar rb 1
fcbadr rw 1
delims db ' =.,:;[]<>',eol
ndelims equ offset $ - offset delims
segnames db 'CS$', 'DS$', 'ES$', 'SS$'
db 'X1$', 'X2$', 'X3$', 'X4$'
parsemessage db 'BAD FILE NAME',0dh,0ah,'$'
end


View File

@@ -0,0 +1,150 @@
;
; timer routines for histogram
; 1/9/83
;
cr equ 0dh
lf equ 0ah
cseg
public inittimer
public starttimer
public stoptimer
extrn collecthist:near
extrn printm:near
extrn systemreset:near
extrn histds:word
port_base equ 50h
master_base equ port_base + 0
slave_base equ port_base + 2
Timer_control equ port_base + 7
Timer_0 equ port_base + 4
int_base equ 29h * 4
timer_int_off equ word ptr .int_base
timer_int_seg equ word ptr .int_base + 2
tickint:
;=======
; Interrupt handler for tick interrupts
push ds
mov ds,histds
test intick,1
jnz overrun
mov intick,1
mov userss,ss
mov usersp,sp
mov ss,histds
mov sp,offset tick_tos
push ax ! push bx
push cx ! push dx
push di ! push si
push bp ! push es
mov es,userss
mov si,usersp
mov ax,es:2[si] ;get ip where int occurred
mov bx,es:4[si] ;get cs where int occurred
call collecthist
pop es ! pop bp ;restore context
pop si ! pop di
pop dx ! pop cx
pop bx ! pop ax
mov ss,userss
mov sp,usersp
pop ds
push ax
mov al,20H
out master_base,al ; reset the master
out slave_base,al ; reset the slave
pop ax
mov intick,0
iret
intick db 0
overrun:
mov dx,offset overrunerr
call printm
call systemreset
;
; initialize timer and interrupt vector
;
inittimer:
; setup clock timer interrupt addr
sub ax,ax
mov es,ax
mov es:word ptr .00a4h,offset tickint
mov es:word ptr .00a6h,cs
;
; set up the master PIC
mov al,15h! out master_base, al
mov al,20h! out master_base + 1,al ; interupts start at interupt 20h
mov al,80h! out master_base + 1, al ; only IRQ 7 has a slave attached
mov al,01h! out master_base + 1, al ; Set for 8088 mode
mov al,0ffh! out master_base + 1, al ; turn off all interupts for now
;
; set up the slave PIC
mov al,015h! out slave_base, al
mov al,028h! out slave_base + 1,al ; base of the slave vectors
mov al,07h! out slave_base + 1, al ; slave ID number
mov al,01h! out slave_base + 1, al ; 8088 mode
mov al, 0ffh! out slave_base + 1, al ; off for now
;
; now set up the timer for 16.67 milliseconds
; timer 0, two byte rl, mode 3
mov al,036h! out timer_control, al ; timer control word
mov al,03ch! out timer_0, al ; timer 0's count
mov al,082h! out timer_0, al
ret
starttimer:
mov al,7fh! out master_base + 1, al
mov al,0fdh! out slave_base + 1, al
sti
ret
stoptimer:
mov al,0ffh! out master_base + 1, al ; turn off all interupts for now
mov al, 0ffh! out slave_base + 1, al ; off for now
ret
dseg
rw 128
tick_tos rw 0
userss rw 1
usersp rw 1
overrunerr db cr,lf,'Timer interrupt overrun',cr,lf,'$'
end