home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power-Programmierung
/
CD1.mdf
/
magazine
/
pcmagazi
/
1988
/
06
/
calc.asm
< prev
next >
Wrap
Assembly Source File
|
1988-03-28
|
64KB
|
1,213 lines
;Calc for the IBM Personal Computer - 1987 by Douglas Boling
bios_data segment at 40h ;BIOS data area
org 17h
bios_kbd_stat db ? ;keyboard status byte
org 4ah
bios_crt_col dw ? ;number of columns on the screen
org 63h
addr_6845 dw ? ;6845 Index Register address
org 84h
bios_crt_row db ? ;number of rows
bios_data ends
code segment para public 'code'
assume cs:code
org 100h
entry: jmp initialize ;jump to initialization code
program db "CALC 1.0 (c) 1988 Ziff Communications Co.",13,10
db "PC Magazine ",254," Douglas Boling",13,10
db "Hot Key is Alt-S",13,10,"$",1ah
adapter db 2 ;0 = CGA, 1 = MDA, 2 = EGA
num_vid_col dw ? ;number of columns on screen
num_vid_rows db ? ;number of columns on the screen
v_segment dw ? ;video segment address
v_page db ? ;current video page
border_attr db ? ;window border attribute
text_attr db ? ;window text attribute
header_attr db ? ;window header attribute
window_row db 3 ;row of left corner of window
window_column db 10 ;column of left corner of window
column_adj dw ? ;screen adjust for index registers
active db 0 ;status of interrupt routine
old_cursor_pos dw ? ;row and column of screen cursor
my_cursor_pos dw ? ;cursor position
;
base_flag dw 10 ;numeric base
pending_op dw 3dh ;pending operation (3d = nop)
fixed_flag db 0 ;indicates fixed math mode
decimal_flag db 0 ;flag for decimal point entry
entry_pres db 0 ;entry reg nonempty flag
;
entry_reg_high dw 0 ;most significant 16 bits
entry_reg_low dw 0 ;least sig. 16 bits for entry num
;
result_reg_high dw 0 ;most sig. 16 bits of result reg
result_reg_low dw 0 ;least sig. 16 bits
;
sign db 0
base_ptr: db "Hex Binary Octal Decimal Fixed "
number_label db "hbod"
op_table: db " and or xor err"
;
old_kbd_status db ?
old_int_9h label dword ;old interrupt vector
old_keyboard_int dw 2 dup (?)
screen_buffer dw offset initialize ;pointer to screen buffer area
;
mono_values dw 0b000h ;segment
db 70h ;border
db 07h ;text
db 07h ;header
color_values dw 0b800h ;segment
db 0fh ;border
db 1fh ;text
db 1eh ;header
enable_values db 2Ch,28h,2Dh,29h ;values to enable CGA display
db 2Ah,2Eh,1Eh
header_text db "Calculator Base: Esc = Quit"
help_text1: db "f1 base f3 and f5 xor f7 sal f9 +/-"
help_text2: db "f2 fixed f4 or f6 not f8 sar f10 clr"
;-----------------------------------------------------------------------------
;Front-end routine for the keyboard interrupt handler. Execution is vectored
;here whenever an interrupt 9 is generated by the PC keyboard.
;-----------------------------------------------------------------------------
main proc near
assume cs:code,ds:nothing,es:nothing,ss:nothing
pushf
cmp cs:active,0 ;Calc currently active?
jne quick_out ;yes, Exit.
sti ;no, start initialization, first
push ax ; enable interrupts and save
push bx ; registers.
push cx
push dx
push si
push di
push ds
push es
push bp
in al,60h ;get scan code from keyboard
cmp al,31 ;check for 's' key
jne out1 ;no, then exit.
mov ah,2 ;check shift keys
int 16h
and al,0fh
cmp al,8 ;check alt key
je main1 ;alt key pressed, pop up.
out1: pop bp
pop es ;Exit, first restore registers,
pop ds ; then jump using the old
pop di ; interrupt vector that was
pop si ; replaced.
pop dx
pop cx
pop bx
pop ax
quick_out: popf
jmp cs:old_int_9h
; Start of 'real' code. Clean up interrupt state and check video mode.
main1: call kb_reset ;The hot key combination has been
push cs ; pressed, spring into action by
pop ds ; resetting the keyboard
assume ds:code ; interrupt, and setting the
mov ax,bios_data ; data segment register.
mov es,ax ;Set es for bios segment. Then
assume es:bios_data ; grab significant data.
mov ax,es:bios_crt_col ;save the number of columns
mov num_vid_col,ax
mov al,es:bios_crt_row ;save the number of rows
mov num_vid_rows,al
mov al,es:bios_kbd_stat ;save the state of the keyboard
mov old_kbd_status,al
or es:bios_kbd_stat,20h ;set num-lock on
mov ah,12h ;Check for EGA by testing video
mov bl,10h ; function 12h. If bl returns
int 10h ; unchanged, then EGA is not
cmp bl,10h ; present.
jne main110
mov adapter,0 ;not EGA, assume CGA
mov num_vid_rows,24 ;No ega must be 25 rows.
mov ax,es:addr_6845
test al,40h
jnz main110 ;if the bit is 1 then its a MDA
inc adapter
main110: push cs ;Set es to same segment as code.
pop es
assume es:code
mov ah,15 ;Get current video mode.
int 10h
cmp al,3 ;is current video mode 0 - 3 ?
jle main2 ;yes, then branch
cmp al,7 ;is current video mode 7?
je main111 ;yes, then branch. else, exit.
done: ; Restore the state of the keyboard
mov al,old_kbd_status ;get old key status
and al,20h ;isolate numlock
cmp al,0 ;was numlock on or off?
jnz done_1 ;it was on - no change needed
mov bx,bios_data
mov ds,bx
assume ds:bios_data
and ds:bios_kbd_stat,not 20h ;write to keyboard flags byte
done_1: pop bp
pop es ;Exit with an interrupt return.
pop ds
pop di
pop si
pop dx
pop cx
pop bx
pop ax
popf
iret
assume cs:code, ds:code, es:code
main111: mov di,offset mono_values ;set monochrome attributes
jmp short main3
main2: mov di,offset color_values ;set color attributes
main3: mov ax,[di]
mov v_segment,ax ;set video segment and attributes
mov ax,2[di]
mov border_attr,al
mov text_attr,ah
mov al,4[di]
mov header_attr,al
mov ax,num_vid_col ;Compute the column adjustment
cmp ax,80 ; value from the number of
jl done ; columns on the screen. If the
sal ax,1 ; number of columns in the
sub al,88 ; screen is < 80, don't pop up.
mov column_adj,ax
mov v_page,bh ;Save video page.
mov ah,3 ;Get cursor position, save it,
int 10h ; then position the cursor to
mov old_cursor_pos,dx ; one row below the screen so
mov dh,num_vid_rows ; that it will be hidden.
add dh,2
mov dl,0
mov ah,2
int 10h
mov active,1 ;indicate calc is active.
call set_cursor
mov di,screen_buffer ;Save the screen where the
xor ax,ax ; window will be. Then, pop up
call screen_ops ; the window.
mov bl,0 ;Display the contents of the
cmp entry_pres,0 ; of the entry register if
je main32 ; something is in it, else
inc bl ; display the result register.
main32: call display_reg
;The window is now displayed on the screen. Wait for a keypress.
main4: mov dx,my_cursor_pos ;Get the position of my 'cursor'.
mov al,"<" ; then write it to the screen.
mov ah,header_attr
call output_char
call display_base ;indicate dec, hex, oct, bin, fix
main41: mov ah,0 ;get a keypress by first executing
int 28h ; a dos idle loop, to let other
mov ah,1 ; programs have a chance, then
int 16h ; checking for a key before
jz main41 ; actually getting the key. If
mov ah,0 ; no key, idle again.
int 16h
main5: cmp al,27 ;ESC key pressed?
je end_it ;yes, clean up and exit
cmp al,08 ;is it a backspace
jne main6
call back_space ;yes, process backspace
jmp short main4
main6: cmp ah,93 ;is it shift f10 ?
je main7 ;yes, jump to clear routine.
cmp ah,59 ;is it f1 ?
je main9 ;yes, change base
cmp ah,60 ;is it f2 ?
je main9 ;yes, change base
cmp ah,64 ;is it between f6 and f10 ?
jl main8 ; If so, call unary operator
cmp ah,68 ; routine
jg main8
main7: call un_proc
jmp short main4
main8: call key_proc
jmp short main4
main9: call base_chang ;f1 or f2, change base
jmp short main4 ;goto end
; Escape key has been pressed, clean up and exit.
end_it: mov di,screen_buffer ;point DI to holding buffer
mov al,1
call screen_ops ;restore video memory contents
mov dx,old_cursor_pos ;Restore the cursor to the
mov bh,v_page ; position it was before calc
mov ah,2 ; was called.
int 10h
mov active,0 ;reset status flag
jmp done ;exit
main endp
;-----------------------------------------------------------------------------
; process keys - Process a all keys but esc, f1, f2, and f6 - f10
;-----------------------------------------------------------------------------
key_proc proc near
cmp al,0 ;see if extended key
jne key0
cmp ah,61 ;if extended, allow only
jl key_end ; keys f3 - f10
cmp ah,68
jg key_end
jmp short key_oper
key0: cmp al,30h ;is it lower than 0
jl key2 ;yes, check for other functions
mov bx,base_flag ;get base
cmp bl,10h ;see if in hex
je key1 ;if so goto hex mode checking
or bl,30h ;convert to ascii
cmp al,bl ;is number less then the base?
jl key_num ;yes, goto number processing
jmp short key2 ;no, check for other characters
;Check for a-f if in hex mode
key1: cmp al,39h ;see if number less than 10
jle key_num
mov bl,al ;copy ascii character
and bl,0dfh ;make lower and upper case same
cmp bl,41h ;is it lower than a ?
jl key2 ;yes, see if other character
cmp bl,46h ;no, is it greater then an f ?
jg key2 ;yes, check for other characters
key_num: call number_key ;no, it must be a number
jmp short key_end ;get another keypress
;Check for math functions
key2: cmp al,2ah ;if ascii code between 2a and
jl key3 ;2f then its a *+,-. or /
cmp al,2fh
jg key3
cmp al,"," ;see if its a comma that
je key_end ; slipped through, if so ret.
cmp al,"." ;is it a decimal point?
jne key_oper
mov decimal_flag,1 ;set flag
jmp short key_end
key3: cmp al,"%" ;see if other mod operator
je key_oper
cmp al,13 ;see if enter was pressed
je key4 ;yes, process enter
cmp al,"=" ;see if = was pressed
jne key5 ;no, check for other keys
key4: mov al,"=" ;yes, clear enter code
call oper_proc ;complete last math operation
mov bl,0 ;display result register
call display_reg ;call display register
jmp short key_end ;get a keypress
key5: cmp al,"\" ;see if mod operation
jne key_end
key_oper: call oper_proc ;call the math procedure
key_end: ret ;end
key_proc endp
;-----------------------------------------------------------------------------
; process base changes - Change the value in the base flag, then display reg.
;-----------------------------------------------------------------------------
base_chang proc near
cmp ah,60 ;see if changing to fixed
je base_fixed
cmp fixed_flag,1 ;if in fixed mode, only change
je base_fixed ; to decimal
mov ax,base_flag ;Get the base flag, add 6 and
add al,6 ; remove the 3rd bit, what you
and al,0bh ; have is the new base except for
jnz base_c1 ; 16 witch can be added to the
add al,16 ; zero result for hex.
base_c1: mov base_flag,ax
jmp short base_end
base_fixed: mov base_flag,10 ;Make sure base = 10 then
cmp fixed_flag,0 ; if already in fixed, make
je base_fixed1 ; integer by dividing by 100.
mov fixed_flag,0 ; If in integer decimal, make
mov decimal_flag,0 ; room for the fraction by
mov ch,01 ; multiplying by 100.
mov di,offset result_reg_high
call fixed_adjust
mov di,offset entry_reg_high
call fixed_adjust
jmp short base_end
base_fixed1: inc fixed_flag ;set fixed flag
xor cx,cx
mov di,offset result_reg_high
call fixed_adjust
mov di,offset entry_reg_high
call fixed_adjust
base_end: mov bl,0 ;display result register unless
cmp entry_pres,0 ; there is a number in the
je base_end1 ; entry register.
inc bl ;change register flag for the
base_end1: call display_reg ; display routine.
ret
base_chang endp
;-----------------------------------------------------------------------------
; mulitply and divide registers by 100
; entry: ch = 1, divide. ch = 0, multiply.
;-----------------------------------------------------------------------------
fixed_adjust proc near ;Moving from fixed to decimal
push bx ; and back again needs an easy
mov bx,100 ; way to multiply and divide by
call mul_div_shrt ; 100.
pop bx
ret ;end
fixed_adjust endp
;-----------------------------------------------------------------------------
; mulitply and divide registers by bx
; entry: ch = 1, divide. ch = 0, multiply.
;-----------------------------------------------------------------------------
mul_div_shrt proc near ;Moving from fixed to decimal
mov cl,sign
push cx
mov sign,0 ; and back again needs an easy
; way to multiply and divide by
cmp base_flag,10 ; 100. The math routines also
jne adj3 ; need this routine to correct
cmp word ptr [di],0 ; the decimal point on multiplys
jge adj3 ; and divides.
call negate_reg
inc sign ;If the register we are converting
adj3: mov ax,[di] ; is negitive, change its sign
; by negating it.
cmp ch,0 ;For divide, get the high word,
je adj4 ; convert it to a double word,
xor dx,dx ; perform the first divide. Save
div bx ; the result, and use the
mov [di],ax ; remainder as the upper word
mov ax,2[di]
div bx ; of the lower word divide.
jmp short adj5
adj4: mul bx ;For multiply, multiply the high
mov [di],ax ; word, then the low, and add the
mov ax,2[di] ; upper 16 bits of the second
mul bx ; product to the first product.
add [di],dx
adj5: mov 2[di],ax ;At the end recall if the register
cmp sign,0 ; as negitive on entry. If so,
je adj6 ; negate the result to return
call negate_reg ; the register to its orginal
adj6: pop cx ; sign.
mov sign,cl
ret
mul_div_shrt endp
;-----------------------------------------------------------------------------
;process unary operations.
;-----------------------------------------------------------------------------
un_proc proc near
cmp entry_pres,0 ;set up di to point to the
je un1 ; proper register.
mov di,offset entry_reg_high
jmp short un2
un1: mov di,offset result_reg_high
un2: cmp ah,64 ;is it f6 ?
jne un3 ;no, check for other keys
not word ptr [di] ;not register
not word ptr 2[di]
jmp short un_end
un3: cmp ah,65 ;is it f7, left shift ?
jne un4
mov bl,0
jmp short un31
un4: cmp ah,66 ;is it f8, right shift ?
jne un5
mov bl,1
un31: call shift_reg
jmp short un_end
un5: cmp ah,68 ;is it f10 ?
jl un6 ;no, check for other keys
xor dx,dx
mov di,offset entry_reg_high
mov [di],dx ;Clear both registers
mov 2[di],dx
mov 4[di],dx
mov 6[di],dx
cmp ah,93 ;do we clear the screen?
jne un_end
mov cx,7 ;yes, do so by scrolling the
un50: call scroll_window ; window up 7 lines.
loop un50
jmp short un_end
un6: cmp ah,67 ;is it f9 ?
jne un_end ;no, check for other keys
call negate_reg ; negate the result register.
un_end: mov bl,entry_pres
call display_reg ;display register
ret
un_proc endp
;-----------------------------------------------------------------------------
;process numbers - insert digits from the keyboard into the entry register.
;-----------------------------------------------------------------------------
number_key proc near
cmp al,39h ;see if a number not a - f
jle num1 ;yes, skip letter conversion
add al,9 ;convert letters to hex values
num1: and ax,000fh ;strip off ascii code
push ax ;save digit
mov sign,ah ;ah = 0, clear sign flag
mov di,offset entry_reg_high
cmp base_flag,10
jne num10
cmp word ptr [di],0 ;Check if the entry register is
jge num10 ; negitive, if so, negate it.
call negate_reg
inc sign
num10: pop ax ;get the digit back
xor dx,dx
cmp fixed_flag,0 ;See if in fixed mode.
je num2
mov cl,decimal_flag
cmp cl,2 ;If were in the fix point mode,
je num11 ; multiply all digits by 100
jg num20 ; until the decimal flag has
mov bx,10 ; been set. Then accept two
mul bx ; fraction digits shifting only
cmp cl,1 ; the tenths digit.
je num11 ; entered.
mul bx
num11: cmp cl,0
je num2
inc decimal_flag
mov bx,2[di]
jmp short num4
num2: mov bx,ax
mov ax,[di] ;get upper 16 bits
cmp base_flag,10
je num210
mul base_flag
jmp short num211
num210: imul base_flag ;make room for the new digit
num211: jc num21
mov [di],ax ;return high word
mov ax,2[di] ;get low 16 bits
mul base_flag ;repeat shift to make room
num4: add ax,bx ;add in new digit
adc dx,0
add [di],dx ;add overflow to the high 16 bits
num42: mov 2[di],ax ;store lower 16 bits
; if a new number, scroll window before displaying
cmp sign,0 ;Before displaying, see if the
je num20 ; register was orginally negitive
call negate_reg ; if so, negate it.
num20: cmp entry_pres,0 ;Check for a number in the entry
jne num21 ; register or a pending operation
mov bx,pending_op
cmp bl,"="
jne num21
call scroll_window
num21: mov bl,1 ;display entry register
call display_reg
inc entry_pres ;entry reg contains a number
num3: ret
number_key endp
;-----------------------------------------------------------------------------
;write_at_cursor - keeps track of the cursor and calls output character
;Entry: al - ascii value of characher to be displayed
;-----------------------------------------------------------------------------
write_at_cur proc near
push ax ;save registers ax and dx
push dx
mov dx,my_cursor_pos ;get cursor position
mov ah,text_attr ;use proper display attribute
call output_char ;else, display character
inc dl ;move cursor over
mov my_cursor_pos,dx ;store new cursor position
wac1: pop dx
pop ax ;same with ax
ret ;done.
write_at_cur endp
;-----------------------------------------------------------------------------
;oper_proc - processes all operators for the program
;Entry: ax - non-numeric keystroke
;-----------------------------------------------------------------------------
oper_proc proc near
push ax ;save the key
cmp entry_pres,0 ;is there a number in the ent reg?
jne oper1 ;yes, perform operation
mov pending_op,ax ;no, save operation and exit
xor cx,cx
jmp short operend
oper1: xchg pending_op,ax ;get pending op and save new op
call math_proc ;process math function
operend: pop dx
cmp cx,0
je oper4
mov dx,cx
oper4: cmp dl,0 ;see if extended code
je operlog
mov al," " ;print space to seperate operator
call write_at_cur
mov ax,dx ;get pending operation
call write_at_cur ;display operator
oper5: call scroll_window
operend1: xor bx,bx ;clear bx
mov entry_reg_high,bx ;clear entry register
mov entry_reg_low,bx
mov decimal_flag,bl
mov entry_pres,bl
operexit: ret ;done.
operlog: mov cx,dx
sub ch,61 ;convert keycode into offset
sal ch,1
sal ch,1
mov bx,offset op_table
add bl,ch ;point to correct label
mov cx,4 ;display a space and 3 characters
operlog1: mov al,[bx] ;get a character to display
call write_at_cur
inc bx
loop operlog1
jmp short oper5
oper_proc endp
;-----------------------------------------------------------------------------
;math_proc - processes all math for the program
;Entry: al - ascii value of the operation or 0 for result reg = entry reg
;-----------------------------------------------------------------------------
math_proc proc near
mov bx,ax ;copy key press
mov dx,entry_reg_high ;get result register
mov ax,entry_reg_low ;
mov di,offset result_reg_high
xor cx,cx ;clear error flag
cmp bx,0
je math02
cmp bl,0
jne math01
jmp short logic_start
math01: cmp bl,"+" ;see if addition
je math1
cmp bl,"-" ;see if subtraction
je math2
cmp bl,"*" ;see if multiplication
je mathmd
cmp bl,"/" ;see if division
je mathmd
cmp bl,"%"
je mathmd
cmp bl,"\" ;see if mod operation
je mathmd
math02: mov [di],dx ;If operator not recognized,
mov 2[di],ax ; copy entry register to
jmp short mathend
math1: add 2[di],ax ;add
adc [di],dx
jo matherr
math11: jmp short mathend
math2: sub 2[di],ax ;subtract
sbb [di],dx
jo matherr ;jump to error if underflow
jmp short mathend
mathmd: call mul_div
mathend: ret
matherr: mov cx,4000h ;point to error tag
ret
logic_start: cmp bh,61 ;see if and operation
jne logic1 ;no, look some more
and [di],dx ;perform and
and 2[di],ax
jmp short logic_end ;print op and end
logic1: cmp bh,62 ;see if or operation
jne logic2 ;no, look some more
or [di],dx ;do or
or 2[di],ax
jmp short logic_end ;print op and end
logic2: cmp bh,63 ;see if xor operation
jne logic_end ;give up looking
xor [di],dx ;do xor
xor 2[di],ax
logic_end: xor cx,cx ;clear error code
ret
math_proc endp
;-----------------------------------------------------------------------------
;Multiply and Divide - process multiply and divide math functions.
;-----------------------------------------------------------------------------
mul_div proc near
xor cx,cx ;zero cl
mov sign,cl ;zero sign flag
cmp word ptr [di],cx ;see if result reg is negative
jge md1 ;no, skip negation
call negate_reg
inc sign ;set 1 negative number
md1: mov si,offset entry_reg_high
cmp word ptr [si],0 ;see if negative
jge md2 ;no, skip negation
push di
mov di,si ;neg entry register
call negate_reg
pop di
inc sign ;set 1 more negative number
; 2 positive numbers, now is mul or div ?
md2: push bx ;save the operation
cmp bl,"*" ;if not '*' it must be divide
jne md6 ; or mod operation.
md3: mov ax,[di] ;get result high
mul word ptr [si] ;multiply by entry high
mov cx,dx
add cx,ax
mov ax,2[di] ;get result low
mul word ptr [si] ;multiply by entry high
add cx,dx
mov bx,ax ;start high word of product
mov ax,[di] ;get result high
mul word ptr 2[si] ;multiply by entry low
add cx,dx
add bx,ax ;continue to build final product
mov ax,2[di] ;get result low
mul word ptr 2[si] ;multiply by entry low
add bx,dx ;complete upper 16 bits
cmp cx,0 ;see if any overflow
jne mderr
cmp base_flag,10 ;If in base 10 protect the sign
jne md55 ; bit by declaring an overflow
cmp bx,0 ; if it has been changed.
jl mderr
md55: pop cx ;get the operation back
jmp short mdend
;Division part of the routine.
md6: cmp word ptr [si],0
jne md61
cmp word ptr 2[si],0 ;see if entry reg = 0
je mderr ; if so, indicate error.
md61: cmp fixed_flag,0 ;If in fixed mode multiply the
je md7 ; dividend by 100 to make room
mov ch,0 ; for the fraction.
call fixed_adjust
md7: cmp word ptr [si],0 ;Now check to see that the
je md8 ; divisor is not too big.
mov bl,1 ; If it is, divide both registers
push di ; by 2 until the high word of the
mov di,si ; divisor is zero.
call shift_reg
pop di
call shift_reg
jmp short md7
md8: mov ax,[di] ;perform the divide. First the
cwd ; high word, then the low one.
idiv word ptr 2[si]
mov bx,ax ;Store the high word result in bx
mov ax,2[di]
div word ptr 2[si]
pop cx ;get operation back
cmp cl,"/" ;see if division or mod operator
je mdend
mov ax,dx ;get remainder in in low word and
xor bx,bx ; clear the high word.
mdend: mov [di],bx
mov 2[di],ax
cmp fixed_flag,0 ;If in fixed mode, divide
je mdend1 ; by 100 to correct fraction
cmp cl,"*"
jne mdend1
mov ch,1
call fixed_adjust
mdend1: cmp sign,1 ;see if odd # of negative numbers
jne mdend2 ;even number, multiply done
call negate_reg ;di already pointing to result reg
mdend2: xor cx,cx ;clear error flag
ret
mderr: pop bx ;clean off stack
mov cx,4000h
ret
mul_div endp
;-----------------------------------------------------------------------------
;Shift register - shifts the register pointed to by di, 1 place.
;-----------------------------------------------------------------------------
shift_reg proc near
push ax
push dx
shift2: mov dx,[di] ;get the register pointed to by bx
mov ax,2[di]
cmp bl,0 ;see if left or right shift
je shift4 ; if bl=0 then left shift
shift3: sar dx,1
rcr ax,1 ;Shift 32 bits by shifting the
jmp short shift5 ; trailing word first so that its
shift4: sal ax,1 ; msb goes into the carry. Then
rcl dx,1 ; shift the leading word.
shift5: mov [di],dx
mov 2[di],ax
shift_end: pop dx
pop ax
ret ;done.
shift_reg endp
;-----------------------------------------------------------------------------
;negate register - subtracts a register from zero.
;entry: di points to the register being negated.
;-----------------------------------------------------------------------------
negate_reg proc near
xor dx,dx ;clear dx
negate2: neg word ptr 2[di] ;negate the register
sbb dx,[di]
mov [di],dx
ret
negate_reg endp
;-----------------------------------------------------------------------------
;back_space - remove last digit entered in entry reg by dividing by the base
;-----------------------------------------------------------------------------
back_space proc near
mov di,offset entry_reg_high
mov bx,base_flag
mov ch,1
cmp fixed_flag,0
je back_sp1
cmp decimal_flag,3
je back_sp1
call fixed_adjust
cmp decimal_flag,2
je back_sp2
back_sp1: call mul_div_shrt
back_sp2: cmp fixed_flag,0
je back_sp5
mov bx,100
cmp decimal_flag,3
jne back_sp3
mov bx,10
back_sp3: xor cx,cx
call mul_div_shrt
back_sp4: cmp decimal_flag,0
je back_sp5
dec decimal_flag
; display new entry register
back_sp5: mov bl,1 ;display entry register
call display_reg ;call display register
back_end: ret ;done.
back_space endp
;-----------------------------------------------------------------------------
;display_reg - writes a register to the screen at the cursor
;entry: bl = 0, write result reg. bl = 1, write entry register
;-----------------------------------------------------------------------------
display_reg proc near
mov cx,36 ;First erase what was here before
sub my_cursor_pos,cx
disp0: mov al," "
call write_at_cur
loop disp0
cmp bl,0 ;which register to display?
jne disp1 ;bl = 0, display result register
mov bx,offset result_reg_high
jmp short disp2
disp1: mov bx,offset entry_reg_high
disp2: mov si,2[bx] ;load register
mov di,[bx]
xor dx,dx ;clear dx
mov ch,1 ;initialize digit counter
mov sign,dl ;clear sign flag
cmp base_flag,10 ;is this a decimal mode?
jne disp_n6 ;no, skip signed display of number
cmp di,0 ;see if negative number
jge disp_n6 ;no, skip negation
neg si ;yes, negate si,di
sbb dx,di
mov di,dx
inc sign
jmp short disp_n6
;Insert digit seperators.
disp_s1: inc ch
cmp fixed_flag,0 ;see if in fixed mode
je disp_n4
cmp ch,3 ;check for place to put decimal pt
jne disp_n30
mov al,"." ;write decimal point
jmp short disp_n51
disp_n30: mov al,ch ;Since we know were in fixed base,
disp_n31: mov cl,3 ; set up for groups of 3.
mov bl,"," ;separate decimal digits by a ","
jmp short disp_n5
disp_n4: mov al,ch ;Depending on the base were in,
dec al ; set up the number of digits to
cmp base_flag,10 ; count between the separators.
je disp_n31 ; For a base of 8 and 16 separate
mov bl," " ; by 4, for binary, separate by 8
mov cl,4
cmp base_flag,2
jne disp_n5
sal cl,1
disp_n5: cbw ;Now that everything is set up,
div cl ; check if a separator in needed
cmp ah,0 ; by checking for a remainder of
jne disp_n6 ; zero when dividing the digit
mov al,bl ; count by the group number.
disp_n51: call write_back
;Display digit.
disp_n6: mov bx,base_flag ;After all this, its time to
mov ax,di ; display the digit. Divide the
xor dx,dx ; register to display by the base
div bx ; for each of the digits.
mov di,ax ;save high word quotent
mov ax,si
div bx
mov si,ax ;save low word quotent
mov ax,dx ;get remainder of division
cmp al,10 ;convert remainder to ascii
jl disp_n2
add al,37h
jmp short disp_n3
disp_n2: or ax,30h
disp_n3: call write_back ;display digit
;check to see if we have completed the display.
cmp di,0 ;check for zero number in register
jne disp_s1
cmp si,0 ;If the registers are zero, make
jne disp_s1 ; sure that at least 1 zero has
cmp fixed_flag,0 ; been displayed (3 for fixed.)
je disp_end ; If so, exit loop.
cmp ch,3
jl disp_s1
disp_end: cmp sign,0 ;If the number displayed was
je disp_end1 ; negative, write a "-" before
dec my_cursor_pos ; the number.
mov al,"-"
call write_at_cur
disp_end1: call set_cursor
ret
display_reg endp
;This small routine keeps track of the cursor when writing backwards.
write_back proc near
dec my_cursor_pos
call write_at_cur
dec my_cursor_pos
ret
write_back endp
;-----------------------------------------------------------------------------
;Display Base - Show what base the calculator is in.
;-----------------------------------------------------------------------------
display_base proc near
mov dh,window_row ;get coordinates of window corner
mov dl,window_column
inc dh ;move down 1 row
add dl,20 ;and over 20 columns
mov bx,offset base_ptr ;load address of base label table
mov ax,base_flag ;find out what base were in
mov cl,al ;copy base
sal cl,1 ;shift bits over 1
or al,cl
and al,0ch ;look only at the bits we want
push ax ;save number for later
sal ax,1
cmp fixed_flag,1 ;see if fixed
jne display_b1
add al,8 ;point to fixed label
display_b1: add bx,ax
mov cx,8
mov ah,header_attr
display_b2: mov al,[bx] ;Display label by looping 8 times
call output_char
inc bx
inc dl
loop display_b2
pop bx ;Now display letter to label
sar bl,1 ; the number currently being
sar bl,1 ; displayed.
mov al,number_label[bx] ;get letter from list
mov ah,text_attr
mov dx,my_cursor_pos
sub dl,37
call output_char
ret
display_base endp
;-----------------------------------------------------------------------------
;screen ops - saves and restores the contents of the screen beneath the window.
;Entry: ES:DI - buffer address,
;-----------------------------------------------------------------------------
screen_ops proc near
push ds ;save es and ds.
push es
push ax ;save entry parameter
cmp adapter,0 ;See if CGA, is so disable it
jne screen_op1 ; before writing to the screen.
call cga_off
screen_op1: mov dh,window_row ;row and column of window corner
mov dl,window_column
mov bl,v_page ;get video page in BX
mov si,di ;save buffer address
call video_ptr ;get starting address of window
mov bx,column_adj ;load index register adjust
mov cx,v_segment ;get video segment to load into
pop ax ; es or ds later.
cmp al,1 ;al=0, screen save. al=1, restore.
je screen_op2 ;Since this routine does both the
assume ds:nothing ; screen save and the screen
assume es:nothing ; restore, es:si and ds:di are
xchg si,di ; set to their proper addresses.
mov ds,cx ; Because of this, make NO
jmp short screen_op3 ; assumptions on es and ds in
screen_op2: mov es,cx ; this section of code.
screen_op3: mov cx,12 ;12 lines to save
cld ;clear DF
screen_op4: push cx ;save line counter
mov cx,44 ;44 words per line
rep movsw ;transfer one line to buffer
cmp al,1
je screen_op5
add si,bx ;adjust si for next line
jmp short screen_op6
screen_op5: add di,bx ;adjust di for next line
screen_op6: pop cx ;get line count
loop screen_op4 ;loop until done
pop es ;Restore the segment registers
assume es:code
pop ds
assume ds:code
cmp al,0 ;If we saved the screen, draw the
jne screen_op7 ; calc window into the screen.
call open_window
screen_op7: cmp adapter,0 ;If CGA, enable it before
jne screen_op8 ; returning.
call cga_on
screen_op8: ret
screen_ops endp
;-----------------------------------------------------------------------------
;Video ptr calculates the offset into the video memory orrsponding to the row,
; column, and video page passed to it in the registers given below.
;entry: dh,dl - row, column exit: di - offset
; bl - video page
;-----------------------------------------------------------------------------
video_ptr proc near
mov al,160 ;Compute the displacment by the
mul dh ; following equation:
sal dl,1 ; (row*160)+(col*2)+(page*1000)
xor dh,dh
add ax,dx
mov di,ax
mov ax,1000h
xor bh,bh
mul bx
add di,ax
ret
video_ptr endp
;-----------------------------------------------------------------------------
;cga off routine to disable the CGA by writing to the mode select register
;-----------------------------------------------------------------------------
cga_off proc near
mov dx,3DAh ;Wait for the vertical retrace,
cga_off1: in al,dx ; then disable the cga by
test al,8 ; writing to the mode select
je cga_off1 ; register.
sub dx,2
mov al,25h
out dx,al
ret
cga_off endp
;-----------------------------------------------------------------------------
;cga on routine to enable the CGA by writing to the mode select register
;-----------------------------------------------------------------------------
cga_on proc near
mov ah,15 ;Get the current video mode,
int 10h ; then, using xlat as a table
mov bx,offset enable_values ; lookup get the value needed
xlat ; to enable the cga video.
mov dx,3D8h ; Finally, write the value to the
out dx,al ; MSR.
ret
cga_on endp
;-----------------------------------------------------------------------------
;KB_RESET resets the keyboard and signals end-of-interrupt to the 8259
;-----------------------------------------------------------------------------
kb_reset proc near
in al,61h ;get current control port value
mov ah,al ;save it in AH
or al,80h ;set bit 7
out 61h,al ;send reset value
mov al,ah ;get original value
out 61h,al ;send it out to enable keyboard
cli ;suspend interrupts
mov al,20h ;get EOI value
out 20h,al ;send EOI to 8259
sti ;enable interrupts
ret
kb_reset endp
;-----------------------------------------------------------------------------
;OUTPUT_CHAR writes the designated character directly to video memory.
;Entry: DH,DL - row, column
; AH,AL - attribute, character
;-----------------------------------------------------------------------------
output_char proc near
push di
push dx
push es
mov es,v_segment
assume es:nothing
push bx
push ax
mov bl,v_page ;get page in BX
call video_ptr ;calculate address to write to
cmp adapter,0 ;is this a CGA?
jne output3 ;no, then skip wait loop
mov dx,3DAh ;get CGA Status Register address
output1: in al,dx ;wait until horiz. retrace done
test al,1
jne output1
cli ;suspend interrupts during write
output2: in al,dx ;wait for next horizontal retrace
test al,1
je output2
output3: pop ax ;get character and attribute
stosw ;write them to video memory
sti ;enable interrupts
pop bx ;Restore registers
pop es
assume es:code
pop dx
pop di
ret
output_char endp
;-----------------------------------------------------------------------------
;OPEN_WINDOW draws the window border onto the screen. Character/attribute
;pairs are sent directly to video memory for fast display speed.
;-----------------------------------------------------------------------------
open_window proc near
mov dh,window_row ;get coordinates of window corner
mov dl,window_column
push es
mov es,v_segment ;point ES to video buffer
assume es:nothing
cld ;clear DF for string operations
mov bl,v_page ;get video page in BX
xor bh,bh
call video_ptr ;calculate starting address
;Write the top line of the window border to video.
mov al,218 ;al = left end character
mov ah,border_attr ;set attribute
mov bl,196 ;bl = middle 42 characters
mov dl,191 ;dl = right end character
mov bh,ah ;copy the border attribute
mov dh,ah
call write_line
;Do the window header line.
mov si,offset header_text ;point SI to text of line
call write_header
;Now write the next 18 lines (no text) to the display.
mov cx,6 ;6 lines to do
mov al,179 ;do leftmost column
mov ah,border_attr
mov bl,32 ;do next 38 columns (blank)
mov bh,text_attr
mov dl,179 ;do rightmost column
mov dh,ah
open2: call write_line
loop open2 ;loop until finished
;Write line to seperate the active screen from the help text
mov al,195 ;lower left corner
mov bl,196 ;line character
mov bh,text_attr
mov dl,180
call write_line
;Write the help lines.
mov si,offset help_text1 ;point SI to text of line
call write_header
mov si,offset help_text2 ;point SI to text of line
call write_header
;Finish things up by writing the last line.
mov al,192 ;lower left corner
mov bl,196 ;line character
mov bh,ah ;copy header attribute
mov dl,217
call write_line
pop es
assume es:code
ret
open_window endp
; routine to write a line of text to the window
write_header proc near
mov al,179 ;left window border character
stosw ;write it
mov cx,42 ;42 characters to write
mov ah,header_attr ;use header attribute for these
open1: lodsb ;get the text character
stosw ;write char/attr pair to video
loop open1 ;repeat for all 28
mov ah,border_attr ;do rightmost column
mov al,179
stosw
add di,column_adj ;adjust DI for next line
ret
write_header endp
;Write a line of characters to the window
write_line proc near
push cx
stosw ;write left end character
mov cx,42 ;next 42 characters
mov ax,bx ;get middle characters from bx
rep stosw
mov ax,dx ;get right end character from dx
stosw
add di,column_adj ;adjust DI for next line
pop cx
ret
write_line endp
;-----------------------------------------------------------------------------
;Scroll window up - scroll window up 1 line.
;-----------------------------------------------------------------------------
scroll_window proc near
push ax ;save ax
push cx
mov al," " ;First remove cursor by writing
mov ah,text_attr ; a space over it.
call write_at_cur
mov ch,window_row ;put upper left corner in CX
add ch,2
mov cl,window_column
inc cl
mov dx,cx ;put lower right corner in DX
add dh,5
add dl,41
mov ah,6h ;bios scroll up function
mov al,1 ;scroll 1 line
mov bh,text_attr ;fill the line with blanks
int 10h ;call bios
call set_cursor
pop cx
pop ax
ret ;done
scroll_window endp
set_cursor proc near
mov dl,window_column ;Set up my own cursor location
mov dh,window_row
add dl,38
add dh,7
mov my_cursor_pos,dx
ret
set_cursor endp
;-----------------------------------------------------------------------------
;INITIALIZE redirects the keyboard interrupt, then reserves enough memory for
;the program to remain resident.
;-----------------------------------------------------------------------------
initialize proc near
mov dx,offset program ;Display installation message
mov ah,9
int 21h
;check for other copies of this program in memory.
xor bx,bx ;start search at segment 0
mov word ptr [entry],bx
mov ax,cs ;get current segment
find_loop: inc bx ;check next segment
cmp ax,bx ;did we find ourselves?
je no_copies ;yes, only 1 copy in memory
mov es,bx ;use es as segment pointer
assume es:nothing
mov si,offset entry ;si is the offset pointer
mov di,si ;look the same place in both segs
mov cx,16 ;check 16 bytes
cld ;incriment pointers during compare
repe cmpsb ;compare bytes
jne find_loop ;if no compare, check another seg
mov dx,offset message1 ;Display other copy found message
mov ah,9
int 21h ;Terminate without remaining
mov ax,4c01h ; resident. Return 1 as rc.
int 21h ;terminate and stay resident
no_copies: mov ah,35h ;get current interrupt 9 vector,
mov al,9 ; save it, then replace it with
int 21h ; my own.
mov old_keyboard_int,bx
mov old_keyboard_int[2],es
mov ah,25h
mov al,9
mov dx,offset main ;point it to body of program
int 21h
;Exit by TSR int 31h. Keep enough memory to store the screen.
mov dx,(offset initialize-offset code+1056+15) shr 4
mov ax,3100h ;Terminate with 0 return code.
int 21h ;terminate and stay resident
initialize endp
message1 db "Calc already present.",13,10,"$"
code ends
end entry