TITLE Hercules graphics module ; Michael Gordon - 8-Dec-86 ; ; Certain routines were taken from the Hercules BIOS of Dave Tutelman - 8/86 ; Others came from pcgraph.asm included in GNUPLOT by Colin Kelley ; ; modified slightly by Colin Kelley - 22-Dec-86 ; added header.mac, parameterized declarations ; added dgroup: in HVmodem to reach HCh_Parms and HGr_Parms - 30-Jan-87 ; modified by Russell Lang 3 Jun 1988 ; added H_init include header.mac if1 include lineproc.mac endif GPg1_Base equ 0B800h ; Graphics page 1 base address _text segment public _H_line, _H_color, _H_mask, _HVmode, _H_puts public _H_init HCfg_Switch equ 03BFH ; Configuration Switch - software switch ; to select graphics card memory map beginproc _H_init mov al, 03H ; allow graphics in b8000:bffff mov dx, HCfg_Switch out dx, al ret _H_init endp hpixel proc near ror word ptr bmask,1 jc cont ret cont: push ax push bx push cx push dx push si mov cx,ax ; x mov dx,bx ; y ; ; [couldn't this be done faster with a lookup table? -cdk] ; ; first compute the address of byte to be modified ; = 90*[row/4] + [col/8] + 2^D*[row/4] + 2^F*page mov bh,cl ; col (low order) in BH mov bl,dl ; row (low order) in BL and bx,0703H ; mask the col & row remainders IFDEF iAPX286 shr cx,3 ; col / 8 shr dx,2 ; row / 4 mov al,90 mul dx ; AX = 90*[ row/4 ] add ax,cx ; ... + col/8 shl bl,5 ; align row remainder ELSE ; same as above, obscure but fast for 8086 shr cx,1 ; divide col by 8 shr cx,1 shr cx,1 shr dx,1 ; divide row by 4 shr dx,1 shl dx,1 ; begin fast multiply by 90 (1011010 B) mov ax,dx shl dx,1 shl dx,1 add ax,dx shl dx,1 add ax,dx shl dx,1 shl dx,1 add ax,dx ; end fast multiply by 90 add ax,cx ; add on the col/8 shl bl,1 ; align row remainder shl bl,1 shl bl,1 shl bl,1 shl bl,1 ENDIF add ah,bl ; use aligned row remainder end_adr_calc: ; address of byte is now in AX mov dx,GPg1_Base ; base of pixel display to DX mov es,dx ; ...and thence to segment reg mov si,ax ; address of byte w/ pixel to index reg mov cl,bh ; bit addr in byte mov al,80H ; '1000 0000' in AL shr al,cl ; shift mask to line up with bit to read/write set_pix: ; set the pixel or es:[si],al ; or the mask with the right byte pop si pop dx pop cx pop bx pop ax ret hpixel endp lineproc _H_line, hpixel ; ; clear - clear page 1 of the screen buffer to zero (effectively, blank ; the screen) ; clear proc near push es push ax push cx push di mov ax, GPg1_Base mov es, ax xor di, di mov cx, 4000h xor ax, ax cld rep stosw ; zero out screen page pop di pop cx pop ax pop es ret clear endp beginproc _H_color push bp mov bp,sp mov al,[bp+X] ; color mov byte ptr color,al pop bp ret _H_color endp beginproc _H_mask push bp mov bp,sp mov ax,[bp+X] ; mask mov word ptr bmask,ax pop bp ret _H_mask endp HCtrl_Port equ 03B8H ; Hercules 6845 control port IO addr HIndx_Port equ 03B4H ; Hercules 6845 index port IO addr HScrn_Enable equ 008h ; Control port bit to enable video HCh_Mode equ 020h ; Character output mode HGr_Mode equ 082h ; Graphics output mode page 1 parm_count equ 12 beginproc _HVmode push bp mov bp, sp push si mov ax, [bp+X] or ah, al mov al, HCh_Mode ; Assume character mode is wanted mov si, offset dgroup:HCh_Parms cmp ah, 0 ; nonzero means switch to graphics jz vmode_ok call near ptr clear ; clear the graphics page mov al, HGr_Mode mov si, offset dgroup:HGr_Parms vmode_ok: mov dx, HCtrl_Port out dx, al ; Set Hercules board to proper mode call near ptr setParms ; Set the 6845 parameters or al, HScrn_Enable ; Enable the video output out dx, al pop si pop bp ret _HVmode endp setParms proc near ; Send 6845 parms to Hercules board push ax push dx push si mov dx, HIndx_Port ; Index port addr -> DX mov ah, 0 ; 0 -> parameter counter sp_loop: mov al, ah out dx, al ; output to 6845 addr register inc dx ; next output to data register mov al, [si] ; next control byte -> al inc si out dx, al ; output control byte dec dx ; 6845 index addr -> dx inc ah ; bump addr cmp ah, parm_count jnz sp_loop pop si pop dx pop ax ret setParms endp ; H_puts - print text in graphics mode ; ; cx = row ; bx = column ; si = address of string (null terminated) to print beginproc _H_puts push bp mov bp, sp push si push ds mov si, [bp+X] ; string offset ifdef LARGE_DATA mov ds, [bp+X+2] ; string segment mov cx, [bp+X+4] ; row mov bx, [bp+X+6] ; col else mov cx, [bp+X+2] ; row mov bx, [bp+X+4] ; col endif ploop: lodsb ; get next char or al, al ; end of display? je pdone call near ptr display inc bx ; bump to next column jmp ploop pdone: pop ds pop si pop bp ret _H_puts endp ; ; display - output an 8x8 character from the IBM ROM to the Herc board ; ; AX = char, BX = column (0-89), CX = row(0-42) ** all preserved ** ; CON8 db 8 CON180 db 180 IBMROM equ 0F000h CHARTAB equ 0FA6Eh display proc near push ds ; save the lot push es push ax push bx push cx push dx push si push di ; setup ds -> IBM ROM, and si -> index into IBM ROM character table located ; at 0fa6eh in the ROM and ax, 07fh mul cs:CON8 ; mult by 8 bytes of table per char mov si, ax mov ax, IBMROM mov ds, ax assume ds:nothing add si, CHARTAB ; add offset of character table ; compute index into Hercules screen memory for scan line 0. The remaining ; seven scan lines are all at fixed offsets from the first. ; ; Since graphics mode treats the screen as sets of 16x4 "characters", ; we need to map an 8x8 real character onto the front or back of ; a pair of graphics "characters". The first four scan lines of our ; 8x8 character will map to the top graphics "character", and the second ; four scan lines map to the graphics character on the "line" (4 scan ; lines high) below it. ; ; For some exotic hardware reason (probably speed), all scan line 0 ; bits (i.e. every fourth scan line) are stored in memory locations ; 0-2000h in the screen buffer. All scan line 1 bits are stored ; 2000h-4000h. Within these banks, they are stored by rows. The first ; scan line on the screen (scan line 0 of graphics character row 0) ; is the first 45 words of memory in the screen buffer. The next 45 ; words are the first scan line graphics row 1, and since graphics ; "characters" are 4 bits high, this second scan line is physically ; the fifth scan line displayed on the screen. ; ; SO, to display an 8x8 character, the 1st and 5th rows of dots are ; both scan line 0 of the graphics "character", the 2nd and 6th are ; scan line 1, and so on. ; ; The column (0-89) tells which byte in a scan line we need to load. ; Since it takes two rows of graphics characters to hold one row of ; our characters, column+90 is a index to scan line 4 rows of pixels ; higher (n+4). Thus 180 bytes of screen memory in any bank (0h, 2000h, ; 4000h, 6000h) represent a row of 8x8 characters. ; ; The starting location in screen memory for the first scan line of ; a character to be displayed will be: (row*180)+column ; The 5th scan line will be at: (row*180)+column+90 ; ; The second and 6th scan lines will be at the above offsets plus ; the bank offset of 2000h. The third and 7th, add 4000h and finally ; the 4th and 8th, add 6000h. ; mov ax, GPg1_Base mov es, ax ; es = hercules page 0 mov ax, cx ; get row mul cs:CON180 ; mult by 180(10) mov di, ax ; di = index reg cld ; insure right direction ;output 8 segments of character to video ram lodsb ; line 0 mov es:[di+bx], al lodsb mov es:[di+bx+2000h], al ; line 1 lodsb mov es:[di+bx+4000h], al ; line 2 lodsb mov es:[di+bx+6000h], al ; line 3 lodsb mov es:[di+bx+90], al ; line 4 lodsb mov es:[di+bx+2000h+90], al ; line 5 lodsb mov es:[di+bx+4000h+90], al ; line 6 lodsb mov es:[di+bx+6000h+90], al ; line 7 pop di pop si pop dx pop cx pop bx pop ax pop es pop ds ret display endp _text ends _data segment bmask dw -1 color db 1 _data ends const segment HCh_Parms db 61H, 50H, 52H, 0FH, 19H, 06H, 19H, 19H, 02H, 0DH, 0BH, 0CH HGr_Parms db 35H, 2DH, 2EH, 07H, 5BH, 02H, 57H, 57H, 02H, 03H, 00H, 00H const ends end