dmt@mtuxt.UUCP (D.TUTELMAN) (08/28/86)
Well, here's the promised Hercules driverfor the PC. It's in SHAR format, and the instructions for assembly and use are in "hercbios.doc". Enjoy! --------------------------------------------------------------- ---=== Dave Tutelman -------===== Physical - AT&T Information Systems ----------====== Room 1H120 ==--------======== Juniper Plaza, Route 9 ====---========= Freehold, NJ 07728 ============ Logical - ...ihnp4!mtuxo!mtuxt!dmt ====== Audible - (201)-577-4232 --------------------------------------------------------------- ------------------cut here------------------------------------ : This is a shar archive. Extract with sh, not csh. : The rest of this file will extract: : hercbios.doc hercbios.h hercbios.asm gchar.asm graph.asm makefile hcharset.asm hercpixl.c testpix.c echo extracting - hercbios.doc sed 's/^X//' > hercbios.doc << '!EOR!' X X X X X X ***** _H_E_R_C_B_I_O_S ***** X _B_I_O_S _P_A_T_C_H _F_O_R _T_H_E _H_E_R_C_U_L_E_S _B_O_A_R_D X X Dave Tutelman 1986 X X X X The accompanying program is a front end to the INT 10 (VIDEO) X functions of the DOS BIOS, so that the important functions work X on a Hercules graphics board or its clones. (It was developed on X a SuperComputer clone of a Hercules) It is a X terminate-and-stay-resident program, that is installed by being X called; it is NOT a DOS driver. If you want it installed at boot, X include it in AUTOEXEC.BAT, not CONFIG.SYS. X X _W_H_A_T _I_T'_S _G_O_O_D _F_O_R X X The major strength of this program is that it will allow you to X write programs for the Hercules board that run in graphics mode, X and still write text easily on the graphics screen. With it, you X can program in those higher-level language processors that use X the DOS or BIOS calls for display, using their standard I/O calls X to write text to the graphics screen. (For a list of known X compatible and incompatible languages, see the section later in X this manual.) X X A second use of this program is to allow the running of existing X graphics programs that use the BIOS calls for ALL screen display. X It will NOT allow most commercial graphics programs written for X the the Color Graphics Adapter (CGA) to run on the Hercules X board. That is because most graphics programs write directly to X the video memory instead of using the BIOS calls. The only X existing graphics program that this has been tested against is X PC-LISP; that is the only graphics program I've encountered that X uses the BIOS exclusively. X X X _H_O_W _I_T _W_O_R_K_S X X HERCBIOS is a terminate-and-stay-resident program that intercepts X all calls to Interrupt 10H, the BIOS video services. It will X either process the interrupt or pass the call on to the real X BIOS, depending on whether something specific to the Hercules X board needs to be done. Specifically, HERCBIOS handles the X interrupt itself if (1) the board is in graphics mode, or (2) the X BIOS call is a request to enter graphics mode. X X Two graphics modes are recognized and processed by HERCBIOS: X X _M_o_d_e _6 - _I_B_M _H_i-_r_e_s _m_o_d_e: This uses part of the 720x348 X Hercules raster as a 640x200 IBM-compatible graphics screen. X It will work with programs for the IBM CGA and its clones, X provided they use the BIOS services for their graphics X display. (Note - such programs are rare.) X X _M_o_d_e _8 - _H_e_r_c_u_l_e_s-_s_p_e_c_i_f_i_c _m_o_d_e: This uses the full X X - 1 - X X X X X X X X X Hercules raster. X X Actually, both modes are quite capable of putting a pixel X anywhere on the Hercules raster. The major difference is that X Mode 6 draws characters in an 8x8 pixel box (like the CGA), while X Mode 8 uses the finer resolution of the Hercules board to improve X legibility by using a 12x8 pixel box for characters. In either X mode, more characters than 25x80 will fit on the screen. Mode 6 X supports 43x90 characters on the screen (but 25x80 inside the X 640x200-pixel sub-screen); Mode 8 supports 29x90 characters. X X The functions implemented by HERCBIOS are: X X Fn 0 - Set mode (6, 7, or 8) X Fn 2 - Set cursor position X Fn 3 - Read cursor position X Fn 5 - New display page X Fn 9 - Write character with attribute X Fn 10 - Write character X Fn 12 - Write pixel X Fn 13 - Read pixel X Fn 14 - Teletypewriter-style character write X Fn 15 - Get video status X X Check your System Programmers' Guide for the use of these BIOS X functions. X X A number of properties of the alphanumeric display are not X supported by the hardware when you enter graphics mode. For X instance, the cursor is not shown in graphics mode, nor are all X of the character attributes. HERCBIOS does its best to emulate X the alphanumeric mode, but it cannot implement a cursor or the X blinking or bold attributes. The table below shows the "best X shot" that HERCBIOS takes at character attributes: X X CODE USUALLY MEANS IBM MODE HERC MODE X 00 invisible invisible invisible X 01 underline [normal] underline X 07 normal normal normal X 0F hi-intens [rev video] [rev video] X 70 rev video rev video rev video X X X Anything else displays as normal X X The teletypewriter-style output protects the bottom line on the X screen as an unscrolled line, for status messages, function key X labels, etc. This is non-standard, but I like it. (And we do have X more rows than the CGA display. It's the 43rd line that isn't X scrolled.) X X X X X X X X - 2 - X X X X X X X X X _M_A_K_I_N_G _A_N_D _I_N_S_T_A_L_L_I_N_G _T_H_E _P_R_O_G_R_A_M X _M_a_k_i_n_g _t_h_e ._C_O_M _F_i_l_e _f_r_o_m _A_s_s_e_m_b_l_e_r _S_o_u_r_c_e X X HERCBIOS was originally developed on ASM 1.0. The version X included with this uses MASM 4.0. I don't know for sure whether X it will assemble with other versions of assembler. X X The commands for making HERCBIOS.COM from the source are in the X MAKEFILE included with the distribution. I run it with NDMAKE, an X excellent MS-DOS shareware MAKE from Don Knellers, but it should X be easy to adapt to your own favorite MAKE. If you make it by X hand, the commands are: X X masm hercbios; X masm gchar; X masm graph; X link hercbios gchar graph, hercbios.exe; X exe2bin hercbios.exe hercbios.com X del hercbios.exe X X If you have a machine whose processor is an iAPX 286, 186, or X 188, you may be able to get some increased performance and a X smaller .COM file by editing one character in the header file X _h_e_r_c_b_i_o_s._h. Simply remove the ";" that comments out the X definition of _i_A_P_X_2_8_6. That will allow some non-8088 instructions X to assemble, as well as changing some code that was optimized for X speed (at the expense of storage and beauty) on the 8088. (This X option is known to assemble and link, but has not been tested; I X have no access to a 286 machine with Hercules graphics.) X X X _I_n_s_t_a_l_l_i_n_g _H_E_R_C_B_I_O_S._C_O_M X X Once you have HERCBIOS.COM, store it where your PATH will find it X and add the line X X HERCBIOS X X to your AUTOEXEC.BAT file somewhere after the PATH command. This X will cause it to be installed during boot, so it will be there X whenever you run your graphics programs. (Its presence won't X affect operation of your computer in alphanumeric mode, since it X passes on to the normal BIOS anything that's not in graphics X mode.) X X I am including a couple of demonstration/test programs in this X distribution, so that you can: X - See how to write programs for HERCBIOS. X - Test to assure that it runs with your computer and monitor. X The programs can be run in their executable form and their source X can be examined. X X X X X X X - 3 - X X X X X X X X X _C_O_M_P_A_T_I_B_I_L_I_T_Y _A_N_D _I_N_C_O_M_P_A_T_I_B_I_L_I_T_Y X X HERCBIOS has been tested on a Hercules board in an IBM PC-XT, a X Hercules-compatible board I built from a SuperComputer bare X board, and a Leading Edge XT clone. The current version works X with all of these, but I have a homebrew monitor that has trouble X syncing to the higher sweep rate of the monochrome display. If X you have trouble with the stability of your image, try fiddling X with the parameters for the 6845 display chip. They are in the X file _H_E_R_C_B_I_O_S._A_S_M, in the "db" statement defining _v_i_d__p_a_r_m__t_a_b_l_e X at the end of Function 0 (Set Video Mode). I have left in (but X commented out) the set of parameters that works on my homebrew X monitor. X X I have written programs using HERCBIOS in a number of languages. X Here are some of the caveats I'd like to pass on: X X - Things are fine using INT 10h calls in assembler. (No big X surprise.) X X - Turbo Pascal works with HERCBIOS, with one caveat (at least X for releases 1 and 2). The Pascal cursor function GoTOXY will X home the cursor if presented with x>80 or y>25. To make full X use of the 29x90 or 43x90 screen, you will have to write your X own version of GoTOXY, using Turbo's machine language escape X to issue the INT 10h. X X - I've written a little in Microsoft C 3.0. No problems so far. X X - The TESTPIX program was written in deSmet C 2.4. It worked X fine, with one caveat. The console I/O routine _g_e_t_c_h_a_r() X seems to write to display memory (perhaps as part of keyboard X echo). This can interfere with what is displayed on the X Hercules board display page 1. (I had no problems on page X 0.) X X - Forget about using it with BASICA or GWBASIC. Microsoft BASIC X graphics routines write directly to display memory, X completely bypassing the BIOS. X X X USE AND ENJOY! X X Bug reports to: X Dave Tutelman X 16 Tilton Drive X Wayside, NJ 07712 X X X Currently receive EMail at ...!mtuxo!mtuxt!dmt X X X Flames to: X /dev/null X X X X - 4 - X X X !EOR! echo extracting - hercbios.h sed 's/^X//' > hercbios.h << '!EOR!' X.XLIST X page 66,132 X;************************************************************ X; X; Header file for inclusion in HERCBIOS assemblies X; X; Dave Tutelman - 8/86 X; X;************************************************************* X X;iAPX286 equ 1 ; UN-COMMENT FOR A 186 or 286 MACHINE! X ; Some of the "ugly" constructs are for speed X ; on the 808X, while the "obvious" constructs X ; run faster on the 186 & 286. XVIDI equ 10H ; video interrupt, 10H for real, 50H for debug Xpixbase equ 0B000H ; beginning segment of graphics board Xmpx_bios equ 0F000H ; MPX-16 BIOS segment address Xvid_offset equ 0F065H ; MPX-16 video offset in BIOS Xcharbase equ 0FA6EH ; MPX-16 BIOS character table offset Xherc_mode equ 8 ; Hercules graphics mode Xibm_mode equ 6 ; IBM Hi-Res color graphics mode. X ; we'll try to handle it. Xvid_port equ 03B4H ; 6845 index register (data reg = 3B5H ) X ; (control port = 3B8H ) X;------------------------------------------ Xbios segment at mpx_bios ; setup call to vid_bios X org vid_offset Xvid_bios proc far Xvid_bios endp Xbios ends X;------------------------------------------------ Xbios_data segment at 040h X org 049h Xvideo_mode db ? ; current video mode Xn_cols dw ? ; number of columns in video display X org 050h Xcurs_pos dw 8 dup(?) ; cursor for each of 8 pages Xcurs_mode dw ? ; cursor mode Xactive_page db ? ; current active display page Xvideo_base dw ? ; video port base Xchip_ctrl db ? ; current setting of 3X8 register Xbios_data ends X;------------------------------------ Xintvec segment at 0 ; interrupt vector X org 4*1Fh ; interrupt 1FH Xuser_table dd ? Xintvec ends X;------------------------------------ X XIFDEF iAPX286 X .286c ; allow 286/186-only instructions XENDIF X X.LIST !EOR! echo extracting - hercbios.asm sed 's/^X//' > hercbios.asm << '!EOR!' X;******************************************************* X;* X;* Main program file for HERCBIOS X;* X;* Dave Tutelman - 8/86 X;* X;*------------------------------------------------------ X;* X;* INT10 -- acts as pre-handler for video interrupts X;* (BIOS calls) for Hercules & SuperComputer X;* monochrome graphics board. Calls real BIOS X;* if not a Hercules special. Handled here are: X;* X;* Fn 0 Set mode (only 6 & 8) X;* and all functions, when in mode 6 or 8. X;* Actually, we've only implemented: X;* Fn 2 Set cursor position X;* Fn 3 Read cursor position X;* Fn 5 New display page X;* Fn 9 Write character with attribute X;* Fn 10 Write character X;* Fn 12 Write pixel X;* Fn 13 Read pixel X;* Fn 14 Teletypewriter-style character write X;* Fn 15 Get video status X;* X;* The only allowable modes for these boards are: X;* 6 IBM graphics (we handle it, but poor aspect ratio). X;* 7 Monochrome (with 2 pages) X;* 8 Hercules graphics mode. X;* X;********************************************************** X; XINCLUDE hercbios.h X Xextrn writechar:near,tty:near,scroll_up:near,scroll_down:near Xextrn scr_start:word,scr_length:word,num_rows:byte Xextrn wr_pixel:near Xextrn end_herc:near Xpublic exit_herc_bios,int10,vid_vector Xpublic set_mode,set_curs,read_curs,new_page,status X;----------------------------------------- Xpage X;************************************************************ X;* X;* Install the Hercules video handler X;* X;************************************************************ Xcseg segment public X assume cs:cseg,ds:cseg X X ORG 100h X; XSTART: X; X push ax X push es X push ds X; X xor ax,ax ; zero the acc X mov es,ax ; ES points to zero (interrupt vector) X ; Get ROM BIOS video entry point, and save in "rom_bios" X mov ax,es:[4*VIDI] X mov word ptr cs:rom_bios,ax X mov ax,es:[4*VIDI+2] X mov word ptr cs:rom_bios+2,ax X ; Now plant the HERCBIOS entry point in interrupt vector X mov ax,offset int10 ; address of video handler to AX X mov es:[4*VIDI],ax ; and store it in interrupt vector 10H X mov ax,cs ; same for the segment of video handler X mov es:[4*VIDI+2],ax ; ... X; X; Leave the message that we're installed X mov ax,cs ; DS:DX pointing to message X mov ds,ax X mov dx,offset install_msg X mov ah,9 ; display-string function X int 21h X X pop ds X pop es X pop ax X; X mov dx,offset end_herc ; set dx to end of this program X int 27H ; terminate, but stay resident X; Xinstall_msg db "BIOS for Hercules-compatible Graphics - " X db "(DMT Aug 1986)",10,13,'$' Xpage X;************************************************************ X;* X;* Beginning of video interrupt handler X;* X;************************************************************ X; X; Xint10 proc far X; X sti ; allow further interrupts X push ds ; save DS X push ax ; save AX X mov ax,bios_data ; bios data segment --> AX X mov ds,ax ; now put bios data segment in DS X assume ds:bios_data ; and tell assembler about it X; ; check current mode X mov ah,video_mode ; get current mode X cmp ah,herc_mode ; test for a graphics mode X je herc_bios X cmp ah,ibm_mode X je herc_bios X; ; setmode to a graphics mode? X pop ax ; restore AX X push ax ; ...but leave it on stack X cmp ax,0006 ; Fn = set to IBM hi-res mode? X je herc_bios X cmp ax,0008 ; Fn = set to Herc graphics mode? X je herc_bios X; Xnorm_bios: ; if we get here, just go to real BIOS X pop ax ; restore stack to pre-interrupt state X pop ds X db 0EAh ; opcode for FAR JUMP to rom_bios Xrom_bios dd ? ; normal video bios address (in ROM) X; Xherc_bios: ; jump table for special Hercules BIOS X; X pop ax ; restore ax X push bx ; save regs X push cx X push dx X push si X push di X push es X; X push ax X mov al,ah ; function # to AX X xor ah,ah X shl ax,1 ; *2 for word vector X mov si,ax ; put in SI to index into vector X pop ax ; restore old AX X cmp si,offset vid_vec_end-offset vid_vector X ; function number within range? X jge exit_herc_bios ; function number out of range X add si,offset vid_vector X jmp word ptr cs:[si] X ; jump to routine via vector X; X Xvid_vector: ; jump vector for hercules video routines X dw offset set_mode ; 0 = set mode X dw offset exit_herc_bios ; 1 = cursor type (NA) X dw offset set_curs ; 2 = set cursor position X dw offset read_curs ; 3 = read cursor position X dw offset exit_herc_bios ; 4 = light pen (NA) X dw offset new_page ; 5 = choose active page X dw offset scroll_up ; 6 = scroll up X dw offset scroll_down ; 7 = scroll down X dw offset exit_herc_bios ; 8 = read character (NA) X dw offset writechar ; 9 = write char & attribute X dw offset writechar ;10 = write character X dw offset exit_herc_bios ;11 = set color palette (NA) X dw offset wr_pixel ;12 = write pixel X dw offset wr_pixel ;13 = read pixel X dw offset tty ;14 = teletype write X dw offset status ;15 = return video state Xvid_vec_end: X; Xexit_herc_bios: X pop es ; restore regs X pop di X pop si X pop dx X pop cx X pop bx X pop ds X iret ; and return Xpage X;******************************************************************** X;* X;* FUNCTION 0 - SET VIDEO MODE X;* X;* Only gets here to set a hi-res graphics mode X;* 6=IBM X;* 8=Hercules X;* [ AX destroyed ] X;* X;******************************************************************* X; Xset_mode: X; ; is it a graphics mode? X cmp al,herc_mode ; set to hercules mode? X je g_mode X cmp al,ibm_mode ; set to IBM mode? X je g_mode X ; Neither. Leave it to normal BIOS X pop es ; restore regs X pop di X pop si X pop dx X pop cx X pop bx X pop ds X jmp vid_bios ; and go to MPX-16 BIOS X; Xg_mode: mov video_mode,al ; save video mode in BIOS data area X mov active_page,ah ; zero the active page in BIOS data X mov n_cols,90 ; 90 character columns in graphics mode X; X;* clear display area X mov ax,pixbase ; get starting address X mov es,ax ; page base to ES X mov cx,8000H ; word counter X mov di,0 ; start at beginning of display X cld ; direction "up" X xor ax,ax ; zero AX X rep stosw ; write zero to both pages X; X;* load the 6845 internal registers X; X; ; first set up the loop X push ds ; save DS X mov ax,cs ; get cseg into DS X mov ds,ax X assume ds:cseg X mov dx,vid_port ; 6845 index port address to DX X mov si,offset vid_parm_table X ; table pointer to SI X mov cx,16 ; 16 parameters in table X xor ax,ax ; zero AX (AH will be index counter) X; ; now execute the loop Xinit_6845_loop: X mov al,ah ; index counter to AL X out dx,al ; output index to 6845 X inc dx ; point DX to data reg X mov al,[si] ; get table entry X out dx,al ; output to 6845 data reg X dec dx ; point DX back to index reg X inc ah ; bump index counter X inc si ; bump table pointer X loop init_6845_loop X pop ds ; restore DS X assume ds:bios_data ; restore DS assumed X; X;* now set the 6845 control register X mov dx,vid_port+4 ; control port address X mov al,0AH ; graphics control byte X cmp active_page,0 ; get current page X je pg0_F0 ; skip if zero X or al,80H ; page 1 to control byte Xpg0_F0: out dx,al ; and ship to 6845 X mov chip_ctrl,al ; also save in bios data area X; X;* save cursor position (0,0) in bios data area X xor ax,ax ; zero AX X mov curs_pos,ax ; write zero to pg.0 cursor postion X mov curs_pos+2,ax ; write zero to pg.1 cursor postion X; X;* initialize scrolling parameters X; X cmp video_mode,herc_mode X je h_parm X mov cs:scr_start,180 ; IBM parameters X mov cs:scr_length,3690 ; X mov cs:num_rows,42 ; X jmp parm_done Xh_parm: mov cs:scr_start,270 ; Herc parameters X mov cs:scr_length,3645 ; X mov cs:num_rows,28 ; Xparm_done: X; X jmp exit_herc_bios X; X;* table of 6845 chip parameters X; Xvid_parm_table db 53,45,46,7,91,2,87,87,2,3,62H,3,0,0,0,0 X;vid_parm_table db 54,45,46,8,91,2,87,87,2,3,62H,3,0,0,0,0 X ;DMT's monitor Xpage X;******************************************************************** X;* X;* FUNCTION 2 - SET CURSOR POSITION X;* X;* DX = new row (DH) and column (DL) X;* BH = display page number X;* X;******************************************************************** X; Xset_curs: X; X; * save in BIOS data area X mov bl,bh ; page # to bl X xor bh,bh ; zero bh (page # = BX) X shl bx,1 ; times 2 for word X mov curs_pos[bx],dx ; store in data area X; X; * if page # = active page, then we should actually move cursor X; * However, cursor doesn't show in graphics mode, so we won't. X; X jmp exit_herc_bios X; Xpage X;******************************************************************** X;* X;* FUNCTION 3 - READ CURSOR POSITION X;* X;* on entry X;* BH = display page number X;* X;* on exit X;* CX = cursor type/size X;* DX = current row (DH) and column (DL) X;* X;******************************************************************** X; Xread_curs: X; ; uncover the return portion of the stack X pop es X pop di X pop si X pop dx X pop cx X; ; now get the data and push onto stack X push curs_mode ; cursor type to CX position in stack X mov bl,bh ; page # to BX X xor bh,bh X shl bx,1 ; *2 for word offset X push curs_pos[bx] ; cursor position for specified page X ; to DX position in stack X; ; refill the stack for return X push si X push di X push es X; X jmp exit_herc_bios Xpage X;******************************************************************** X;* X;* FUNCTION 5 - SELECT NEW DISPLAY PAGE X;* X;* AL = new display page number X;* X;******************************************************************** X; Xnew_page: X cmp al,2 ; page < 2? X jl f5_1 ; yup X jmp exit_herc_bios ; nope. can't do it. X; Xf5_1: mov active_page,al ; put away active page in bios data X; * put starting address in 6845 register 12 X mov ah,al ; save page number in AH X mov dx,vid_port ; index pointer address X mov al,12 ; save in register 12 X out dx,al ; and set index pointer to 12 X mov al,ah ; restore page to AL X ror al,1 ; page to high-order bit X shr al,1 ; two bytes per word X inc dx X out dx,al ; ...and output it to register X; * put control byte in 6845 port X mov al,ah ; get back page number X ror al,1 ; page to high-order bit X or al,0AH ; create chip control byte X mov chip_ctrl,al ; save it in data area X mov dx,vid_port+4 ; control port address X out dx,al ; send control byte to chip X; X jmp exit_herc_bios Xpage X;*********************************************************************** X;* X;* FUNCTION 15 - RETURN VIDEO STATUS X;* X;* On exit: X;* AL = current video mode X;* AH = number of active display columns X;* BH = current active page number X;* X;*********************************************************************** X; Xstatus: X; ; first uncover the stack X pop es X pop di X pop si X pop dx X pop cx X pop bx X; ; now get the parameters needed X mov al,video_mode X mov ah,90 ; all our graphics modes have 90 cols X mov bh,active_page X; ; and push back onto the stack for return X push bx X push cx X push dx X push si X push di X push es X; X jmp exit_herc_bios X; Xint10 endp X; Xcseg ends X end START !EOR! echo extracting - gchar.asm sed 's/^X//' > gchar.asm << '!EOR!' X;******************************************************************** X;* X;* GRAPHIC CHARACTER HANDLING X;* X;* Dave Tutelman - 8/86 X;* X;*------------------------------------------------------------------- X;* X;* Fn 6 Scroll up - not yet implemented X;* Fn 7 Scroll down - not yet implemented X;* Fn 9 Write character with attribute X;* Fn 10 Write character normal X;* Fn 14 Write character Teletypewriter style X;* X;* Also includes subroutines to: X;* get_address convert page/row/col to display address X;* do_char write a character to an address on display X;* do_attrib change a display address to some attribute X;* full_screen_scroll used by Fn 14, when it needs to scroll X;* flash momentarily flash to reverse video and back X;* X;******************************************************************** X; XINCLUDE hercbios.h X X;----------------------------------------------- Xextrn exit_herc_bios:near Xpublic writechar,scroll_up,scroll_down,tty Xpublic scr_start,scr_length,num_rows X;------------------------------------ Xcseg segment public X assume cs:cseg,ds:bios_data X; X X;************************************************************** X;* X;* FUNCTION 6 & 7 - SCROLL UP & DOWN X;* (Placeholder only - not yet implemented) X;* X;* AL = Number of rows to scroll (if 0, blank scroll area) X;* BH = Fill attribute (we ignore, since writechar destroys old attrib) X;* CX = Upper left corner (CH=row, CL=col) X;* DX = Lower right corner (DH=row, DL=col) X;* X;**************************************************************** X; Xscroll_up: Xscroll_down: X; X jmp exit_herc_bios Xpage X;******************************************************************** X;* X;* FUNCTION 9 & 10 - WRITE A CHARACTER AT CURRENT CURSOR X;* X;* AL = character code to be written X;* AH = 9 (write char with attribute) or 10 (write char normal) X;* BL = new attribute (limited selection in graphics mode) X;* BH = display page number X;* CX = count of how many characters to write X;* X;******************************************************************** X; Xwritechar: X; ; Get the address corresponding to cursor[page] X push bx X mov bl,bh ; page to BX X xor bh,bh X shl bx,1 ; *2 for word pointer X mov dx,curs_pos[bx] ; appropriate cursor position to DX X pop bx X call get_address ; get display address in DI X; Xwrchar_loop: X; ; Write a character to that address X call do_char ; arguments set up already X; X; ; If function 9, modify the character's attributes X cmp ah,9 ; Function 9? X jne no_attrib ; no, don't do attributes X call do_attrib ; yes, and arguments already set up Xno_attrib: X; X inc di ; move to next position, without moving X ; the official cursor X loop wrchar_loop ; continue until CX count exhausted X jmp exit_herc_bios Xpage X;***************************************************************** X;* X;* FUNCTION 14 - TELETYPEWRITER-STYLE CHARACTER WRITE X;* X;* AL = Character to be written X;* X;****************************************************************** X; Xtty: X assume ds:bios_data X mov bl,active_page ; active page to BX X xor bh,bh X mov dx,curs_pos[bx] ; get cursor for active page X push bx X mov bh,bl ; move page to BH X call get_address ; address of character to DI X pop bx X; X; ; process the character X; ; check if CR X cmp al,13 ; carriage return? X jne not_cr X mov dl,0 ; go to first column X jmp fix_curs X; ; check if LF Xnot_cr: cmp al,10 ; line feed? X jne not_lf X inc dh ; next line X jmp fix_curs X; ; check if BS Xnot_lf: cmp al,8 ; backspace? X jne not_bs X cmp dl,0 ; already first column? X je fix_curs ; yup. do nothing X dec dl ; nope. move cursor left one X dec di ; also move address pointer left one X mov al,32 ; set character to space X call do_char ; and write the space, but don't move X jmp fix_curs X; ; check if BEL Xnot_bs: cmp al,7 ; bell character? X jne not_bl X pop es ; restore registers X pop di X pop si X pop dx X pop cx X pop bx X pop ds X jmp vid_bios ; ... and use the normal BIOS to ring the bell X; call flash ; can't do BEL standard. Blink display instead X; jmp fix_curs X; ; ordinary printing character, so display it Xnot_bl: call do_char ; write it to screen X inc dl ; cursor one to the right X; X; ; now look at the cursor, do what's necessary to X; ; fix it up, and save it away. Xfix_curs: X cmp dl,89 ; beyond last column? X jle chk_scroll ; not yet X xor dl,dl ; yes. do a CR X inc dh ; ... and a LF Xchk_scroll: X cmp dh,cs:num_rows ; now see if we're beyond last row? X jl exit_tty ; not yet X call full_screen_scroll X ; yes. Scroll the screen up one X dec dh ; ... and scroll cursor, too. X jmp chk_scroll X; Xexit_tty: X mov curs_pos[bx],dx ; save cursor position X jmp exit_herc_bios Xpage X;-------------------------------------------------------------------- X; X; GET_ADDRESS SUBROUTINE X; X; BH = display page X; DX = cursor position (DH=row, DL=col) X; X; returns: X; DI = displacement of top row of pixels X; X;-------------------------------------------------------------------- X; Xget_address: X push cx ; save it X X; ; compute display address from cursor_pos X mov cl,dh ; get row # in cx X xor ch,ch X shl cx,1 ; begin fast multiply by 90 (1011010 B) X mov di,cx X shl cx,1 X shl cx,1 X add di,cx X shl cx,1 X add di,cx X shl cx,1 X shl cx,1 X add di,cx ; end fast multiply by 90 X mov cx,di ; copy answer back to cx X shl di,1 ; *2 for ibm graphics mode X cmp video_mode,herc_mode X jne ibm_ad ; not herc mode X add di,cx ; *3 for herc mode Xibm_ad: xor ch,ch ; columns in CX X mov cl,dl X add di,cx ; add in col. address in DI X cmp bh,0 ; if page 1, set high-order bit of address X je pg0 X or di,8000H Xpg0: X; ; address now in DI X; X pop cx ; restore it X ret Xpage X;-------------------------------------------------------------------- X; X; DO_CHAR SUBROUTINE X; X; AL = character to write X; DI = diplacement (address) of top row of pixels X; X; Note: SI and ES are destroyed X; X;-------------------------------------------------------------------- X; Xdo_char: X push ax X push bx X push cx X push di X push ds X; X; ; get scan pattern table pointer into BX X cmp video_mode,herc_mode X je herc_1 X mov bx,offset ibm_pattern X ; IBM graphics mode - use appropriate table X jmp c_1 Xherc_1: mov bx,offset herc_pattern X ; herc graphics mode - use appropriate table Xc_1: X; X; ; set up source address registers X xor ah,ah ; character to SI X mov si,ax XIFDEF iAPX286 X shl si,3 ; *8 for 8-byte table entry XELSE X shl si,1 ; *8 for 8-byte table entry X shl si,1 X shl si,1 XENDIF X ; next, find beginning of table X cmp al,7Fh ; ROM or user table? X jg u_tbl X ; ROM table X add si,charbase ; character table base added to offset X mov ax,mpx_bios ; BIOS code segment to DS X mov ds,ax X jmp c_2 Xu_tbl: ; user table X xor ax,ax ; zero (interrupt vector) to DS X mov ds,ax X mov ax,si ; save table offset in AX X assume ds:intvec X lds si,user_table ; load DS:SI from interrupt vector X add si,ax ; add offset into table base Xc_2: X; X; ; set up destination address registers X mov ax,pixbase ; get display segment in ES X mov es,ax X ; displacement already in DI X; X; X; ; transfer the character X mov ax,di ; save top-row displacement in AX X mov cx,8 ; transfer 8 rows X cld ; direction = up Xc_loop: mov di,ax ; top-row displacement X add di,cs:[bx] ; add entry from scan-pattern table X movsb ; actually transfer a byte and bump SI & DI X add bx,2 ; next entry in scan-pattern table X loop c_loop X; X; ; if hercules mode, blank the extra rows X pop ds ; restore DS to Bios data X assume ds:bios_data X cmp video_mode,herc_mode X jne c_3 X ; Hercules mode X mov si,ax ; don't need SI. Save top-row displacement X xor ax,ax ; zero AX X mov cx,4 ; four rows to blank X cld Xc_blnk: mov di,si ; top-row displacement X add di,cs:[bx] ; add entry from scan-pattern table X stosb ; transfer a zero byte X add bx,2 ; next entry in scan-pattern table X loop c_blnk Xc_3: X; X; ; clean up and return X pop di X pop cx X pop bx X pop ax X ret Xpage X;--------------------------------------------------------------------- X; X; DO_ATTRIB SUBROUTINE X; X; BL = attribute byte X; DI = displacement (address) of top row of pixels X; ES is destroyed X; X; Because attributes don't "just happen" in graphics mode, here's X; what we have to transform them to. X; X; CODE USUALLY MEANS IBM MODE HERC MODE X; 00 invisible invisible invisible X; 01 underline [normal] underline X; 07 normal normal normal X; 0F hi-intens [rev video] [rev video] X; 70 rev video rev video rev video X; X; Anything else displays as normal X; Note that there's no way to make blinking happen. X; X;----------------------------------------------------------------------- X; Xdo_attrib: X assume ds:bios_data X push ax X mov ax,pixbase ; Display base to ES X mov es,ax X pop ax X; X; ; which attribute, if any? X and bl,7Fh ; mask off blink bit X cmp bl,0 ; invisible? X je invisible X cmp bl,0Fh ; reverse video? (instead of bright) X je reverse X cmp bl,70H ; reverse video? X je reverse X cmp bl,01 ; underline? X je underline X ret ; none of the above. Display normal. X; X; ; underline the character Xunderline: X cmp video_mode,herc_mode X je ul_1 X ret ; don't do it for IBM mode Xul_1: mov byte ptr es:[di+40B4h],0FFh X ; move ones to 11th line of pixel array X ; 40B4h is the 11th entry in herc_pattern X ret X; X; ; make it invisible Xinvisible: X push ax X push bx X push cx X push dx X xor ax,ax ; zero the AX X cmp video_mode,herc_mode X je herc_3 X mov bx,offset ibm_pattern X ; point to scan pattern X jmp inv_1 Xherc_3: mov bx,offset herc_pattern X ; point to scan pattern Xinv_1: mov dx,di ; save addr of top row of pixels in DX X mov cx,8 ; 8 bytes to be moved X cld ; direction = up Xinvis_loop: X mov di,dx ; top row address X add di,cs:[bx] ; add scan-table offset X stosb ; move a zero byte X add bx,2 ; bump the scan-table pointer X loop invis_loop X pop dx X pop cx X pop bx X pop ax X ret X; X; ; reverse video Xreverse: X push bx X push cx X push dx X cmp video_mode,herc_mode X je herc_4 X mov bx,offset ibm_pattern X ; point to scan pattern X mov cx,8 ; 8 scan lines for IBM mode X jmp rev_1 Xherc_4: mov bx,offset herc_pattern X ; point to scan pattern X mov cx,12 ; 12 scan lines for Hercules mode Xrev_1: mov dx,di ; save addr of top row of pixels in DX X cld ; direction = up Xrev_loop: X mov di,dx ; top row address X add di,cs:[bx] ; add scan-table offset X not es:byte ptr[di] ; invert one scan line X add bx,2 ; bump the scan-table pointer X loop rev_loop X pop dx X pop cx X pop bx X ret Xpage X;-------------------------------------------------------------- X; X; SUBROUTINE FULL_SCREEN_SCROLL X; X; This scrolls the entire screen up one print line (8 or 12 pixels). X; Actually, we'll protect one line on the bottom (e.g.-function keys). X; X;----------------------------------------------------------------- X; X; ; A few constants, initialized by set_mode Xnum_rows db ? ; number of rows in display X ; = 42 (IBM) or 28 (Herc) Xscr_start dw ? ; 2*90 or 3*90 depending on mode Xscr_length dw ? ; number of words to transfer in a sweep X ; = 41*start /2 = 3690 (IBM) X ; = 27*start /2 = 3645 (Herc) X; Xfull_screen_scroll: X assume ds:bios_data X push ds X push ax X push cx X mov ax,pixbase ; start getting display segment X cmp active_page,0 ; page 0? X je scr_pg0 X add ax,800H ; page 1. bump by half of 64K Xscr_pg0: X mov ds,ax ; save display segment in DS X mov es,ax ; ... and in ES X; X xor ax,ax ; zero AX X call scr_shift X mov ax,2000H ; bump interlace counter X call scr_shift X mov ax,4000H X call scr_shift X mov ax,6000H X call scr_shift X; X pop cx X pop ax X pop ds X ret X; X; X; scr_shift does the actual work of scrolling, one set of interlace X; lines at a time. X; Xscr_shift: X cld ; block moves will be UP X mov di,ax ; interlace scan ID to DI X mov si,ax ; ... and to SI X add si,cs:scr_start X ; but bump by "start" X mov cx,cs:scr_length X ; set counter for transfer X rep movsw ; and scroll a set of lines X xor ax,ax ; set up a zero word X mov cx,cs:scr_start ; set counter for one more line X shr cx,1 ; /2 for word transfers X rep stosw ; and blank the line X ret Xpage X;----------------------------------------------------------- X; X; SUBROUTINE FLASH X; X; Flashes the screen inverse video and back to normal. X; Used in place of an audible bell. X; X;------------------------------------------------------------- X; Xflash: X push cx X push di X push es X; X mov di,pixbase ; get display area base X cmp active_page,0 ; page 0? X je pg0_f X add di,800H ; no, page 1 Xpg0_f: mov es,di ; put resulting pointer to display in ES X; X; ; loop to invert screen X xor di,di ; point to beginning of display area X mov cx,4000H ; number of words to invert Xflash_loop_1: X not word ptr es:[di] X ; invert one location X add di,2 ; bump location pointer a word X loop flash_loop_1 X; X; ; and invert it back to normal X xor di,di ; point to beginning of display area X mov cx,4000H ; number of words to invert Xflash_loop_2: X not word ptr es:[di] X ; invert one location X add di,2 ; bump location pointer a word X loop flash_loop_2 X; X pop es X pop di X pop cx X ret Xpage X;***************************************************** X;* Data areas for character handling X;***************************************************** X; Xpixels db 12 dup(?) ; 12 bytes for pixel pattern Xibm_pattern dw 0000h,2000h,4000h,6000h,005Ah,205Ah,405Ah,605Ah Xherc_pattern dw 4000h,6000h,005Ah,205Ah,405Ah,605Ah,00B4h,20B4h Xblank_pattern dw 0000h,2000h,40B4h,60B4h X; X; Xcseg ends X end !EOR! echo extracting - graph.asm sed 's/^X//' > graph.asm << '!EOR!' X;******************************************************* X;* X;* SET OF GRAPHICS ROUTINES FOR HERCULES BIOS X;* X;* Dave Tutelman - 8/86 X;* X;*------------------------------------------------------- X;* X;* do_pixel: draws, erases, or reads a pixel, given an X;* offset and mask. X;* X;********************************************************** X; XINCLUDE hercbios.h X X;------------------------------------------ Xpublic wr_pixel,end_herc Xextrn exit_herc_bios:near X;----------------------------------------- X; Xcseg segment public X assume cs:cseg,ds:bios_data X; X; X;********************************************** X;* X;* read or write a pixel: X;* DX = row # ( y ) X;* CX = col # ( x ) X;* AH = 12 for write, 13 for read X;* AL = value (0,1, if bit 7=1 EXOR the value) X;* X;********************************************** X; Xwr_pixel: X mov bh,video_mode ; check for valid mode X cmp bh,herc_mode X je do_pixel X cmp bh,ibm_mode X je do_pixel X jmp exit_herc_bios ; invalid mode. don't do it. X; Xdo_pixel: X push ax ; save function and pixel value X; X; ; first compute the address of byte to be modified X; ; = 90*[row/4] + [col/8] + 2^D*[row/4] + 2^F*page X mov bh,cl ; col (low order) in BH X mov bl,dl ; row (low order) in BL X and bx,0703H ; mask the col & row remainders XIFDEF iAPX286 X shr cx,3 ; col / 8 X shr dx,2 ; row / 4 X mov al,90 X mul dx ; AX = 90*[ row/4 ] X add ax,cx ; ... + col/8 X shl bl,5 ; align row remainder XELSE ; same as above, obscure but fast for 8086 X shr cx,1 ; divide col by 8 X shr cx,1 X shr cx,1 X shr dx,1 ; divide row by 4 X shr dx,1 X shl dx,1 ; begin fast multiply by 90 (1011010 B) X mov ax,dx X shl dx,1 X shl dx,1 X add ax,dx X shl dx,1 X add ax,dx X shl dx,1 X shl dx,1 X add ax,dx ; end fast multiply by 90 X add ax,cx ; add on the col/8 X shl bl,1 ; align row remainder X shl bl,1 X shl bl,1 X shl bl,1 X shl bl,1 XENDIF X add ah,bl ; use aligned row remainder X cmp active_page,0 ; page 0 active? X je end_adr_calc ; yup X or ah,80H ; page 1 active. Set MSB of address Xend_adr_calc: ; address of byte is now in AX X; X mov dx,pixbase ; base of pixel display to DX X mov es,dx ; ...and thence to segment reg X mov si,ax ; address of byte w/ pixel to index reg X mov cl,bh ; bit addr in byte X mov al,80H ; '1000 0000' in AL X shr al,cl ; shift mask to line up with bit to read/write X mov bl,al X; X pop bx ; now retrieve original AX into BX X ; function=BH, pixel value=BL X cmp bh,13 ; what to do with the pixel? X je read_pix ; read the pixel. X cmp bl,0 ; write the pixel. But how? X je clr_pix ; clear the pixel X jl exor_pix ; exclusive-or the pixel X; Xset_pix: ; set the pixel X or es:[si],al ; or the mask with the right byte X jmp exit_herc_bios X; Xclr_pix: ;clear the pixel X not al ; invert the mask, so zero on bit to be cleared X and es:[si],al ; and the mask with the right byte X jmp exit_herc_bios X; Xexor_pix: ; exclusive-or the pixel X mov ah,07fH ; mask to rid 7th bit X and ah,bl ; pixval w/o XOR flag X jnz do_exor ; ExOr with a 1? X jmp exit_herc_bios ; no! XOR (0,x) = x. just return. Xdo_exor: X xor es:[si],al ; EXOR the pixel with the mask. X jmp exit_herc_bios X; Xread_pix: ; read the pixel X and al,es:[si] ; read the bit into AL X jz rd_zro X mov al,1 ; if it ain't zero, it's one Xrd_zro: jmp exit_herc_bios X; X; Xend_herc: ; label for end of package. Used by hercbios X ; to install it. X; Xcseg ends X end !EOR! echo extracting - makefile sed 's/^X//' > makefile << '!EOR!' X# Makefile for HERCBIOS.COM XSRC=hercbios gchar graph XDEST=a: X Xhercbios.com : hercbios.obj gchar.obj graph.obj X link hercbios gchar graph,,/MAP; X exe2bin hercbios.exe hercbios.com X del hercbios.exe X Xhercbios.obj : hercbios.asm hercbios.h Xgchar.obj : gchar.asm hercbios.h Xgraph.obj : graph.asm hercbios.h X X X# Makes for the demo & test program, using deSmet C X Xtestpix.exe : testpix.o hercpixl.o X bind testpix hercpixl X Xtestpix.o : testpix.c X c88 testpix X Xhercpixl.o : hercpixl.c X c88 hercpixl X X# Make a backup or distribution disk Xbackup : X for %f in ($(SRC)) do copy %f.asm $(DEST) X copy hercbios.h $(DEST) X copy hercbios.com $(DEST) X copy hercbios.doc $(DEST) X copy makefile $(DEST) X Xdistrib : X make backup X copy hercpixl.c $(DEST) X copy testpix.* $(DEST) X copy hcharset.* $(DEST) !EOR! echo extracting - hcharset.asm sed 's/^X//' > hcharset.asm << '!EOR!' X;************************************************************** X;* X;* Tests Hercules BIOS, specifically the functions 9 & 10 X;* to put characters on the screen in graphics mode. X;* X;*************************************************************** XVIDI equ 10H ; video interrupt, 10H (50H for debug) Xcseg segment common X assume cs:cseg,ds:cseg XSTART proc X; ; prompt for IBM or Hercules graphics mode X mov ah,9 ; DOS 9 = display string X mov dx,offset mode_prmt+100H X ; display mode prompt X int 21H ; DOS function X mov ah,1 ; DOS 1 = kbd input X int 21H X mov bl,al ; input char --> BL X mov ax,6 ; ibm graphics mode X cmp bl,"h" ; input "h" for Hercules X jne i_mode X add ax,2 ; hercules mode Xi_mode: int VIDI X; X xor bh,bh ; page 0 X mov bl,7 ; normal attribute X mov dh,1 ; cursor at <1,1> to start X mov dl,1 X; X mov cx,24 ; do 24 rows Xrow: push cx ; save row counter X inc dh ; next row X mov dl,0 ; back to first column X mov ah,2 ; cursor move function X int VIDI X; ; compute video mode X mov bl,dh ; row # to BL X xor bh,bh X shr bl,1 ; 4 rows per mode X shr bl,1 X mov bl,mode_seq[bx+100H] ; index into mode sequence for mode X; X; ; for debug, first print [row+64] X mov al,dh X add al,64 X mov ah,10 ; fn 10 prints w/o attributes X mov cx,1 X int VIDI X; X xor bh,bh ; page 0 X mov cx,64 ; each row 64 characters Xachar: push cx ; save char counter X mov ah,9 ; write_char function X mov al,dh ; character = col + (row mod 2)*64 X and al,1 ; row mod 2 X ror al,1 ; *128 X shr al,1 X add al,dl ; + col X mov cx,1 ; write one of them X int VIDI X inc dl ; increment row counter X mov ah,2 ; cursor move function X int VIDI X pop cx ; restore character counter X loop achar X pop cx ; restore row counter X loop row X; X; ; draw a line where it should start X mov dx,5 ; row 5 X mov cx,0 ; col 0 Xline: push cx X push dx X mov ax,0C01H ; function 12=pixel X int VIDI X pop dx X pop cx X inc cx ; step col X cmp cx,700 ; ...until 700 X jle line X; X; ; wait for a keystroke X mov ah,1 X int 21H X; X mov ax,7 ; back to alpha mode X int VIDI X; X; ; Now return to system X xor ax,ax ; zero --> AX X int 21H ; DOS function call 0 - terminate normally X; Xmode_prmt db "hercules or ibm mode? [ibm] $" Xmode_seq db 7,0Fh,1,70h,0,9 ; sequence norm,hi,ul,rev,invis,norm XSTART endp Xcseg ends !EOR! echo extracting - hercpixl.c sed 's/^X//' > hercpixl.c << '!EOR!' X/* X * HERCPIXL.C X * Dave Tutelman - last modified 8/86 X * X * This is a set of basic graphic routines for the Hercules X * Board (or at least the SuperComputer version of it; I assume X * that they work with the real thing). X * alfa () puts us in alphanumeric mode. X * grafix (page) puts us in graphics mode in page 0 or 1 X * pixel (x,y,val) puts a pixel at <x,y>. Bright dot if X * val=1, dark dot if val=0. X * dchar (x,y,c) puts character "c" at <x,y>. Note that X * character raster is 90 x 29. X * swpage (page) switches to a different page. X * waitkey () just waits till a key is pressed. X * X * Actually, the routines should work with any board, since the X * BIOS calls are used throughout. It's Hercules-specific only X * because I've defined the graphics and alpha modes for my X * Hercules BIOS. X * X * Compile with deSmet C. X */ X X#define VIDI 0x10 /* video interrupt, normally 10H */ X#define KBD 0x16 /* keyboard interrupt */ X#define ALFA_MODE 7 /* monochrome alpha mode */ X#define GRAF_MODE 8 /* Hercules graphics mode */ X Xint page = 0; Xextern unsigned _rax,_rbx,_rcx,_rdx; X X/* X * This puts us back in alphanumeric mode X */ X Xalfa (dummy) X unsigned int dummy; X{ X _rax = ALFA_MODE; /* mono 80-col mode */ X _doint ( VIDI ); /* set mode */ X} X X/* X * This one switches us to hercules graphics mode X * in page 0 or 1 X */ X Xgrafix (newpage) X int newpage; X{ X X _rax = GRAF_MODE; /* herc grafix mode */ X _doint ( VIDI ); /* set mode */ X X /* now set the page */ X swpage (newpage); X} X X/* X * This writes a pixel at (x,y), where (0,0) is the upper-left X * corner of the screen. If val = 0 then the pixel is erased. X */ X Xpixel (x, y, val) X int x, y, val; X{ X X _rax = 0x0C00 + val; /* function 12 */ X _rcx = x; X _rdx = y; X _doint ( VIDI ); /* set mode */ X X} X X X/* X * dchar (x,y,c) puts character "c" at <x,y>. Note that X * character raster is 90 x 25. X */ X Xdchar (x,y,c) X int x,y; X char c; X{ X X _rax = 2*256; /* AH=Fn#2 */ X _rdx = 256*y + x; /* DH=row, DX=col */ X _rbx = page * 256; /* BH=page */ X _doint (VIDI); /* set cursor */ X X _rax = 10*256 + (int) c; /* AH=Fn#10, AL=char */ X _rbx = page * 256; /* BH=page */ X _rcx = 1; /* CX=count */ X _doint (VIDI); /* write character */ X} X X X/* X * This one switches us to a different page, without changing X * the contents of that page. X */ X Xswpage (newpage) X int newpage; X{ X page = newpage; X _rax = 0x500 + page; /* new page function */ X _doint ( VIDI ); /* interrupt call */ X} X X Xwaitkey () X{ X _rax = 0; /* keyboard blocking read function */ X _doint (KBD); X} !EOR! echo extracting - testpix.c sed 's/^X//' > testpix.c << '!EOR!' X X/* MAIN tests PIXEL by drawing lines on the screen */ X X#include "stdio.h" X Xmain () X{ X int i,j; X char line [90]; X X /* first clear screen */ X grafix (0); X X /* now draw a couple of lines */ X for (i=0; i<696; i++) X { X pixel (i, i/2, 1); X pixel (i, 348 - i/2, 1); X } X X waitkey(); /* wait for CR */ X X /* a different pattern on page 1 */ X swpage (1); X for (i=0; i<720; i++) X for (j=0; j<348; j=j+50) X pixel (i,j,1); X for (i=0; i<720; i=i+90) X for (j=0; j<348; j++) X pixel (i,j,1); X X /* switch back and forth a couple of times */ X waitkey(); X swpage (0); X waitkey(); X swpage (1); X waitkey(); X swpage (0); X waitkey(); X swpage (1); X waitkey(); X X#ifndef NOTONE X /* add some writing */ X dline (10, 4, "Hello, there!"); X dline (10, 5, "Second LINE of text."); X dline (10, 6, "all on display page 1."); X X waitkey(); X for (i=0; i<8; i++) X for (j=0; j<32; j++) X dchar (j+5, i+13, (char) (32*i+j)); X X waitkey(); X#endif X /* repeat for page 0 */ X swpage (0); X X#ifndef NOTZERO X /* add some writing */ X dline (10, 4, "Hello, there!"); X dline (10, 5, "Second LINE of text."); X dline (10, 6, "this time on display page 0."); X X waitkey(); X for (i=0; i<8; i++) X for (j=0; j<32; j++) X dchar (j+5, i+13, (char) (32*i+j)); X#endif X X waitkey(); X X /* back to alpha mode */ X waitkey(); X alfa(); X} X Xdline (x, y, chstr) /* write character-string at x,y */ X int x,y; X char chstr [90]; X{ X int i; X char *p; X X i=0; X for ( p=chstr; *p != '\0'; p++ ) X { X dchar ( x+i, y, *p ); X i++; X } X} !EOR!