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