[net.sources] Hercules graphics BIOS

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!