eject ; Dec 9, 1983 reorg10 equ offset $ cseg org reorg10 ;************************************************ ;* * ;* SCREEN PARAMETERS * ;* * ;************************************************ crt_rows equ 24 ; crt height crt_cols equ 80 ; crt width crt_size equ crt_rows * crt_cols crt_full equ 100H*(crt_rows-1) + (crt_cols-1) s_l_offset equ crt_size ; status line offset buffer_size equ crt_size * (num_vir_cons*2 + 1) ; this allows 2x bytes for each ; console and 1x for the map mono_seg equ 0B000h ; monochrome memory segment color_seg equ 0B800h ; color memory segment mono_port equ 03B4h ; monochrome controller addr color_port equ 03D4h ; color controller addr mono_cursor equ 0B0Ch ; monochrome cursor code color_cursor equ 0607h ; color cursor code compaq_cursor equ 0B0Ch ; hi-res cursor on color card vrt equ 08h ; vertical retrace bit hrt equ 01h ; horiz retrace bit movs_burst equ 112 ; string move per vert retrace stos_burst equ 168 ; string store per vert retrace neg4 equ -4 ; for reverse indexing blank equ 0720h ; space q_make equ 86h q_open equ 87h q_read equ 89h q_write equ 8Bh ; video controller port numbers cursor_start equ 10 cursor_end equ 11 display_start_hi equ 12 display_start_low equ 13 cursor_hi equ 14 cursor_low equ 15 ;******************************************************** ;* * ;* CONSOLE OUPUT ROUTINES * ;* * ;******************************************************** ;======== io_conout: ;======== ; xios console character out routine ; entry: cl = character ; dl = device number call point_vs ; bx -> virt structure call get_mx ;; lock out other entries mov al,cl ;; copy character to al mov dx,vs_cursor ;; cursor row, column push bx ;; in case of back door stuff call vs_vector ;; state machine branch ;; to con_norm or con_esc's pop bx mov vs_mx,0 ; release for other entries ret ; normal console vector state ; entry: cl,al = character ; bx = vs_ base ; dx = cursor con_normal: mov di,offset norm_scan mov cx,norm_count call con_scan ; scan for special chars jmp ds:norm_table[si] norm_scan db cr,lf,bs,bel,esc norm_count equ 5 norm_table dw vc_out_cr, vc_out_lf, vc_out_bs dw vc_out_bel, vc_out_esc, vc_out ; console escape sequence branch point ; entry: cl,al = character ; bx = vs_base ; dx = cursor con_escape: call restore_state ; vs_vector back to normal mov di,offset esc_scan mov cx,esc_count call con_scan ; scan for escape chars jmp ds:esc_table[si] esc_scan db 'ABCDEH' db 'IJKdlo' db 'LMNYbc' db 'efjkpq' db 'rstuvw' db 'xya:01' db '234567' db '!z' esc_count equ offset $ - offset esc_scan esc_table dw z_up, z_down, z_forward dw z_back, z_erase, z_home dw z_rev_index,z_erase_eos,z_erase_eol dw z_erase_bos,z_erase_line,z_erase_bol dw z_insert_line,z_delete_line,z_delete_char dw z_set_cursor,z_set_fore,z_set_back dw z_cursor_on,z_cursor_off,z_save_cursor dw z_restore_cursor,z_rev_on,z_rev_off dw z_intense_on,z_blink_on,z_blink_off dw z_intense_off,z_wrap_on,z_wrap_off dw z_set_color,z_set_mono,z_video_mode dw z_prog_pfk,z_sl_off,z_sl_mono dw z_sl_color,z_sl_both,z_clk_off dw z_clk_on,z_pfk_off,z_pfk_on dw z_back_door,z_norm_attr dw vc_out ; xios virtual console switch routine ; entry: dl = vc to switch to io_switch: call check_no_switch ; in case of back doors jnz io_switch_done ; skip if no switch call get_all_mxs ;; block all updates call draw_frame_s ;; background old frame call point_vs ;; bx -> virt structure call new_priority ;; set priority tables call new_vc_list ;; make a new list call draw_frame_d ;; frame the window call update_window ;; update within mx call release_match ;; if full top, release it call new_cursor_on ;; take the cursor call free_all_mxs ; allow updates call check_flag_wait ; in case of graphics io_switch_done: ret ; back door window control routines ; return pointers to window relevant information ; entry: dl = vc number or 0FFH ; exit: if dl = vc then ; ax = vc structure pointer ; if dl = 0FFH then ; ax = window data block pointer ww_pointer: cmp dl,nvcns ; if dl = legal vc jb ww_point1 ; then skip mov ax,offset im_here ret ; return window data block ww_point1: call point_vs ; if legal vc xchg ax,bx ; return vs_pointer ret ; set window manager process state ; entry: cl = im_here state ; 0 => manager not resident ; 1 => resident but not active ; 2 => resident and active ; 3 => leave im_here unchanged ; dl = vc number to switch to top ; if dl = 0FFH, then no switch ww_im_here: cmp cl,3 ; if cl > 2, just switch jae ww_just_switch mov im_here,cl ; set state variable cmp cl,2 jz ww_im_here_done ; if staying resident, skip mov key_flag,ci_flag ; switch back to normal ww_just_switch: mov al,dl ; get vc number cmp al,nvcns jae ww_im_here_done ; if illegal, skip add al,70H ; convert to control key mov ah,0FFH ; type = special mov key,ax ; save for conin mov dx,ci_flag ; now activate conin call flagset pushf ; fake an interrupt callf dword ptr dispatcher ww_im_here_done: ret ; create a new window ; entry: dl = virtual console number ; cx = top left (row,column) ; ax (bx at entry) = bottom right (row,column) ww_new_window: push ax ; save bot_right call point_vs ; bx -> virt structure call get_all_mxs ;; lock out the world pop ax cmp cx,vs_top_left ;; if new top_left jnz new_win1 ;; then skip cmp ax,vs_bot_right ;; or if new bottom_right jnz new_win1 ;; then skip ;; if same window as before call update_no_check ;; then just update it jmps new_win3 ;; release and exit new_win1: push ax call copy_full_top ;; save to image if full mov ax,vs_top_left ;; get current corner mov vs_old_t_l,ax ;; and save for full switch mov ax,vs_bot_right ;; do the same mov vs_old_b_r,ax ;; for the other corner mov vs_top_left,cx pop cx mov vs_bot_right,cx call new_count ;; calc rows and cols call do_true_view ;; correct view point call new_vc_map ;; redo the vc_map mov dl,vs_mode ;; get sync bit mov dh,mono_bits ;; assume monochrome mov bx,vs_crt_seg ;; vs_ ptr is hammered cmp bx,mono_seg jz new_win2 ;; skip if mono mov dh,color_bits ;; color if not new_win2: call erase_crt ;; clear appropriate screen call update_all ;; and show the windows new_win3: jmp free_all_mxs ; release the works ; set cursor tracking mode and viewpoint ; entry: dl = vc number ; dh = cursor tracking mode ; 0 => window is fixed on vc image ; 1 => window tracks scrolling ; cx = row,column of top,left viewpoint ww_cursor_view: call point_vs ; bx -> structure call get_mx ;; lock this vc mov vs_cur_track,dh ;; save tracking mode mov vs_view_point,cx call do_true_view ;; correct view_point call update_window ;; show the view call show_cursor ;; and update the cursor mov vs_mx,0 ; release this vc ret ; set virtual console wrap around column ; entry: dl = vc number ; cl = wrap column number ww_wrap_column: call point_vs mov vs_width,cl ; set warp column ret ; switch between full screen and small window ; entry: dl = vc number ww_full_window: call point_vs ;point to structure mov cx,0 ;top left mov ax,crt_full ;bottom right cmp vs_top_left,cx ;if the current top console jnz w_full1 ; is not a full screen, cmp vs_bot_right,ax ; then make it one. jnz w_full1 mov cx,vs_old_t_l ;if it is a full screen mov ax,vs_old_b_r ; then switch to previous. cmp cx,0 ;if old window is not jnz w_full1 ; a full screen cmp ax,crt_full ; then go ahead. jnz w_full1 ret ;if both full, bag it w_full1: jmp ww_new_window ;create the window ; switch between monochrome and color monitors ; entry: dl = vc number ; cl = monitor code (0 => mono; 1 => color) ww_switch_display: call point_vs ;set up bx pointer cmp cl,01 ;if color jz ww_sw_disp1 ; then skip cmp cl,00 ;if mono jz ww_sw_disp2 ; then skip ret ;if illegal code, return ww_sw_disp1: jmp z_set_color ;jump to routine ww_sw_disp2: jmp z_set_mono ;jump to mono set ; Get / Set Screen mode XIOS function 30 ; entry: dl = vc number ; ch = 0 for set, ch = 1 for get ; cl = x,y nibbles when ch = 0 (set) ; x = graphics mode (most sig) ; y = alpha mode (least sig) ; exit: if ch = 1 (get) ; then al = mode byte, ah = 80 (cols) get_set_screen: cmp dl,num_vir_cons jae screen_bad_param ; skip if too high call point_vs ; bx -> vs_ cmp ch,0 ; if set mode jz screen_set ; then skip cmp ch,1 ; if not get mode jnz screen_bad_param ; then bag it screen_get: mov al,vs_screen_mode ; get our current state mov ah,80d ; and our column width ret ; and go back screen_set: cmp num_color,0 ; if we lack a color card jz screen_not_sup ; then forget it mov ch,cl ; save a copy of mode mov al,cl ; and in al for branch mov si,offset alpha_vector ; default branch vector test cl,0F0h ; if most sig nibble = 0 jz screen_set_go ; then branch to alpha test cl,00Fh ; if both nibbles on jnz screen_bad_param ; then bag it mov al,vs_bit not al ; al = other vc's bits test graphic_bits,al ; if somebody else is in jnz screen_not_sup ; graphics mode, bag it mov si,offset graph_vector mov cl,4 mov al,ch ; copy mode byte shr al,cl ; mov graph nibble down screen_set_go: and ax,000Fh ; mask nibble only shl ax,1 ; conv to word pointer add si,ax ; and index into table jmp ds:word ptr[si] ; branch to routine ; error returns screen_bad_param: mov ax,0FFFEh ret screen_not_sup: mov ax,0FFFFh ret alpha_vector dw screen_bad_param, general_alpha dw screen_not_sup, screen_not_sup dw black_white_80, color_80 dw screen_not_sup, screen_not_sup dw screen_not_sup, screen_not_sup dw screen_not_sup, screen_not_sup dw screen_not_sup, screen_not_sup dw screen_not_sup, screen_not_sup graph_vector dw screen_bad_param, general_graph dw color_320, black_white_320 dw black_white_640, screen_not_sup dw screen_not_sup, screen_not_sup dw screen_not_sup, screen_not_sup dw screen_not_sup, screen_not_sup dw screen_not_sup, screen_not_sup dw screen_not_sup, screen_not_sup ; alpha - numeric screen modes general_alpha: mov vs_screen_mode,ch ; save the new mode mov al,vs_bit not al and graphic_bits,al ; mask us out call update_all ; restore alpha info mov dh,sl_crt_flag call z_sl_blank ; restore status line sub ax,ax ; successful return code ret black_white_80: mov ax,002Dh ; video mode / color sel jmps color_alpha_init ; blast it and return color_80: mov ax,0029h ; video mode / color sel ; jmps color_alpha_init ; fall through color_alpha_init: push ax ; save video mode call general_alpha ; take care of variables pop ax ; restore video mode mov si,alpha_str ; alpha video init string jmps color_video_init ; set string and blast ; graphics screen modes general_graph: mov al,top_screen ; if we're on top already cmp al,vs_number ; then go for it jz general_graph_go mov vs_mx,0 ; to prevent a block mov vs_flag_wait,true ; if not, then we must push cx ; wait for a switch mov dx,screen_flag call flag_wait ; set by io_switch pop cx ; now we're cooking general_graph_go: mov vs_screen_mode,ch ; save our new mode mov al,vs_bit or graphic_bits,al ; tell stat line we're here sub ax,ax ; successful return code ret ; and go home color_320: mov ax,200Ah ; video mode / color sel jmps color_graph_init ; blast it and return black_white_320: mov ax,200Eh ; video mode / color sel jmps color_graph_init ; blast it and return black_white_640: mov ax,071Eh ; video mode / color sel ; jmps color_graph_init ; fall through color_graph_init: push ax ; save video mode call general_graph ; take care of variables pop ax ; restore video mode mov si,offset graph_str ; graphics video init string ; jmps color_video_init ; fall through color_video_init: mov dx,color_port ; all get/set to color card ; jmps video_init ; fall through ; set up the initial video port values ; entry: dx = port base address ; si -> video init data string ; ax = video_mode / color select reg video_init: push ax ; save mode / color mov cx,16 ; param count mov ah,0 ; port address video_init_loop: mov al,ah out dx,al ; set up port inc dx ; to data port lodsb ; get data byte out dx,al ; store data byte dec dx ; back to addr port inc ah ; next port loop video_init_loop pop ax ; restore mode / color add dx,4 out dx,al inc dx mov al,ah ; color select out dx,al sub ax,ax ; for successful return ret ; if graphics flag wait, then flag set ; called from io_switch check_flag_wait: mov dl,top_screen call point_vs ; bx -> top vs_ mov al,0 xchg al,vs_flag_wait test al,al ; if we weren't waiting jz check_flag_done ; then finished mov dx,screen_flag call flag_set ; release the graphics process check_flag_done: ret ; *** console out subroutines *** ; printable character to virtual console ram image ; entry: bx -> vs_ ; cl = character ; dx = cursor vc_out: push es call point_cursor ; di -> cursor pos mov ch,vs_attrib test vs_mode,match_bit jz vc_out0 ; skip if full on top mov es,vs_vc_seg ; go to the vc image mov es:[di],cx ; store char, attrib ; now update screen if character belongs mov es,vc_map_seg ; for ownership check add ax,vs_correct xchg ax,si ; save for map check call in_window ; is char in window? jc vc_out1 ; skip if not mov di,si shl di,1 ; back to screen coords mov ax,cx ; ax = character + attrib call put_if_ours ; print it if we own it jmps vc_out1 vc_out0: mov ax,cx ; char to ax call put_crt_c ; go right to physical vc_out1: pop es inc dl ; advance cursor cmp dl,vs_width ; if not wrapping around jb put_cursor ; then do cursor test vs_mode,wrap_bit jz vc_out2 ; exit if not wrapping mov dl,0 mov vs_cur_col,dl ; force a cr,lf call vc_out_lf jmps show_cursor vc_out2: ret ; don't touch cursor ; vc carriage return out vc_out_cr: mov dl,0 ; first column jmps put_cursor ; update and display ; display the cursor at its current position show_cursor: mov dx,vs_cursor ; grab the current pos ; jmps put_cursor ; and fall through ; update and display cursor if appropriate put_cursor: mov vs_cursor,dx mov al,vs_number ; get our vc number cmp al,top_screen ; if not the top vc jnz put_cur2 ; then skip it test vs_mode,cursor_bit jz put_cur2 ; skip if cursor off call in_window ; if cursor outside jnc put_cur1 ; then just jmp old_cursor_off ; turn it off put_cur1: call point_cursor ; ax = byte pointer add ax,vs_correct xchg ax,cx ; cx = cursor pointer mov al,cursor_hi ; set cursor code call video_out ; set crt registers mov cx,vs_cur_type ; if inside window mov al,cursor_start ; then turn it on call video_out put_cur2: ret ; is the current row,col in the window ; entry: dx = row,col ; bx -> vs_ ; exit: cf set if outside of window (or on frame) in_window: mov ax,vs_true_view ; updated viewpoint cmp dl,al jb in_window2 ; skip if left cmp dh,ah jb in_window2 ; skip if high add al,vs_colsb add ah,vs_rowsb ; past bottom right cmp dl,al jae in_window1 ; skip if right cmp dh,ah ; clc if low in_window1: cmc in_window2: ret ; vc line feed out vc_out_lf: cmp dh,crt_rows-1 ; if at the bottom jz vc_lf1 ; do a scroll inc dh ; if not, move down mov vs_cursor,dx ; save the cursor value call update_window ; update if not full on top jmp show_cursor vc_lf1: sub ax,ax ; zero offset from mov di,ax ; the top left mov si,crt_cols*2 ; one row down jmp z_line ; delete one line to scroll ; vc back space vc_out_bs: dec dl ; back one column jns put_cursor ; if not at left test vs_mode,wrap_bit jz vc_bs1 ; if no wrap, done mov dl,vs_width ; if at left, wrap sub dx,0101h ; up one, left one jns put_cursor ; if not at top vc_bs1: ret ; if top left, bag it ; vc beep the bell for top screen only vc_out_bel: mov al,vs_number ; get our vc number cmp al,top_screen jnz vc_bel_done ; skip if not top inc beep_counter ; otherwise, add a beep vc_bel_done: ret ; vc escape character received vc_out_esc: mov vs_vector,offset con_escape ret ; set vector and leave ; *** console escape routines follow *** ; esc A - cursor up z_up: dec dh ; next row up jns z_cursor ; if not already on top, ret ; then set cursor ; esc B - cursor down z_down: inc dh ; next row down cmp dh,crt_rows ; if not already at bottom, jb z_cursor ; then set cursor ret ; esc C - cursor forward z_forward: inc dl ; next column right cmp dl,crt_cols ; if not already at right, jb z_cursor ; then set cursor ret ; esc D - cursor backward z_back: dec dl ; next column left jns z_cursor ; if not already at left, ret ; then set cursor ; esc E - erase console z_erase: call z_home ; get to top left sub dx,dx ; top left jmps z_erase_eos ; erase to end ; esc H - home cursor z_home: sub dx,dx ; 0,0 = top left corner z_cursor: jmp put_cursor ; save and display if visible ; esc I - reverse index z_rev_index: test dh,dh ; if not on the top row, jnz z_up ; then just move up jmp z_insert_line ; else scroll down from top ; esc J - erase to end of screen z_erase_eos: mov cx,crt_rows shl 8 jmps eraser ; common code ; esc K - erase to end of line z_erase_eol: mov ch,dh ; this row inc ch ; to the next row, mov cl,0 ; first column jmps eraser ; common code ; esc d - erase from beginning of screen z_erase_bos: mov cx,dx ; current location sub dx,dx ; top left corner start jmps eraser ; esc l - erase entire line z_erase_line: mov dl,0 ; from first column mov cx,dx ; to the first column inc ch ; of the next row jmps eraser ; esc o - erase from beginning of line z_erase_bol: mov cx,dx ; erase to cursor mov dl,0 ; from first column ; jmps eraser ; common erase routine ; entry: dx = start of erase row,column ; cx = one past ending row,column eraser: xchg cx,dx ; dx = one past the end call point_cursor ; ax = end pointer xchg cx,dx ; dx = start cursor xchg ax,cx ; cx = end pointer call point_cursor ; ax = start pointer sub cx,ax ; cx = erase char count jnz erase ; skip if something to erase ret ; return if nothing erase: mov al,' ' ; erase to blanks mov ah,vs_attrib ; of the current attribute test vs_mode,match_bit jz erase1 ; skip if full on top push es mov es,vs_vc_seg ; base of vc image rep stosw ; blank a bunch pop es cld ; in case df was set jmp update_window ; and show it erase1: jmp put_crt_s ; if full top, go to physical ; esc L - insert a blank line z_insert_line: mov dl,0 ; first column call point_cursor ; ax = count from top mov di,(crt_size-1)*2 ; end of screen mov si,(crt_size-crt_cols-1)*2 std ; backwards move jmps z_line ; shared code ; esc M - delete one line z_delete_line: mov dl,0 call point_cursor ; di -> line start mov si,di add si,crt_cols*2 ; next row down ; external entry point ; entry: si,di set up for movsw ; ax = count from top of screen z_line: mov cx,crt_size-crt_cols sub cx,ax ; cx = character count jbe zline1 ; skip if nothing to move call z_movsw ; movsw in vc_segment z_line1: mov cx,crt_cols ; one line's worth jmps erase ; blank one line ; repeat movsw in current screen segment z_movsw: push es push ds ; save around the move mov ah,0 ; assume no sync mov es,vs_vc_seg test vs_mode,match_bit jnz z_movsw1 ; skip if not full top mov ah,in_sync + out_sync mov es,vs_crt_seg ; else move physical z_movsw1: push es pop ds ; move within the seg call put_crt_m ; like a movsw with sync pop ds pop es ret ; esc N - delete one character z_delete_char: call point_cursor ; point to current char mov cx,crt_cols-1 ; last column sub cl,dl ; cx = chars to line end jz z_del_ch1 ; skip if at line end mov si,di inc si ; next char right inc si call z_movsw ; shift left one char z_del_ch1: mov cx,1 ; erase one last char jmps erase ; esc Y - set cursor position z_set_cursor: mov vs_vector,offset z_set1 ret ; advance to row set z_set1: mov al,crt_rows-1 ; maximum allowable row call z_limit ; clip oversize values mov vs_cur_row,cl ; save the row mov vs_vector,offset z_set2 ret ; advance to col set z_set2: mov al,crt_cols-1 ; maximum allowable column call z_limit mov dl,cl ; dx = new cursor location call restore_state ; back to normal jmp put_cursor ; update and display new cur z_limit: sub cl,' ' ; correct space offset jns z_limit1 ; skip if positive mov cl,0 ; else set to zero z_limit1: cmp cl,al ; check upper bound jbe z_limit2 ; skip if below it mov cl,al ; else set to it z_limit2: ret ; esc b - set foreground color z_set_fore: mov vs_vector,offset z_fore1 ret ; advance to foreground bits z_fore1: mov ah,vs_attrib ; current attribute jmps z_fg_bg ; shared code ; esc c - set background color z_set_back: mov vs_vector,offset z_back1 ret ; advance to background bits z_back1: mov ah,al ; ah = bg bits mov cl,4 rol ah,cl ; bg bits to ms nibble mov al,vs_attrib z_fg_bg: and al,0fh ; take the 4 lsb's of al and ah,0f0h ; and the 4 msb's of ah or al,ah ; and mash 'em together mov vs_attrib,al ; that's the new attribute jmp restore_state ; back to normal ; entry used by switch to restore cursor new_cursor_on: test vs_mode,cursor_bit jz z_cursor_off ; skip if cursor off ; jmps z_cursor_on ; else turn it on ; esc e - enable cursor z_cursor_on: or vs_mode,cursor_bit mov cx,vs_cur_type ; monochrome or color mov al,cursor_start ; cursor display code call video_out jmp show_cursor ; esc f - disable cursor z_cursor_off: and vs_mode,not cursor_bit old_cursor_off: ; external entry mov cx,vs_cur_type ; monochrome or color or ch,20h ; disable bit mov al,cursor_start ; cursor display code jmp video_out ; set the port ; esc j - save cursor position z_save_cursor: mov vs_save_cursor,dx ret ; save for later ; esc k - restore cursor position z_restore_cursor: mov dx,vs_save_cursor jmp put_cursor ; back where it was ; esc p - reverse video on z_rev_on: test vs_mode,rev_bit ; if already reversed jnz z_rev2 ; then forget it or vs_mode,rev_bit ; remember jmps z_rev1 ; to common code ; esc q - reverse video off z_rev_off: test vs_mode,rev_bit ; if already off jz z_rev2 ; then forget it and vs_mode,not rev_bit z_rev1: ; shared code mov al,vs_attrib ; get current colors mov ah,al ; copy for msb's and ax,8877h ; mask colors only mov cl,4 rol al,cl ; swap colors or al,ah ; restore intense, blink mov vs_attrib,al ; new attribute z_rev2: ret ; esc r - intensity on z_intense_on: or vs_attrib,08h ; set the intense bit ret ; esc u - intensity off z_intense_off: and vs_attrib,0f7h ; reset the intense bit ret ; esc s - blink on z_blink_on: or vs_attrib,80h ; set the blink bit ret ; esc t - blink off z_blink_off: and vs_attrib,7fh ; reset the blink bit ret ; esc v - wrap at line end on z_wrap_on: or vs_mode,wrap_bit ret ; esc w - no wrap at line end z_wrap_off: and vs_mode,not wrap_bit z_return: ret ; esc x - switch console to color monitor z_set_color: cmp num_color,0 ; if no color card jz z_return ; then do nothing call copy_full_top ; if full on top, copy back call old_cursor_off call set_color ; update vs_ variables jmp set_new_mon ; redo all windows ; esc y - switch console to monochrome monitor z_set_mono: cmp num_mono,0 ; if no monochrome card jz z_return ; then do nothing call copy_full_top ; if full on top, copy back call old_cursor_off call set_mono ; update vs_ variables jmp set_new_mon ; redo all windows ; set vs_ variables to color monitor set_color: mov vs_crt_seg,color_seg mov vs_xlat,offset color_xlat mov ax,var_cursor ; different for compaq mov vs_cur_type,ax mov al,var_sync ; different for compaq or vs_mode,al mov al,vs_bit or color_bits,al ; add to color vc's not al ; and subtract from mono's and mono_bits,al ret ; set vs_ variables to monochrome monitor set_mono: mov vs_crt_seg,mono_seg mov vs_xlat,offset mono_xlat mov vs_cur_type,mono_cursor and vs_mode,not sync_bit mov al,vs_bit or mono_bits,al ; add to mono vc's not al ; and remove from color's and color_bits,al ret ; esc a - set color graphics card video mode z_video_mode: mov vs_vector,offset z_video_param ret ; advance to next state z_video_param: call restore_state ; back to con_normal and al,07h ; allow only mode 0-7 cbw mov si,ax ; table index mov cl,ds:screen_table[si] mov ch,0 ; set mode code mov dl,vs_number ; vc number jmp get_set_screen ; just like a back door screen_table db 00h,00h,04h,05h db 20h,30h,40h,00h ; esc : - program a programmable function key z_prog_pfk: mov vs_vector,offset z_pfk_code ret ; advance to next state z_pfk_code: push es push ds pop es ; local es for scan mov di,offset pfk_code_tbl mov dx,di ; save start addr mov cx,low_pfks + high_pfks ; pfk count repnz scasb ; scan for legal code pop es jnz z_pfk_done ; done if not legal dec di ; correct overscan sub di,dx xchg ax,di ; ax = index value call point_pfk ; set pointer and count mov vs_vector,offset z_pfk_val ret ; advance to next state z_pfk_val: mov di,vs_pfk_ptr ; point to table entry mov [di],al ; store the value inc di mov vs_pfk_ptr,di ; bump the pointer test al,al ; if char = 0 jz z_pfk_val1 ; then we're done dec vs_pfk_count ; if entry is filled jz z_pfk_done ; we're also done ret ; if not, stay here z_pfk_val1: mov vs_pfk_count,al ; zero the count z_pfk_done: jmp restore_state ; reset the vector ; esc 0 - turn status line off z_sl_off: mov dh,0 ; finish with none jmps z_sl_blank ; shared routine ; esc 1 - turn on monochrome status only z_sl_mono: mov dh,sl_mono_bit ; finish with mono jmps z_sl_blank ; shared routine ; esc 2 - turn on color status only z_sl_color: mov dh,sl_color_bit ; finish with color jmps z_sl_blank ; shared routine ; esc 3 - turn on both status lines z_sl_both: mov dh,sl_mono_bit + sl_color_bit ; jmps z_sl_blank ; try for both ; shared status line blank and change ; entry: dh = new sl_crt_flag to try z_sl_blank: mov al,0FFh ; first get sole control xchg al,sline_locked test al,al ; if someone's there jnz z_sl_blank ; then wait push bx ; save vs_ pointer push es mov dl,byte ptr num_color shl dl,1 ; 2 if there, 0 if not or dl,byte ptr num_mono ; 3, 2, 1, or 0 call sl_disp_prep ; prepare for blanking z_sl_blank1: mov ax,blank call sl_put_char ; write one blank loop z_sl_blank1 ; 80 times pop es pop bx ; restore vs_ pointer and dl,dh ; see who's left mov sl_crt_flag,dl ; and save for updates mov sline_locked,0 ; release the semaphore ret ; esc 4 - turn status line clock display off z_clk_off: push es push ds pop es ; local for stos mov sl_clk_flag,0 ; disable update mov di,offset smsg_hour mov cx,11 ; clock characters mov al,' ' rep stosb ; blank 'em pop es ret ; esc 5 - enable status line clock display z_clk_on: mov smsg_c1,':' ; first delimiter mov smsg_c2,':' ; second delimiter mov smsg_hour,'0 ' ; assume initial time mov word ptr sl_hour_save,0FF00h ; force update mov sl_clk_flag,0FFh ; if hour not 0 ret ; esc 6 - turn pfk expansion off z_pfk_off: mov vs_pfk_exp,0 ; expansion flag false ret ; esc 7 - turn pfk expansion on z_pfk_on: mov vs_pfk_exp,0FFh ; expansion flag true ret ; esc ! - general XIOS back door entry z_back_door: mov vs_back_count,0 ; register counter mov vs_vector,offset z_back_fill ret z_back_fill: mov al,vs_back_count cbw ; ax -> which register lea di,vs_back_ax mov si,di ; save for loading add di,ax ; point to register mov [di],cl ; save register value inc ax ; next reg mov vs_back_count,al cmp al,8 ; max 8 bit register jb z_back_ret ; stay here for 8 ; ready to go for it call restore_state ; reset conout vector push es push ds pop es ; local es for scan mov di,offset legal_func_list mov cx,length legal_func_list lodsw ; fetch ax (al=func #) repnz scasb ; check for legal call pop es jnz z_back_ret ; if illegal, return ; a legal function number has been given mov vs_mx,0 ; allow entry xchg ax,dx ; dx saves ax call value lodsw ! xchg ax,bx ; bx call value lodsw ! xchg ax,cx ; cx call value lodsw ! xchg ax,dx ; dx and ax call value add sp,6 ; clear stack returns jmp entry ; and go for it! z_back_ret: ret legal_func_list db 7,8,18,19,20,21,22,23,24 ; esc z - restore normal attributes z_norm_attr: mov vs_attrib,07h ; white on black and vs_mode,0FFh-04 ; not reverse video or vs_mode,03h ; wrap and cursor jmp z_cursor_on ; display cursor ; *** console out subroutines *** ; clear both screens, new xlats, and update all ; called from init, esc x, and esc y ; this routine locks up the mx semaphores set_new_mon: cli ;; a little critical section mov vs_mx,0 ;; to keep from blocking new_monitor: ;; entry from init call get_all_mxs ;; lock 'em up sti mov si,offset vc_priority mov cx,num_vir_cons new_mon1: push cx mov dl,[si] ;; get vc number call point_vs ;; bx -> structure call new_xlat ;; redo the table pop cx inc si ;; next vc up loop new_mon1 cmp num_mono,0 ;; if no monochrome card jz new_mon2 ;; then skip erase mov dh,mono_bits ;; which vc's are mono mov dl,0 ;; no sync mov bx,mono_seg call erase_crt ;; clear mono screen new_mon2: cmp num_color,0 ;; if no color card jz new_mon3 ;; then skip erase mov dh,color_bits ;; which vc's are color mov dl,sync_bit ;; retrace sync mov bx,color_seg call erase_crt ;; clear color screen new_mon3: call update_all ;; new screens all around jmp free_all_mxs ; let my semaphores go ; update all windows ; called from new_window, new_monitor and general_alpha update_all: call new_vc_list ; redo the row list mov si,offset vc_priority mov cx,num_vir_cons ; loop counter up_all1: push si push cx mov dl,[si] ; get vc number call point_vs ; bx -> virt structure mov si,offset slines cmp cl,1 ; if not front vc jnz up_all2 ; then skip mov si,offset dlines up_all2: call draw_frame ; frame the window and call update_window ; fill it pop cx pop si inc si ; next window up loop up_all1 ; num_vir_cons times call release_match ; if full top, release it call new_cursor_on ; turn on the cursor jmp show_cursor ; display and done ; move the vc image to its window unless full on top ; entry: bx -> vs_ update_window: test vs_mode,match_bit jz up_win2 ; skip if full on top update_no_check: call correct_ptr ; calc correction shl dx,1 ; word pointer now mov si,vs_list_ptr ; point to row list up_win1: lodsw ; get count xchg ax,cx jcxz up_win2 ; done when zero lodsw ; get pointer mov di,ax ; point to crt sub ax,dx ; correct window pos push si push ds push es xchg ax,si ; point to vc image mov es,vs_crt_seg mov ds,vs_vc_seg mov ah,out_sync ; from image to crt call put_crt_m ; move string pop es pop ds pop si jmps up_win_1 ; do next row up_win2: ret ; if full screen on top and no sync, reset match_bit ; entry: bx -> vs_ release_match: or vs_mode,match_bit call check_full_top jnz release_done test vs_mode,sync_bit jnz release_done ; if color, don't release and vs_mode,not match_bit release_done: ret ; if full screen on top, set zero flag ; entry: bx -> vs_ check_full_top: cmp vs_rowsb,crt_rows jnz check_ft_done ; skip if not full cmp vs_colsb,crt_cols jnz check_ft_done ; skip if not full mov al,top_screen cmp vs_number,al ; if top, set zf check_ft_done: ret ; get semaphore for this vc ; entry: bx -> vs_ get_mx: mov al,0FFh ; ownership true xchg al,vs_mx ; test and set semaphore test al,al ; if nobody owned it jz got_mx ; then we're done ; if semaphore is blocked, wait a tick and try again push bx push cx push dx push es mov cx,p_delay mov dx,1 ; wait at least 1 tic mov bx,rlr ; ready list root mov es,10h[bx] ; load the uda callf supervisor ; like a bdos int pop es pop dx pop cx pop bx jmps get_mx ; back and try again got_mx: ret ; get semaphore queues for all vc's get_all_mxs: push bx push cx mov bx,offset first_vs mov cx,num_vir_cons get_all1: call get_mx ; get one at a time add bx,size_vs loop get_all1 ; for each vc pop cx pop bx ret ; release semaphores for all vc's free_all_mxs: push bx push cx mov bx,offset first_vs mov cx,num_vir_cons free_all1: mov vs_mx,0 ; free the semaphore add bx,size_vs loop free_all1 ; for each vc pop cx pop bx ret ; calculate window position offset correction correct_ptr: cmp vs_cur_track,0 ; if no tracking, jz cor_ptr2 ; then skip mov ah,vs_cur_row ; this corrects for mov al,vs_true_row ; a scrolling screen cmp al,ah ; if cursor above view ja cor_ptr1 ; then move view up sub ah,vs_rowsb ; if within a window js cor_ptr2 ; of top, skip cmp al,ah ; if cursor within view ja cor_ptr2 ; then also skip inc ah ; cursor at window bottom cor_ptr1: mov vs_true_row,ah cor_ptr2: mov dx,vs_top_left call point_cursor ; static correction xchg ax,dx ; save in dx mov al,crt_cols mul vs_true_row sub dx,ax ; correct for row mov al,vs_true_col cbw sub dx,ax ; and column mov vs_correct,dx ; save for char out ret ; update vc priority list and xlat table ; entry: bl -> vs_ ; dl = vc number new_priority: push es push ds pop es ; local es mov di,offset vc_priority mov cx,num_vir_cons ; number of vc's mov al,dl ; this vc repnz scasb ; find ours pop es jcxz new_xlat ; done if on top dec di ; for overscan new_prior1: mov al,.1[di] ; get the one above mov [di],al ; and move it down inc di loop new_prior1 ; to top of list mov [di],dl ; put us on top ; jmps new_xlat ; fall through ; update vc_xlat table new_xlat: mov di,vs_xlat mov al,vs_bit ; bit position = vc mov ah,0 ; count mov cx,1 shl num_vir_cons ; bytes per vc_xlat new_xlat1: test ah,al ; is this ours? jz new_xlat2 ; if not, skip mov [di],dl ; if so, take it over new_xlat2: inc ah ; next number inc di ; next table byte loop new_xlat1 ; through xlat table ret ; make a new vc_list for switch or new_window new_vc_list: push es push bx ; save vs_ pointer mov es,vc_map_seg ; base of vc_map mov si,offset vc_list mov list_vc,num_vir_cons-1 ; vc counter new_list1: mov dl,list_vc ; get vc number call point_vs ; bx -> vs_ mov list_next,-1 ; impossible value mov vs_list_ptr,si mov dx,vs_top_left mov cx,vs_rows ; rows in window new_list2: push cx push dx call point_cursor ; ax -> vc_map pos xchg ax,di mov cx,vs_cols ; columns in window new_list3: mov al,es:[di] ; 1st vc_map code push ax call xlat_priority ; al = who owns it cmp al,list_vc ; if it's us pop ax jz new_list4 ; then skip repz scasb ; if not, just scan jz new_list8 ; skip if end of line dec di ; else, correct for inc cx ; over scan jmps new_list3 new_list4: mov dx,di ; save first pointer repz scasb ; scan through our own jz new_list5 ; skip if line end dec di ; else, correct for inc cx ; over scan new_list5: mov ax,di sub ax,dx ; ax = scan count cmp dx,list_next ; if string contiguous jz new_list6 ; then add to count mov [si],ax ; if not, save count inc si inc si mov [si],dx ; and pointer add [si],dx ; vc_ptr = 2*map_ptr inc si inc si jmps new_list7 ; and carry on new_list6: add .neg4[si],ax ; add to last count new_list7: add ax,dx ; pointer plus count mov list_next,ax ; update next pointer test cx,cx ; if scan not done jnz new_list3 ; then continue new_list8: pop dx pop cx inc dh ; next row down cmp dh,vs_bottom jbe new_list2 ; do all rows mov word ptr [si],0 ; list end flag inc si inc si ; to next vc's list dec list_vc jns new_list1 ; do all vc's pop bx ; restore vs_ ptr pop es ret ; draw a single line frame ; save: dx for double frame draw_frame_s: push dx mov dl,top_screen ; old top frame call point_vs ; bx -> old vs_ call copy_full_top ; if full, copy to image call old_cursor_off ; for 2 monitor switch mov si,offset s_lines call draw_frame ; single line frame pop dx ret ; draw a double line frame ; entry: bx -> vs_ draw_frame_d: mov si,offset d_lines ; jmps draw_frame ; double line frame ; draw frame around the window ; entry: bx -> vs_ ; si -> line data draw_frame: mov dx,vs_top_left mov cx,vs_cols ; column count dec dh ; up one row js draw_f1 ; skip if top row mov ax,top_corners call hline ; top line + corners draw_f1: mov dh,vs_bottom inc dh ; down on row cmp dh,crt_rows jz draw_f2 ; skip if bottom mov ax,bot_corners call hline ; bottom line + corners draw_f2: mov dx,vs_top_left mov cx,vs_rows ; row count dec dl ; left one column js draw_f3 ; skip if 1st column call vline ; left line only draw_f3: mov dl,vs_right inc dl ; right one column cmp dl,crt_cols jz draw_f4 ; skip if last column call vline ; right line only draw_f4: ret ; draw a horizontal line with corners ; entry: dx=row,col cx=col count ax=corners hline: push es push si push dx push cx push ax ; save corners mov ax,horiz ; horiz line + attrib mov ds:h_copy,ax ; save for draw call point_cursor ; di -> line start xchg ax,si ; si -> map mov es,vc_map_seg ; for owner check cmp dl,0 ; if at left edge jz hline1 ; skip first corner pop ax push ax dec di ; back up for corner dec di dec si ; on the map too mov ah,ds:a_copy ; frame attribute call put_if_ours ; to the screen hline1: add dl,cl ; get to right column dec dl mov ax,ds:h_copy ; horiz line + attrib hline2: call put_if_ours ; to the screen loop hline2 ; cx times pop ax ; corners again cmp dl,crt_cols-1 ; if at right edge jz hline3 ; skip last corner mov al,ds:a_copy ; frame attribute xchg al,ah call put_if_ours ; to the screen hline3: pop cx pop dx pop si pop es ret c_copy rb 1 ; character a_copy rb 1 ; attribute h_copy equ word ptr c_copy ; draw a vertical line (no corners) ; entry: dx=row,col cx=row count vline: push es push si push cx push vert ; save vert line call point_cursor ; di -> line start xchg ax,si ; si -> map mov es,vc_map_seg ; for owner check pop ax ; vert line + attrib vline1: call put_if_ours ; to the screen add di,2*(crt_cols-1) add si,crt_cols-1 loop vline1 ; num of rows times pop cx pop si pop es ret ; put a frame char to screen if we own it ; entry: ax = char + attrib ; es:si -> vc_map ; di -> crt put_if_ours: push ax ; save char mov al,es:[si] ; get map code inc si ; to next char call xlat_priority ; who owns it? cmp al,vs_number ; if not us pop ax ; then skip jnz put_if1 jmp put_crt_c ; print one char put_if1: inc di ; bump destination inc di ret ; update row and column counts ; entry: bx -> vs_ new_count: mov al,vs_bottom sub al,vs_top ; rows - 1 cbw inc ax ; row count mov vs_rows,ax mov al,vs_right sub al,vs_left ; columns - 1 cbw inc ax ; column count mov vs_cols,ax ret ; correct view point if window is off image ; entry: bx -> vs_ do_true_view: mov dx,vs_view_point mov al,crt_rows sub al,vs_rowsb ; get max view row cmp al,dh jae do_true1 ; skip if ok mov dh,al ; else replace with max do_true1: mov al,crt_cols sub al,vs_colsb ; get max view col cmp al,dl jae do_true2 ; skip if ok mov dl,al ; else replace with max do_true2: mov vs_true_view,dx ; save for correct_ptr ret ; update vc_map for change of window ; entry: bx -> vs_ ; dl = vc number new_vc_map: push es mov es,vc_map_seg sub di,di ; top left corner mov cx,crt_size mov al,vs_bit ; get vc bit mask not al new_map1: and es:[di],al ; wipe out old window inc di loop new_map1 ; for entire screen ; then calculate window limits mov dx,vs_top_left cmp dh,0 ; if at crt top jz new_map2 ; skip top frame dec dh ; include frame new_map2: cmp dl,0 ; if at crt left jz new_map3 ; skip left frame dec dl ; include frame new_map3: mov cx,vs_bot_right cmp ch,crt_rows-1 ; if at crt bottom jz new_map4 ; skip bot frame inc ch ; include frame new_map4: cmp cl,crt_cols-1 ; if at crt right jz new_map5 ; skip right frame inc cl ; include frame new_map5: call point_cursor ; ax -> top left xchg ax,di sub cx,dx ; row & col difference add cx,0101h ; ch=rows, cl=cols ; now install the new window mov al,vs_bit ; restore vc bit mask new_map6: push di push cx new_map7: or es:[di],al ; or in vc's bit inc di dec cl ; column count jnz new_map7 pop cx pop di add di,crt_cols ; next row dec ch jnz new_map6 pop es ret ; point to virtual console structure ; entry: dl = device number ; exit: bx -> vs_ point_vs: mov al,dl cbw shl ax,1 ; ax = 2 * vc number xchg ax,bx mov bx,table_vs[bx] ; fetch the pointer ret ; point to vc image from cursor row,column ; entry: dx = (row,column) ; exit: ax = 80*row + column ; di = 2*ax point_cursor: mov al,176 ; 256 - 80 = 176 mul dh neg ax ; ax = -176*row add ax,dx ; ax = 80*row + col mov di,ax add di,di ; di = 2*ax ret ; translate vc_map code to owner number ; entry: al = vc_map code ; exit: al = owner number xlat_priority: push bx ; save vs_ mov bx,vs_xlat xlat bx pop bx ret ; scan character string for match ; entry: di -> string ; cx = count ; al = charater ; exit: si = word table index, or just past the end ; of the table if character is not found ; al,cl = character con_scan: push es push ds pop es ; local extra segment mov si,cx repnz scasb ; look for match jnz con_scan1 ; if no match, skip dec si ; correct count con_scan1: sub si,cx ; si = char number shl si,1 ; word pointer mov cl,al ; copy character pop es ret ; restore the state vector to normal restore_state: mov vs_vector,offset con_normal ret ; that's all ; clear the unowned portions of the screen ; entry: dh = vc bit mask ; dl = vs_mode (08 to sync) ; bx = crt_segment erase_crt: push es sub si,si ; start at top left sub di,di mov es,vc_map_seg mov cx,crt_size mov ax,blank ; space + attrib erase_crt1: test es:byte ptr [si],dh jnz erase_crt2 ; skip if owned call put_mon_c ; straight to monitor jmps erase_crt3 ; di taken care of erase_crt2: inc di ; next crt char inc di erase_crt3: inc si ; next map char loop erase_crt1 pop es ; restore extra seg ret ; copy crt back to vc image when full screen on top ; entry: bx -> vs_ copy_full_top: call check_full_top ; if not full top screen jnz copy_f_done ; then skip ; ; top screen is full, save it ; push cx push dx push ds push es sub si,si ; top left sub di,di ; of both mov cx,crt_size mov es,vs_vc_seg ; image is destination mov ds,vs_crt_seg mov ah,in_sync ; sync for reading call put_crt_m ; general mover pop es pop ds pop dx pop cx or vs_mode,match_bit copy_f_done: ret ; store characters to crt (like a rep stosw) ; entry: di -> cursor location ; ax = attribute, character ; bx -> vs_ ; cx = char count put_crt_s: push bx ! push dx ! push es mov dx,vs_crt_seg ; get our segment mov es,dx ; and set it up cmp graphic_bits,0 jz put_s_alpha ; if alpha, continue cmp dx,color_seg jz put_s_done ; if color, forget it put_s_alpha: test vs_mode,sync_bit jz put_s_all ; skip if not syncing mov dx,color_port+6 put_s_top: mov bx,ax ; save char in bx put_s_wait: sti ; allow an interrupt nop cli ;; hold the ints in al,dx test al,vrt ;; if vertical retrace jnz put_s_burst ;; then store a burst test al,hrt ;; if horiz retrace jnz put_s_wait ;; then stay here put_s_no_hrt: in al,dx test al,hrt jz put_s_no_hrt ; wait for fresh hrt xchg ax,bx ; char back to ax stosw ; put it out loop put_s_top ; more if you've got 'em jmps put_s_done ; done if not ; vertical retrace, put a burst put_s_burst: xchg ax,bx ;; char back to ax mov bx,stos_burst ;; number of chars to store cmp cx,bx ;; if count is too big jbe put_s_all ;; then do a burst ;; at a time sub cx,bx ;; first correct the count xchg bx,cx ;; cx = burst count rep stosw ;; store a burst xchg bx,cx ;; corrected count to cx jmps put_s_top ;; go back for more put_s_all: rep stosw ; store them all ; ; finished with the store ; put_s_done: sti ; allow interrupts pop es ! pop dx ! pop bx ret ; generalized move to/from crt ; entry: ds:si -> source ; es:di -> destination ; cx = char count ; ah = sync flag in_sync equ 01h out_sync equ 02h put_crt_m: push bx push dx push bp cmp cs:graphic_bits,0 jz put_m_alpha ; if alpha, continue cmp cs:vs_crt_seg,color_seg jz put_m_done ; if color, forget it put_m_alpha: test cs:vs_mode,sync_bit jz put_m_all ; skip if not syncing test ah,ah ; if no sync bits jz put_m_all ; then just move mov dx,color_port+6 mov bh,ah ; sync bits live here put_m_top: test bh,in_sync ; do we wait on input? jz put_m2 ; if not, skip put_m0: sti ; allow an interrupt nop cli ;; hold the ints in al,dx test al,vrt ;; if vertical retrace jnz put_m_burst ;; then move a burst in al,dx ;; get a fresh one test al,hrt ;; if no horiz retrace jnz put_m0 ;; then stay here put_m1: in al,dx test al,hrt jz put_m1 ; wait for fresh hrt put_m2: lodsw ; get crt character dec si dec si ; in case we burst test bh,out_sync ; do we wait on ouput? jz put_m5 ; if not, skip xchg ax,bp ; save char in bp put_m3: sti ; allow an interrupt nop cli ;; hold the ints in al,dx test al,vrt ;; if vertical retrace jnz put_m_burst ;; then move a burst test al,hrt ;; if no horiz retrace jnz put_m3 ;; then stay here put_m4: in al,dx test al,hrt jz put_m4 ; wait for fresh hrt xchg ax,bp ; char back to ax put_m5: stosw ; put it out inc si ; now it's safe to inc inc si loop put_m_top ; more if you've got 'em jmps put_m_done ; done if not ; vertical retrace, put a burst put_m_burst: mov ax,movs_burst ;; move count cmp cx,ax ;; if count is too big jbe put_m_all ;; then do a burst ;; at a time sub cx,ax ;; first correct the count xchg ax,cx ;; cx = burst count rep movsw ;; move a burst xchg ax,cx ;; corrected count to cx jmps put_m_top ;; go back for more put_m_all: rep movsw ; move them all ; ; finished with the move ; put_m_done: sti ; allow interrupts pop bp pop dx pop bx ret ; store one char directly to monitor (used by erase_crt) ; entry: bx:di -> cursor location ; ax = attrib, char ; dl = vs_mode (08 to sync) put_mon_c: push es push dx push bx mov es,bx ; set crt segment jmps put_crt_c0 ; sneak in ; store one character to crt (like a stosw) ; entry: di -> cursor location ; ax = attribute, character put_crt_c: push es push dx push bx cmp graphic_bits,0 jz put_c_alpha ; if alpha, continue cmp vs_crt_seg,color_seg jz put_c_done ; if color, forget it put_c_alpha: mov es,vs_crt_seg ; mono or color mov dl,vs_mode put_crt_c0: xchg ax,bx ; save char in bx test dl,sync_bit jz put_c3 ; if no sync, skip mov dx,color_port+6 ; crt status put_c1: sti ; allow an interrupt nop cli ;; hold the ints in al,dx test al,vrt ;; if vert retrace jnz put_c3 ;; just do it in al,dx ;; get a fresh one test al,hrt ;; if horiz retrace on jnz put_c1 ;; wait for it to go away put_c2: in al,dx test al,hrt ;; wait for fresh horiz rt jz put_c2 put_c3: xchg ax,bx ;; char back to ax stosw ;; the fastest put_c_done: sti pop bx pop dx pop es ret ; video controller port output ; entry: al = first data register number ; bx -> vs_ ; cx = two data bytes video_out: push dx mov dx,mono_port ; assume monochrome cmp vs_crt_seg,mono_seg jz video_out1 ; skip if true mov dx,color_port ; color port if false video_out1: cli ; critical section out dx,al ; set port number inc dx ; data register xchg al,ch ; al = first data out dx,al ; dec dx ; back to set port xchg al,ch ; inc ax ; next port out dx,al ; inc dx ; mov al,cl ; second data out dx,al ; sti ; section done pop dx ret ;******************************************************** ;* * ;* CONSOLE OUT VARIABLES * ;* * ;******************************************************** reorg9 equ (offset $ + 1) and -2 dseg org reorg9 vc_map_seg rw 1 ; filled by init list_next rw 1 ; for new vc list list_vc rb 1 ; same here mono_bits rb 1 ; which vc's are mono color_bits rb 1 ; which vc's are color graphic_bits db 0 ; which vc's in graphics ; single / double line frame drawing data horiz equ word ptr 0[si] vert equ word ptr 2[si] top_corners equ word ptr 4[si] bot_corners equ word ptr 6[si] s_lines dw 07C4h,07B3h dw 0BFDAh,0D9C0h d_lines dw 0FCDh,0FBAh dw 0BBC9h,0BCC8h ; variable video params for the compaq var_cursor dw color_cursor ; hi or low res var_sync db sync_bit ; to sync or not db 0DBh ; pad ; window data block returned by ww_pointer im_here db 0 ; initially not resident db num_vir_cons vc_priority rb num_vir_cons-1 top_screen rb 1 sl_crt_flag rb 1 ; to reinstate status line db 0DBh ; pad ; priority translate table mono_xlat rb 1 shl num_vir_cons ; for mono vc's color_xlat rb 1 shl num_vir_cons ; for color vc's ; virtual console structure definition vs_cursor equ word ptr 0[bx] ; cursor row,column vs_cur_col equ byte ptr 0[bx] vs_cur_row equ byte ptr 1[bx] vs_top_left equ word ptr 2[bx] ; t l window corner vs_left equ byte ptr 2[bx] vs_top equ byte ptr 3[bx] vs_bot_right equ word ptr 4[bx] ; b r window corner vs_right equ byte ptr 4[bx] vs_bottom equ byte ptr 5[bx] vs_old_t_l equ word ptr 6[bx] ; last t l corner vs_old_b_r equ word ptr 8[bx] ; last b r corner vs_crt_size equ word ptr 10[bx] ; total rows, columns vs_win_size equ word ptr 12[bx] ; including invisible vs_view_point equ word ptr 14[bx] ; window top left vs_rows equ word ptr 16[bx] ; window row count vs_rowsb equ byte ptr 16[bx] vs_cols equ word ptr 18[bx] ; window column count vs_colsb equ byte ptr 18[bx] vs_correct equ word ptr 20[bx] ; char position factor vs_vc_seg equ word ptr 22[bx] ; vc map base segment vs_crt_seg equ word ptr 24[bx] ; base of screen memory vs_list_ptr equ word ptr 26[bx] ; start of row updates vs_attrib equ byte ptr 28[bx] ; current char attrib vs_mode equ byte ptr 29[bx] ; wrap, cursor on/off vs_cur_track equ byte ptr 30[bx] ; cursor tracking mode vs_width equ byte ptr 31[bx] ; wrap width vs_number equ byte ptr 32[bx] ; virt console num vs_bit equ byte ptr 33[bx] ; vc num = bit pos vs_save_cursor equ word ptr 34[bx] ; cursor save loc vs_vector equ word ptr 36[bx] ; console state vector vs_xlat equ word ptr 38[bx] ; mono/color xlat table vs_null equ word ptr 40[bx] ; unused (up for grabs) vs_true_view equ word ptr 42[bx] ; corrected view point vs_true_col equ byte ptr 42[bx] vs_true_row equ byte ptr 43[bx] vs_cur_type equ word ptr 44[bx] ; mono or color vs_pfk_tbl equ word ptr 46[bx] ; prog func key base vs_pfk_ptr equ word ptr 48[bx] ; pointer to pfk table vs_pfk_count equ byte ptr 50[bx] ; pfk count down vs_pfk_exp equ byte ptr 51[bx] ; pfk expansion flag vs_mx equ byte ptr 52[bx] ; mutual exclusion sema vs_back_count equ byte ptr 53[bx] ; XIOS back door count vs_back_ax equ word ptr 54[bx] ; ax for XIOS back door vs_back_bx equ word ptr 56[bx] vs_back_cx equ word ptr 58[bx] vs_back_dx equ word ptr 60[bx] vs_screen_mode equ byte ptr 62[bx] ; alpha/graphics mode vs_flag_wait equ byte ptr 63[bx] ; if waiting for graphics size_vs equ 64 ; vs_mode bit definitions wrap_bit equ 01h ; wrap mode enabled cursor_bit equ 02h ; cursor displayed rev_bit equ 04h ; reverse video mode sync_bit equ 08h ; sync crt accesses match_bit equ 10h ; image matches physical init_mode equ wrap_bit + cursor_bit + match_bit table_vs dw first_vs,second_vs,third_vs,fourth_vs first_vs dw 0,0,crt_full dw 0,crt_full,crt_full dw 1850H,0 dw crt_rows,crt_cols,0 dw 0,0,vc_list db 07h,init_mode,1,crt_cols,0,1 dw 0,con_normal,0 dw 0,0,0 dw pfk_tbl0,0,0FF00h dw 0,0,0,0,0 db 1,0 second_vs dw 0,0,crt_full dw 0,crt_full,crt_full dw 1850H,0 dw crt_rows,crt_cols,0 dw 0,0,vc_list db 07h,init_mode,1,crt_cols,1,2 dw 0,con_normal,0 dw 0,0,0 dw pfk_tbl1,0,0FF00h dw 0,0,0,0,0 db 1,0 third_vs dw 0,0,crt_full dw 0,crt_full,crt_full dw 1850H,0 dw crt_rows,crt_cols,0 dw 0,0,vc_list db 07h,init_mode,1,crt_cols,2,4 dw 0,con_normal,0 dw 0,0,0 dw pfk_tbl2,0,0FF00h dw 0,0,0,0,0 db 1,0 fourth_vs dw 0,0,crt_full dw 0,crt_full,crt_full dw 1850H,0 dw crt_rows,crt_cols,0 dw 0,0,vc_list db 07h,init_mode,1,crt_cols,3,8 dw 0,con_normal,0 dw 0,0,0 dw pfk_tbl3,0,0FF00h dw 0,0,0,0,0 db 1,0 ; video controller configuration data tables ; used by get_set_screen routine mono_table db 61h,50h,52h,0Fh,19h,06h,19h,19h,02h,0Dh,2Bh,0Ch,0,0,0,0 graph_str db 38h,28h,2Dh,0Ah,7Fh,06h,64h,70h,02h,01h,26h,07h,0,0,0,0 alpha_str dw ibm_str color_table rb 0 ibm_str db 71h,50h,5Ah,0Ah,1Fh,06h,19h,1Ch,02h,07h,26h,07h,0,0,0,0 compaq_str db 71h,50h,5Ah,0Ah,19h,06h,19h,19h,02h,0Dh,2Bh,0Ch,0,0,0,0 ; dynamic row update list vc_list rw (2*num_vir_cons-1) * crt_rows * 2 + 4 ; console control blocks have moved to xchar.lib