home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS 1992 December
/
simtel1292_SIMTEL_1292_Walnut_Creek.iso
/
msdos
/
pcmag
/
vol6n05.arc
/
ASC.ASM
next >
Wrap
Assembly Source File
|
1987-12-13
|
35KB
|
684 lines
;ASC for the IBM Personal Computer - 1986 by Jeff Prosise
;
kb_data equ 60h ;keyboard data port
kb_ctrl equ 61h ;keyboard control port
eoi equ 20h ;8259 end-of-interrupt value
int_ctrl_port equ 20h ;8259 PIC port
a_key equ 30 ;scan code for 'A' key
alt_key equ 8 ;shift code for Alt key
;
bios_data segment at 40h ;BIOS data area
org 63h
addr_6845 dw ? ;6845 Index Register address
bios_data ends
;
code segment para public 'code'
assume cs:code
org 100h
begin: jmp initialize ;jump to initialization code
;
notice db 'Copyright 1986 Ziff-Davis Publishing Co.'
notice2 db 'Programmed by Jeff Prosise '
ibm_signature db 'IBM' ;EGA BIOS signature
adapter db 2 ;0 = CGA, 1 = MDA, 2 = EGA
video_segment dw 0B800h ;video segment address
video_page db ? ;current video page
border_attr db 0Fh ;window border attribute
text_attr db 1Fh ;window text attribute
header_attr db 1Eh ;window header attribute
window_row db 1 ;row of left corner of window
window_column db 3 ;column of left corner of window
start_value db 0 ;first value in ASCII table
int_status db 0 ;status of interrupt routine
cursor_mode dw ? ;cursor scan line definition
old_int_9h label dword ;old interrupt vector
old_keyboard_int dw 2 dup (?)
screen_buffer dw offset initialize ;pointer to screen buffer area
;
enable_values db 2Ch,28h,2Dh,29h, ;values to enable CGA display
db 2Ah,2Eh,1Eh
header_text db ' Dec Hex Char Dec Hex Char ' ;window header text
;
;-----------------------------------------------------------------------------
;Front-end routine for the keyboard interrupt handler. Execution is vectored
;here whenever an interrupt 9 is generated by the PC keyboard.
;-----------------------------------------------------------------------------
ascview proc near
cmp int_status,0 ;is ASCII table already displayed?
jne short_exit ;yes, then exit immediately
sti ;enable interrupts
push ax ;save registers
push bx
push cx
push dx
push si
push di
push ds
push es
in al,kb_data ;get scan code from keyboard
cmp al,a_key ;was the 'A' key pressed?
jne exit ;no, then exit to normal routine
mov ah,2 ;get state of shift keys
int 16h
test al,alt_key ;is the Alt key depressed?
jne asc1 ;yes, then continue
;
;Exit is achieved thru here when execution is to be transferred to the normal
;BIOS keyboard interrupt handling routine.
;
exit: pop es ;restore registers
pop ds
pop di
pop si
pop dx
pop cx
pop bx
pop ax
short_exit: jmp old_int_9h ;goto BIOS keyboard routine
;
;The key combination Alt-A was just pressed. Reset the keyboard and issue an
;EOI to the 8259 PIC to enable hardware interrupts.
;
asc1: call kb_reset ;reset keyboard, end 8259 int
push cs ;set DS and ES to the code segment
pop ds
push cs
pop es
assume ds:code
;
;Check the current video mode to see if it's one of the 80-column text modes
;(2, 3, or 7). If it is, then continue. If it's not, gracefully abort this
;routine by exiting thru an IRET.
;
mov ah,15 ;get video page and display mode
int 10h
cmp al,2 ;video mode 2?
je asc2 ;yes, then continue
cmp al,3 ;mode 3?
je asc2 ;yes, then continue
cmp al,7 ;mode 7 (monochrome)?
je asc2 ;yes, then continue
done: pop es ;restore register values for exit
pop ds
pop di
pop si
pop dx
pop cx
pop bx
pop ax
iret ;return to interrupted program
;
;Save the current video page number and the cursor scan line parameters,
;set the interrupt routine status flag, and blank the cursor.
;
asc2: mov video_page,bh ;save page number
mov int_status,1 ;set interrupt routine status flag
mov ah,3 ;get the cursor shape
int 10h
mov cursor_mode,cx ;save it
mov ah,1 ;hide the cursor for now
mov ch,20h
int 10h
;
;Save the contents of the portion of the screen that will underlie the window.
;
cmp adapter,0 ;is this a CGA?
jne asc3 ;no, then skip disable
call disable_cga ;disable CGA video
asc3: mov di,screen_buffer ;point DI to storage buffer
call save_screen ;save screen contents
;
;Pop the window border onto the display by writing directly to video. Finish
;things up by filling the blank window with the text of the ASCII table.
;
call open_window ;pop up the window border
cmp adapter,0 ;is this a CGA?
jne asc4 ;no, then skip enable
call enable_cga ;enable CGA video
asc4: call fill_window ;write the window text
;
;The window is now displayed on the screen. Wait for a keypress.
;
asc5: mov ah,0 ;get a keypress
int 16h
cmp al,0 ;is it an extended code?
je asc10 ;yes, then jump
cmp al,27 ;ESC key pressed?
jne asc5 ;no, then get another keypress
;
;The ESC key has been pressed. Restore the original contents of the screen,
;restore the cursor, reset the status flag, and exit to the application.
;
cmp adapter,0 ;blank video if CGA installed
jne asc6
call disable_cga
asc6: mov si,screen_buffer ;point SI to holding buffer
call restore_screen ;restore video memory contents
cmp adapter,0 ;enable video if necessary
jne asc7
call enable_cga
asc7: mov ah,1 ;unblank the cursor
mov cx,cursor_mode ;define cursor scan lines
int 10h
mov int_status,0 ;reset status flag
jmp done ;exit
;
;A key has been pressed that returned an extended code.
;
asc10: cmp ah,72 ;Up-Arrow pressed?
jne asc11 ;no, then jump to next test
call scroll_down ;yes, then scroll down
jmp asc5 ;return for another keypress
asc11: cmp ah,80 ;Down-Arrow pressed?
jne asc12
call scroll_up ;scroll window up
jmp asc5
asc12: cmp ah,73 ;PgUp pressed?
jne asc13
call last_window_page ;flip window page back
jmp asc5
asc13: cmp ah,81 ;PgDn pressed?
jne asc14
call next_window_page ;flip window page forward
jmp asc5
asc14: cmp ah,79 ;End pressed?
jne asc15
call end_window_page ;flip to last window page
jmp asc5
asc15: cmp ah,71 ;Home pressed?
jne asc16 ;no, then start again
call first_window_page ;goto first window page
asc16: jmp asc5 ;look for another keypress
ascview endp
;
;-----------------------------------------------------------------------------
;SAVE_SCREEN saves the contents of the screen beneath the window.
;Entry: ES:DI - buffer address
;-----------------------------------------------------------------------------
save_screen proc near
mov dh,window_row ;row and column of window corner
mov dl,window_column
mov bl,video_page ;get video page in BX
xor bh,bh
push di ;save buffer address
call video_offset ;get starting address of window
mov si,di ;transfer address to SI
pop di ;retrieve buffer address
push ds ;save DS
mov ds,video_segment ;set DS to video segment
assume ds:nothing
mov cx,19 ;19 lines to save
cld ;clear DF
save1: push cx ;save line counter
mov cx,30 ;30 words per line
rep movsw ;transfer one line to buffer
add si,100 ;adjust SI for next line
pop cx ;get line count
loop save1 ;loop until done
pop ds ;restore DS
assume ds:code
ret
save_screen endp
;
;-----------------------------------------------------------------------------
;RESTORE_SCREEN restores the contents of the screen beneath the window.
;Entry: DS:SI - buffer address
;-----------------------------------------------------------------------------
restore_screen proc near
mov dh,window_row ;row & column where window starts
mov dl,window_column
mov bl,video_page ;get video page in BX
xor bh,bh
call video_offset ;get window starting address
mov es,video_segment ;set ES to video segment
mov cx,19 ;19 lines to restore
cld ;clear DF
restore1: push cx ;save line counter
mov cx,30 ;30 words per line
rep movsw ;restore one line
add di,100 ;set DI for next line
pop cx ;retrieve count
loop restore1 ;loop until done
ret
restore_screen endp
;
;-----------------------------------------------------------------------------
;VIDEO_OFFSET calculates the offset address in video memory that corresponds
;to the given row, column, and video page.
;Entry: DH,DL - row, column | Exit: DI - offset
; BX - video page |
;-----------------------------------------------------------------------------
video_offset proc near
mov al,160 ;row * 160
mul dh ;result in AX
shl dl,1 ;column * 2
xor dh,dh ;byte to word in DX
add ax,dx ;add the two
mov di,ax ;save result in DI
mov ax,1000h ;length of one video page
mul bx ;page * 1000h
add di,ax ;complete the offset address
ret
video_offset endp
;
;-----------------------------------------------------------------------------
;DISABLE_CGA and ENABLE_CGA routines disable and enable CGA video by writing
;to the Mode Select Register at port address 3D8h.
;-----------------------------------------------------------------------------
disable_cga proc near
mov dx,3DAh ;load Status Register address
disable1: in al,dx ;get video status
test al,8 ;vertical retrace active?
je disable1 ;no, then wait until it is
sub dx,2 ;load MSR address
mov al,25h ;value to disable video
out dx,al ;send it to the adapter
ret
disable_cga endp
;
enable_cga proc near
mov ah,15 ;get current video mode
int 10h
lea bx,enable_values ;offset of enable value table
xlat ;get the value to enable video
mov dx,3D8h ;load address of MSR
out dx,al ;OUT the enable value
ret
enable_cga endp
;
;-----------------------------------------------------------------------------
;KB_RESET resets the keyboard and issues an EOI to the 8259 PIC.
;-----------------------------------------------------------------------------
kb_reset proc near
in al,kb_ctrl ;get current control port value
mov ah,al ;save it in AH
or al,80h ;set bit 7
out kb_ctrl,al ;send reset value
mov al,ah ;get original value
out kb_ctrl,al ;send it out to enable keyboard
cli ;suspend interrupts
mov al,eoi ;get EOI value
out int_ctrl_port,al ;send EOI to 8259
sti ;enable interrupts
ret
kb_reset endp
;
;-----------------------------------------------------------------------------
;BIN2ASC outputs a byte value to the screen in decimal or hexadecimal form.
;Entry: DH,DL - row, column
; AL - number to output
; BL - divisor (10 for decimal output, 16 for hex)
;-----------------------------------------------------------------------------
bin2asc proc near
push ax ;save value to output
xor cx,cx ;zero digit counter
xor ah,ah ;byte to word in AX
bin1: div bl ;divide AX by 10 or 16
push ax ;save AH (remainder)
inc cx ;increment counter
xor ah,ah ;byte to word (quotient)
or al,al ;was the quotient zero?
jne bin1 ;no, then go back for more
bin2: pop ax ;get digit to print next
mov al,ah ;put it in AL
or al,48 ;convert binary to ASCII
cmp al,'9' ;alphanumeric hex digit?
jna bin3 ;no, then jump ahead
add al,7 ;yes, then more conversion needed
bin3: mov ah,text_attr ;set text attribute for write
call output_char ;print the digit
inc dl ;advance cursor
loop bin2 ;loop until done
pop ax ;restore value of AX
ret
bin2asc 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 dx ;save DX and AX
push ax
mov bl,video_page ;get page in BX
xor bh,bh
call video_offset ;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 dx ;restore DX
ret
output_char endp
;
;-----------------------------------------------------------------------------
;FILL_WINDOW writes the text of the ASCII table to the window.
;-----------------------------------------------------------------------------
fill_window proc near
mov dh,window_row ;set DH for first text row
add dh,2
mov al,start_value ;get first table value in AL
mov cx,16 ;16 lines to fill
fill1: push cx ;save line counter
mov dl,window_column ;specify starting column
add dl,2
call write_line ;write first half of the line
add dl,5 ;advance cursor for next half
add al,16 ;adjust AL for next half
call write_line ;write second half
sub al,15 ;set AL for start of next line
inc dh ;set cursor for next row
pop cx ;retrieve line counter
loop fill1 ;loop until table full
ret
fill_window endp
;
;-----------------------------------------------------------------------------
;WRITE_LINE outputs one ASCII character and its decimal and hex equivalents.
;Entry: AL - ASCII code for character to output
; DH,DL - row, column to start writing at
;-----------------------------------------------------------------------------
write_line proc near
mov es,video_segment ;set ES to video for writing
cmp al,100 ;advance cursor if AL < 100
jae write1
inc dl
cmp al,10 ;advance again if AL < 10
jae write1
inc dl
write1: mov bl,10 ;set BL for decimal output
call bin2asc ;output decimal text
add dl,2 ;set cursor to next field
cmp al,10h ;advance cursor if AL < 10h
jae write2
inc dl
write2: mov bl,16 ;set BL for hex output
call bin2asc ;output hexadecimal text
add dl,2 ;goto next field
mov ah,text_attr ;set text attribute
call output_char ;print the character
ret
write_line 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
mov es,video_segment ;point ES to video buffer
cld ;clear DF for string operations
mov bl,video_page ;get video page in BX
xor bh,bh
call video_offset ;calculate starting address
;
;Write the top line of the window border to video.
;
mov al,218 ;start with upper left corner
mov ah,border_attr ;set attribute
stosw
mov cx,28 ;do the next 28 characters
mov al,196
rep stosw
mov al,191 ;do upper right corner
stosw
;
;Do the window header line.
;
add di,100 ;set DI for new line
mov al,179 ;left window border character
stosw ;write it
lea si,header_text ;point SI to text of line
mov cx,28 ;28 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
;
;Now write the next 16 lines (no text) to the display.
;
add di,100 ;set DI for new line
mov cx,16 ;16 lines to do
open2: push cx ;save line counter
mov al,179 ;do leftmost column
mov ah,border_attr
stosw
mov al,32 ;do next 28 columns (blank)
mov ah,text_attr
mov cx,28
rep stosw
mov al,179 ;do rightmost column
mov ah,border_attr
stosw
add di,100 ;adjust DI for next line
pop cx ;retrieve counter
loop open2 ;loop until finished
;
;Finish things up by writing the last line.
;
mov al,192 ;lower left corner
stosw
mov cx,28 ;next 28 characters
mov al,196
rep stosw
mov al,217 ;lower right corner
stosw
ret
open_window endp
;
;-----------------------------------------------------------------------------
;SCROLL_UP scrolls the window contents up one line.
;-----------------------------------------------------------------------------
scroll_up proc near
cmp start_value,224 ;can we scroll more?
je scrup1 ;no, then do nothing
mov ah,6 ;use INT 10h to scroll up
mov al,1 ;one line
call define_window ;set CX and DX for scroll
mov bh,text_attr ;define attribute
int 10h ;scroll up
inc start_value ;adjust starting value
mov al,start_value ;calculate new value to write
add al,15
mov dh,window_row ;determine row to be written to
add dh,17
mov dl,window_column ;set starting column
add dl,2
call write_line ;write the first half of new line
add al,16 ;adjust AL
add dl,5 ;move cursor to next half of line
call write_line ;write second half
scrup1: ret
scroll_up endp
;
;-----------------------------------------------------------------------------
;SCROLL_DOWN scrolls the window contents down one line.
;-----------------------------------------------------------------------------
scroll_down proc near
cmp start_value,0 ;can we scroll more?
je scrdn1 ;no, then do nothing
mov ah,7 ;use INT 10h to scroll down
mov al,1 ;one line
call define_window ;set CX and DX
mov bh,text_attr ;set attribute
int 10h ;do the scroll
dec start_value ;adjust starting value
mov al,start_value ;new value to write
mov dh,window_row ;determine row for write
add dh,2
mov dl,window_column ;set starting column
add dl,2
call write_line ;write new line at top
add al,16 ;adjust AL
add dl,5 ;move cursor to second half
call write_line ;write second half of line
scrdn1: ret
scroll_down endp
;
;-----------------------------------------------------------------------------
;LAST_WINDOW_PAGE pages the window back one page.
;-----------------------------------------------------------------------------
last_window_page proc near
cmp start_value,0 ;can we go back more?
je last3 ;no, then do nothing
cmp start_value,31 ;starting value > 31?
ja last1 ;yes, then jump
mov start_value,0 ;no, then zero value
jmp last2 ;skip next line
last1: sub start_value,32 ;adjust START_VALUE
last2: call clear_window ;clear interior of window
call fill_window ;write the new table
last3: ret
last_window_page endp
;
;-----------------------------------------------------------------------------
;NEXT_WINDOW_PAGE pages the window forward one page.
;-----------------------------------------------------------------------------
next_window_page proc near
cmp start_value,224 ;can we go forward more?
je next3 ;no, then do nothing
cmp start_value,193 ;starting value <193?
jb next1 ;yes, then jump
mov start_value,224 ;set START_VALUE to maximum
jmp next2 ;skip next line
next1: add start_value,32 ;adjust for next page
next2: call clear_window ;clear window
call fill_window ;write new text page
next3: ret
next_window_page endp
;
;-----------------------------------------------------------------------------
;FIRST_WINDOW_PAGE flips the window contents to the first page.
;-----------------------------------------------------------------------------
first_window_page proc near
cmp start_value,0 ;already at beginning?
je first1 ;yes, then do nothing
mov start_value,0 ;zero starting value
call clear_window ;clear window
call fill_window ;write text to window
first1: ret
first_window_page endp
;
;-----------------------------------------------------------------------------
;END_WINDOW_PAGE flips the window contents to the final page.
;-----------------------------------------------------------------------------
end_window_page proc near
cmp start_value,224 ;already at end?
je end1 ;yes, then do nothing
mov start_value,224 ;set START_VALUE to maximum
call clear_window ;clear window
call fill_window ;write window text
end1: ret
end_window_page endp
;
;-----------------------------------------------------------------------------
;CLEAR_WINDOW clears the interior of the window.
;-----------------------------------------------------------------------------
clear_window proc near
mov ah,6 ;use INT 10h to clear window
mov al,0 ;code for blank function
call define_window ;set CX and DX
mov bh,text_attr ;set attribute for cleared area
int 10h ;perform the clear
ret
clear_window endp
;
;-----------------------------------------------------------------------------
;DEFINE_WINDOW sets the values of CX and DX for call to BIOS scroll functions.
;-----------------------------------------------------------------------------
define_window proc near
mov ch,window_row ;define upper left corner in CX
add ch,2
mov cl,window_column
add cl,2
mov dx,cx ;define lower right corner in DX
add dh,15
add dl,25
ret
define_window endp
;
;-----------------------------------------------------------------------------
;INITIALIZE performs a variety of tasks to set the stage for the resident part
;of the program.
;-----------------------------------------------------------------------------
initialize proc near
;
;See if the display adapter is an EGA by looking for the EGA BIOS signature.
;
mov ax,0C000h ;set ES to EGA BIOS segment
mov es,ax
mov di,1Eh ;point DI to signature location
lea si,ibm_signature ;point SI to 'IBM' text
mov cx,3 ;three bytes to compare
cld ;clear DF
repe cmpsb ;check three bytes
je init1 ;jump if EGA signature found
;
;The display adapter is not an EGA. Determine whether it's a CGA or an MDA.
;
mov adapter,0 ;zero ADAPTER for CGA
mov ah,15 ;get video mode
int 10h
cmp al,7 ;is it mode 7?
jne init1 ;no, then it's a CGA - jump
inc adapter ;yes, it's an MDA - set ADAPTER
;
;If this is a monochrome system, modify color-dependent values accordingly.
;
init1: cmp al,7 ;is current video mode 7?
je init2 ;yes, then branch
cmp al,15 ;is current video mode 15?
jne init3 ;no, then skip modification code
init2: sub video_segment,800h ;set VIDEO_SEGMENT for monochrome
mov border_attr,70h ;change attributes for monochrome
mov text_attr,07h
mov header_attr,07h
;
;Reset the cursor to scan lines 6 and 7 (color) or 12 and 13 (monochrome).
;
init3: mov cx,0C0Dh ;monochrome cursor type
cmp al,7 ;video mode 7?
je init4 ;yes, then jump
cmp al,15 ;video mode 15?
je init4 ;yes, then jump
mov cx,0607h ;scan lines 6 and 7 for color
init4: mov ah,1 ;set cursor type
int 10h
;
;Now save the old interrupt 9 vector and replace it with one pointing to the
;code that we will leave behind in memory.
;
mov ah,35h ;get current interrupt 9 vector
mov al,9
int 21h
mov old_keyboard_int,bx ;save vector offset
mov old_keyboard_int[2],es ;save vector segment
mov ah,25h ;set new vector
mov al,9
lea dx,ascview ;point it to body of program
int 21h
;
;Exit thru INT 27h and reserve enough room beyond the actual code to store the
;contents of the area of screen that underlies the pop-up window.
;
mov dx,offset initialize+1140 ;reserve space for code and buffer
int 27h ;terminate-but-stay-resident
initialize endp
;
code ends
end begin