Category : Files from Magazines
Archive   : VOL7N2.ZIP
Filename : PAINT.ASM
;PAINT.COM for the IBM Personal Computer - 1987 by Jeff Prosise
bios_data segment at 40h
org 1Ah
buffer_head dw ? ;pointer to keyboard buffer head
buffer_tail dw ? ;pointer to keyboard buffer tail
org 63h
addr_6845 dw ? ;CRT Controller address
org 80h
buffer_start dw ? ;starting keyboard buffer address
buffer_end dw ? ;ending keyboard buffer address
bios_data ends
code segment para public 'code'
assume cs:code
org 100h
begin: jmp main
copyright db 'PAINT 1.0 (c) 1987 Ziff Communications Co.',13,10
db 'PC Magazine ',254,' Jeff Prosise',13,10,'$',1Ah
errmsg1 db 13,10,'File not found',13,10,'$'
errmsg2 db 13,10,'Error reading file',13,10,'$'
errmsg3 db 13,10,'Not enough memory',13,10,'$'
kbcode db 10h ;get keystroke function code
fileptr dw 81h ;pointer to file name text
data_segment dw ? ;buffer segment
doscolor db ? ;screen color before execution
insert_flag db 0 ;0=overwrite, 1=insert
write_mode db 0 ;video insertion mode
bufferptr db 0 ;pointer to current buffer page
maxpage db 0 ;maximum page number
mode db 0 ;0=color, 1=monochrome
columns db ? ;number of display columns -1
lcolumns dw ? ;number of display columns
crtc_addr dw ? ;CRT Controller base address
blockxy dw 0FFFFh ;first corner of block
attribute db 1Fh ;current paint attribute
graphics db 0 ;current box character
defchar db ? ;default box character
boxid db ? ;box environment byte
maskval db 01010101b ;graphics mask value
menu_attr db 1Bh ;menu line attribute
video_segment dw 0B800h ;video buffer segment
delta dw ? ;line length variable
linelength dw ? ;line length in bytes
keyboard label dword
old9h dw 2 dup (?) ;old interrupt 9h vector
quit_text db 'Exit to DOS (Y/N)?',0
save_text db 'Save as: ',0
box_text db '1-',179,32,32,'2-',186,32,32,'3-',176,32,32,'4-',177,32,32
db '5-',178,32,32,'6-',219,32,32,'7-*',0
fore_text db 'Foreground: 0 1 2 3 4 5 6 7 8 9 '
db 'A B C D E F',0
back_text db 'Back',0
mono_text db '1-Normal 2-Reverse 3-Bold 4-Underline',0
block_text db '1-Clear 2-Paint',0
mode_text db '1-Text Only 2-Text and Attributes',0
help_text db 'F1-Help F2-Attribute F3-Line F4-Mode F5-Block '
db 'F6-Save F7-Quit',0
key_table db 81,73,65,64,63,62,61,60,59,160,157,155,152,145,116
db 115,141,83,82,79,71,80,77,75,72
gr_table db 17,81,145,98,96,144,162,34,160,130,66,129,80
db 5,69,84,21,68,85,25,38,10,40,138,168,42,136
db 170,137,70,152,100,6,9,24,36,102,153,65,20
box_table db 176,177,178,219,42
mono_table db 7,70h,0Fh,1
jump_table dw offset up ;cursor-up key
dw offset left ;cursor-left key
dw offset right ;cursor-right key
dw offset down ;cursor-down key
dw offset home ;HOME key
dw offset endkey ;END key
dw offset insert ;INS key
dw offset delete ;DEL key
dw offset up ;Ctrl-Up
dw offset left ;Ctrl-Left
dw offset right ;Ctrl-Right
dw offset down ;Ctrl-Down
dw offset BoxUp ;Alt-Up
dw offset BoxLeft ;Alt-Left
dw offset BoxRight ;Alt-Right
dw offset BoxDown ;Alt-Down
dw offset help ;F1
dw offset SelectAttr ;F2
dw offset SelectBox ;F3
dw offset SelectMode ;F4
dw offset block ;F5
dw offset save ;F6
dw offset quit ;F7
dw offset PgUp ;PgUp
dw offset PgDn ;PgDn
;-----------------------------------------------------------------------------
;KBINT handles interrupt 9 and generates new extended keycodes.
;-----------------------------------------------------------------------------
kbint proc near
sti ;interrupts on
push ax ;save AX and BX
push bx
in al,60h ;read scan code
cmp al,72 ;cursor up?
je checkctrl
cmp al,75 ;cursor left?
je checkctrl
cmp al,77 ;cursor right?
je checkctrl
cmp al,80 ;cursor down?
je checkctrl
oldint: pop bx ;restore AX and BX
pop ax
jmp keyboard ;exit to BIOS handler
;
;Generate extended codes for Ctrl-Up, Down.
;
checkctrl: mov bl,al ;save scan code in BL
mov ah,2 ;get shift key status
int 16h
test al,4 ;Ctrl key pressed?
jz checkalt ;no, then check Alt key
cmp bl,75 ;pass left or right key to BIOS
je oldint
cmp bl,77
je oldint
add bl,45h ;create new extended code
cmp bl,141
je process
mov bl,145
jmp short process ;process it
;
;Generate extended codes for Alt-Up, Down, Left, and Right.
;
checkalt: test al,8 ;Alt key pressed?
jz oldint ;no, then exit to BIOS
add bl,50h ;create new extended code
;
;Reset the keyboard and clear the interrupt.
;
process: in al,61h ;read control port value
mov ah,al ;save it in AH
or al,80h ;set the high bit
out 61h,al ;reset keyboard
mov al,ah ;retrieve original value
out 61h,al ;enable keyboard
cli
mov al,20h ;end the interrupt
out 20h,al
sti
;
;Insert the new keycode into the keyboard buffer.
;
mov ah,bl ;transfer code to AH
xor al,al ;zero AL
push dx ;save DX and DS
push ds
mov bx,bios_data ;point DS to BIOS data area
mov ds,bx
assume ds:bios_data
cli ;interrupts off
mov bx,buffer_tail ;get current tail address
mov dx,bx ;transfer it to DX
add dx,2 ;advance to next position
cmp dx,buffer_end ;wrap around if necessary
jne buffer
mov dx,buffer_start
buffer: cmp dx,buffer_head ;is the buffer full?
je kbexit ;yes, then exit now
mov [bx],ax ;insert keycode into buffer
mov buffer_tail,dx ;advance tail
kbexit: sti ;enable interrupts
pop ds ;restore registers
assume ds:nothing
pop dx
pop bx
pop ax
iret ;and exit
kbint endp
;-----------------------------------------------------------------------------
;MAIN is the main procedure.
;-----------------------------------------------------------------------------
main proc near
assume cs:code,ds:code,es:code
;
;Request 64K of memory for buffer space.
;
mov ah,4Ah ;release memory beyond code seg
mov bx,4096
int 21h
mov ah,48h ;request 4000 paragraphs
mov bx,4000
int 21h
jnc save_seg ;continue if request granted
mov dx,offset errmsg3 ;abort if request denied
jmp short error_exit
save_seg: mov data_segment,ax ;save data segment
;
;Parse the command line for a filename.
;
cld ;clear DF
mov si,81h ;point SI to command line
parse1: lodsb ;find first non-space
cmp al,32
je parse1
cmp al,13 ;end-of-line?
jne filefound ;no, then it's a filename
mov byte ptr ds:[81h],0 ;place delimiter for no file
jmp short exbios ;skip file load
filefound: dec si ;save starting text address
mov fileptr,si
mov bl,ds:[80h] ;zero last byte in string
xor bh,bh
mov byte ptr ds:[bx+81h],0
;
;Open the file designated on the command line for input.
;
mov ax,3D00h ;request read-only access
mov dx,si ;point DS:DX to filespec
int 21h
jnc read_file ;branch if open succeeded
mov dx,offset errmsg1 ;print 'File not found'
error_exit: mov ah,9
int 21h
mov ax,4C01h ;terminate with error code
int 21h
;
;Read character/attribute data from the file.
;
read_file: mov bx,ax ;transfer file handle to BX
mov ah,3Fh ;read file data
mov cx,64000 ;request 64,000 bytes
push ds ;point DS to data segment
mov ds,data_segment
assume ds:nothing
xor dx,dx ;point DX to beginning of segment
int 21h
pop ds ;restore DS
assume ds:code
jnc check_read ;branch if no error occurred
mov dx,offset errmsg2 ;abort on read error
jmp short error_exit
check_read: or ax,ax ;abort if no bytes read
jne paginate
mov dx,offset errmsg2
jmp short error_exit
paginate: dec ax ;decrement count by 1
xor dx,dx ;determine number of pages
mov bx,4000
div bx
mov maxpage,al ;save maximum page number
close_file: mov ah,3Eh ;close file
int 21h
;
;Determine whether or not the BIOS supports extended keyboard functions.
;
exbios: mov ah,5 ;write FFFFh to keyboard buffer
mov cx,0FFFFh
int 16h
mov ah,10h ;then read it back
int 16h
cmp ax,0FFFFh ;is AX set correctly?
je video ;yes, then don't reset INT 9
;
;Point the interrupt 9 vector to the internal keyboard handler.
;
mov kbcode,0 ;modify KBCODE for old BIOS
push es
assume es:nothing
mov ax,3509h ;get current vector
int 21h
mov old9h,bx ;save it
mov old9h[2],es
mov ax,2509h ;then reset it
mov dx,offset kbint
int 21h
pop es
assume es:code
;
;Determine whether video is color or monochrome.
;
video: push ds ;get address of CRT Controller
mov ax,bios_data
mov ds,ax
assume ds:bios_data
mov ax,addr_6845
mov crtc_addr,ax ;save it
pop ds
assume ds:code
test ax,40h ;is bit 6 of CRTC address set?
jnz more_video ;yes, then it's a color adapter
inc mode ;no, then it's monochrome
mov video_segment,0B000h ;modify attributes for monochrome
mov attribute,7
mov menu_attr,7
;
;Determine number of display columns, screen color, and video page number.
;
more_video: mov ah,15 ;get number of columns and page
int 10h
mov al,ah
xor ah,ah
mov lcolumns,ax ;store number of columns
mov linelength,ax ;store line length in bytes
shl linelength,1
dec al ;store number of columns - 1
mov columns,al
mov cl,79 ;determine number of bytes
sub cl,al ; from end of one line to
shl cl,1 ; beginning of next
xor ch,ch
mov delta,cx
cmp al,79 ;adjust if more than 80 columns
jna no_adjust ; are currently displayed
mov columns,79
mov lcolumns,80
mov delta,0
no_adjust: mov ah,8 ;read current attribute
int 10h
mov doscolor,ah ;store it for use upon exit
or bh,bh ;make sure page zero is active
je clrscr
mov ax,0500h ;activate it if it's not
int 10h
;
;Clear the screen or write data read from screen file to video memory.
;
clrscr: cmp byte ptr ds:[81h],0 ;was a data file read?
jne show_file ;yes, then display contents
mov bh,attribute ;no, then clear the screen
call ClearScreen
jmp short getkey ;leave starting screen blank
show_file: call ShowFile
;
;Solicit keystrokes and process non-extended keycodes.
;
getkey: call ReadKey ;get a keystroke
or al,al ;branch on entry of extended code
je excode
cmp al,0E0h
je excode
cmp al,8 ;BACKSPACE key?
jne enter
or dl,dl ;currently at left edge of screen?
je getkey ;yes, then ignore it
call backspace ;delete last character
jmp short getkey ;return for more
enter: cmp al,13 ;ENTER key?
jne escape
mov ah,2 ;advance cursor to next line
xor bh,bh
inc dh
cmp dh,25 ;wrap around if necessary
jne nowrap
xor dh,dh
nowrap: xor dl,dl
int 10h
jmp short getkey ;return for more
escape: cmp al,27 ;ESC key?
jne charkey
mov blockxy,0FFFFh ;reset block indicator
jmp short getkey ;return for more
charkey: cmp al,32 ;character key?
jb getkey ;no, then ignore it
call WriteChar ;yes, then process it
jmp short getkey ;return for more
;
;Process an extended keycode.
;
excode: mov al,ah ;transfer keycode to AL
mov di,offset key_table ;point DI to keycode table
mov cx,25 ;25 keycodes to check
repne scasb ;scan table for current keycode
jne getkey ;return if keycode not found
mov bx,cx ;move index to BX
pcall: shl bx,1 ;double it
call cs:[offset jump_table+bx] ;call handling routine
mov ah,3 ;get cursor position
xor bh,bh
int 10h
jmp short getkey ;return for more
main endp
;-----------------------------------------------------------------------------
;ShowFile displays a 4000-byte block indexed by DATA_SEGMENT and BUFFERPTR.
;-----------------------------------------------------------------------------
ShowFile proc near
mov ax,4000 ;find starting buffer address
mov bl,bufferptr
xor bh,bh
mul bx
mov si,ax ;transfer it to SI
push ds ;point DS to data segment
assume ds:nothing
mov ds,data_segment
xor dx,dx ;set DX for home cursor position
mov cx,25 ;25 display lines
show1: push cx
mov cx,lcolumns ;number of display columns
show2: push cx
mov ah,2 ;position the cursor
int 10h
lodsw ;get one C/A pair
mov bl,ah ;transfer attribute to BL
mov ah,9 ;display character and attribute
mov cx,1
int 10h
inc dl ;advance cursor
pop cx
loop show2 ;loop until this line is done
inc dh ;home cursor to start of next line
xor dl,dl
add si,delta ;adjust SI for other than 80 cols
pop cx
loop show1 ;loop until 25 lines are done
pop ds ;restore DS
assume ds:code
mov ah,2 ;home the cursor
xor dx,dx
int 10h
ret ;and exit
ShowFile endp
;-----------------------------------------------------------------------------
;ReadKey reads a keypress.
;Exit: AX - keycode
;-----------------------------------------------------------------------------
ReadKey proc near
mov ah,kbcode ;get function code
inc ah
int 16h ;check buffer status
jnz read ;branch if a keycode is ready
int 28h ;generate interrupt 28h
jmp short ReadKey ;enter polling loop again
read: mov ah,kbcode ;read key
int 16h
ret ;and exit
ReadKey endp
;-----------------------------------------------------------------------------
;ClearScreen clears the 25-line viewing area and homes the cursor.
;Entry: BH - attribute
;-----------------------------------------------------------------------------
ClearScreen proc near
mov ax,0600h ;clear screen with function 6
xor cx,cx
mov dh,24
mov dl,columns
int 10h
mov ah,2 ;home the cursor
xor dx,dx
xor bh,bh
int 10h
ret
ClearScreen endp
;-----------------------------------------------------------------------------
;HELP presents a help line denoting function key assignments.
;-----------------------------------------------------------------------------
help proc near
mov si,offset help_text ;display help text
call MenuLine
help1: call readkey ;wait for a keypress
call RestoreLine ;erase the help line
ret
help endp
;-----------------------------------------------------------------------------
;QUIT exits the application.
;-----------------------------------------------------------------------------
quit proc near
mov si,offset quit_text ;display menu line
call MenuLine
quit1: call ReadKey ;get a keypress
and al,0DFh ;capitalize response
cmp al,'Y' ;was the response 'Yes?'
je quit2 ;yes, then exit
cmp al,'N' ;was the response 'No?'
je quit4 ;yes, then return to application
cmp al,27 ;ESC key?
je quit4 ;yes, then return to application
jmp short quit1 ;get another keypress
quit2: mov bh,doscolor ;clear screen before exit
call ClearScreen
mov ah,1 ;restore the cursor
mov cx,cursor_mode
int 10h
cmp kbcode,10h ;skip ahead if extended BIOS
je quit3
xor ax,ax ;restore the interrupt 9 vector
mov es,ax
cli
mov ax,old9h
mov es:[24h],ax
mov ax,old9h[2]
mov es:[26h],ax
sti
quit3: add sp,4 ;clean up the stack
mov ah,9
mov dx,offset copyright
int 21h
mov ax,4C00h ;terminate
int 21h
quit4: call RestoreLine ;restore menu line
ret ;return to application
quit endp
;-----------------------------------------------------------------------------
;SAVE saves the current screen to disk.
;-----------------------------------------------------------------------------
save proc near
mov si,offset save_text ;display menu line
call MenuLine
mov ah,1 ;display cursor
mov cx,cursor_mode
int 10h
getname: mov si,fileptr ;read filespec from keyboard
mov di,si
mov cl,70
mov dx,1809h
call ReadString
or cl,cl ;stop if nothing was entered
jne save_file
call RestoreLine
ret
save_file: mov ah,3Ch ;open the file for writing
xor cx,cx
mov dx,fileptr
int 21h
jc save_error ;jump on error
push ax ;save file handle
call RestoreLine ;restore menu line
mov ah,3 ;read and save cursor position
xor bh,bh
int 10h
push dx
call PutVideo ;copy video to data buffer
mov ah,2 ;reset cursor
pop dx
int 10h
mov ax,4000 ;write data to disk
mov bl,maxpage
inc bl
xor bh,bh
mul bx
mov cx,ax
mov ah,40h
pop bx
push ds
mov ds,data_segment
assume ds:nothing
xor dx,dx
int 21h
pop ds
assume ds:code
mov ah,3Eh ;close the file
int 21h
ret
;
;An error was encountered opening the file. Resolicit the filespec.
;
save_error: mov ax,0E07h ;beep
int 10h
mov ah,2 ;reset cursor
mov dx,1809h
xor bh,bh
int 10h
jmp getname ;solicit filespec again
save endp
;-----------------------------------------------------------------------------
;PutVideo writes the current video page to the data buffer.
;-----------------------------------------------------------------------------
PutVideo proc near
push es ;point ES:DI to buffer
mov es,data_segment
assume es:nothing
mov ax,4000
mov bl,bufferptr
xor bh,bh
mul bx
mov di,ax
xor dx,dx
mov cx,25 ;copy contents a line at a time
put2: push cx
mov cx,lcolumns
put3: mov ah,2
int 10h
mov ah,8
int 10h
stosw
inc dl
loop put3
add di,delta ;adjust for other than 80 columns
inc dh
xor dl,dl
pop cx
loop put2
pop es ;restore ES and exit
assume es:code
ret
PutVideo endp
;-----------------------------------------------------------------------------
;BLOCK function allows screen regions to be cleared or painted.
;-----------------------------------------------------------------------------
bwidth db ? ;block width in columns
firstcol db ? ;starting column number of block
c_loc dw ? ;cursor position
block proc near
cmp blockxy,0FFFFh ;first corner?
jne block1 ;no, then branch
mov blockxy,dx ;yes, then save cursor position
ret ;and exit
block1: mov cx,blockxy ;retrieve opposite corner location
cmp cx,dx ;are CX and DX the same?
jne notfull ;no, then branch
xor cx,cx ;yes, then indicate full screen
mov dh,24
mov dl,columns
jmp short block3
notfull: cmp cl,dl ;swap if necessary
jbe block2
xchg cl,dl
block2: cmp ch,dh
jbe block3
xchg ch,dh
block3: push cx ;save block parameters
push dx
mov si,offset block_text ;display menu line
call MenuLine
;
;Block corners are recorded. Get menu option and act accordingly.
;
block4: call ReadKey ;get response
cmp al,27 ;ESC key?
jne block5
mov blockxy,0FFFFh ;reset block definition
call RestoreLine ;restore menu line
pop dx ;clean up the stack and exit
pop cx
ret
;
;Clear the indicated region if menu option '1' was selected.
;
block5: cmp al,'1' ;clear region?
jne block6
call RestoreLine ;restore last line
pop dx ;retrieve block corrdinates
pop cx
mov ax,0600h ;yes, then clear contents
mov bh,attribute
int 10h
mov blockxy,0FFFFh ;reset block function
ret
;
;Paint the indicated region if menu option '2' was selected.
;
block6: cmp al,'2' ;paint region?
jne block4
call RestoreLine ;restore menu line
mov ah,3 ;get cursor position
xor bh,bh
int 10h
mov c_loc,dx ;save it
pop cx ;retrieve and swap parameters
pop dx
mov firstcol,dl ;save starting column number
sub cl,dl ;determine block width
inc cl
mov bwidth,cl
sub ch,dh ;then block height
inc ch
mov cl,ch
xor ch,ch
block7: push cx ;scan region and set attributes
mov cl,bwidth
xor ch,ch
block8: push cx
mov ah,2 ;position cursor
int 10h
call SetAttribute ;set current attribute
inc dl ;advance to next column
pop cx
loop block8 ;loop until this line is complete
inc dh ;advance to next row
mov dl,firstcol
pop cx
loop block7 ;loop until all lines are done
mov ah,2 ;restore cursor position
mov dx,c_loc
int 10h
mov blockxy,0FFFFh ;reset block function
ret
block endp
;-----------------------------------------------------------------------------
;Box routines process presses of Alt-Up, Down, Right, and Left.
;-----------------------------------------------------------------------------
BoxUp proc near
or dh,dh ;top line?
je boxup1 ;yes, then ignore keypress
mov al,7 ;generate default code
mul graphics
add al,179
mov bx,dx ;define next cursor location
dec bh
mov boxid,16 ;bias box code
call DrawChars ;draw box characters
boxup1: ret
BoxUp endp
BoxDown proc near
cmp dh,24 ;bottom line?
je boxdn1 ;yes, then ignore keypress
mov al,7 ;generate default code
mul graphics
add al,179
mov bx,dx ;define next cursor location
inc bh
mov boxid,1 ;bias box code
call DrawChars ;draw box characters
boxdn1: ret
BoxDown endp
BoxLeft proc near
or dl,dl ;left edge of screen?
je boxlf1 ;yes, then ignore keypress
mov al,9 ;generate default code
mul graphics
add al,196
mov bx,dx ;define next cursor location
dec bl
mov boxid,4 ;bias box code
call DrawChars ;draw box characters
boxlf1: ret
BoxLeft endp
BoxRight proc near
cmp dl,columns ;right edge of screen?
je boxrt1 ;yes, then ignore keypress
mov al,9 ;generate default code
mul graphics
add al,196
mov bx,dx ;define next cursor location
inc bl
mov boxid,64 ;bias box code
call DrawChars ;draw box characters
boxrt1: ret
BoxRight endp
;-----------------------------------------------------------------------------
;DrawChars is called by the directional box routines to draw box characters.
;Entry: AL - default box character
; BX - next cursor position
;-----------------------------------------------------------------------------
DrawChars proc near
mov defchar,al ;store default code
push bx ;save next cursor location
cmp graphics,0 ;adjust BOXID if double line
je drawc1
shl boxid,1
drawc1: call DrawBox ;draw character in current cell
mov ah,2 ;move cursor
pop dx
xor bh,bh
int 10h
mov boxid,0 ;reset box code
call DrawBox ;draw next box character
ret
DrawChars endp
;-----------------------------------------------------------------------------
;DrawBox draws the appropriate box character at the current cursor position.
;-----------------------------------------------------------------------------
DrawBox proc near
cmp graphics,1 ;branch if line drawing characters
jna db1 ; are selected
mov al,graphics ;form character code
sub al,2
mov bx,offset box_table
xlat box_table
jmp short db3 ;display character and exit
db1: call GetEnv ;determine character code
mov al,boxid ;transfer to AL
mov cl,4 ;generate desired character code
rol al,cl
mov cx,40 ;see if character exists
mov di,offset gr_table
repne scasb
je db2 ;display it if it does
and al,maskval ;mask mixed characters
mov cx,40 ;search again
mov di,offset gr_table
repne scasb
je db2
mov al,defchar ;default to line character
jmp short db3
db2: mov al,218 ;print the character
sub al,cl
db3: call DisplayChar
ret
DrawBox endp
;-----------------------------------------------------------------------------
;SelectMode lets the user specify whether attributes will be inserted
;along with typed text.
;-----------------------------------------------------------------------------
SelectMode proc near
mov si,offset mode_text ;display menu line
call MenuLine
sm1: call ReadKey ;get response
cmp al,27 ;check for ESC key
je sm2
cmp al,'1' ;reject invalid entries
jb sm1
cmp al,'2'
ja sm1
sub al,'1' ;normalize entry
mov write_mode,al ;save it
sm2: call Restoreline ;close and exit
ret
SelectMode endp
;-----------------------------------------------------------------------------
;SelectBox pops up the box character selection menu.
;-----------------------------------------------------------------------------
SelectBox proc near
mov si,offset box_text ;display menu line
call MenuLine
sb1: call ReadKey ;get response
cmp al,27 ;ESC key?
je sb3 ;yes, then exit
cmp al,13 ;ENTER?
je sb3 ;yes, then exit
cmp al,'1' ;reject invalid responses
jb sb1
cmp al,'7'
ja sb1
sub al,'1' ;normalize entry
mov graphics,al ;then store it
or al,al ;set mask value
jne sb2
mov maskval,01010101b
jmp short sb3
sb2: cmp al,1
jne sb3
mov maskval,10101010b
sb3: call RestoreLine ;restore menu line
ret
SelectBox endp
;-----------------------------------------------------------------------------
;SelectAttr pops up the screen attribute selection menu.
;-----------------------------------------------------------------------------
tmpcol db ? ;attribute under cursor
SelectAttr proc near
mov ah,8 ;get attribute under cursor
xor bh,bh
int 10h
mov tmpcol,ah ;save it
cmp mode,0 ;color video?
je sa0 ;yes, then branch
;
;Display attribute menu for monochrome systems.
;
mov si,offset mono_text ;display menu line
call MenuLine
mono1: call ReadKey ;get response
or al,al ;function key F2?
jne mono2
cmp ah,60
jne mono1
mov ah,tmpcol ;use attribute under cursor
mov attribute,ah
jmp short mono3 ;exit
mono2: cmp al,27 ;exit if ESC was pressed
je mono3
cmp al,'1' ;reject invalid responses
jb mono1
cmp al,'4'
ja mono1
sub al,'1' ;normalize entry
mov bx,offset mono_table ;determine new attribute
xlat mono_table
mov attribute,al ;save it
mono3: call RestoreLine ;restore line and exit
ret
;
;Display attribute menu for color systems.
;
sa0: mov si,offset fore_text ;display menu line
call MenuLine
mov dx,180Eh ;display colors
xor bx,bx
sa1: mov ah,2
int 10h
mov ax,09DBh
mov cx,2
int 10h
add dl,4
inc bl
test bl,10h
jz sa1
sa2: call ReadKey ;get foreground color response
or al,al ;function key F2?
jne notf1
cmp ah,60
jne sa2
mov ah,tmpcol ;use attribute under cursor
mov attribute,ah
jmp short endselect ;and exit
notf1: cmp al,13 ;ENTER key?
je sa4
cmp al,27 ;ESC?
je endselect
cmp al,'0' ;reject invalid responses
jb sa2
cmp al,'9'
jna sa3
and al,0DFh ;normalize entry
cmp al,'A'
jb sa2
cmp al,'F'
ja sa2
sub al,7
sa3: sub al,30h
and attribute,0F0h ;modify foreground color
or attribute,al
sa4: mov si,offset back_text ;display background colors
mov dx,1800h
mov bl,menu_attr
call WriteLine
mov ah,2
mov dx,182Ch
int 10h
mov ax,0920h
mov cx,34
int 10h
sa5: call ReadKey ;get background color response
cmp al,13
je endselect
cmp al,27
je endselect
cmp al,'0'
jb sa4
cmp al,'7'
ja sa4
sub al,30h
mov cl,4
shl al,cl
and attribute,0Fh ;modify background color
or attribute,al
endselect: call RestoreLine ;restore menu line
ret
SelectAttr endp
;-----------------------------------------------------------------------------
;WriteChar processes a press of a character key.
;-----------------------------------------------------------------------------
WriteChar proc near
cmp insert_flag,0 ;insert state on?
je disp_char ;no, then skip insert sequence
mov cl,columns ;calculate number of shifts
sub cl,dl
xor ch,ch
jcxz disp_char
push ax ;save character and position
push dx
call VideoAddr ;calculate video address
mov bx,cx ;determine starting address
shl bx,1
add si,bx
mov di,si
sub si,2
push ds ;save DS and ES
push es
assume ds:nothing,es:nothing
mov ds,video_segment
mov es,video_segment
std ;set DF temporarily
mov dx,crtc_addr ;get CRT Controller address
add dx,6 ;address status register
vwait: in al,dx ;wait for vertical retrace
test al,8
jz vwait
cmp write_mode,0 ;push text and attributes?
jne push_all ;yes, then branch
write_loop: movsb ;shift text only
dec si
dec di
loop write_loop
jmp short shift_done
push_all: rep movsw ;shift text and attributes
shift_done: cld ;restore DF
pop es ;restore DS and ES
pop ds
assume ds:code,es:code
pop dx ;retrieve character and position
pop ax
disp_char: call DisplayChar ;display the character
mov ah,2 ;advance the cursor
cmp dl,columns ;prevent overrun
je nomove
inc dl
nomove: int 10h
ret
WriteChar endp
;-----------------------------------------------------------------------------
;VideoAddr calculates the page zero video buffer address that corresponds
;to the cursor coordinates held in DX.
;Entry: DH,DL - cursor row and column | Exit: SI - buffer offset
;-----------------------------------------------------------------------------
VideoAddr proc near
mov ax,linelength ;row * 160
mul dh
shl dl,1 ;add column * 2
xor dh,dh
add ax,dx
mov si,ax ;transfer to SI
ret
VideoAddr endp
;-----------------------------------------------------------------------------
;INSERT processes a press of the INS key.
;-----------------------------------------------------------------------------
insert proc near
xor insert_flag,1 ;toggle flag
ret
insert endp
;-----------------------------------------------------------------------------
;UP processes a press of Cursor-Up or Ctrl-Up.
;-----------------------------------------------------------------------------
up proc near
or dh,dh ;currently on top row?
je done ;yes, then ignore the keypress
push ax ;save keycode
cmp al,100 ;paint cell if Ctrl is pressed
jb up1
call SetAttribute
up1: dec dh ;move cursor up a row
set_cursor: mov ah,2
xor bh,bh
int 10h
pop ax ;retrieve keycode
cmp al,100 ;paint cell if Ctrl is pressed
jb done
call SetAttribute
done: ret
up endp
;-----------------------------------------------------------------------------
;LEFT processes a press of Cursor-Left or Ctrl-Left.
;-----------------------------------------------------------------------------
left proc near
or dl,dl ;currently at left edge?
je done ;yes, then ignore the keypress
push ax ;save keycode
cmp al,100 ;paint cell if Ctrl is pressed
jb lf1
call SetAttribute
lf1: dec dl ;move cursor left one column
jmp short set_cursor
left endp
;-----------------------------------------------------------------------------
;RIGHT processes a press of Cursor-Right or Ctrl-Right.
;-----------------------------------------------------------------------------
right proc near
cmp dl,columns ;currently at right border?
je done ;yes, then ignore the keypress
push ax ;save keycode
cmp al,100 ;paint cell if Ctrl is pressed
jb rt1
call SetAttribute
rt1: inc dl ;move cursor right one column
jmp short set_cursor
right endp
;-----------------------------------------------------------------------------
;DOWN processes a press of Cursor-Down or Ctrl-Down.
;-----------------------------------------------------------------------------
down proc near
cmp dh,24 ;currently on bottom row?
je done ;yes, then ignore the keypress
push ax ;save keycode
cmp al,100 ;paint cell if Ctrl is pressed
jb dn1
call SetAttribute
dn1: inc dh ;move cursor down a row
jmp short set_cursor
down endp
;-----------------------------------------------------------------------------
;HOME processes a press of the HOME key.
;-----------------------------------------------------------------------------
home proc near
xor dl,dl ;set cursor to start of line
home1: mov ah,2
xor bh,bh
int 10h
ret
home endp
;-----------------------------------------------------------------------------
;ENDKEY processes a press of the END key.
;-----------------------------------------------------------------------------
endkey proc near
mov dl,columns ;set cursor to end of line
jmp home1
endkey endp
;-----------------------------------------------------------------------------
;PgUp and PgDn routines flip to the last/next page in the buffer.
;-----------------------------------------------------------------------------
PgUp proc near
cmp maxpage,0 ;ignore if there's only one page
je nopress
call PutVideo ;write current page to buffer
dec bufferptr ;go back one page
test bufferptr,80h ;wrap around if necessary
jz pg1
mov al,maxpage
mov bufferptr,al
pg1: call ShowFile ;display new page
nopress: ret ;exit
PgUp endp
PgDn proc near
cmp maxpage,0 ;ignore if there's only one page
je nopress
call PutVideo
inc bufferptr ;advance page pointer
mov al,maxpage ;wrap if necessary
cmp al,bufferptr
jae pg1
mov bufferptr,0
jmp short pg1
PgDn endp
;-----------------------------------------------------------------------------
;DisplayChar writes the character in AL to the current cursor position.
;-----------------------------------------------------------------------------
DisplayChar proc near
mov ah,9
cmp write_mode,0 ;write attribute also?
jne dc1
inc ah ;no, then change function number
dc1: xor bh,bh
mov bl,attribute
mov cx,1
int 10h
ret
DisplayChar endp
;-----------------------------------------------------------------------------
;SetAttribute sets the attribute of the character under the cursor.
;-----------------------------------------------------------------------------
SetAttribute proc near
mov ah,8 ;get character
xor bh,bh
int 10h
mov ah,9 ;write character and attribute
mov bl,attribute
mov cx,1
int 10h
ret
SetAttribute endp
;-----------------------------------------------------------------------------
;SaveLine saves the contents of the menu line.
;-----------------------------------------------------------------------------
cursor_mode dw ? ;cursor mode
cursor_pos dw ? ;cursor position
SaveLine proc near
mov ah,3 ;get cursor position and shape
xor bh,bh
int 10h
mov cursor_mode,cx ;save cursor parameters
mov cursor_pos,dx
mov ah,1 ;hide the cursor
mov ch,20h
int 10h
mov dx,1800h ;set starting cursor position
mov di,offset data_buffer ;point DI to save buffer
mov cx,lcolumns
sr1: mov ah,2 ;position the cursor
int 10h
mov ah,8 ;read character and attribute
int 10h
stosw ;store them
inc dl ;advance cursor to next column
loop sr1 ;loop until line is done
ret
SaveLine endp
;-----------------------------------------------------------------------------
;RestoreLine restores the contents of the previously saved menu line.
;-----------------------------------------------------------------------------
RestoreLine proc near
mov ah,1 ;hide the cursor
mov ch,20h
int 10h
xor bh,bh
mov si,offset data_buffer ;point SI to saved char/attr's
mov cx,lcolumns
mov dx,1800h ;initialize cursor position
rr1: push cx
mov ah,2 ;position the cursor
int 10h
lodsw ;get character and attribute
mov bl,ah ;transfer attribute to BL
mov ah,9 ;write character and attribute
mov cx,1
int 10h
inc dl ;advance cursor to next column
pop cx
loop rr1 ;loop until line is done
mov ah,2 ;restore cursor position and shape
mov dx,cursor_pos
int 10h
mov ah,1
mov cx,cursor_mode
int 10h
ret
RestoreLine endp
;-----------------------------------------------------------------------------
;GetEnv returns a value reflecting what lies on all four sides of the cursor.
;Entry: DH,DL - cursor position | Exit: BOXID - neighbor code
;-----------------------------------------------------------------------------
c_pos label word
c_col db ? ;cursor column
c_row db ? ;cursor row
GetEnv proc near
mov c_pos,dx ;save cursor position
call VideoAddr ;calculate video address
mov dx,crtc_addr
add dx,6
push ds ;save DS
mov ds,video_segment ;point DS to video buffer
assume ds:nothing
;
;Determine if what lies on the left affects the current cell.
;
sub si,2 ;address cell on left
cmp c_col,0 ;at left edge of screen?
je check2 ;yes, then skip test
test boxid,12 ;branch if BOXID is biased
jnz check2
mov ah,4
call MakeCode ;generate BOXID code
;
;Then do the same for the right side.
;
check2: add si,4
mov al,columns
cmp c_col,al
je check4
test boxid,192
jnz check4
mov ah,64
call MakeCode
;
;Check the character above the current cell.
;
check4: sub si,linelength
sub si,2
cmp c_row,0
je check6
test boxid,48
jnz check6
mov ah,16
call MakeCode
;
;Finish up by checking the character below.
;
check6: add si,linelength
add si,linelength
cmp c_row,24
je check8
test boxid,3
jnz check8
mov ah,1
call MakeCode
check8: pop ds ;restore DS
assume ds:code
mov dx,c_pos ;restore DX
ret ;and exit
GetEnv endp
;-----------------------------------------------------------------------------
;MakeCode modifies BOXID to reflect the character addressed by DS:SI.
;Entry: DS:SI - character
; AH - test value
; DX - video status register
;-----------------------------------------------------------------------------
MakeCode proc near
assume ds:nothing
call GetChar ;retrieve character
cmp al,179 ;exit if not a graphics character
jb make_exit
cmp al,218
ja make_exit
sub al,179 ;check for single extension
mov bx,offset gr_table
xlat gr_table
test al,ah
jz make1
or boxid,ah
jmp short make_exit
make1: shl ah,1 ;check for double extension
test al,ah
jz make_exit
or boxid,ah
make_exit: ret
MakeCode endp
;-----------------------------------------------------------------------------
;GetChar returns the character addressed by DS:SI in AL.
;Entry: DS:SI - character cell | Exit: AL - character code
; DX - video status register |
;-----------------------------------------------------------------------------
GetChar proc near
assume ds:nothing
in al,dx ;wait for horizontal scan
test al,1
jnz GetChar
cli ;interrupts off
wait1: in al,dx ;wait for next retrace
test al,1
jz wait1
mov al,[si] ;get the character
sti ;interrupts on
ret
GetChar endp
assume ds:code
;-----------------------------------------------------------------------------
;MenuLine opens a menu line at the bottom of the screen.
;Entry: DS:SI - text of menu line
;-----------------------------------------------------------------------------
MenuLine proc near
push si ;save string address
call SaveLine ;save current line contents
mov ah,2 ;position cursor on menu line
xor bh,bh
mov dx,1800h
int 10h
mov ax,0920h ;clear menu line
mov bl,menu_attr
mov cx,lcolumns
int 10h
pop si ;retrieve string address
call WriteLine ;display text
ret
MenuLine endp
;-----------------------------------------------------------------------------
;WriteLine displays a line of ASCIIZ text.
;Entry: DS:SI - text string
; DH,DL - cursor row and column
;-----------------------------------------------------------------------------
WriteLine proc near
mov ah,2 ;position the cursor
xor bh,bh
int 10h
write1: lodsb ;get a character
or al,al ;exit if it's zero
je write2
mov ah,10 ;write it
mov cx,1
int 10h
mov ah,2
inc dl
int 10h
jmp write1 ;loop back for more
write2: ret
WriteLine endp
;-----------------------------------------------------------------------------
;ReadString reads a string of text from the keyboard.
;Entry: CL - maximum length
; DH,DL - cursor position
; DS:SI - default string
; ES:DI - input buffer
;-----------------------------------------------------------------------------
maxlen db ? ;maximum accepted length
ReadString proc near
mov maxlen,cl ;store max length
xor cl,cl ;initialize counter
xor bh,bh
rs1: lodsb ;input default string
or al,al ;end of string?
je rs2 ;yes, then exit loop
inc di ;advance buffer pointer
inc cl ;increment count
mov ah,14 ;print the character
int 10h
jmp short rs1 ;loop until zero byte is reached
rs2: call ReadKey ;read the keyboard
cmp al,8 ;BACKSPACE key?
jne rs3
or cl,cl ;at beginning of line?
je rs2 ;yes, then ignore it
mov ah,14 ;move cursor back one space
int 10h
push cx ;delete the last character
mov ax,0A20h
mov cx,1
int 10h
pop cx
dec cl ;decrement count and pointer
dec di
jmp short rs2 ;return for more
rs3: cmp al,13 ;ENTER key?
jne rs4 ;no, then branch
xor al,al ;mark end of string
stosb
ret ;done
rs4: cmp al,27 ;ESC key?
jne rs5 ;no, then branch
xor cl,cl ;zero count
mov al,cl ;place delimiter
stosb
ret
rs5: cmp al,32 ;ASCII code less than 32?
jb rs2 ;yes, then ignore it
cmp cl,maxlen ;room for more?
je rs2 ;no, then ignore keypress
stosb ;buffer the character
inc cl ;increment count
mov ah,14 ;print the character
int 10h
jmp short rs2 ;return for more
ReadString endp
;-----------------------------------------------------------------------------
;BACKSPACE backspaces over the last character.
;Entry: DH,DL - cursor row and column
;-----------------------------------------------------------------------------
backspace proc near
mov ah,2 ;move cursor back one column
dec dl
xor bh,bh
int 10h
cmp insert_flag,0 ;is insert mode active?
je bs_exit ;no, then we're done
push dx ;save cursor position
call delete ;draw in everything to the right
pop dx ;retrieve position
bs_exit: ret
backspace endp
;-----------------------------------------------------------------------------
;DELETE shifts everything right of the cursor one cell left.
;Entry: DH,DL - cursor row and column
;-----------------------------------------------------------------------------
delete proc near
mov cl,columns ;calculate number of shifts
sub cl,dl
xor ch,ch
jcxz nodelete ;branch if there are none
call VideoAddr ;calculate video address
mov di,si
add si,2
push ds ;save DS and ES
push es
assume ds:nothing,es:nothing
mov ds,video_segment
mov es,video_segment
mov dx,crtc_addr ;get CRT Controller address
add dx,6 ;address status register
rwait: in al,dx ;wait for vertical retrace
test al,8
jz rwait
cmp write_mode,0 ;delete text and attribute?
jne delete_all ;yes, then branch
del_loop: movsb ;delete text only
inc si
inc di
loop del_loop
jmp short finish
delete_all: rep movsw ;shift characters and attributes
finish: mov byte ptr es:[di],32 ;blank final cell
pop es ;restore DS and ES
pop ds
assume ds:code,es:code
ret
nodelete: mov ax,0A20h ;blank character under cursor
xor bh,bh
mov cx,1
int 10h
ret
delete endp
data_buffer label byte ;buffer for menu line
code ends
end begin
Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!
This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.
But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/