; ; Low level IBM/PC console keyboard/window interface routines ; for use with the DDS MICRO-C compiler. ; ; Copyright 1989-2000 Dave Dunfield ; All rights reserved. ; ; Permission granted for personal (non-commercial) use only. ; ?CBASE EQU $B800 ; Color video screen segment ?MBASE EQU $B000 ; Monochrome video screen segment ; ; Initialized variables & tables ; W_BASE DW ?CBASE ; Mono=B000, Cga=B800 W_PAGE DB 0 ; Current video page W_OPEN DW 0 ; Last opened window ; Special key table - General keys ?W_KEYS DW $4800 ; KUA (Up arrow) DW $5000 ; KDA (Down arrow) DW $4B00 ; KLA (Left arrow) DW $4D00 ; KRA (Right arrow) DW $4900 ; KPU (PgUp) DW $5100 ; KPD (PgDn) DW $4700 ; KHO (Home) DW $4F00 ; KEN (End) DW $4E2B ; KKP (Keypad '+') DW $4A2D ; KKM (Keypad '-') DW $5200 ; KIN (Ins) DW $5300 ; KDL (Del) DW $0E08 ; KBS (Backspace) ; Function keys DW $3B00 ; K1 (F1) DW $3C00 ; K2 (F2) DW $3D00 ; K3 (F3) DW $3E00 ; K4 (F4) DW $3F00 ; K5 (F5) DW $4000 ; K6 (F6) DW $4100 ; K7 (F7) DW $4200 ; K8 (F8) DW $4300 ; K9 (F9) DW $4400 ; K10 (F10) ; Special control keys DW $8400 ; CPU (CTRL-PgUp) DW $7600 ; CPD (CTRL-PgDn) DW $7700 ; CHO (CTRL-Home) DW $7500 ; CEN (CTRL-End) DW 0 ; BOX character table ?BOXTAB DB $C4,$B3,$DA,$BF,$C0,$D9 DB $CD,$BA,$C9,$BB,$C8,$BC DB $CD,$B3,$D5,$B8,$D4,$BE ; ; Open a window: w_open(px, py, sx, sy, flags) ; wopen PUSH BP ; Save callers stack frame MOV BP,SP ; Address parameters PUSH ES ; Save ES ; Set up video mode MOV AH,#$0F ; Get video mode INT $10 ; Call BIOS MOV W_PAGE,BH ; Save video page MOV BX,#?CBASE ; Assume COLOR address CMP AL,#$07 ; Monochrome? JNZ ?oinit1 ; No, assumption correct MOV BX,#?MBASE ; Get MONOCHROME address ?oinit1 MOV W_BASE,BX ; Set video base address MOV ES,BX ; Set up segment ; Allocate a window buffer XOR AH,AH ; zero high MOV AL,5[BP] ; Get video flags AND AL,#$80 ; Do we save screen JZ ?oinit2 ; No, do not MOV AL,6[BP] ; Get 'Y' size MOV BL,8[BP] ; Get 'X' size MUL BL ; Calculate total size SHL AX,1 ; X2 for attributes ?oinit2 ADD AX,#14 ; Include overhead PUSH AX ; Pass as parameter CALL malloc ; Allocate the buffer MOV SI,AX ; Set up pointer POP DX ; Clean up stack AND SI,SI ; Did we get it? JNZ ?oinit3 ; Yes, proceed ; Couldn't allocate, return with bad news POP ES ; Restore extra set POP BP ; Restore caller RET ; Fill in window parameter block ?oinit3 MOV DL,12[BP] ; Get 'X' position MOV DH,10[BP] ; Get 'Y' position MOV 2[SI],DX ; Save it MOV DL,8[BP] ; Get 'X' size MOV DH,6[BP] ; Get 'Y' size MOV 4[SI],DX ; Save size MOV BX,4[BP] ; Get window flags MOV [SI],BX ; Save attributes & flags MOV DX,W_OPEN ; Get last opened window MOV 8[SI],DX ; Save in buffer XOR DX,DX ; Reset cursor position MOV 6[SI],DX ; Save initial cursor position ; Save window contents if required TEST BH,#$80 ; Do we save it? JZ ?oinit5 ; No we don't ; ... Set up pointers to screen & save area CALL ?xyaddr ; Get base address MOV DX,4[SI] ; Get 'X' and 'Y' sizes LEA DI,14[SI] ; Offset to data area PUSH SI ; Save SI MOV SI,BX ; Get base window address ; ... Save one line at a time PUSH ES ; Stack ES PUSH DS ; And DS POP ES ; For swap POP DS ; ES <> DS ?oinit4 PUSH SI ; Save source MOV CL,DL ; Get 'X' size XOR CH,CH ; Zero high size REP MOVSW ; Move one line POP SI ; Restore it ADD SI,#160 ; Offset to next line DEC DH ; Reduce count JNZ ?oinit4 ; And proceed ; ... Restore registers and continue PUSH ES ; Stack ES PUSH DS ; And DS POP ES ; For swap POP DS ; ES <> DS POP SI ; Restore SI XOR DX,DX ; Reset cursor position MOV BH,5[BP] ; Get flags back ; Draw box if required ?oinit5 MOV BP,#?BOXTAB ; Point to BOX table MOV CX,4[SI] ; Get size of box SUB CH,#2 ; Adjust for bar DEC CL ; Adjust for bar AND BH,#$60 ; Draw BOX? JZ ?oinit8 ; No we don't ; ... Select box type CMP BH,#$40 ; Box1? JZ ?oinit6 ; Use this one ADD BP,#6 ; Offset to next CMP BH,#$20 ; Box2? JZ ?oinit6 ; Use this one ADD BP,#6 ; Offset to next ?oinit6 PUSH CX ; Save size CALL ?xyaddr ; Get address ; ... Draw the top (With corners) MOV AH,[SI] ; Get attribute MOV AL,2[BP] ; Top left corner MOV ES:[BX],AX ; Write top corner LEA DI,2[BX] ; Get value XOR CH,CH ; Zero high DEC CL ; Adjust for bar MOV AL,0[BP] ; Horizontal line REP STOSW ; Draw the line MOV AL,3[BP] ; Top right hand corner MOV ES:[DI],AX ; Write it POP CX ; Restore position ; ... Draw the box sides ?oinit7 INC DH ; Advance MOV AL,1[BP] ; Box side CALL ?xyaddr ; Get position MOV ES:[BX],AX ; Write it ADD DL,CL ; Offset CALL ?xyaddr ; Get position MOV ES:[BX],AX ; Write it SUB DL,CL ; Backup DEC CH ; Reduce count JNZ ?oinit7 ; Do them all ; ... Draw the bottom (With corners) INC DH ; Advance to bottom CALL ?xyaddr ; get address MOV AL,4[BP] ; Lower left corner MOV ES:[BX],AX ; Write it LEA DI,2[BX] ; Position to line DEC CL ; Adjust for bar MOV AL,0[BP] ; Horizontal line REP STOSW ; Write it out MOV AL,5[BP] ; Lower right corner MOV ES:[DI],AX ; Write it out ; ... Reduce size of active region MOV AX,2[SI] ; Get position INC AL ; Advance 'X' INC AH ; Advance 'Y' MOV 2[SI],AX ; Resave MOV AX,4[SI] ; Get size SUB AH,#2 ; Chop out bars SUB AL,#2 ; Chop out bars MOV 4[SI],AX ; And resave XOR DX,DX ; Reset cursor position ; Clear screen if required ?oinit8 MOV BH,1[SI] ; Get flags back TEST BH,#$10 ; Clear screen? JZ ?oinit9 ; No, its ok CALL ?cleos ; Clear one line ; Save cursor information ?oinit9 MOV BH,W_PAGE ; Get video page MOV AH,#$03 ; Get cursor info INT $10 ; Get cursor MOV 10[SI],CX ; Save shape MOV 12[SI],DX ; Save position MOV AX,SI ; Get buffer address MOV W_OPEN,AX ; Save this window pointer POP ES ; Restore ES POP BP ; Restore callers stack frame RET ; ; Close current window: wclose() ; wclose CALL ?xsetup ; Get last window JMP <?wclos0 ; And proceed ; ; Close a window: w_close(window) ; w_close CALL ?wsetup ; Get parameters ?wclos0 XOR DX,DX ; Zero position MOV BH,1[SI] ; Get open flags ; If window was BOXed, adjust sizes first TEST BH,#$60 ; Was it BOXed JZ ?wclos1 ; No, don't adjust MOV AX,2[SI] ; Get position DEC AL ; Expand 'X' DEC AH ; Expand 'Y' MOV 2[SI],AX ; Resave MOV AX,4[SI] ; Get size ADD AH,#2 ; Adjust for bars ADD AL,#2 ; Adjust for bars MOV 4[SI],AX ; Resave ; Clear window if requested ?wclos1 TEST BH,#$08 ; Clear window? JZ ?wclos2 ; No, try next CALL ?cleos ; Clear the window MOV BH,1[SI] ; Get flags back ; Restore previous contents if saved ?wclos2 TEST BH,#$80 ; Do we restore it JZ ?wclos4 ; No, just clear ; ... Set up pointers to screen & save area CALL ?xyaddr ; Calculate base address MOV DX,4[SI] ; Get 'X' and 'Y' sizes PUSH SI ; Save SI LEA SI,14[SI] ; Offset to data area MOV DI,BX ; Get base window address ; ... Restore one line at a time ?wclos3 PUSH DI ; Save source MOV CL,DL ; Get 'X' size XOR CH,CH ; Zero high size REP MOVSW ; Move one line POP DI ; Restore it ADD DI,#160 ; Offset to next line DEC DH ; Reduce count JNZ ?wclos3 ; And proceed POP SI ; Restore SI MOV BH,1[SI] ; Get flags back ; Restore the cursor ?wclos4 MOV CX,10[SI] ; Get cursor shape MOV AH,#$01 ; Set shape INT $10 ; Call BIOS MOV DX,12[SI] ; Get position MOV BH,W_PAGE ; Get display page MOV AH,#$02 ; Set position INT $10 ; Call BIOS ; If this is the active window, switch to previously open CMP SI,W_OPEN ; Is this it? JNZ ?wclos5 ; No, don't reset MOV AX,8[SI] ; Get last free MOV W_OPEN,AX ; Save it ; Release the buffer contents ?wclos5 PUSH SI ; Save address of buffer CALL free ; Release it POP SI ; Fixup stack POP ES ; Restore ES POP BP ; Restore BP RET ; ; Write character into current window: wputc(int c) ; wputc CALL ?xsetup ; Get last open window MOV AX,4[BP] ; Get character JMP <?tstbel ; And proceed ; ; Write a character to the video display: w_putc(int c, window) ; w_putc CALL ?wsetup ; Set up video addresses MOV AX,6[BP] ; Get character to display ; Handle BELL ?tstbel CMP AX,#'G'-$40 ; BELL code? JNZ ?tstcr ; No, try next MOV AX,#$0E07 ; Write BELL code XOR BX,BX ; Write to page 0 INT $10 ; Call BIOS JMP <?vedit ; and exit ; Handle CARRIAGE RETURN ?tstcr CMP AX,#'M'-$40 ; Is it carriage return JNZ ?tstbs ; No, try next XOR DL,DL ; Reset 'X' position JMP <?vedit ; and proceed ; Handle BACKSPACE ?tstbs CMP AX,#'H'-$40 ; Is it backspace JNZ ?tstnl ; No, try line-feed AND DL,DL ; Already at first col? JZ ?vedit ; Yes, don't backup DEC DL ; Reduce 'X' position JMP <?vedit ; And exit ; Handle NEWLINE ?tstnl CMP AX,#'J'-$40 ; Is it newline? JNZ ?norchr ; Yes, advance line MOV AL,1[SI] ; Get flags TEST AL,#$04 ; Special case? JNZ ?advy ; Yes, don't reset 'X' XOR DL,DL ; Reset cursor JMP <?advy ; And goto a new line ; Normal Character, output it ?norchr CALL ?xyaddr ; Calculate address MOV AH,[SI] ; Get video attributes MOV ES:[BX],AX ; Write to video display ; Advance 'X' position ?advx INC DL ; Advance 'X' CMP DL,4[SI] ; Are we over? JB ?vedit ; No, its ok DEC DL ; Restore it MOV AL,1[SI] ; Get flags TEST AL,#$01 ; Line wrap enabled? JZ ?vedit ; No, skip it XOR DL,DL ; Reset 'X' position ; Advance 'Y' position ?advy INC DH ; Advance 'Y' position CMP DH,5[SI] ; are we over? JB ?vedit ; No, Its OK DEC DH ; Reset it MOV AL,1[SI] ; Get flags TEST AL,#$02 ; Screen wrap enabled? JZ ?vedit ; No, skip it CALL ?scroll ; Scroll window ; Restore callers environment & exit ?vedit MOV 6[SI],DX ; Resave 'X' and 'Y' POP ES ; Restore callers ES POP BP ; Restore callers stack frame RET ; ; CLEAR current window: wclwin() ; wclwin CALL ?xsetup ; Get current window JMP <?clscr1 ; And proceed ; ; CLEAR entire window: w_clwin(window) ; w_clwin CALL ?wsetup ; Setup video ?clscr1 XOR DX,DX ; Reset to top of screen MOV 6[SI],DX ; Reset cursor CALL ?updt2 ; Position cursor XOR DX,DX ; Reset again JMP <?clscr2 ; And proceed ; ; CLEAR to end of current window: wcleow() ; wcleow CALL ?xsetup ; Get current window JMP <?clscr2 ; And proceed ; ; CLEAR to END OF WINDOW: w_cleow(window) ; w_cleow CALL ?wsetup ; Setup video ?clscr2 CALL ?cleos ; Clear to end POP ES ; Restore ES POP BP ; Restore caller RET ; ; CLEAR to end of line in current: wcleol() ; wcleol CALL ?xsetup ; Get current window JMP <?cleol1 ; And proceed ; ; CLEAR to END OF LINE: w_cleol(window) ; w_cleol CALL ?wsetup ; Setup video ?cleol1 CALL ?xyaddr ; Get address MOV DI,BX ; Set up address MOV CL,4[SI] ; Get size of line SUB CL,DL ; Calculate remaining XOR CH,CH ; Zero high MOV AH,[SI] ; Get attribute MOV AL,#' ' ; Clear to space REP STOSW ; Clear a line POP ES ; REstore ES ?cleol2 POP BP ; Restore caller RET ; ; Position the cursor in current window: wgotoxy(int x, int y) ; wgotoxy PUSH BP ; Save callers stack frame MOV BP,SP ; Address parameters MOV AL,6[BP] ; Get 'X' value MOV AH,4[BP] ; Get 'Y' value MOV SI,W_OPEN ; Get open window JMP <?goto1 ; And proceed ; ; Position the cursor in window: w_gotoxy(int x, int y, window) ; w_gotoxy PUSH BP ; Save callers stack frame MOV BP,SP ; Address parameters MOV AL,8[BP] ; Get 'X' value MOV AH,6[BP] ; Get 'Y' value MOV SI,4[BP] ; Get window buffer ?goto1 CMP AL,4[SI] ; In range? JAE ?cleol2 ; No, error CMP AH,5[SI] ; In range JAE ?cleol2 ; No, error MOV 6[SI],AX ; Save values JMP <?updt1 ; And proceed ; ; Update the cursor in current window: wgotoxy() ; wupdatexy MOV SI,W_OPEN ; Get open window JMP <?updt2 ; And proceed ; ; Update the cursor position: w_updatexy(window) ; w_updatexy PUSH BP ; Save callers stack frame MOV BP,SP ; Address parameters MOV SI,4[BP] ; Get caller ?updt1 POP BP ; Resture caller ?updt2 MOV DX,2[SI] ; Get starting address ADD DX,6[SI] ; Offset into window MOV BH,W_PAGE ; Get video page MOV AH,#$02 ; Set cursor function INT $10 ; Call DOS RET ; ; Check for a key from the keyboard: c = w_tstc(window) ; w_tstc MOV AH,#$01 ; Check for key INT $16 ; Do we have key? JNZ w_getc ; Yes, read it ?wtst1 XOR AX,AX ; No key today RET ; ; Check for key with cursor in current window: c = wtstc() ; wtstc MOV AH,#$01 ; Check for key INT $16 ; Do we have key? JZ ?wtst1 ; No, return zero ; ; Get in cursor in current window: c = wgetc() ; wgetc MOV SI,W_OPEN ; Get active window JMP <?getc1 ; And proceed ; ; Get a key from the keyboard with translations: c = w_getc(window) ; w_getc MOV BX,SP ; Address parameters MOV SI,2[BX] ; Get window ?getc1 CALL ?updt2 ; Update the cursor position ; Call BIOS to read key XOR AH,AH ; Function code 0 - read key INT $16 ; Call bios ; Lookup key for special entries MOV CL,#$80 ; Beginning function code MOV BX,#?W_KEYS ; Address of keys array ?lokkey MOV DX,[BX] ; Get key entry CMP AX,DX ; Does it match? JZ ?fndkey ; We found it ADD BX,#2 ; Skip ahead INC CL ; Advance key code OR DH,DL ; End of table? JNZ ?lokkey ; Keep looking ; Translate ENTER key to newline CMP AX,#$1C0D ; ENTER key? JNZ ?norkey ; Yes, we have it MOV CL,#'J'-$40 ; Newline is LINE-FEED ; Translate special keys ?fndkey MOV AL,CL ; Set return value ?norkey CBW ; Set high bits RET ; ; Set the CURSOR OFF: wcursor_off() ; wcursor_off EQU * MOV CX,#$2020 ; Value for cursor off JMP <?setc2 ; And set it ; ; Set the CURSOR to a block: wcursor_block() ; wcursor_block EQU * MOV CX,#$0006 ; Color block cursor MOV DX,#$000B ; Monochrome block cursor JMP <?setc1 ; and proceed ; ; Set the CURSOR to a LINE: wcursor_line() ; wcursor_line EQU * MOV CX,#$0607 ; Color line cursor MOV DX,#$0B0C ; Monochrome line cursor ?setc1 MOV AX,W_BASE ; Get video base CMP AX,#?MBASE ; Monochrome? JNZ ?setc2 ; No, do it MOV CX,DX ; Copy for later ; Set cursor to value in CX ?setc2 MOV AH,#1 ; Set cursor type INT $10 ; Call BIOS RET ; ; Set up addressability to video display & stack registers ; Exit: ES = video base address ; SI = window buffer address ; DX = cursor address (X/Y) ; ?wsetup POP AX ; Get return address PUSH BP ; Save callers stack frame MOV BP,SP ; Address parameters MOV SI,4[BP] ; Get window buffer ?wset1 PUSH ES ; Save callers Extra segment PUSH AX ; Replace return address MOV ES,W_BASE ; Point to video base MOV DX,6[SI] ; Get 'X' and 'Y' position RET ; ; Set up addressability to currently open window ; ?xsetup POP AX ; Get return address PUSH BP ; Save callers stack frame MOV BP,SP ; Address parameters MOV SI,W_OPEN ; Get open window JMP <?wset1 ; And proceed ; ; Scroll window forward one line ; ?scroll PUSH DS ; Save data segment PUSH DX ; Save cursor PUSH SI ; Save SI ; First, calculate base address of window MOV AL,3[SI] ; Get 'Y' position MOV BL,#160 ; Size of line MUL BL ; Calculate 'Y' offset MOV BL,2[SI] ; Get 'X' position XOR BH,BH ; Zero high SHL BX,1 ; * 2 for char & attribute bytes ADD BX,AX ; BX = character position MOV DI,BX ; Get base window address ; Scroll one line at a time MOV DX,4[SI] ; Get 'X' and 'Y' sizes DEC DH ; Don't copy to last PUSH ES ; Stack ES POP DS ; To place in DS ?scrol1 PUSH DI ; Save DI MOV SI,DI ; Begin with same address ADD SI,#160 ; Offset to next line MOV CL,DL ; Get 'X' size XOR CH,CH ; Zero high size REP MOVSW ; Move one line POP DI ; Restore it ADD DI,#160 ; Offset to next line DEC DH ; Reduce count JNZ ?scrol1 ; And proceed ; Clear bottom line of screen MOV CL,DL ; Get 'X' size POP SI ; Restore SI POP DX ; Restore cursor POP DS ; Restore data segment MOV AH,[SI] ; Get attribute MOV AL,#' ' ; And space REP STOSW ; Clear it RET ; ; Clear from cursor(DX) to end of line ; ?cleos PUSH DX ; Save cursor ?cleos1 CALL ?xyaddr ; Get address MOV DI,BX ; Set up address MOV CL,4[SI] ; Get size of line SUB CL,DL ; Calculate remaining XOR CH,CH ; Zero high MOV AH,[SI] ; Get attribute MOV AL,#' ' ; Clear to space REP STOSW ; Clear a line XOR DL,DL ; Zero 'X' INC DH ; Advance 'Y' CMP DH,5[SI] ; Are we finished JB ?cleos1 ; Keep going POP DX ; Restore cursor RET ; ; Calculate screen address from X/Y position (in DX) ; On exit: BX = address ; ?xyaddr PUSH AX ; Save AX MOV AL,DH ; Get 'Y' position ADD AL,3[SI] ; Offset from start of screen MOV BL,#160 ; Size of physical screen MUL BL ; Calculate 'Y' offset MOV BL,DL ; Get 'X' position ADD BL,2[SI] ; Offset from start of screen XOR BH,BH ; Zero high byte SHL BX,1 ; * 2 for char & attribute bytes ADD BX,AX ; BX = character position POP AX ; Restore it RET $EX:malloc $EX:free „