The /tests/pc folder is now /tests/pcx86 (to be consistent with the associated emulator)

This commit is contained in:
Jeff Parsons 2016-08-09 11:48:09 -07:00
commit 663343a1ad
68 changed files with 24 additions and 16 deletions

9
tests/pcx86/.gitignore vendored Normal file
View file

@ -0,0 +1,9 @@
*.com
*.COM
*.lst
*.LST
*.obj
*.OBJ
*.exe
*.EXE
80186/

View file

@ -0,0 +1,12 @@
---
layout: page
title: 80386 Tests
permalink: /tests/pcx86/80386/
redirect_from:
- /tests/pc/80386/
---
Overview
---
See [test386.asm](test386.asm).

View file

@ -0,0 +1,10 @@
all: test386.json test386.img
test386.com: test386.asm ../inc/dos.inc ../inc/misc.inc ../inc/x86.inc
nasm -i../inc/ -f bin test386.asm -l test386.lst -o test386.com
test386.json: test386.com
node ../../../modules/filedump/bin/filedump --file=test386.com --output=test386.json --overwrite
test386.img: test386.com
node ../../../modules/diskdump/bin/diskdump --path=test386.com --output=test386.img --format=img --overwrite

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

28764
tests/pcx86/80386/test386.txt Normal file

File diff suppressed because it is too large Load diff

14
tests/pcx86/README.md Normal file
View file

@ -0,0 +1,14 @@
---
layout: page
title: PC Test Resources
permalink: /tests/pcx86/
redirect_from:
- /tests/pc/
---
PCx86 Test Resources
--------------------
* [80386 Tests](80386/)
* [Trace Utility](trace/)
* [VGA "Black Book" Tests](vga/)

645
tests/pcx86/cpu/cpuid.asm Normal file
View file

@ -0,0 +1,645 @@
; Posted in comp.sys.ibm.pc by Michael A. Shiels 8/16/89
; From https://groups.google.com/forum/#!searchin/comp.sys.ibm.pc/single-step$20interrupt/comp.sys.ibm.pc/irWPIdzmCHQ/SyqEtq9mqCEJ
title CPUID - Determine CPU & NDP Type
page 58,122
name CPUID
;
; CPUID uniquely identifies each NEC & Intel CPU & NDP.
;
; Notes on program structure:
;
; This program uses four segments, two classes, and one group.
; It demonstrates a useful technique for programmers who generate
; .COM programs. In particular, it shows how to use segment
; classes to re-order segments, and how to eliminate the linker's
; warning message about the absence of a stack segment.
;
; The correspondence between segments and classes is as follows:
;
; Segment Class
; ------- -----
; STACK prog
; DATA data
; MDATA data
; CODE prog
;
; The segments apprear in the above order in the program source
; to avoid forward references in the CODE segment to labels in
; the DATA/MDATA segments. However, because the STACK segment
; appears first in the file, it and all segments in the same
; class are made contiguous by the linker. Thus they precede
; the DATA/MDATA segments in the resulting .COM file because
; the latter are in a different class. In this manner, although
; DATA and MDATA precede CODE in the source file, their order
; is swapped in the .COM file. That way there is no need for
; an initial skip over the data areas to get to the CODE
; segment. As a side benefit, declaring a STACK segment (as
; the first segment in the source) also eliminates the linker's
; warning about that segment missing. Finally, all segments
; are declared to be in the same group so the linker can properly
; resolve offsets.
;
; Note that if you re-assemble the code for any reason, it is
; important to use an assembler later than the IBM version 1.0.
; That version has a number of bugs including an annoying habit
; of alphabetizing segment names in the .OBJ file. If you use
; IBM MASM 2.0, be sure to specify /S to order the segments
; properly.
;
; If the program reports results at variance with your knowledge
; of the system, please contact the author.
;
; Environments tested in:
;
; CPU Speed
; System in MHz CPU NDP
; ------ --------- --- ---
; IBM PC AT 6 Intel 80286 Intel 80287
; IBM PC AT 9 Intel 80286 Intel 80287
; IBM PC AT 6 Intel 80286 none
; IBM PC AT 8.5 Intel 80286 none
; IBM PC 4.77 Intel 8088 Intel 8087-3
; IBM PC 4.77 Intel 8088* Intel 8087-3
; IBM PC XT 4.77 Intel 8088 none
; IBM PC XT 4.77 Intel 8088 Intel 8087-3
; IBM PC Portable 4.77 NEC V20 none
; COMPAQ 4.77 Intel 8088 none
; COMPAQ 4.77 NEC V20 none
; AT&T PC 6300 8 Intel 8086 Intel 8087-2
; AT&T PC 6300 8 NEC V30 Intel 8087-2
; Tandy 2000 8 Intel 80186 none
;
; * = faulty CPU
;
; Program structure:
;
; Group PGROUP:
; Stack segment STACK, byte-aligned, stack, class 'prog'
; Program segment CODE, byte-aligned, public, class 'prog'
; Data segment DATA, byte-aligned, public, class 'data'
; Data segment MDATA, byte-aligned, public, class 'data'
;
; Assembly requirements:
;
; Use MASM 1.25 or later.
; With IBM's MASM 2.0 only, use /S to avoid alphabetizing the segment names.
; Use /r option to generate real NDP code.
;
; MASM CPUID/r; to convert .ASM to .OBJ
; LINK CPUID; to convert .OBJ to .EXE
; EXE2BIN CPUID CPUID.COM to convert .EXE to .COM
; ERASE CPUID.EXE to avoid executing .EXE
;
; Note that the linker doesn't warn about a missing stack segment.
;
; Author:
;
; Original code by:
;
; Bob Smith May 1985
; Qualitas, Inc.
; 8314 Thoreau Dr.
; Bethesda, MD 20817
;
; Arthur Zachai suggested the technique to distinguish within the
; 808x and 8018x families by exploiting the difference in the
; length of their pre-fetch instruction queues.
;
; Published in PC Tech Journal - April 1986 - Vol 4 No 4
;
subttl Structures, Records, Equates, & Macros
page
ARG_STR struct
ARG_BP dw ? ; caller's BP
ARG_OFF dw ? ; caller's offset
ARG_SEG dw ? ; segment
ARG_FLG dw ? ; flags
ARG_STR ends
; Record to define bits in the CPU's & NDP's flags' registers
CPUFLAGS record RO:1,NT:1,IOPL:2,OF:1,_DF:1,_IF:1,TF:1,SF:1,ZF:1,R1:1,AF:1,R2:1,PF:1,R3:1,CF:1
NDPFLAGS record R4:3,IC:1,RC:2,PC:2,IEM:1,R5:1,PM:1,UM:1,OM:1,ZM:1,DM:1,IM:1
; FLG_PIQL Pre-fetch instruction queue length, 0 => 4-byte, 1 => 6-byte
; FLG_08 Intel 808x
; FLG_NEC NEC V20 or V30
; FLG_18 Intel 8018x
; FLG_28 Intel 8028x
; FLG_87 Intel 8087
; FLG_287 Intel 80287
;
; FLG_CERR Faulty CPU
; FLG_NERR Faulty NDP switch setting
FLG record RSVD:9,FLG_NERR:1,FLG_CERR:1,FLG_NDP:2,FLG_CPU:3
; CPU-related flags
FLG_PIQL equ 001b shl FLG_CPU
FLG_08 equ 000b shl FLG_CPU
FLG_NEC equ 010b shl FLG_CPU
FLG_18 equ 100b shl FLG_CPU
FLG_28 equ 110b shl FLG_CPU
FLG_8088 equ FLG_08
FLG_8086 equ FLG_08 or FLG_PIQL
FLG_V20 equ FLG_NEC
FLG_v30 equ FLG_NEC or FLG_PIQL
FLG_80188 equ FLG_18
FLG_80186 equ FLG_18 or FLG_PIQL
FLG_80286 equ FLG_28 or FLG_PIQL
; NDP-related flags
; 00b shl FLG_NDP Not Present
FLG_87 equ 01b shl FLG_NDP
FLG_287 equ 10b shl FLG_NDP
BEL equ 07h
LF equ 0ah
CR equ 0dh
EOS equ '$'
POPFF macro
local L1,L2
jmp short L2 ; skip over IRET
L1:
iret ; pop the CP & IP pushed below along
; with the flags, our original purpose
L2:
push cs ; prepare for IRET by pushing CS
call L1 ; push IP, jump to IRET
endm ; POPFF macro
TAB macro TYP
push bx ; save for a moment
and bx,mask FLG_&TYP ; isolate flags
mov cl,FLG_&TYP ; shift amount
shr bx,cl ; shift to low-order
shl bx,1 ; times two to index table of words
mov dx,TYP&MSG_TAB[bx] ; ds:dx => descriptive message
pop bx ; restore
mov ah,09h ; function code to display string
int 21h ; request dos service
endm ; TAB macro
page
INT_VEC segment at 0 ; start INT_VEC segment
dd ? ; pointer to INT 00h
INT01_OFF dw ? ; pointer to INT 01h
INT01_SEG dw ?
INT_VEC ends ; end INT_VEC segment
PGROUP group STACK,CODE,DATA,MDATA
; The following segment both positions class 'prog' segments lower in
; memory than others so the first byte of the resulting .COM file is
; in the CODE segment, as well as satisfies the LINKer's need to have
; a stack segment.
STACK segment byte stack 'prog' ; start STACK segment
STACK ends ; end STACK segment
I11_REC record I11_PRN:2,I11_RSV1:2,I11_COM:3,I11_RSV2:1,I11_DISK:2,I11_VID:2,I11_RSV3:2,I11_NDP:1,I11_IPL:1
DATA segment byte public 'data' ; start DATA segment
assume ds:PGROUP
OLDINT01_VEC label dword ; save area for original INT 01h handler
OLDINT01_OFF dw ?
OLDINT01_SEG dw ?
NDP_CW label word ; save area for NDP control word
db ?
NDP_CW_HI db 0 ; high byte of control word
NDP_ENV dw 7 dup(?) ; save area for NDP environment
DATA ends
subttl Message Data Area
page
MDATA segment byte public 'data' ; start MDATA segment
assume ds:PGROUP
MSG_START db 'CPUID -- Version 1.0'
db CR,LF,CR,LF,EOS
MSG_8088 db 'CPU is an Intel 8088.'
db CR,LF,EOS
MSG_8086 db 'CPU is an Intel 8086.'
db CR,LF,EOS
MSG_V20 db 'CPU is an NEC V20.'
db CR,LF,EOS
MSG_V30 db 'CPU is an NEC V30.'
db CR,LF,EOS
MSG_80188 db 'CPU is an Intel 80188.'
db CR,LF,EOS
MSG_80186 db 'CPU is an Intel 80186.'
db CR,LF,EOS
MSG_UNK db 'CPU is a maverick -- 80288??.'
db CR,LF,EOS
MSG_80286 db 'CPU is an Intel 80286.'
db CR,LF,EOS
CPUMSG_TAB dw PGROUP:MSG_8088 ; 000 = Intel 8088
dw PGROUP:MSG_8086 ; 001 = Intel 8086
dw PGROUP:MSG_V20 ; 010 = NEC V20
dw PGROUP:MSG_V30 ; 011 = NEC V30
dw PGROUP:MSG_80188 ; 100 = Intel 80188
dw PGROUP:MSG_80186 ; 101 = Intel 80186
dw PGROUP:MSG_UNK ; 110 = ?
dw PGROUP:MSG_80286 ; 111 = Intel 80286
NDPMSG_TAB dw PGROUP:MSG_NDPX ; 00 = No NDP
dw PGROUP:MSG_8087 ; 01 = Intel 8087
dw PGROUP:MSG_80287 ; 10 = Intel 80287
MSG_NDPX db 'NDP is not present.'
db CR,LF,EOS
MSG_8087 db 'NDP is an Intel 8087.'
db CR,LF,EOS
MSG_80287 db 'NDP is an Intel 80287.'
db CR,LF,EOS
CERRMSG_TAB dw PGROUP:MSG_CPUOK ; 0 = CPU healthy
dw PGROUP:MSG_CPUBAD ; 1 = CPU faulty
MSG_CPUOK db 'CPU appears to be healthy.'
db CR,LF,EOS
MSG_CPUBAD db BEL,'*** CPU incorrectly allows interrupts '
db 'after a change to SS ***',CR,LF
db 'It should be replaced with a more recent '
db 'version as it could crash the',CR,LF
db 'system at seemingly random times.',CR,LF,EOS
NERRMSG_TAB dw PGROUP:MSG_NDPSWOK ; 0 = NDP switch set correctly
dw PGROUP:MSG_NDPSWERR ; 1 = NDP switch set incorrectly
MSG_NDPSWOK db EOS ; no message
MSG_NDPSWERR db '*** Although there is an NDP installed '
db 'on this sytem, the corresponding',CR,LF
db 'system board switch is not properly set. '
db 'To correct this, flip switch 2 of',CR,LF
db 'switch block 1 on the system board.',CR,LF,EOS
MDATA ends ; end MDATA segment
subttl Main Routine
page
CODE segment byte public 'prog' ; start CODE segment
assume cs:PGROUP,ds:PGROUP,es:PGROUP
org 100h ; skip over PSP
INITIAL proc near
mov dx,offset ds:MSG_START ; starting message
mov ah,09h ; function code to display string
int 21h ; request DOS service
call CPU_ID ; check the CPU's identity
TAB CPU ; display CPU results
TAB NDP ; display NDP results
TAB CERR ; display CPU ERR results
TAB NERR ; display NDP ERR results
ret ; return to DOS
INITIAL endp ; end INITIAL procedure
subttl CPU_ID Procedure
page
CPU_ID proc near ; start CPU_ID procedure
assume cs:PGROUP,ds:PGROUP,es:PGROUP
; This procedure determines the type of CPU and NDP (if any) in use.
;
; The possibilities include:
;
; Intel 8086
; Intel 8088
; NEC V20
; NEC V30
; Intel 80186
; Intel 80188
; Intel 80286
; Intel 8087
; Intel 80287
;
; Also checked is whether or not the CPU allows interrupts after
; changing the SS register segment. If the CPU does, it is faulty
; and should be replaced.
;
; Further, if an NDP is installed, non-AT machines should have a
; system board switch set. Such a discrepancy is reported.
;
; On exit, BX contains flag settings (as defined in FLG record) which
; the caller can check. For example, to test for an Intel 80286, use
;
; and bx,mask FLAG_CPU
; cmp bx,FLG_80286
; je ITSA286
irp XX,<ax,cx,di,ds,es> ; save registers
push XX
endm
; test for 80286 -- this CPU executes PUSH SP by first storing SP on
; stack, then decrementing it. earlier CPU's decrement, THEN store.
mov bx,FLG_28 ; assume it's a 286
push sp ; only 286 pushes pre-push SP
pop ax ; get it back
cmp ax,sp ; check for same
je CHECK_PIQL ; they are, so it's a 286
; test for 80186/80188 -- 18xx and 286 CPU's mask shift/rotate
; operations mod 32; earlier CPUs use all 8 bits of CL.
mov bx,FLG_18 ; assume it's an 8018x
mov cl,32+1 ; 18x masks shift counts mod 32
; note we can't use just 32 in CL
mov al,0ffh ; start with all bits set
shl al,cl ; shift one position if 18x
jnz CHECK_PIQL ; some bits still on,
; so its a 18x, check PIQL
; test for V20
mov bx,FLG_NEC ; assume it's an NEC V-series CPU
call CHECK_NEC ; see if it's an NEC chip
jcxz CHECK_PIQL ; good guess, check PIQL
mov bx,FLG_08 ; it's an 808x
subttl Check Length of Pre-Fetch Instruction Queue
page
; Check the length of the pre-fetch instruction queue (PIQ).
;
; xxxx6 CPUs have a PIQ length of 6 bytes,
; xxxx8 CPUs have a PIQ length of 4 bytes
;
; Self-modifying code is used to distinguish the two PIQ lengths.
CHECK_PIQL:
call PIQL_SUB ; handle via subroutine
jcxz CHECK_ERR ; if CX is 0, INC was not executed,
; hence PIQ length is 4
or bx,FLG_PIQL ; PIQ length is 6
subttl Check for Allowing Interrupts After POP SS
page
; Test for faulty chip (allows interrupts after change to SS register)
CHECK_ERR:
xor ax,ax ; prepare to address
; interrupt vector segment
mov ds,ax ; DS points to segment 0
assume ds:INT_VEC ; tell the assembler
cli ; nobody move while we swap
mov ax,offset cs:INT01 ; point to our own handler
xchg ax,INT01_OFF ; get and swap offset
mov OLDINT01_OFF,ax ; save to restore later
mov ax,cs ; our handler's segment
xchg ax,INT01_SEG ; get and swap segment
mov OLDINT01_SEG,ax ; save to restore later
; note we continue with interrupts disabled to avoid
; an external interrupt occuring during this test
mov cx,1 ; initialize a register
push ss ; save ss to store back into itself
pushf ; move flags
pop ax ; ... into ax
or ax,mask TF ; set trap flag
push ax ; place onto stack
POPFF ; ... and then into effect
; some CPUs affect the trap flag
; immediately, some
; wait one instruction
nop ; allow interrupt to take effect
POST_NOP:
pop ss ; change the stack segment register
; (to itself)
dec cx ; normal cpu's execute this instruction
; before recognizing the single-step
; interrupt
hlt ; we never get here
INT01:
; Note: IF=TF=0
; If we're stopped at or before POST_NOP, continue on
push bp ; prepare to address the stack
mov bp,sp ; hello, Mr. stack
cmp [bp].ARG_STR.ARG_OFF,offset cs:POST_NOP ; check offset
pop bp ; restore
ja INTO1_DONE ; we're done
iret ; return to caller
INTO1_DONE:
; restore old INT 01h handler
les ax,OLDINT01_VEC ; ES:AX ==> old INT 01h handler
assume es:nothing ; tell the assembler
mov INT01_OFF,ax ; restore offset
mov INT01_SEG,es ; ... and segment
sti ; allow interrupts again (IF=1)
add sp,3*2 ; strip IP, CS, and flags from stack
push cs ; setup DS for code below
pop ds
assume ds:PGROUP ; tell the assembler
jcxz CHECK_NDP ; if cx is 0, the dec cx was executed,
; and the cpu is ok
or bx,mask FLG_CERR ; it's a faulty chip
subttl Check For Numeric Data Processor
page
; Test for a Numeric Data Processor -- Intel 8087 or 80287. The
; technique used is passive -- it leaves the NDP in the same state in
; which it is found.
CHECK_NDP:
cli ; protect FNSTENV
fnstenv NDP_ENV ; if NDP present, save
; current environment,
; otherwise, this instruction
; is ignored
mov cx,50/7 ; cycle this many times
loop $ ; wait for result to be stored
sti ; allow interrupts
fninit ; initialize processor to known state
jmp short $+2 ; wait for initialization
fnstcw NDP_CW ; save control word
jmp short $+2 ; wait for result to be stored
jmp short $+2
cmp NDP_CW_HI,03h ; check for NDP initial control word
jne CPUID_EXIT ; no NDP installed
int 11h ; get equipment flags into ax
test ax,mask I11_NDP ; check NDP-installed bit
jnz CHECK_NDP1 ; it's correctly set
or bx,mask FLG_NERR ; mark as in error
CHECK_NDP1:
and NDP_CW,not mask IEM ; enable interrupts
; (IEM=0, 8087 only)
fldcw NDP_CW ; reload control word
fdisi ; disable interrupts (IEM=1) on 8087,
; ignored by 80287
fstcw NDP_CW ; save control word
fldenv NDP_ENV ; restore original NDP environment
; no need to wait
; for environment to be loaded
test NDP_CW,mask IEM ; check interrupt enable mask
; (8087 only)
jnz CPUID_8087 ; it changed, hence NDP is an 8087
or bx,FLG_287 ; NDP is an 80287
jmp short CPUID_EXIT ; exit with falgs in BX
CPUID_8087:
or bx,FLG_87 ; NDP is an 8087
CPUID_EXIT:
irp XX,<es,ds,di,cx,ax> ; restore registers
pop XX
endm
assume ds:nothing,es:nothing
ret ; return to caller
CPU_ID endp ; end CPU_ID procedure
subttl Check For NEC V20/V30
page
CHECK_NEC proc near
; The NEC V20/V30 are very compatible with the Intel 8086/8088.
; The only point of "incompatibility" is that they do not contain
; a bug found in the Intel CPU's. Specifically, the NEC CPU's
; correctly restart an interrupted multi-prefix string instruction
; at the start of the instruction. The Intel CPU's incorrectly
; restart in the middle of the instruction. This routine tests
; for that situation by executing such an instruction for a
; sufficiently long period of time for a timer interrupt to occur.
; If at the end of the instruction, CX is zero, it must be an NEC
; CPU; if not, it's an Intel CPU.
;
; Note that we're counting on the timer interrupt to do its thing
; every 18.2 times per second.
;
; Here's a worst case analysis: An Intel 8088/8086 executes 65535
; iterations of LODSB ES[SI] in 2+9+13*65535 = 851,966 clock ticks.
; If the Intel 8088/8086 is running at 10 MHz, each clock tick is
; 100 nanoseconds, hence the entire operation takes 85 milliseconds.
; If the timer is running at normal speed, it interrupts the CPU every
; 55ms and so should interrupt the repeated string instruction at least
; once.
mov cx,0ffffh ; move a lot of data
sti ; ensure timer enabled
; execute multi-prefix instruction. note that the value of ES as
; well as the direction flag setting is irrelevant.
push ax ; save registers
push si
rep lods byte ptr es:[si]
pop si ; restore
pop ax
; on exit: if cx is zero, it's an NEC CPU, otherwise it's an Intel CPU
ret ; return to caller
CHECK_NEC endp
subttl Pre-Fetch Instruction Queue Subroutine
page
PIQL_SUB proc near
; This subroutine discerns the length of the CPU's pre-fetch
; instruction queue (PIQ).
;
; The technique used is to first ensure that the PIQ is full, then
; change an instruction which should be in a 6-byte PIQ but not in a
; 4-byte PIQ. Then, if the original instruction is executed, the PIQ
; is 6-bytes long; if the new instruction is executed, PIQ length is 4.
;
; We ensure the PIQ is full be executing an instruction which takes
; long enough so that the Bus Interface Unit (BIU) can fill the PIQ
; while the instruction is executing.
;
; Specifically, for all byt the last STOSB, we're simple marking time
; waiting for the BIU to fill the PIQ. The last STOSB actually changes
; the instruction. By that time, the orignial instruction should be in
; a six-byte PIQ byt not a four-byte PIQ.
assume cs:PGROUP,es:PGROUP
@REP equ 3 ; repeat the store this many times
std ; store backwards
mov di,offset es:LAB_INC+@REP-1 ; change the instructions
; at ES:DI
; and preceding
mov al,ds:LAB_STI ; change to a sti
mov cx,@REP ; give the BIU time
; to pre-fetch instructions
cli ; ensure interrupts are disabled,
; otherwise a timer tick
; could change the PIQ filling
rep stosb ; change the instruction
; during execution of this instruction
; the BIU is refilling the PIQ. The
; current instruction is no longer
; in the PIQ.
; Note at end, CX is 0.
; The PIQ begins filling here
cld ; restore direction flag
nop ; PIQ fillers
nop
nop
; The following instruction is beyond a four-byte-PIQ CPU's reach,
; but within that of a six-byte-PIQ CPU.
LAB_INC label byte
inc cx ; executed only if PIQ length is 6
LAB_STI label byte
rept @REP-1
sti ; restore interrupts
endm
ret ; return to caller
assume ds:nothing,es:nothing
PIQL_SUB endp ; end PIQL_SUB procedure
CODE ends ; end code segment
end INITIAL ; end CPU_ID module

58
tests/pcx86/exec/exec.asm Normal file
View file

@ -0,0 +1,58 @@
.model tiny
.code
ExecBlk Struc
Psp dw ?
Cmdline dw ?
CmdSeg dw ?
FCB1 dw ?
FCB1seg dw ?
FCB2 dw ?
FCB2seg dw ?
ExecBlk ends
org 100h
main proc
lea bx,pspblk
mov sp,bx
call resize
mov execb.psp,0 ; copy our environment
mov execb.cmdline,80h ; pass command line as is
mov execb.FCB1,5ch
mov execb.FCB2,6ch
mov execb.cmdseg,cs
mov execb.FCB1seg,cs
mov execb.FCB2seg,cs
lea dx,execf
push ds
pop es
lea bx,execb
mov ax,4b00h
int 21h
merr:
mov ax,4c00h
int 21h
main endp
resize proc
add bx,15
mov cl,4
shr bx,cl
mov ax,cs
add bx,ax
mov ah,4ah
int 21h
jc merr
ret
resize endp
execf db "123.EXE",0 ; ASCIIZ pathname of file to be
execd
execb ExecBlk <>
_stack dw 1000h dup(?)
pspblk label byte
end main

141
tests/pcx86/inc/dos.inc Normal file
View file

@ -0,0 +1,141 @@
INT_DOSEXIT EQU 20H
INT_DOS EQU 21H
DOS_ABORT EQU 00H
DOS_STD_CON_INPUT EQU 01H
DOS_STD_CON_OUTPUT EQU 02H
DOS_STD_AUX_INPUT EQU 03H
DOS_STD_AUX_OUTPUT EQU 04H
DOS_STD_PRINTER_OUTPUT EQU 05H
DOS_RAW_CON_IO EQU 06H
DOS_RAW_CON_INPUT EQU 07H
DOS_STD_CON_INPUT_NO_ECHO EQU 08H
DOS_STD_CON_STRING_OUTPUT EQU 09H
DOS_STD_CON_STRING_INPUT EQU 0AH
DOS_STD_CON_INPUT_STATUS EQU 0BH
DOS_STD_CON_INPUT_FLUSH EQU 0CH
DOS_DISK_RESET EQU 0DH
DOS_SET_DEFAULT_DRIVE EQU 0EH
DOS_FCB_OPEN EQU 0FH
DOS_FCB_CLOSE EQU 10H
DOS_DIR_SEARCH_FIRST EQU 11H
DOS_DIR_SEARCH_NEXT EQU 12H
DOS_FCB_DELETE EQU 13H
DOS_FCB_SEQ_READ EQU 14H
DOS_FCB_SEQ_WRITE EQU 15H
DOS_FCB_CREATE EQU 16H
DOS_FCB_RENAME EQU 17H
DOS_RESERVED18 EQU 18H
DOS_GET_DEFAULT_DRIVE EQU 19H
DOS_SET_DMA EQU 1AH
DOS_RESERVED1B EQU 1BH
DOS_RESERVED1C EQU 1CH
DOS_RESERVED1D EQU 1DH
DOS_RESERVED1E EQU 1EH
DOS_GET_DEFAULT_DPB EQU 1FH
DOS_RESERVED20 EQU 20H
DOS_FCB_RANDOM_READ EQU 21H
DOS_FCB_RANDOM_WRITE EQU 22H
DOS_GET_FCB_FILE_LENGTH EQU 23H
DOS_GET_FCB_POSITION EQU 24H
DOS_SET_INTERRUPT_VECTOR EQU 25H
DOS_CREATE_PDB EQU 26H
DOS_FCB_RANDOM_READ_BLOCK EQU 27H
DOS_FCB_RANDOM_WRITE_BLOCK EQU 28H
DOS_PARSE_FILE_DESCRIPTOR EQU 29H
DOS_GET_DATE EQU 2AH
DOS_SET_DATE EQU 2BH
DOS_GET_TIME EQU 2CH
DOS_SET_TIME EQU 2DH
DOS_SET_VERIFY_ON_WRITE EQU 2EH
DOS_GET_DMA EQU 2FH
DOS_GET_VERSION EQU 30H
DOS_KEEP_PROCESS EQU 31H
DOS_GET_DPB EQU 32H
DOS_SET_CTRL_C_TRAPPING EQU 33H
DOS_GET_INDOS_FLAG EQU 34H
DOS_GET_INTERRUPT_VECTOR EQU 35H
DOS_GET_DRIVE_FREESPACE EQU 36H
DOS_CHAR_OPER EQU 37H
DOS_INTERNATIONAL_SUPPORT EQU 38H
DOS_MKDIR EQU 39H
DOS_RMDIR EQU 3AH
DOS_CHDIR EQU 3BH
DOS_CREAT EQU 3CH
DOS_OPEN EQU 3DH
DOS_CLOSE EQU 3EH
DOS_READ EQU 3FH
DOS_WRITE EQU 40H
DOS_UNLINK EQU 41H
DOS_LSEEK EQU 42H
DOS_CHMOD EQU 43H
DOS_IOCTL EQU 44H
DOS_XDUP EQU 45H
DOS_XDUP2 EQU 46H
DOS_GETDIR EQU 47H
DOS_ALLOC EQU 48H
DOS_DEALLOC EQU 49H
DOS_SETBLOCK EQU 4AH
DOS_EXEC EQU 4BH
DOS_EXIT EQU 4CH
DOS_WAIT EQU 4DH
DOS_FIND_FIRST EQU 4EH
DOS_FIND_NEXT EQU 4FH
DOS_SET_CURRENT_PDB EQU 50H
DOS_GET_CURRENT_PDB EQU 51H
DOS_GET_SYSINIT_VARS EQU 52H
DOS_SET_DPB EQU 53H
DOS_GET_VERIFY_ON_WRITE EQU 54H
DOS_DUP_PDB EQU 55H
DOS_RENAME EQU 56H
DOS_FILE_TIMES EQU 57H
DOS_RESERVED58 EQU 58H
DOS_GET_EXTENDED_ERROR EQU 59H
DOS_CREATE_TEMP_FILE EQU 5AH
DOS_CREATE_NEW_FILE EQU 5BH
DOS_LOCK_OPER EQU 5CH
DOS_SERVER_CALL EQU 5DH
DOS_USER_OPER EQU 5EH
DOS_ASSIGN_OPER EQU 5FH
DOS_XNAMETRANS EQU 60H
DOS_JSA EQU 61H
DOS_GET_CURRENT_PSP EQU 62H
DOS_HONGEUL EQU 63H
DOS_SYSINITSLEAZE EQU 64H
DOSERR_NONE EQU 00H
DOSERR_BADFUNCTION EQU 01H
DOSERR_NOFILE EQU 02H
DOSERR_NOPATH EQU 03H
DOSERR_NOHANDLES EQU 04H
DOSERR_NOACCESS EQU 05H
DOSERR_BADHANDLE EQU 06H
DOSERR_BADARENA EQU 07H
DOSERR_NOMEMORY EQU 08H
DOSERR_BADADDRESS EQU 09H
DOSERR_BADENVIRONMENT EQU 0AH
DOSERR_BADFORMAT EQU 0BH
DOSERR_BADACCESS EQU 0CH
DOSERR_BADDATA EQU 0DH
DOSERR_BADUNIT EQU 0EH
DOSERR_BADDISK EQU 0FH
DOSERR_DELETECURDIR EQU 10H
DOSERR_NOTSAMEDEVICE EQU 11H
DOSERR_NOMOREFILES EQU 12H
DOSERR_WRITEPROTECT EQU 13H
DOSERR_UNKNOWNUNIT EQU 14H
DOSERR_DRIVENOTREADY EQU 15H
DOSERR_BADCOMMAND EQU 16H
DOSERR_DATA EQU 17H
DOSERR_BADREQUEST EQU 18H
DOSERR_SEEK EQU 19H
DOSERR_UNKNOWNMEDIA EQU 1AH
DOSERR_BADSECTOR EQU 1BH
DOSERR_NOPAPER EQU 1CH
DOSERR_WRITEFAULT EQU 1DH
DOSERR_READFAULT EQU 1EH
DOSERR_GENERALFAILURE EQU 1FH
DOSERR_NOTSHARED EQU 20H
DOSERR_LOCKED EQU 21H
DOSERR_DISKCHANGE EQU 22H
SEEK_SET EQU 00H
SEEK_CUR EQU 01H
SEEK_END EQU 02H

17
tests/pcx86/inc/misc.inc Normal file
View file

@ -0,0 +1,17 @@
CR equ 0x0d
LF equ 0x0a
;
; The "set" macro initializes a register to the specified value (eg, "set eax,0")
;
%macro set 2
%ifnum %2
%if %2 = 0
xor %1,%1
%else
mov %1,%2
%endif
%else
mov %1,%2
%endif
%endmacro

39
tests/pcx86/inc/x86.inc Normal file
View file

@ -0,0 +1,39 @@
;
; X86.DESC defines (see x86.js)
;
PS_CF equ 0x0001
PS_PF equ 0x0004
PS_AF equ 0x0010
PS_ZF equ 0x0040
PS_SF equ 0x0080
PS_TF equ 0x0100
PS_IF equ 0x0200
PS_DF equ 0x0400
PS_OF equ 0x0800
PS_ARITH equ (PS_CF | PS_PF | PS_AF | PS_ZF | PS_SF | PS_OF)
PS_LOGIC equ (PS_CF | PS_PF | PS_ZF | PS_SF | PS_OF)
PS_MULTIPLY equ (PS_CF | PS_OF) ; only CF and OF are "defined" following MUL or IMUL
PS_DIVIDE equ 0 ; none of the Processor Status flags are "defined" following DIV or IDIV
CR0_MSW_PE equ 0x0001
CR0_PG equ 0x80000000 ; set if paging enabled
ACC_TYPE_GATE386_INT equ 0x0E00
ACC_TYPE_SEG equ 0x1000
ACC_PRESENT equ 0x8000
ACC_TYPE_CODE equ 0x0800
ACC_TYPE_READABLE equ 0x0200
ACC_TYPE_WRITABLE equ 0x0200
ACC_TYPE_CODE_READABLE equ 0x1a00
ACC_TYPE_DATA_WRITABLE equ 0x1200
EXT_NONE equ 0x0000
EXT_BIG equ 0x0040
PTE_FRAME equ 0xfffff000
PTE_DIRTY equ 0x00000040 ; page has been modified
PTE_ACCESSED equ 0x00000020 ; page has been accessed
PTE_USER equ 0x00000004 ; set for user level (CPL 3), clear for supervisor level (CPL 0-2)
PTE_READWRITE equ 0x00000002 ; set for read/write, clear for read-only (affects CPL 3 only)
PTE_PRESENT equ 0x00000001 ; set for present page, clear for not-present page

View file

@ -0,0 +1,59 @@
---
layout: page
title: PC Trace Utility
permalink: /tests/pcx86/trace/
redirect_from:
- /tests/pc/trace/
---
WARNING: This utility is obsolete. It has been superseded by [test386](/tests/pcx86/80386/),
which, going forward, serves as our new model for instruction-level testing. As a result,
the **traceLog()** functionality mentioned below has been removed from the PCjs source code
(but you can always dig it back up if you really want it).
Overview
---
TRACE.COM takes an instruction log, as recorded by the PCjs Debugger's traceLog() function, and
"plays" the instructions back on another machine DOS-compatible 8086 machine, verifying that:
1. Results match the recorded results;
2. Any "modified" flags match the recorded flags; and
3. Any "unmodified" flags remain unmodified
The format of an instruction log entry is a series of lines (ASCII characters terminated by an LF),
where each line looks like:
F000:EEFF SHL(0480,0002,F006) 1200,F006
specifically:
address, space, instruction, parenthesis, dst operand, comma, src operand, comma,
input flags, parenthesis, space, result, comma, and output flags
WARNING: For the shift and rotate tests to pass on a real x86 CPU, we either have to distinguish
between single-bit shifts and multi-bit shifts (because the latter leaves PS_OF in an "undefined"
state), or we have to ignore PS_OF altogether. For now, I'm specifying PS_ALL_BUT_OF for those
instructions, even though we'll be missing OVERFLOW validation for all single-bit shifts and rotates.
Operation
---
To load TRACE.COM and TRACE.TXT onto a virtual disk image that PCjs can access, you can add an
"automount" setting to your PCjs <fdc> configuration that will dynamically generate a fresh disk image
every time the machine is loaded, via the DiskDump API. This is useful when you're constantly
generating new test results:
```xml
<fdc id="fdcNEC" automount='{B:{name:"Trace Tests",path:"/tests/pcx86/trace/trace.com;trace.txt"}}'/>
```
Alternatively, if you want to run the tests in another virtual PC environment (eg, VMware Fusion),
you can create an IMG disk image from a directory using DiskDump's command-line interface:
cd tests/pcx86
node ../../modules/diskdump/bin/diskdump --dir=trace --format=img --output=trace.img
OS X users can also create an ISO image like so:
hdiutil makehybrid -o tests/pcx86/trace.iso tests/pcx86/trace -iso -joliet

View file

@ -0,0 +1,4 @@
all: trace.com
trace.com: trace.nasm
nasm -f bin trace.nasm -l trace.lst -o trace.com

View file

@ -0,0 +1,710 @@
;
; trace.nasm
; Copyright © 2012-2015 Jeff Parsons <Jeff@pcjs.org>
;
; This file is part of PCjs, which is part of the JavaScript Machines Project (aka JSMachines)
; at <http://jsmachines.net/> and <http://pcjs.org/>.
;
; PCjs is free software: you can redistribute it and/or modify it under the terms of the
; GNU General Public License as published by the Free Software Foundation, either version 3
; of the License, or (at your option) any later version.
;
; PCjs is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
; even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public License along with PCjs. If not,
; see <http://www.gnu.org/licenses/gpl.html>.
;
; You are required to include the above copyright notice in every source code file of every
; copy or modified version of this work, and to display that copyright notice on every screen
; that loads or runs any version of this software (see Computer.sCopyright).
;
; Some PCjs files also attempt to load external resource files, such as character-image files,
; ROM files, and disk image files. Those external resource files are not considered part of the
; PCjs program for purposes of the GNU General Public License, and the author does not claim
; any copyright as to their contents.
;
; Overview
; --------
; Takes an instruction log, as recorded by the Debugger's traceLog() function, and
; "plays" the instructions back on another machine DOS-compatible 8086 machine, verifying that:
;
; 1) results match the recorded results
; 2) any "modified" flags match the recorded flags
; 3) any "unmodified" flags remain unmodified
;
; The format of an instruction log entry is a series of lines (ASCII characters terminated by an LF),
; where each line looks like:
;
; F000:EEFF SHL(0480,0002,F006) 1200,F006
;
; ie, address, space, instruction, parenthesis, dst operand, comma, src operand, comma,
; input flags, parenthesis, space, result, comma, and output flags.
;
; WARNING: For the shift and rotate tests to pass on a real x86 CPU, we either have to distinguish
; between single-bit shifts and multi-bit shifts (because the latter leaves PS_OF in an "undefined"
; state), or we have to ignore PS_OF altogether. For now, I'm specifying PS_ALL_BUT_OF for those
; instructions, even though we'll be missing OVERFLOW validation for all single-bit shifts and rotates.
;
CPU 8086
;
; Bit masks for all the arithmetic flags we care about
;
PS_NONE equ 0x0000
PS_CF equ 0x0001 ; bit 0: Carry flag
PS_PF equ 0x0004 ; bit 2: Parity flag
PS_AF equ 0x0010 ; bit 4: Auxiliary Carry flag (aka Arithmetic flag)
PS_ZF equ 0x0040 ; bit 6: Zero flag
PS_SF equ 0x0080 ; bit 7: Sign flag
PS_OF equ 0x0800 ; bit 11: Overflow flag
PS_ALL equ PS_CF | PS_PF | PS_AF | PS_ZF | PS_SF | PS_OF
PS_ALL_BUT_AF equ PS_CF | PS_PF | PS_ZF | PS_SF | PS_OF
PS_ALL_BUT_OF equ PS_CF | PS_PF | PS_AF | PS_ZF | PS_SF
%macro openF 1
section .data
%%name: db %1,0
section .text
mov dx,%%name
mov ax,0x3D00
int 0x21
%endmacro
%macro readF 3
%ifnidni %1,bx
mov bx,%1
%endif
%ifnidni %2,dx
mov dx,%2
%endif
%ifnidni %3,cx
mov cx,%3
%endif
mov ah,0x3F
int 0x21
%endmacro
%macro print 1
%ifidni %1,line
push ax
push cx
push dx
push si
mov dx,si
dec cx
add si,cx
mov byte [si],'$'
mov ah,0x09
int 0x21
mov dx,strCRLF
mov ah,0x09
int 0x21
pop si
pop dx
pop cx
pop ax
%else
%ifstr %1
section .data
%%str: db %1,'$'
section .text
push dx
mov dx,%%str
%elifnidni %1,dx
push dx
mov dx,%1
%endif
push ax
mov ah,0x09
int 0x21
pop ax
%ifnidni %1,dx
pop dx
%endif
%endif
%endmacro
%macro exit 0-2
%ifstr %1
section .data
%%msg: db %1,'$'
section .text
%ifidni %2,oncarry
jnc %%ok
%endif
mov dx,%%msg
mov ah,0x09
int 0x21
%endif
int 0x20
%%ok:
%endmacro
%macro break 0
;
; "int3" generates the 1-byte breakpoint instruction; "int 3" generates a 2-byte software interrupt
;
int3
%endmacro
org 0x100
section .text
openF "TRACE.TXT"
exit "unable to open file",oncarry
mov di,file_buffer
readF ax,di,file_buffer_len
m1: exit "unable to read file",oncarry
test ax,ax ; AX contains how many bytes were actually read
jnz m2
exit "processing complete"
m2: cld
mov si,file_buffer
add di,ax
;
; At this point, DS:SI is the current line pointer, and DI is the end-of-buffer
; position. getLine() will update CX to the length of the current line (including
; the terminating LF).
;
m3: call getLine
jnc m4
;
; Oops, carry is set, so we're missing part or all of the next line. Move the
; partial line to the top of the file buffer and then fill the rest of the buffer.
;
mov di,file_buffer
rep movsb
mov cx,file_buffer_end
sub cx,di
readF bx,di,cx
jmp m1
;
; OK, we now have a complete line at DS:SI, guaranteed to end with an LF, with a
; length of CX (although CX will soon be overwritten by calls to getHex).
;
m4:
; print line
mov ah,' '
call skipTo
m4err: exit "missing space",oncarry
inc si
push di
mov di,ins_name
m5: lodsb
cmp al,'('
je m6
stosb
jmp m5
m6: mov al,'$'
stosb
pop di
print ins_name
print strColon
call getHex
mov [dst_operand],ax
mov [dst_operand+2],dx
mov [dst_size],cx
call printHex
print strComma
inc si
call getHex
mov [src_operand],ax
mov [src_operand+2],dx
mov [src_size],cx
call printHex
inc si
call getHex
and ax,PS_ALL
mov [operand_flags],ax
jcxz m6b
print strComma
call printHex
m6b: mov ah,' '
call skipTo
jc m4err
inc si
call getHex
mov [result_operand],ax
mov [result_operand+2],dx
mov [result_size],cx
print strEquals
call printHex
print strComma
inc si
call getHex
and ax,PS_ALL
mov [result_flags],ax
call printHex
print strCRLF
;
; Now that we know operand sizes, it's time to look up the instruction function
;
push si
mov si,ins_table
push di
m7a: mov di,ins_name
m7b: lodsb
test al,al
jz m8
mov ah,[es:di]
inc di
cmp al,ah
je m7b
m7c: lodsb
test al,al
jnz m7c
add si,8 ; after then name, each ins_table entry is 4 words long
cmp byte [si],0
jne m7a
print "missing function: "
print ins_name
exit
m8: mov ax,[si]
mov cx,[result_size]
mov dx,compare8
cmp cl,4
jb m8a
mov ax,[si+2]
mov dx,compare16
cmp cl,8
jb m8a
mov ax,[si+4]
mov dx,compare32
m8a: test ax,ax
jnz m8b
print "missing "
print ins_name
xchg ax,cx
call printHex
exit
m8b: mov [ins_function],ax
mov [ins_compare],dx
mov ax,[si+6]
mov [relevant_flags],ax
;
; Let's call the instruction function now, loading the PS_ALL flags with the same values
; that the emulator recorded (operand_flags).
;
pushf
pop cx ; CX == current flags
mov ax,PS_ALL
not ax
and cx,ax ; CX == current flags, with PS_ALL flags cleared
or cx,[operand_flags]
push cx ; CX == current flags, with PS_ALL flags from operand_flags included
popf
call [ins_function]
pushf
pop cx
call [ins_compare]
;
; If we're still here, the instruction passed, so restore the line pointer and move to the next line
;
pop di
pop si
;
; When we finished reading the current line, DS:SI should have been left pointing at the terminating LF;
; however, if we used "print" to display it, that LF would have replaced with a '$'. In any case, we don't
; really need to call skipTo, if we know we're at the end of the current line.
;
; mov ah,0x0A
; call skipTo
inc si ; step over the LF (or '$', in case we printed the line before processing it)
jmp m3
compare8:
mov ah,[result_operand]
cmp al,ah
jne c8err
jmp compareFlags
c8err: print "byte mismatch:"
print strActual
mov cx,2
call printHex
print strRecorded
mov al,ah
call printHex
print strCRLF
exit
compare16:
mov dx,[result_operand]
cmp ax,dx
jne c16err
jmp compareFlags
c16err: print "word mismatch:"
print strActual
mov cx,4
call printHex
print strRecorded
xchg ax,dx
call printHex
print strCRLF
exit
compare32:
cmp ax,[result_operand]
jne c32err
cmp dx,[result_operand+2]
je compareFlags
c32err: print "dword mismatch:"
print strActual
mov cx,8
call printHex
print strRecorded
mov ax,[result_operand]
mov dx,[result_operand+2]
call printHex
print strCRLF
exit
compareFlags:
mov dx,[result_flags]
and cx,[relevant_flags]
and dx,[relevant_flags]
cmp cx,dx
je cfret
print "flag mismatch:"
print strActual
xchg ax,cx
mov cx,4
call printHex
print strRecorded
xchg ax,dx
call printHex
print strCRLF
exit
cfret: ret
testROL8:
mov al,[dst_operand]
mov cl,[src_operand]
rol al,cl
ret
testROL16:
mov ax,[dst_operand]
mov cl,[src_operand]
rol ax,cl
ret
testROR8:
mov al,[dst_operand]
mov cl,[src_operand]
ror al,cl
ret
testROR16:
mov ax,[dst_operand]
mov cl,[src_operand]
ror ax,cl
ret
testRCL8:
mov al,[dst_operand]
mov cl,[src_operand]
rcl al,cl
ret
testRCL16:
mov ax,[dst_operand]
mov cl,[src_operand]
rcl ax,cl
ret
testRCR8:
mov al,[dst_operand]
mov cl,[src_operand]
rcr al,cl
ret
testRCR16:
mov ax,[dst_operand]
mov cl,[src_operand]
rcr ax,cl
ret
testSHL8:
mov al,[dst_operand]
mov cl,[src_operand]
shl al,cl
ret
testSHL16:
mov ax,[dst_operand]
mov cl,[src_operand]
shl ax,cl
ret
testMUL16:
mov al,[dst_operand]
mov cl,[src_operand]
mul cl
ret
testMUL32:
mov ax,[dst_operand]
mov cx,[src_operand]
mul cx
ret
testIMUL16:
mov al,[dst_operand]
mov cl,[src_operand]
imul cl
ret
testIMUL32:
mov ax,[dst_operand]
mov cx,[src_operand]
imul cx
ret
testDIV16:
mov ax,[dst_operand]
mov cl,[src_operand]
div cl
ret
testDIV32:
mov ax,[dst_operand]
mov dx,[dst_operand+2]
mov cx,[src_operand]
div cx
ret
testIDIV16:
mov ax,[dst_operand]
mov cl,[src_operand]
idiv cl
ret
testIDIV32:
mov ax,[dst_operand]
mov dx,[dst_operand+2]
mov cx,[src_operand]
idiv cx
ret
;
; getHex: get value of hex string
;
; Inputs
; DS:SI -> hex string
;
; Outputs
; CX == number of characters
; DX:AX == corresponding value
; DS:SI -> next non-hex character
;
; Uses
; AX, CX, DX, SI, Flags
;
; Notes
; Supports upper-case alpha chars only, with no prefixes (eg, "0x") or suffixes (eg, "h");
; if there are more than 8 hex characters, the value will represent only the last 8 characters.
;
getHex:
push bx
sub bx,bx ; BX holds the low 16 bits
sub dx,dx ; DX holds the high 16 bits
sub cx,cx ; CX holds the character count
gh1: lodsb
cmp al,'0'
jb gh9
cmp al,'9'
ja gh3
sub al,'0'
gh2: shl bx,1
rcl dx,1
shl bx,1
rcl dx,1
shl bx,1
rcl dx,1
shl bx,1
rcl dx,1
or bl,al
inc cx
jmp gh1
gh3: cmp al,'A'
jb gh9
cmp al,'F'
ja gh9
sub al,'A'-10
jmp gh2
gh9: dec si
xchg ax,bx ; DX:AX now holds the final 32-bit result
pop bx
ret
;
; printHex: print value in hex
;
; Inputs
; DX:AX == value
; CX == # of characters
;
; Outputs
; None
;
; Uses
; Flags
;
printHex:
push ax
push bx
push cx
push dx
push di
mov bx,ax ; DX:BX now holds the value to print
mov di,hex_buffer_end - 1
mov al,'$'
std
stosb
ph1: jcxz ph3
mov al,bl
and al,0x0F
add al,'0'
cmp al,'9'
jbe ph2
add al,'A'-'0'-10
ph2: stosb
dec cx
shr dx,1
rcr bx,1
shr dx,1
rcr bx,1
shr dx,1
rcr bx,1
shr dx,1
rcr bx,1
jmp ph1
ph3: cld
inc di
print di
pop di
pop dx
pop cx
pop bx
pop ax
ret
;
; getLine: find the length of the current line
;
; Inputs
; DS:SI -> start of line
; DS:DI -> first byte past end of line buffer
;
; Outputs
; CX == length of line, including the terminating LF (or partial length)
; Carry clear if line complete, carry set if line incomplete (SI reached DI)
;
; Uses
; AL, CX, Flags
;
getLine:
push si
sub cx,cx
gl1: cmp si,di
jb gl2
stc
jmp gl9
gl2: lodsb
inc cx
cmp al,0x0A
jne gl1
gl9: pop si
ret
;
; skipTo: skip to the character in AH
;
; Inputs
; AH == specified character
; DS:SI -> LF-terminated line
;
; Outputs
; DS:SI -> specified character if carry clear, or LF if carry set
;
; Uses
; AL, SI, Flags
;
skipTo:
lodsb
cmp al,ah
je st9
cmp al,0x0A
jne skipTo
stc
st9: dec si
ret
;
; The following is "const" (read-only) data...
;
section .data
ins_table db "ROL",0
dw testROL8, testROL16, 0, PS_ALL_BUT_OF
db "ROR",0
dw testROR8, testROR16, 0, PS_ALL_BUT_OF
db "RCL",0
dw testRCL8, testRCL16, 0, PS_ALL_BUT_OF
db "RCR",0
dw testRCR8, testRCR16, 0, PS_ALL_BUT_OF
db "SHL",0
dw testSHL8, testSHL16, 0, PS_ALL_BUT_AF
db "MUL",0
dw 0, testMUL16, testMUL32, PS_CF | PS_OF
db "IMUL",0
dw 0, testIMUL16, testIMUL32, PS_CF | PS_OF
db "DIV",0
dw 0, testDIV16, testDIV32, PS_NONE
db "IMUL",0
dw 0, testIDIV16, testIDIV32, PS_NONE
db 0 ; end of instruction table
strCRLF db 0x0D,0x0A,'$'
strColon db ":$"
strEquals db "=$"
strComma db ",$"
strActual db " actual=$"
strRecorded db " recorded=$"
;
; We end with all the unitialized data (ie, data that doesn't need to be stored in the binary)
;
section .bss
ins_name resb 6
ins_function resw 1
ins_compare resw 1
dst_operand resw 2
dst_size resw 1
src_operand resw 2
src_size resw 1
operand_flags resw 1
relevant_flags resw 1
result_operand resw 2
result_size resw 1
result_flags resw 1
hex_buffer resb 9
hex_buffer_end equ $
hex_buffer_len equ hex_buffer_end - hex_buffer
file_buffer resb 0x1000
file_buffer_end equ $
file_buffer_len equ file_buffer_end - file_buffer

1353
tests/pcx86/trace/trace.txt Normal file

File diff suppressed because it is too large Load diff

555
tests/pcx86/vga/L23-1.ASM Normal file
View file

@ -0,0 +1,555 @@
; Sample VGA program.
; Animates four balls bouncing around a playfield by using
; page flipping. Playfield is panned smoothly both horizontally
; and vertically.
; By Michael Abrash.
;
stack segment para stack 'STACK'
db 512 dup(?)
stack ends
;
MEDRES_VIDEO_MODE equ 0 ;define for 640x350 video mode
; comment out for 640x200 mode
VIDEO_SEGMENT equ 0a000h ;display memory segment for
; true VGA graphics modes
LOGICAL_SCREEN_WIDTH equ 672/8 ;width in bytes and height in scan
LOGICAL_SCREEN_HEIGHT equ 384 ; lines of the virtual screen
; we'll work with
PAGE0 equ 0 ;flag for page 0 when page flipping
PAGE1 equ 1 ;flag for page 1 when page flipping
PAGE0_OFFSET equ 0 ;start offset of page 0 in VGA memory
PAGE1_OFFSET equ LOGICAL_SCREEN_WIDTH * LOGICAL_SCREEN_HEIGHT
;start offset of page 1 (both pages
; are 672x384 virtual screens)
BALL_WIDTH equ 24/8 ;width of ball in display memory bytes
BALL_HEIGHT equ 24 ;height of ball in scan lines
BLANK_OFFSET equ PAGE1_OFFSET * 2;start of blank image
; in VGA memory
BALL_OFFSET equ BLANK_OFFSET + (BALL_WIDTH * BALL_HEIGHT)
;start offset of ball image in VGA memory
NUM_BALLS equ 4 ;number of balls to animate
;
; VGA register equates.
;
SC_INDEX equ 3c4h ;SC index register
MAP_MASK equ 2 ;SC map mask register
GC_INDEX equ 3ceh ;GC index register
GC_MODE equ 5 ;GC mode register
CRTC_INDEX equ 03d4h ;CRTC index register
START_ADDRESS_HIGH equ 0ch ;CRTC start address high byte
START_ADDRESS_LOW equ 0dh ;CRTC start address low byte
CRTC_OFFSET equ 13h ;CRTC offset register
INPUT_STATUS_1 equ 03dah ;VGA status register
VSYNC_MASK equ 08h ;vertical sync bit in status register 1
DE_MASK equ 01h ;display enable bit in status register 1
AC_INDEX equ 03c0h ;AC index register
HPELPAN equ 20h OR 13h ;AC horizontal pel panning register
; (bit 7 is high to keep palette RAM
; addressing on)
dseg segment para common 'DATA'
CurrentPage db PAGE1 ;page to draw to
CurrentPageOffset dw PAGE1_OFFSET
;
; Four plane's worth of multicolored ball image.
;
BallPlane0Image label byte ;blue plane image
db 000h, 03ch, 000h, 001h, 0ffh, 080h
db 007h, 0ffh, 0e0h, 00fh, 0ffh, 0f0h
db 4 * 3 dup(000h)
db 07fh, 0ffh, 0feh, 0ffh, 0ffh, 0ffh
db 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
db 4 * 3 dup(000h)
db 07fh, 0ffh, 0feh, 03fh, 0ffh, 0fch
db 03fh, 0ffh, 0fch, 01fh, 0ffh, 0f8h
db 4 * 3 dup(000h)
BallPlane1Image label byte ;green plane image
db 4 * 3 dup(000h)
db 01fh, 0ffh, 0f8h, 03fh, 0ffh, 0fch
db 03fh, 0ffh, 0fch, 07fh, 0ffh, 0feh
db 07fh, 0ffh, 0feh, 0ffh, 0ffh, 0ffh
db 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
db 8 * 3 dup(000h)
db 00fh, 0ffh, 0f0h, 007h, 0ffh, 0e0h
db 001h, 0ffh, 080h, 000h, 03ch, 000h
BallPlane2Image label byte ;red plane image
db 12 * 3 dup(000h)
db 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
db 0ffh, 0ffh, 0ffh, 07fh, 0ffh, 0feh
db 07fh, 0ffh, 0feh, 03fh, 0ffh, 0fch
db 03fh, 0ffh, 0fch, 01fh, 0ffh, 0f8h
db 00fh, 0ffh, 0f0h, 007h, 0ffh, 0e0h
db 001h, 0ffh, 080h, 000h, 03ch, 000h
BallPlane3Image label byte ;intensity on for all planes,
; to produce high-intensity colors
db 000h, 03ch, 000h, 001h, 0ffh, 080h
db 007h, 0ffh, 0e0h, 00fh, 0ffh, 0f0h
db 01fh, 0ffh, 0f8h, 03fh, 0ffh, 0fch
db 03fh, 0ffh, 0fch, 07fh, 0ffh, 0feh
db 07fh, 0ffh, 0feh, 0ffh, 0ffh, 0ffh
db 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
db 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
db 0ffh, 0ffh, 0ffh, 07fh, 0ffh, 0feh
db 07fh, 0ffh, 0feh, 03fh, 0ffh, 0fch
db 03fh, 0ffh, 0fch, 01fh, 0ffh, 0f8h
db 00fh, 0ffh, 0f0h, 007h, 0ffh, 0e0h
db 001h, 0ffh, 080h, 000h, 03ch, 000h
;
BallX dw 15, 50, 40, 70 ;array of ball x coords
BallY dw 40, 200, 110, 300 ;array of ball y coords
LastBallX dw 15, 50, 40, 70 ;previous ball x coords
LastBallY dw 40, 100, 160, 30 ;previous ball y coords
BallXInc dw 1, 1, 1, 1 ;x move factors for ball
BallYInc dw 8, 8, 8, 8 ;y move factors for ball
BallRep dw 1, 1, 1, 1 ;# times to keep moving
; ball according to current
; increments
BallControl dw Ball0Control, Ball1Control ;pointers to current
dw Ball2Control, Ball3Control ; locations in ball
; control strings
BallControlString dw Ball0Control, Ball1Control ;pointers to
dw Ball2Control, Ball3Control ; start of ball
; control strings
;
; Ball control strings.
;
Ball0Control label word
dw 10, 1, 4, 10, -1, 4, 10, -1, -4, 10, 1, -4, 0
Ball1Control label word
dw 12, -1, 1, 28, -1, -1, 12, 1, -1, 28, 1, 1, 0
Ball2Control label word
dw 20, 0, -1, 40, 0, 1, 20, 0, -1, 0
Ball3Control label word
dw 8, 1, 0, 52, -1, 0, 44, 1, 0, 0
;
; Panning control string.
;
ifdef MEDRES_VIDEO_MODE
PanningControlString dw 32, 1, 0, 34, 0, 1, 32, -1, 0, 34, 0, -1, 0
else
PanningControlString dw 32, 1, 0, 184, 0, 1, 32, -1, 0, 184, 0, -1, 0
endif
PanningControl dw PanningControlString ;pointer to current location
; in panning control string
PanningRep dw 1 ;# times to pan according to current
; panning increments
PanningXInc dw 1 ;x panning factor
PanningYInc dw 0 ;y panning factor
HPan db 0 ;horizontal pel panning setting
PanningStartOffset dw 0 ;start offset adjustment to produce vertical
; panning & coarse horizontal panning
dseg ends
;
; Macro to set indexed register P2 of chip with index register
; at P1 to AL.
;
SETREG macro P1, P2
mov dx,P1
mov ah,al
mov al,P2
out dx,ax
endm
;
cseg segment para public 'CODE'
assume cs:cseg, ds:dseg
start proc near
mov ax,dseg
mov ds,ax
;
; Select graphics mode.
;
ifdef MEDRES_VIDEO_MODE
mov ax,010h
else
mov ax,0eh
endif
int 10h
;
; ES always points to VGA memory.
;
mov ax,VIDEO_SEGMENT
mov es,ax
;
; Draw border around playfield in both pages.
;
mov di,PAGE0_OFFSET
call DrawBorder ;page 0 border
mov di,PAGE1_OFFSET
call DrawBorder ;page 1 border
;
; Draw all four plane's worth of the ball to undisplayed VGA memory.
;
mov al,01h ;enable plane 0
SETREG SC_INDEX, MAP_MASK
mov si,offset BallPlane0Image
mov di,BALL_OFFSET
mov cx,BALL_WIDTH * BALL_HEIGHT
rep movsb
mov al,02h ;enable plane 1
SETREG SC_INDEX, MAP_MASK
mov si,offset BallPlane1Image
mov di,BALL_OFFSET
mov cx,BALL_WIDTH * BALL_HEIGHT
rep movsb
mov al,04h ;enable plane 2
SETREG SC_INDEX, MAP_MASK
mov si,offset BallPlane2Image
mov di,BALL_OFFSET
mov cx,BALL_WIDTH * BALL_HEIGHT
rep movsb
mov al,08h ;enable plane 3
SETREG SC_INDEX, MAP_MASK
mov si,offset BallPlane3Image
mov di,BALL_OFFSET
mov cx,BALL_WIDTH * BALL_HEIGHT
rep movsb
;
; Draw a blank image the size of the ball to undisplayed VGA memory.
;
mov al,0fh ;enable all memory planes, since the
SETREG SC_INDEX, MAP_MASK ; blank has to erase all planes
mov di,BLANK_OFFSET
mov cx,BALL_WIDTH * BALL_HEIGHT
sub al,al
rep stosb
;
; Set VGA to write mode 1, for block copying ball and blank images.
;
mov dx,GC_INDEX
mov al,GC_MODE
out dx,al ;point GC Index to GC Mode register
inc dx ;point to GC Data register
jmp $+2 ;delay to let bus settle
in al,dx ;get current state of GC Mode
and al,not 3 ;clear the write mode bits
or al,1 ;set the write mode field to 1
jmp $+2 ;delay to let bus settle
out dx,al
;
; Set VGA offset register in words to define logical screen width.
;
mov al,LOGICAL_SCREEN_WIDTH / 2
SETREG CRTC_INDEX, CRTC_OFFSET
;
; Move the balls by erasing each ball, moving it, and
; redrawing it, then switching pages when they're all moved.
;
BallAnimationLoop:
mov bx,( NUM_BALLS * 2 ) - 2
EachBallLoop:
;
; Erase old image of ball in this page (at location from one more earlier).
;
mov si,BLANK_OFFSET ;point to blank image
mov cx,[LastBallX+bx]
mov dx,[LastBallY+bx]
call DrawBall
;
; Set new last ball location.
;
mov ax,[BallX+bx]
mov [LastballX+bx],ax
mov ax,[BallY+bx]
mov [LastballY+bx],ax
;
; Change the ball movement values if it's time to do so.
;
dec [BallRep+bx] ;has current repeat factor run out?
jnz MoveBall
mov si,[BallControl+bx] ;it's time to change movement values
lodsw ;get new repeat factor from
; control string
and ax,ax ;at end of control string?
jnz SetNewMove
mov si,[BallControlString+bx] ;reset control string
lodsw ;get new repeat factor
SetNewMove:
mov [BallRep+bx],ax ;set new movement repeat factor
lodsw ;set new x movement increment
mov [BallXInc+bx],ax
lodsw ;set new y movement increment
mov [BallYInc+bx],ax
mov [BallControl+bx],si ;save new control string pointer
;
; Move the ball.
;
MoveBall:
mov ax,[BallXInc+bx]
add [BallX+bx],ax ;move in x direction
mov ax,[BallYInc+bx]
add [BallY+bx],ax ;move in y direction
;
; Draw ball at new location.
;
mov si,BALL_OFFSET ;point to ball's image
mov cx,[BallX+bx]
mov dx,[BallY+bx]
call DrawBall
;
dec bx
dec bx
jns EachBallLoop
;
; Set up the next panning state (but don't program it into the
; VGA yet).
;
call AdjustPanning
;
; Wait for display enable (pixel data being displayed) so we know
; we're nowhere near vertical sync, where the start address gets
; latched and used.
;
call WaitDisplayEnable
;
; Flip to the new page by changing the start address.
;
mov ax,[CurrentPageOffset]
add ax,[PanningStartOffset]
push ax
SETREG CRTC_INDEX, START_ADDRESS_LOW
mov al,byte ptr [CurrentPageOffset+1]
pop ax
mov al,ah
SETREG CRTC_INDEX, START_ADDRESS_HIGH
;
; Wait for vertical sync so the new start address has a chance
; to take effect.
;
call WaitVSync
;
; Set horizontal panning now, just as new start address takes effect.
;
mov al,[HPan]
mov dx,INPUT_STATUS_1
in al,dx ;reset AC addressing to index reg
mov dx,AC_INDEX
mov al,HPELPAN
out dx,al ;set AC index to pel pan reg
mov al,[HPan]
out dx,al ;set new pel panning
;
; Flip the page to draw to the undisplayed page.
;
xor [CurrentPage],1
jnz IsPage1
mov [CurrentPageOffset],PAGE0_OFFSET
jmp short EndFlipPage
IsPage1:
mov [CurrentPageOffset],PAGE1_OFFSET
EndFlipPage:
;
; Exit if a key's been hit.
;
mov ah,1
int 16h
jnz Done
jmp BallAnimationLoop
;
; Finished, clear key, reset screen mode and exit.
;
Done:
mov ah,0 ;clear key
int 16h
;
mov ax,3 ;reset to text mode
int 10h
;
mov ah,4ch ;exit to DOS
int 21h
;
start endp
;
; Routine to draw a ball-sized image to all planes, copying from
; offset SI in VGA memory to offset CX,DX (x,y) in VGA memory in
; the current page.
;
DrawBall proc near
mov ax,LOGICAL_SCREEN_WIDTH
mul dx ;offset of start of top image scan line
add ax,cx ;offset of upper left of image
add ax,[CurrentPageOffset] ;offset of start of page
mov di,ax
mov bp,BALL_HEIGHT
push ds
push es
pop ds ;move from VGA memory to VGA memory
DrawBallLoop:
push di
mov cx,BALL_WIDTH
rep movsb ;draw a scan line of image
pop di
add di,LOGICAL_SCREEN_WIDTH ;point to next destination scan line
dec bp
jnz DrawBallLoop
pop ds
ret
DrawBall endp
;
; Wait for the leading edge of vertical sync pulse.
;
WaitVSync proc near
mov dx,INPUT_STATUS_1
WaitNotVSyncLoop:
in al,dx
and al,VSYNC_MASK
jnz WaitNotVSyncLoop
WaitVSyncLoop:
in al,dx
and al,VSYNC_MASK
jz WaitVSyncLoop
ret
WaitVSync endp
;
; Wait for display enable to happen (pixels to be scanned to
; the screen, indicating we're in the middle of displaying a frame).
;
WaitDisplayEnable proc near
mov dx,INPUT_STATUS_1
WaitDELoop:
in al,dx
and al,DE_MASK
jnz WaitDELoop
ret
WaitDisplayEnable endp
;
; Perform horizontal/vertical panning.
;
AdjustPanning proc near
dec [PanningRep] ;time to get new panning values?
jnz DoPan
mov si,[PanningControl] ;point to current location in
; panning control string
lodsw ;get panning repeat factor
and ax,ax ;at end of panning control string?
jnz SetnewPanValues
mov si,offset PanningControlString ;reset to start of string
lodsw ;get panning repeat factor
SetNewPanValues:
mov [PanningRep],ax ;set new panning repeat value
lodsw
mov [PanningXInc],ax ;horizontal panning value
lodsw
mov [PanningYInc],ax ;vertical panning value
mov [PanningControl],si ;save current location in panning
; control string
;
; Pan according to panning values.
;
DoPan:
mov ax,[PanningXInc] ;horizontal panning
and ax,ax
js PanLeft ;negative means pan left
jz CheckVerticalPan
mov al,[HPan]
inc al ;pan right; if pel pan reaches
cmp al,8 ; 8, it's time to move to the
jb SetHPan ; next byte with a pel pan of 0
sub al,al ; and a start offset that's one
inc [PanningStartOffset] ; higher
jmp short SetHPan
PanLeft:
mov al,[HPan]
dec al ;pan left; if pel pan reaches -1,
jns SetHPan ; it's time to move to the next
mov al,7 ; byte with a pel pan of 7 and a
dec [PanningStartOffset] ; start offset that's one lower
SetHPan:
mov [HPan],al ;save new pel pan value
CheckVerticalPan:
mov ax,[PanningYInc] ;vertical panning
and ax,ax
js PanUp ;negative means pan up
jz EndPan
add [PanningStartOffset],LOGICAL_SCREEN_WIDTH
;pan down by advancing the start
; address by a scan line
jmp short EndPan
PanUp:
sub [PanningStartOffset],LOGICAL_SCREEN_WIDTH
;pan up by retarding the start
; address by a scan line
EndPan:
ret
;
; Draw textured border around playfield that starts at DI.
;
DrawBorder proc near
;
; Draw the left border.
;
push di
mov cx,LOGICAL_SCREEN_HEIGHT / 16
DrawLeftBorderLoop:
mov al,0ch ;select red color for block
call DrawBorderBlock
add di,LOGICAL_SCREEN_WIDTH * 8
mov al,0eh ;select yellow color for block
call DrawBorderBlock
add di,LOGICAL_SCREEN_WIDTH * 8
loop DrawLeftBorderLoop
pop di
;
; Draw the right border.
;
push di
add di,LOGICAL_SCREEN_WIDTH - 1
mov cx,LOGICAL_SCREEN_HEIGHT / 16
DrawRightBorderLoop:
mov al,0eh ;select yellow color for block
call DrawBorderBlock
add di,LOGICAL_SCREEN_WIDTH * 8
mov al,0ch ;select red color for block
call DrawBorderBlock
add di,LOGICAL_SCREEN_WIDTH * 8
loop DrawRightBorderLoop
pop di
;
; Draw the top border.
;
push di
mov cx,(LOGICAL_SCREEN_WIDTH - 2) / 2
DrawTopBorderLoop:
inc di
mov al,0eh ;select yellow color for block
call DrawBorderBlock
inc di
mov al,0ch ;select red color for block
call DrawBorderBlock
loop DrawTopBorderLoop
pop di
;
; Draw the bottom border.
;
add di,(LOGICAL_SCREEN_HEIGHT - 8) * LOGICAL_SCREEN_WIDTH
mov cx,(LOGICAL_SCREEN_WIDTH - 2) / 2
DrawBottomBorderLoop:
inc di
mov al,0ch ;select red color for block
call DrawBorderBlock
inc di
mov al,0eh ;select yellow color for block
call DrawBorderBlock
loop DrawBottomBorderLoop
ret
DrawBorder endp
;
; Draws an 8x8 border block in color in AL at location DI.
; DI preserved.
;
DrawBorderBlock proc near
push di
SETREG SC_INDEX, MAP_MASK
mov al,0ffh
rept 8
stosb
add di,LOGICAL_SCREEN_WIDTH - 1
endm
pop di
ret
DrawBorderBlock endp
AdjustPanning endp
cseg ends
end start

254
tests/pcx86/vga/L24-1.ASM Normal file
View file

@ -0,0 +1,254 @@
; Program to illustrate operation of ALUs and latches of the VGA's
; Graphics Controller. Draws a variety of patterns against
; a horizontally striped background, using each of the 4 available
; logical functions (data unmodified, AND, OR, XOR) in turn to combine
; the images with the background.
; By Michael Abrash.
;
stack segment para stack 'STACK'
db 512 dup(?)
stack ends
;
VGA_VIDEO_SEGMENT equ 0a000h ;VGA display memory segment
SCREEN_HEIGHT equ 350
SCREEN_WIDTH_IN_BYTES equ 80
DEMO_AREA_HEIGHT equ 336 ;# of scan lines in area
; logical function operation
; is demonstrated in
DEMO_AREA_WIDTH_IN_BYTES equ 40 ;width in bytes of area
; logical function operation
; is demonstrated in
VERTICAL_BOX_WIDTH_IN_BYTES equ 10 ;width in bytes of the box used to
; demonstrate each logical function
;
; VGA register equates.
;
GC_INDEX equ 3ceh ;GC index register
GC_ROTATE equ 3 ;GC data rotate/logical function
; register index
GC_MODE equ 5 ;GC mode register index
;
dseg segment para common 'DATA'
;
; String used to label logical functions.
;
LabelString label byte
db 'UNMODIFIED AND OR XOR '
LABEL_STRING_LENGTH equ $-LabelString
;
; Strings used to label fill patterns.
;
FillPatternFF db 'Fill Pattern: 0FFh'
FILL_PATTERN_FF_LENGTH equ $ - FillPatternFF
FillPattern00 db 'Fill Pattern: 000h'
FILL_PATTERN_00_LENGTH equ $ - FillPattern00
FillPatternVert db 'Fill Pattern: Vertical Bar'
FILL_PATTERN_VERT_LENGTH equ $ - FillPatternVert
FillPatternHorz db 'Fill Pattern: Horizontal Bar'
FILL_PATTERN_HORZ_LENGTH equ $ - FillPatternHorz
;
dseg ends
;
; Macro to set indexed register INDEX of GC chip to SETTING.
;
SETGC macro INDEX, SETTING
mov dx,GC_INDEX
mov ax,(SETTING SHL 8) OR INDEX
out dx,ax
endm
;
;
; Macro to call BIOS write string function to display text string
; TEXT_STRING, of length TEXT_LENGTH, at location ROW,COLUMN.
;
TEXT_UP macro TEXT_STRING, TEXT_LENGTH, ROW, COLUMN
mov ah,13h ;BIOS write string function
mov bp,offset TEXT_STRING ;ES:BP points to string
mov cx,TEXT_LENGTH
mov dx,(ROW SHL 8) OR COLUMN;position
sub al,al ;string is chars only, cursor not moved
mov bl,7 ;text attribute is white (light gray)
int 10h
endm
;
cseg segment para public 'CODE'
assume cs:cseg, ds:dseg
start proc near
mov ax,dseg
mov ds,ax
;
; Select 640x350 graphics mode.
;
mov ax,010h
int 10h
;
; ES points to VGA memory.
;
mov ax,VGA_VIDEO_SEGMENT
mov es,ax
;
; Draw background of horizontal bars.
;
mov dx,SCREEN_HEIGHT/4
;# of bars to draw (each 4 pixels high)
sub di,di ;start at offset 0 in display memory
mov ax,0ffffh ;fill pattern for light areas of bars
mov bx,DEMO_AREA_WIDTH_IN_BYTES / 2 ;length of each bar
mov si,SCREEN_WIDTH_IN_BYTES - DEMO_AREA_WIDTH_IN_BYTES
mov bp,(SCREEN_WIDTH_IN_BYTES * 3) - DEMO_AREA_WIDTH_IN_BYTES
BackgroundLoop:
mov cx,bx ;length of bar
rep stosw ;draw top half of bar
add di,si ;point to start of bottom half of bar
mov cx,bx ;length of bar
rep stosw ;draw bottom half of bar
add di,bp ;point to start of top of next bar
dec dx
jnz BackgroundLoop
;
; Draw vertical boxes filled with a variety of fill patterns
; using each of the 4 logical functions in turn.
;
SETGC GC_ROTATE, 0 ;select data unmodified
; logical function...
mov di,0
call DrawVerticalBox ;...and draw box
;
SETGC GC_ROTATE, 08h ;select AND logical function...
mov di,10
call DrawVerticalBox ;...and draw box
;
SETGC GC_ROTATE, 10h ;select OR logical function...
mov di,20
call DrawVerticalBox ;...and draw box
;
SETGC GC_ROTATE, 18h ;select XOR logical function...
mov di,30
call DrawVerticalBox ;...and draw box
;
; Reset the logical function to data unmodified, the default state.
;
SETGC GC_ROTATE, 0
;
; Label the screen.
;
push ds
pop es ;strings we'll display are passed to BIOS
; by pointing ES:BP to them
;
; Label the logical functions, using the VGA BIOS's
; write string function.
;
TEXT_UP LabelString, LABEL_STRING_LENGTH, 24, 0
;
; Label the fill patterns, using the VGA BIOS's
; write string function.
;
TEXT_UP FillPatternFF, FILL_PATTERN_FF_LENGTH, 3, 42
TEXT_UP FillPattern00, FILL_PATTERN_00_LENGTH, 9, 42
TEXT_UP FillPatternVert, FILL_PATTERN_VERT_LENGTH, 15, 42
TEXT_UP FillPatternHorz, FILL_PATTERN_HORZ_LENGTH, 21, 42
;
; Wait until a key's been hit to reset screen mode & exit.
;
WaitForKey:
mov ah,1
int 16h
jz WaitForKey
;
; Finished. Clear key, reset screen mode and exit.
;
Done:
mov ah,0 ;clear key that we just detected
int 16h
;
mov ax,3 ;reset to text mode
int 10h
;
mov ah,4ch ;exit to DOS
int 21h
;
start endp
;
; Subroutine to draw a box 80x336 in size, using currently selected
; logical function, with upper left corner at the display memory offset
; in DI. Box is filled with four patterns. Top quarter of area is
; filled with 0FFh (solid) pattern, next quarter is filled with 00h
; (empty) pattern, next quarter is filled with 33h (double pixel wide
; vertical bar) pattern, and bottom quarter is filled with double pixel
; high horizontal bar pattern.
;
; Macro to draw a column of the specified width in bytes, one-quarter
; of the height of the box, with the specified fill pattern.
;
DRAW_BOX_QUARTER macro FILL, WIDTH
local RowLoop, ColumnLoop
mov al,FILL ;fill pattern
mov dx,DEMO_AREA_HEIGHT / 4 ;1/4 of the full box height
RowLoop:
mov cx,WIDTH
ColumnLoop:
mov ah,es:[di] ;load display memory contents into
; GC latches (we don't actually care
; about value read into AH)
stosb ;write pattern, which is logically
; combined with latch contents for each
; plane and then written to display
; memory
loop ColumnLoop
add di,SCREEN_WIDTH_IN_BYTES - WIDTH
;point to start of next line down in box
dec dx
jnz RowLoop
endm
;
DrawVerticalBox proc near
DRAW_BOX_QUARTER 0ffh, VERTICAL_BOX_WIDTH_IN_BYTES
;first fill pattern: solid fill
DRAW_BOX_QUARTER 0, VERTICAL_BOX_WIDTH_IN_BYTES
;second fill pattern: empty fill
DRAW_BOX_QUARTER 033h, VERTICAL_BOX_WIDTH_IN_BYTES
;third fill pattern: double-pixel
; wide vertical bars
mov dx,DEMO_AREA_HEIGHT / 4 / 4
;fourth fill pattern: horizontal bars in
; sets of 4 scan lines
sub ax,ax
mov si,VERTICAL_BOX_WIDTH_IN_BYTES ;width of fill area
HorzBarLoop:
dec ax ;0ffh fill (smaller to do word than byte DEC)
mov cx,si ;width to fill
HBLoop1:
mov bl,es:[di] ;load latches (don't care about value)
stosb ;write solid pattern, through ALUs
loop HBLoop1
add di,SCREEN_WIDTH_IN_BYTES - VERTICAL_BOX_WIDTH_IN_BYTES
mov cx,si ;width to fill
HBLoop2:
mov bl,es:[di] ;load latches
stosb ;write solid pattern, through ALUs
loop HBLoop2
add di,SCREEN_WIDTH_IN_BYTES - VERTICAL_BOX_WIDTH_IN_BYTES
inc ax ;0 fill (smaller to do word than byte DEC)
mov cx,si ;width to fill
HBLoop3:
mov bl,es:[di] ;load latches
stosb ;write empty pattern, through ALUs
loop HBLoop3
add di,SCREEN_WIDTH_IN_BYTES - VERTICAL_BOX_WIDTH_IN_BYTES
mov cx,si ;width to fill
HBLoop4:
mov bl,es:[di] ;load latches
stosb ;write empty pattern, through ALUs
loop HBLoop4
add di,SCREEN_WIDTH_IN_BYTES - VERTICAL_BOX_WIDTH_IN_BYTES
dec dx
jnz HorzBarLoop
;
ret
DrawVerticalBox endp
cseg ends
end start

230
tests/pcx86/vga/L25-1.ASM Normal file
View file

@ -0,0 +1,230 @@
; Program to illustrate operation of data rotate and bit mask
; features of Graphics Controller. Draws 8x8 character at
; specified location, using VGA's 8x8 ROM font. Designed
; for use with modes 0Dh, 0Eh, 0Fh, 10h, and 12h.
; By Michael Abrash.
;
stack segment para stack 'STACK'
db 512 dup(?)
stack ends
;
VGA_VIDEO_SEGMENT equ 0a000h ;VGA display memory segment
SCREEN_WIDTH_IN_BYTES equ 044ah ;offset of BIOS variable
FONT_CHARACTER_SIZE equ 8 ;# bytes in each font char
;
; VGA register equates.
;
GC_INDEX equ 3ceh ;GC index register
GC_ROTATE equ 3 ;GC data rotate/logical function
; register index
GC_BIT_MASK equ 8 ;GC bit mask register index
;
dseg segment para common 'DATA'
TEST_TEXT_ROW equ 69 ;row to display test text at
TEST_TEXT_COL equ 17 ;column to display test text at
TEST_TEXT_WIDTH equ 8 ;width of a character in pixels
TestString label byte
db 'Hello, world!',0 ;test string to print.
FontPointer dd ? ;font offset
dseg ends
;
; Macro to set indexed register INDEX of GC chip to SETTING.
;
SETGC macro INDEX, SETTING
mov dx,GC_INDEX
mov ax,(SETTING SHL 8) OR INDEX
out dx,ax
endm
;
cseg segment para public 'CODE'
assume cs:cseg, ds:dseg
start proc near
mov ax,dseg
mov ds,ax
;
; Select 640x480 graphics mode.
;
mov ax,012h
int 10h
;
; Set driver to use the 8x8 font.
;
mov ah,11h ;VGA BIOS character generator function,
mov al,30h ; return info subfunction
mov bh,3 ;get 8x8 font pointer
int 10h
call SelectFont
;
; Print the test string.
;
mov si,offset TestString
mov bx,TEST_TEXT_ROW
mov cx,TEST_TEXT_COL
StringOutLoop:
lodsb
and al,al
jz StringOutDone
call DrawChar
add cx,TEST_TEXT_WIDTH
jmp StringOutLoop
StringOutDone:
;
; Reset the data rotate and bit mask registers.
;
SETGC GC_ROTATE, 0
SETGC GC_BIT_MASK, 0ffh
;
; Wait for a keystroke.
;
mov ah,1
int 21h
;
; Return to text mode.
;
mov ax,03h
int 10h
;
; Exit to DOS.
;
mov ah,4ch
int 21h
Start endp
;
; Subroutine to draw a text character in a linear graphics mode
; (0Dh, 0Eh, 0Fh, 010h, 012h).
; Font used should be pointed to by FontPointer.
;
; Input:
; AL = character to draw
; BX = row to draw text character at
; CX = column to draw text character at
;
; Forces ALU function to "move".
;
DrawChar proc near
push ax
push bx
push cx
push dx
push si
push di
push bp
push ds
;
; Set DS:SI to point to font and ES to point to display memory.
;
lds si,[FontPointer] ;point to font
mov dx,VGA_VIDEO_SEGMENT
mov es,dx ;point to display memory
;
; Calculate screen address of byte character starts in.
;
push ds ;point to BIOS data segment
sub dx,dx
mov ds,dx
xchg ax,bx
mov di,ds:[SCREEN_WIDTH_IN_BYTES] ;retrieve BIOS
; screen width
pop ds
mul di ;calculate offset of start of row
push di ;set aside screen width
mov di,cx ;set aside the column
and cl,0111b;keep only the column in-byte address
shr di,1
shr di,1
shr di,1 ;divide column by 8 to make a byte address
add di,ax ;and point to byte
;
; Calculate font address of character.
;
sub bh,bh
shl bx,1 ;assumes 8 bytes per character; use
shl bx,1 ; a multiply otherwise
shl bx,1 ;offset in font of character
add si,bx ;offset in font segment of character
;
; Set up the GC rotation.
;
mov dx,GC_INDEX
mov al,GC_ROTATE
mov ah,cl
out dx,ax
;
; Set up BH as bit mask for left half,
; BL as rotation for right half.
;
mov bx,0ffffh
shr bh,cl
neg cl
add cl,8
shl bl,cl
;
; Draw the character, left half first, then right half in the
; succeeding byte, using the data rotation to position the character
; across the byte boundary and then using the bit mask to get the
; proper portion of the character into each byte.
; Does not check for case where character is byte-aligned and
; no rotation and only one write is required.
;
mov bp,FONT_CHARACTER_SIZE
mov dx,GC_INDEX
pop cx ;get back screen width
dec cx
dec cx ; -2 because do two bytes for each char
CharacterLoop:
;
; Set the bit mask for the left half of the character.
;
mov al,GC_BIT_MASK
mov ah,bh
out dx,ax
;
; Get the next character byte & write it to display memory.
; (Left half of character.)
;
mov al,[si] ;get character byte
mov ah,es:[di] ;load latches
stosb ;write character byte
;
; Set the bit mask for the right half of the character.
;
mov al,GC_BIT_MASK
mov ah,bl
out dx,ax
;
; Get the character byte again & write it to display memory.
; (Right half of character.)
;
lodsb ;get character byte
mov ah,es:[di] ;load latches
stosb ;write character byte
;
; Point to next line of character in display memory.
;
add di,cx
;
dec bp
jnz CharacterLoop
;
pop ds
pop bp
pop di
pop si
pop dx
pop cx
pop bx
pop ax
ret
DrawChar endp
;
; Set the pointer to the font to draw from to ES:BP.
;
SelectFont proc near
mov word ptr [FontPointer],bp ;save pointer
mov word ptr [FontPointer+2],es
ret
SelectFont endp
;
cseg ends
end start

81
tests/pcx86/vga/L25-2.ASM Normal file
View file

@ -0,0 +1,81 @@
; Program to illustrate operation of Map Mask register when drawing
; to memory that already contains data.
; By Michael Abrash.
;
stack segment para stack 'STACK'
db 512 dup(?)
stack ends
;
EGA_VIDEO_SEGMENT equ 0a000h ;EGA display memory segment
;
; EGA register equates.
;
SC_INDEX equ 3c4h ;SC index register
SC_MAP_MASK equ 2 ;SC map mask register
;
; Macro to set indexed register INDEX of SC chip to SETTING.
;
SETSC macro INDEX, SETTING
mov dx,SC_INDEX
mov al,INDEX
out dx,al
inc dx
mov al,SETTING
out dx,al
dec dx
endm
;
cseg segment para public 'CODE'
assume cs:cseg
start proc near
;
; Select 640x480 graphics mode.
;
mov ax,012h
int 10h
;
mov ax,EGA_VIDEO_SEGMENT
mov es,ax ;point to video memory
;
; Draw 24 10-scan-line high horizontal bars in green, 10 scan lines apart.
;
SETSC SC_MAP_MASK,02h ;map mask setting enables only
; plane 1, the green plane
sub di,di ;start at beginning of video memory
mov al,0ffh
mov bp,24 ;# bars to draw
HorzBarLoop:
mov cx,80*10 ;# bytes per horizontal bar
rep stosb ;draw bar
add di,80*10 ;point to start of next bar
dec bp
jnz HorzBarLoop
;
; Fill screen with blue, using Map Mask register to enable writes
; to blue plane only.
;
SETSC SC_MAP_MASK,01h ;map mask setting enables only
; plane 0, the blue plane
sub di,di
mov cx,80*480 ;# bytes per screen
mov al,0ffh
rep stosb ;perform fill (affects only
; plane 0, the blue plane)
;
; Wait for a keystroke.
;
mov ah,1
int 21h
;
; Restore text mode.
;
mov ax,03h
int 10h
;
; Exit to DOS.
;
mov ah,4ch
int 21h
start endp
cseg ends
end start

108
tests/pcx86/vga/L25-3.ASM Normal file
View file

@ -0,0 +1,108 @@
; Program to illustrate operation of set/reset circuitry to force
; setting of memory that already contains data.
; By Michael Abrash.
;
stack segment para stack 'STACK'
db 512 dup(?)
stack ends
;
EGA_VIDEO_SEGMENT equ 0a000h ;EGA display memory segment
;
; EGA register equates.
;
SC_INDEX equ 3c4h ;SC index register
SC_MAP_MASK equ 2 ;SC map mask register
GC_INDEX equ 3ceh ;GC index register
GC_SET_RESET equ 0 ;GC set/reset register
GC_ENABLE_SET_RESET equ 1 ;GC enable set/reset register
;
; Macro to set indexed register INDEX of SC chip to SETTING.
;
SETSC macro INDEX, SETTING
mov dx,SC_INDEX
mov al,INDEX
out dx,al
inc dx
mov al,SETTING
out dx,al
dec dx
endm
;
; Macro to set indexed register INDEX of GC chip to SETTING.
;
SETGC macro INDEX, SETTING
mov dx,GC_INDEX
mov al,INDEX
out dx,al
inc dx
mov al,SETTING
out dx,al
dec dx
endm
;
cseg segment para public 'CODE'
assume cs:cseg
start proc near
;
; Select 640x480 graphics mode.
;
mov ax,012h
int 10h
;
mov ax,EGA_VIDEO_SEGMENT
mov es,ax ;point to video memory
;
; Draw 24 10-scan-line high horizontal bars in green, 10 scan lines apart.
;
SETSC SC_MAP_MASK,02h ;map mask setting enables only
; plane 1, the green plane
sub di,di ;start at beginning of video memory
mov al,0ffh
mov bp,24 ;# bars to draw
HorzBarLoop:
mov cx,80*10 ;# bytes per horizontal bar
rep stosb ;draw bar
add di,80*10 ;point to start of next bar
dec bp
jnz HorzBarLoop
;
; Fill screen with blue, using set/reset to force plane 0 to 1's and all
; other plane to 0's.
;
SETSC SC_MAP_MASK,0fh ;must set map mask to enable all
; planes, so set/reset values can
; be written to memory
SETGC GC_ENABLE_SET_RESET,0fh ;CPU data to all planes will be
; replaced by set/reset value
SETGC GC_SET_RESET,01h ;set/reset value is 0ffh for plane 0
; (the blue plane) and 0 for other
; planes
sub di,di
mov cx,80*480 ;# bytes per screen
mov al,0ffh ;since set/reset is enabled for all
; planes, the CPU data is ignored-
; only the act of writing is
; important
rep stosb ;perform fill (affects all planes)
;
; Turn off set/reset.
;
SETGC GC_ENABLE_SET_RESET,0
;
; Wait for a keystroke.
;
mov ah,1
int 21h
;
; Restore text mode.
;
mov ax,03h
int 10h
;
; Exit to DOS.
;
mov ah,4ch
int 21h
start endp
cseg ends
end start

108
tests/pcx86/vga/L25-4.ASM Normal file
View file

@ -0,0 +1,108 @@
; Program to illustrate operation of set/reset circuitry in conjunction
; with CPU data to modify setting of memory that already contains data.
; By Michael Abrash.
;
stack segment para stack 'STACK'
db 512 dup(?)
stack ends
;
EGA_VIDEO_SEGMENT equ 0a000h ;EGA display memory segment
;
; EGA register equates.
;
SC_INDEX equ 3c4h ;SC index register
SC_MAP_MASK equ 2 ;SC map mask register
GC_INDEX equ 3ceh ;GC index register
GC_SET_RESET equ 0 ;GC set/reset register
GC_ENABLE_SET_RESET equ 1 ;GC enable set/reset register
;
; Macro to set indexed register INDEX of SC chip to SETTING.
;
SETSC macro INDEX, SETTING
mov dx,SC_INDEX
mov al,INDEX
out dx,al
inc dx
mov al,SETTING
out dx,al
dec dx
endm
;
; Macro to set indexed register INDEX of GC chip to SETTING.
;
SETGC macro INDEX, SETTING
mov dx,GC_INDEX
mov al,INDEX
out dx,al
inc dx
mov al,SETTING
out dx,al
dec dx
endm
;
cseg segment para public 'CODE'
assume cs:cseg
start proc near
;
; Select 640x350 graphics mode.
;
mov ax,010h
int 10h
;
mov ax,EGA_VIDEO_SEGMENT
mov es,ax ;point to video memory
;
; Draw 18 10-scan-line high horizontal bars in green, 10 scan lines apart.
;
SETSC SC_MAP_MASK,02h ;map mask setting enables only
; plane 1, the green plane
sub di,di ;start at beginning of video memory
mov al,0ffh
mov bp,18 ;# bars to draw
HorzBarLoop:
mov cx,80*10 ;# bytes per horizontal bar
rep stosb ;draw bar
add di,80*10 ;point to start of next bar
dec bp
jnz HorzBarLoop
;
; Fill screen with alternating bars of red and brown, using CPU data
; to set plane 1 and set/reset to set planes 0, 2 & 3.
;
SETSC SC_MAP_MASK,0fh ;must set map mask to enable all
; planes, so set/reset values can
; be written to planes 0, 2 & 3
; and CPU data can be written to
; plane 1 (the green plane)
SETGC GC_ENABLE_SET_RESET,0dh ;CPU data to planes 0, 2 & 3 will be
; replaced by set/reset value
SETGC GC_SET_RESET,04h ;set/reset value is 0ffh for plane 2
; (the red plane) and 0 for other
; planes
sub di,di
mov cx,80*350/2 ;# words per screen
mov ax,07e0h ;CPU data controls only plane 1;
; set/reset controls other planes
rep stosw ;perform fill (affects all planes)
;
; Turn off set/reset.
;
SETGC GC_ENABLE_SET_RESET,0
;
; Wait for a keystroke.
;
mov ah,1
int 21h
;
; Restore text mode.
;
mov ax,03h
int 10h
;
; Exit to DOS.
;
mov ah,4ch
int 21h
start endp
cseg ends
end start

293
tests/pcx86/vga/L26-1.ASM Normal file
View file

@ -0,0 +1,293 @@
; Program to illustrate operation of write mode 3 of the VGA.
; Draws 8x8 characters at arbitrary locations without disturbing
; the background, using VGA's 8x8 ROM font. Designed
; for use with modes 0Dh, 0Eh, 0Fh, 10h, and 12h.
; Runs only on VGAs (in Models 50 & up and IBM Display Adapter
; and 100% compatibles).
; Assembled with MASM
; By Michael Abrash
;
stack segment para stack 'STACK'
db 512 dup(?)
stack ends
;
VGA_VIDEO_SEGMENT equ 0a000h ;VGA display memory segment
SCREEN_WIDTH_IN_BYTES equ 044ah ;offset of BIOS variable
FONT_CHARACTER_SIZE equ 8 ;# bytes in each font char
;
; VGA register equates.
;
SC_INDEX equ 3c4h ;SC index register
SC_MAP_MASK equ 2 ;SC map mask register index
GC_INDEX equ 3ceh ;GC index register
GC_SET_RESET equ 0 ;GC set/reset register index
GC_ENABLE_SET_RESET equ 1 ;GC enable set/reset register index
GC_ROTATE equ 3 ;GC data rotate/logical function
; register index
GC_MODE equ 5 ;GC Mode register
GC_BIT_MASK equ 8 ;GC bit mask register index
;
dseg segment para common 'DATA'
TEST_TEXT_ROW equ 69 ;row to display test text at
TEST_TEXT_COL equ 17 ;column to display test text at
TEST_TEXT_WIDTH equ 8 ;width of a character in pixels
TestString label byte
db 'Hello, world!',0 ;test string to print.
FontPointer dd ? ;font offset
dseg ends
;
cseg segment para public 'CODE'
assume cs:cseg, ds:dseg
start proc near
mov ax,dseg
mov ds,ax
;
; Select 640x480 graphics mode.
;
mov ax,012h
int 10h
;
; Set the screen to all blue, using the readability of VGA registers
; to preserve reserved bits.
;
mov dx,GC_INDEX
mov al,GC_SET_RESET
out dx,al
inc dx
in al,dx
and al,0f0h
or al,1 ;blue plane only set, others reset
out dx,al
dec dx
mov al,GC_ENABLE_SET_RESET
out dx,al
inc dx
in al,dx
and al,0f0h
or al,0fh ;enable set/reset for all planes
out dx,al
mov dx,VGA_VIDEO_SEGMENT
mov es,dx ;point to display memory
mov di,0
mov cx,8000h ;fill all 32k words
mov ax,0ffffh ;because of set/reset, the value
; written actually doesn't matter
rep stosw ;fill with blue
;
; Set driver to use the 8x8 font.
;
mov ah,11h ;VGA BIOS character generator function,
mov al,30h ; return info subfunction
mov bh,3 ;get 8x8 font pointer
int 10h
call SelectFont
;
; Print the test string, cycling through colors.
;
mov si,offset TestString
mov bx,TEST_TEXT_ROW
mov cx,TEST_TEXT_COL
mov ah,0 ;start with color 0
StringOutLoop:
lodsb
and al,al
jz StringOutDone
push ax ;preserve color
call DrawChar
pop ax ;restore color
inc ah ;next color
and ah,0fh ;colors range from 0 to 15
add cx,TEST_TEXT_WIDTH
jmp StringOutLoop
StringOutDone:
;
; Wait for a key, then set to text mode & end.
;
mov ah,1
int 21h ;wait for a key
mov ax,3
int 10h ;restore text mode
;
; Exit to DOS.
;
mov ah,4ch
int 21h
Start endp
;
; Subroutine to draw a text character in a linear graphics mode
; (0Dh, 0Eh, 0Fh, 010h, 012h). Background around the pixels that
; make up the character is preserved.
; Font used should be pointed to by FontPointer.
;
; Input:
; AL = character to draw
; AH = color to draw character in (0-15)
; BX = row to draw text character at
; CX = column to draw text character at
;
; Forces ALU function to "move".
; Forces write mode 3.
;
DrawChar proc near
push ax
push bx
push cx
push dx
push si
push di
push bp
push ds
push ax ;preserve character to draw in AL
;
; Set up set/reset to produce character color, using the readability
; of VGA register to preserve the setting of reserved bits 7-4.
;
mov dx,GC_INDEX
mov al,GC_SET_RESET
out dx,al
inc dx
in al,dx
and al,0f0h
and ah,0fh
or al,ah
out dx,al
;
; Select write mode 3, using the readability of VGA registers
; to leave bits other than the write mode bits unchanged.
;
mov dx,GC_INDEX
mov al,GC_MODE
out dx,al
inc dx
in al,dx
or al,3
out dx,al
;
; Set DS:SI to point to font and ES to point to display memory.
;
lds si,[FontPointer] ;point to font
mov dx,VGA_VIDEO_SEGMENT
mov es,dx ;point to display memory
;
; Calculate screen address of byte character starts in.
;
pop ax ;get back character to draw in AL
push ds ;point to BIOS data segment
sub dx,dx
mov ds,dx
xchg ax,bx
mov di,ds:[SCREEN_WIDTH_IN_BYTES] ;retrieve BIOS
; screen width
pop ds
mul di ;calculate offset of start of row
push di ;set aside screen width
mov di,cx ;set aside the column
and cl,0111b ;keep only the column in-byte address
shr di,1
shr di,1
shr di,1 ;divide column by 8 to make a byte address
add di,ax ;and point to byte
;
; Calculate font address of character.
;
sub bh,bh
shl bx,1 ;assumes 8 bytes per character; use
shl bx,1 ; a multiply otherwise
shl bx,1 ;offset in font of character
add si,bx ;offset in font segment of character
;
; Set up the GC rotation. In write mode 3, this is the rotation
; of CPU data before it is ANDed with the Bit Mask register to
; form the bit mask. Force the ALU function to "move". Uses the
; readability of VGA registers to leave reserved bits unchanged.
;
mov dx,GC_INDEX
mov al,GC_ROTATE
out dx,al
inc dx
in al,dx
and al,0e0h
or al,cl
out dx,al
;
; Set up BH as bit mask for left half, BL as rotation for right half.
;
mov bx,0ffffh
shr bh,cl
neg cl
add cl,8
shl bl,cl
;
; Draw the character, left half first, then right half in the
; succeeding byte, using the data rotation to position the character
; across the byte boundary and then using write mode 3 to combine the
; character data with the bit mask to allow the set/reset value (the
; character color) through only for the proper portion (where the
; font bits for the character are 1) of the character for each byte.
; Wherever the font bits for the character are 0, the background
; color is preserved.
; Does not check for case where character is byte-aligned and
; no rotation and only one write is required.
;
mov bp,FONT_CHARACTER_SIZE
mov dx,GC_INDEX
pop cx ;get back screen width
dec cx
dec cx ; -2 because do two bytes for each char
CharacterLoop:
;
; Set the bit mask for the left half of the character.
;
mov al,GC_BIT_MASK
mov ah,bh
out dx,ax
;
; Get the next character byte & write it to display memory.
; (Left half of character.)
;
mov al,[si] ;get character byte
mov ah,es:[di] ;load latches
stosb ;write character byte
;
; Set the bit mask for the right half of the character.
;
mov al,GC_BIT_MASK
mov ah,bl
out dx,ax
;
; Get the character byte again & write it to display memory.
; (Right half of character.)
;
lodsb ;get character byte
mov ah,es:[di] ;load latches
stosb ;write character byte
;
; Point to next line of character in display memory.
;
add di,cx
;
dec bp
jnz CharacterLoop
;
pop ds
pop bp
pop di
pop si
pop dx
pop cx
pop bx
pop ax
ret
DrawChar endp
;
; Set the pointer to the font to draw from to ES:BP.
;
SelectFont proc near
mov word ptr [FontPointer],bp ;save pointer
mov word ptr [FontPointer+2],es
ret
SelectFont endp
;
cseg ends
end start

318
tests/pcx86/vga/L26-2.ASM Normal file
View file

@ -0,0 +1,318 @@
; Program to illustrate high-speed text-drawing operation of
; write mode 3 of the VGA.
; Draws a string of 8x14 characters at arbitrary locations
; without disturbing the background, using VGA's 8x14 ROM font.
; Designed for use with modes 0Dh, 0Eh, 0Fh, 10h, and 12h.
; Runs only on VGAs (in Models 50 & up and IBM Display Adapter
; and 100% compatibles).
; Assembled with MASM
; By Michael Abrash
;
stack segment para stack 'STACK'
db 512 dup(?)
stack ends
;
VGA_VIDEO_SEGMENT equ 0a000h ;VGA display memory segment
SCREEN_WIDTH_IN_BYTES equ 044ah ;offset of BIOS variable
FONT_CHARACTER_SIZE equ 14 ;# bytes in each font char
;
; VGA register equates.
;
SC_INDEX equ 3c4h ;SC index register
SC_MAP_MASK equ 2 ;SC map mask register index
GC_INDEX equ 3ceh ;GC index register
GC_SET_RESET equ 0 ;GC set/reset register index
GC_ENABLE_SET_RESET equ 1 ;GC enable set/reset register index
GC_ROTATE equ 3 ;GC data rotate/logical function
; register index
GC_MODE equ 5 ;GC Mode register
GC_BIT_MASK equ 8 ;GC bit mask register index
;
dseg segment para common 'DATA'
TEST_TEXT_ROW equ 69 ;row to display test text at
TEST_TEXT_COL equ 17 ;column to display test text at
TEST_TEXT_COLOR equ 0fh ;high intensity white
TestString label byte
db 'Hello, world!',0 ;test string to print.
FontPointer dd ? ;font offset
dseg ends
;
cseg segment para public 'CODE'
assume cs:cseg, ds:dseg
start proc near
mov ax,dseg
mov ds,ax
;
; Select 640x480 graphics mode.
;
mov ax,012h
int 10h
;
; Set the screen to all blue, using the readability of VGA registers
; to preserve reserved bits.
;
mov dx,GC_INDEX
mov al,GC_SET_RESET
out dx,al
inc dx
in al,dx
and al,0f0h
or al,1 ;blue plane only set, others reset
out dx,al
dec dx
mov al,GC_ENABLE_SET_RESET
out dx,al
inc dx
in al,dx
and al,0f0h
or al,0fh ;enable set/reset for all planes
out dx,al
mov dx,VGA_VIDEO_SEGMENT
mov es,dx ;point to display memory
mov di,0
mov cx,8000h ;fill all 32k words
mov ax,0ffffh ;because of set/reset, the value
; written actually doesn't matter
rep stosw ;fill with blue
;
; Set driver to use the 8x14 font.
;
mov ah,11h ;VGA BIOS character generator function,
mov al,30h ; return info subfunction
mov bh,2 ;get 8x14 font pointer
int 10h
call SelectFont
;
; Print the test string.
;
mov si,offset TestString
mov bx,TEST_TEXT_ROW
mov cx,TEST_TEXT_COL
mov ah,TEST_TEXT_COLOR
call DrawString
;
; Wait for a key, then set to text mode & end.
;
mov ah,1
int 21h ;wait for a key
mov ax,3
int 10h ;restore text mode
;
; Exit to DOS.
;
mov ah,4ch
int 21h
Start endp
;
; Subroutine to draw a text string left-to-right in a linear
; graphics mode (0Dh, 0Eh, 0Fh, 010h, 012h) with 8-dot-wide
; characters. Background around the pixels that make up the
; characters is preserved.
; Font used should be pointed to by FontPointer.
;
; Input:
; AH = color to draw string in
; BX = row to draw string on
; CX = column to start string at
; DS:SI = string to draw
;
; Forces ALU function to "move".
; Forces write mode 3.
;
DrawString proc near
push ax
push bx
push cx
push dx
push si
push di
push bp
push ds
;
; Set up set/reset to produce character color, using the readability
; of VGA register to preserve the setting of reserved bits 7-4.
;
mov dx,GC_INDEX
mov al,GC_SET_RESET
out dx,al
inc dx
in al,dx
and al,0f0h
and ah,0fh
or al,ah
out dx,al
;
; Select write mode 3, using the readability of VGA registers
; to leave bits other than the write mode bits unchanged.
;
mov dx,GC_INDEX
mov al,GC_MODE
out dx,al
inc dx
in al,dx
or al,3
out dx,al
mov dx,VGA_VIDEO_SEGMENT
mov es,dx ;point to display memory
;
; Calculate screen address of byte character starts in.
;
push ds ;point to BIOS data segment
sub dx,dx
mov ds,dx
mov di,ds:[SCREEN_WIDTH_IN_BYTES] ;retrieve BIOS
; screen width
pop ds
mov ax,bx ;row
mul di ;calculate offset of start of row
push di ;set aside screen width
mov di,cx ;set aside the column
and cl,0111b ;keep only the column in-byte address
shr di,1
shr di,1
shr di,1 ;divide column by 8 to make a byte address
add di,ax ;and point to byte
;
; Set up the GC rotation. In write mode 3, this is the rotation
; of CPU data before it is ANDed with the Bit Mask register to
; form the bit mask. Force the ALU function to "move". Uses the
; readability of VGA registers to leave reserved bits unchanged.
;
mov dx,GC_INDEX
mov al,GC_ROTATE
out dx,al
inc dx
in al,dx
and al,0e0h
or al,cl
out dx,al
;
; Set up BH as bit mask for left half, BL as rotation for right half.
;
mov bx,0ffffh
shr bh,cl
neg cl
add cl,8
shl bl,cl
;
; Draw all characters, left portion first, then right portion in the
; succeeding byte, using the data rotation to position the character
; across the byte boundary and then using write mode 3 to combine the
; character data with the bit mask to allow the set/reset value (the
; character color) through only for the proper portion (where the
; font bits for the character are 1) of the character for each byte.
; Wherever the font bits for the character are 0, the background
; color is preserved.
; Does not check for case where character is byte-aligned and
; no rotation and only one write is required.
;
; Draw the left portion of each character in the string.
;
pop cx ;get back screen width
push si
push di
push bx
;
; Set the bit mask for the left half of the character.
;
mov dx,GC_INDEX
mov al,GC_BIT_MASK
mov ah,bh
out dx,ax
LeftHalfLoop:
lodsb
and al,al
jz LeftHalfLoopDone
call CharacterUp
inc di ;point to next character location
jmp LeftHalfLoop
LeftHalfLoopDone:
pop bx
pop di
pop si
;
; Draw the right portion of each character in the string.
;
inc di ;right portion of each character is across
; byte boundary
;
; Set the bit mask for the right half of the character.
;
mov dx,GC_INDEX
mov al,GC_BIT_MASK
mov ah,bl
out dx,ax
RightHalfLoop:
lodsb
and al,al
jz RightHalfLoopDone
call CharacterUp
inc di ;point to next character location
jmp RightHalfLoop
RightHalfLoopDone:
;
pop ds
pop bp
pop di
pop si
pop dx
pop cx
pop bx
pop ax
ret
DrawString endp
;
; Draw a character.
;
; Input:
; AL = character
; CX = screen width
; ES:DI = address to draw character at
;
CharacterUp proc near
push cx
push si
push di
push ds
;
; Set DS:SI to point to font and ES to point to display memory.
;
lds si,[FontPointer] ;point to font
;
; Calculate font address of character.
;
mov bl,14 ;14 bytes per character
mul bl
add si,ax ;offset in font segment of character
mov bp,FONT_CHARACTER_SIZE
dec cx ; -1 because one byte per char
CharacterLoop:
lodsb ;get character byte
mov ah,es:[di] ;load latches
stosb ;write character byte
;
; Point to next line of character in display memory.
;
add di,cx
;
dec bp
jnz CharacterLoop
;
pop ds
pop di
pop si
pop cx
ret
CharacterUp endp
;
; Set the pointer to the font to draw from to ES:BP.
;
SelectFont proc near
mov word ptr [FontPointer],bp ;save pointer
mov word ptr [FontPointer+2],es
ret
SelectFont endp
;
cseg ends
end start

218
tests/pcx86/vga/L27-1.ASM Normal file
View file

@ -0,0 +1,218 @@
; Program to illustrate one use of write mode 2 of the VGA and EGA by
; animating the image of an "A" drawn by copying it from a chunky
; bit-map in system memory to a planar bit-map in VGA or EGA memory.
;
; Assemble with MASM or TASM
;
; By Michael Abrash
;
Stack segment para stack 'STACK'
db 512 dup(0)
Stack ends
SCREEN_WIDTH_IN_BYTES equ 80
DISPLAY_MEMORY_SEGMENT equ 0a000h
SC_INDEX equ 3c4h ;Sequence Controller Index register
MAP_MASK equ 2 ;index of Map Mask register
GC_INDEX equ 03ceh ;Graphics Controller Index reg
GRAPHICS_MODE equ 5 ;index of Graphics Mode reg
BIT_MASK equ 8 ;index of Bit Mask reg
Data segment para common 'DATA'
;
; Current location of "A" as it is animated across the screen.
;
CurrentX dw ?
CurrentY dw ?
RemainingLength dw ?
;
; Chunky bit-map image of a yellow "A" on a bright blue background
;
AImage label byte
dw 13, 13 ;width, height in pixels
db 000h, 000h, 000h, 000h, 000h, 000h, 000h
db 009h, 099h, 099h, 099h, 099h, 099h, 000h
db 009h, 099h, 099h, 099h, 099h, 099h, 000h
db 009h, 099h, 099h, 0e9h, 099h, 099h, 000h
db 009h, 099h, 09eh, 0eeh, 099h, 099h, 000h
db 009h, 099h, 0eeh, 09eh, 0e9h, 099h, 000h
db 009h, 09eh, 0e9h, 099h, 0eeh, 099h, 000h
db 009h, 09eh, 0eeh, 0eeh, 0eeh, 099h, 000h
db 009h, 09eh, 0e9h, 099h, 0eeh, 099h, 000h
db 009h, 09eh, 0e9h, 099h, 0eeh, 099h, 000h
db 009h, 099h, 099h, 099h, 099h, 099h, 000h
db 009h, 099h, 099h, 099h, 099h, 099h, 000h
db 000h, 000h, 000h, 000h, 000h, 000h, 000h
Data ends
Code segment para public 'CODE'
assume cs:Code, ds:Data
Start proc near
mov ax,Data
mov ds,ax
mov ax,10h
int 10h ;select video mode 10h (640x350)
;
; Prepare for animation.
;
mov [CurrentX],0
mov [CurrentY],200
mov [RemainingLength],600 ;move 600 times
;
; Animate, repeating RemainingLength times. It's unnecessary to erase
; the old image, since the one pixel of blank fringe around the image
; erases the part of the old image not overlapped by the new image.
;
AnimationLoop:
mov bx,[CurrentX]
mov cx,[CurrentY]
mov si,offset AImage
call DrawFromChunkyBitmap ;draw the "A" image
inc [CurrentX] ;move one pixel to the right
mov cx,0 ;delay so we don't move the
DelayLoop: ; image too fast; adjust as
; needed
loop DelayLoop
dec [RemainingLength]
jnz AnimationLoop
;
; Wait for a key before returning to text mode and ending.
;
mov ah,01h
int 21h
mov ax,03h
int 10h
mov ah,4ch
int 21h
Start endp
;
; Draw an image stored in a chunky-bit map into planar VGA/EGA memory
; at the specified location.
;
; Input:
; BX = X screen location at which to draw the upper-left corner
; of the image
; CX = Y screen location at which to draw the upper-left corner
; of the image
; DS:SI = pointer to chunky image to draw, as follows:
; word at 0: width of image, in pixels
; word at 2: height of image, in pixels
; byte at 4: msb/lsb = first & second chunky pixels,
; repeating for the remainder of the scan line
; of the image, then for all scan lines. Images
; with odd widths have an unused null nibble
; padding each scan line out to a byte width
;
; AX, BX, CX, DX, SI, DI, ES destroyed.
;
DrawFromChunkyBitmap proc near
cld
;
; Select write mode 2.
;
mov dx,GC_INDEX
mov al,GRAPHICS_MODE
out dx,al
inc dx
mov al,02h
out dx,al
;
; Enable writes to all 4 planes.
;
mov dx,SC_INDEX
mov al,MAP_MASK
out dx,al
inc dx
mov al,0fh
out dx,al
;
; Point ES:DI to the display memory byte in which the first pixel
; of the image goes, with AH set up as the bit mask to access that
; pixel within the addressed byte.
;
mov ax,SCREEN_WIDTH_IN_BYTES
mul cx ;offset of start of top scan line
mov di,ax
mov cl,bl
and cl,111b
mov ah,80h ;set AH to the bit mask for the
shr ah,cl ; initial pixel
shr bx,1
shr bx,1
shr bx,1 ;X in bytes
add di,bx ;offset of upper-left byte of image
mov bx,DISPLAY_MEMORY_SEGMENT
mov es,bx ;ES:DI points to the byte at which the
; upper left of the image goes
;
; Get the width and height of the image.
;
mov cx,[si] ;get the width
inc si
inc si
mov bx,[si] ;get the height
inc si
inc si
mov dx,GC_INDEX
mov al,BIT_MASK
out dx,al ;leave the GC Index register pointing
inc dx ; to the Bit Mask register
RowLoop:
push ax ;preserve the left column's bit mask
push cx ;preserve the width
push di ;preserve the destination offset
ColumnLoop:
mov al,ah
out dx,al ;set the bit mask to draw this pixel
mov al,es:[di] ;load the latches
mov al,[si] ;get the next two chunky pixels
shr al,1
shr al,1
shr al,1
shr al,1 ;move the first pixel into the lsb
stosb ;draw the first pixel
ror ah,1 ;move mask to next pixel position
jc CheckMorePixels ;is next pixel in the adjacent byte?
dec di ;no
CheckMorePixels:
dec cx ;see if there are any more pixels
jz AdvanceToNextScanLine ; across in image
mov al,ah
out dx,al ;set the bit mask to draw this pixel
mov al,es:[di] ;load the latches
lodsb ;get the same two chunky pixels again
; and advance pointer to the next
; two pixels
stosb ;draw the second of the two pixels
ror ah,1 ;move mask to next pixel position
jc CheckMorePixels2;is next pixel in the adjacent byte?
dec di ;no
CheckMorePixels2:
loop ColumnLoop ;see if there are any more pixels
; across in the image
jmp short CheckMoreScanLines
AdvanceToNextScanLine:
inc si ;advance to the start of the next
; scan line in the image
CheckMoreScanLines:
pop di ;get back the destination offset
pop cx ;get back the width
pop ax ;get back the left column's bit mask
add di,SCREEN_WIDTH_IN_BYTES
;point to the start of the next scan
; line of the image
dec bx ;see if there are any more scan lines
jnz RowLoop ; in the image
ret
DrawFromChunkyBitmap endp
Code ends
end Start

327
tests/pcx86/vga/L27-2.ASM Normal file
View file

@ -0,0 +1,327 @@
; Program to illustrate one use of write mode 2 of the VGA and EGA by
; drawing lines in color patterns.
;
; Assemble with MASM or TASM
;
; By Michael Abrash
;
Stack segment para stack 'STACK'
db 512 dup(0)
Stack ends
SCREEN_WIDTH_IN_BYTES equ 80
GRAPHICS_SEGMENT equ 0a000h ;mode 10 bit-map segment
SC_INDEX equ 3c4h ;Sequence Controller Index register
MAP_MASK equ 2 ;index of Map Mask register
GC_INDEX equ 03ceh ;Graphics Controller Index reg
GRAPHICS_MODE equ 5 ;index of Graphics Mode reg
BIT_MASK equ 8 ;index of Bit Mask reg
Data segment para common 'DATA'
Pattern0 db 16
db 0, 1, 2, 3, 4, 5, 6, 7, 8
db 9, 10, 11, 12, 13, 14, 15
Pattern1 db 6
db 2, 2, 2, 10, 10, 10
Pattern2 db 8
db 15, 15, 15, 0, 0, 15, 0, 0
Pattern3 db 9
db 1, 1, 1, 2, 2, 2, 4, 4, 4
Data ends
Code segment para public 'CODE'
assume cs:Code, ds:Data
Start proc near
mov ax,Data
mov ds,ax
mov ax,10h
int 10h ;select video mode 10h (640x350)
;
; Draw 8 radial lines in upper-left quadrant in pattern 0.
;
mov bx,0
mov cx,0
mov si,offset Pattern0
call QuadrantUp
;
; Draw 8 radial lines in upper-right quadrant in pattern 1.
;
mov bx,320
mov cx,0
mov si,offset Pattern1
call QuadrantUp
;
; Draw 8 radial lines in lower-left quadrant in pattern 2.
;
mov bx,0
mov cx,175
mov si,offset Pattern2
call QuadrantUp
;
; Draw 8 radial lines in lower-right quadrant in pattern 3.
;
mov bx,320
mov cx,175
mov si,offset Pattern3
call QuadrantUp
;
; Wait for a key before returning to text mode and ending.
;
mov ah,01h
int 21h
mov ax,03h
int 10h
mov ah,4ch
int 21h
;
; Draws 8 radial lines with specified pattern in specified mode 10h
; quadrant.
;
; Input:
; BX = X coordinate of upper left corner of quadrant
; CX = Y coordinate of upper left corner of quadrant
; SI = pointer to pattern, in following form:
; Byte 0: Length of pattern
; Byte 1: Start of pattern, one color per byte
;
; AX, BX, CX, DX destroyed
;
QuadrantUp proc near
add bx,160
add cx,87 ;point to the center of the quadrant
mov ax,0
mov dx,160
call LineUp ;draw horizontal line to right edge
mov ax,1
mov dx,88
call LineUp ;draw diagonal line to upper right
mov ax,2
mov dx,88
call LineUp ;draw vertical line to top edge
mov ax,3
mov dx,88
call LineUp ;draw diagonal line to upper left
mov ax,4
mov dx,161
call LineUp ;draw horizontal line to left edge
mov ax,5
mov dx,88
call LineUp ;draw diagonal line to lower left
mov ax,6
mov dx,88
call LineUp ;draw vertical line to bottom edge
mov ax,7
mov dx,88
call LineUp ;draw diagonal line to bottom right
ret
QuadrantUp endp
;
; Draws a horizontal, vertical, or diagonal line (one of the eight
; possible radial lines) of the specified length from the specified
; starting point.
;
; Input:
; AX = line direction, as follows:
; 3 2 1
; 4 * 0
; 5 6 7
; BX = X coordinate of starting point
; CX = Y coordinate of starting point
; DX = length of line (number of pixels drawn)
;
; All registers preserved.
;
; Table of vectors to routines for each of the 8 possible lines.
;
LineUpVectors label word
dw LineUp0, LineUp1, LineUp2, LineUp3
dw LineUp4, LineUp5, LineUp6, LineUp7
;
; Macro to draw horizontal, vertical, or diagonal line.
;
; Input:
; XParm = 1 to draw right, -1 to draw left, 0 to not move horz.
; YParm = 1 to draw up, -1 to draw down, 0 to not move vert.
; BX = X start location
; CX = Y start location
; DX = number of pixels to draw
; DS:SI = line pattern
;
MLineUp macro XParm, YParm
local LineUpLoop, CheckMoreLine
mov di,si ;set aside start offset of pattern
lodsb ;get length of pattern
mov ah,al
LineUpLoop:
lodsb ;get color of this pixel...
call DotUpInColor ;...and draw it
if XParm EQ 1
inc bx
endif
if XParm EQ -1
dec bx
endif
if YParm EQ 1
inc cx
endif
if YParm EQ -1
dec cx
endif
dec ah ;at end of pattern?
jnz CheckMoreLine
mov si,di ;get back start of pattern
lodsb
mov ah,al ;reset pattern count
CheckMoreLine:
dec dx
jnz LineUpLoop
jmp LineUpEnd
endm
LineUp proc near
push ax
push bx
push cx
push dx
push si
push di
push es
mov di,ax
mov ax,GRAPHICS_SEGMENT
mov es,ax
push dx ;save line length
;
; Enable writes to all planes.
;
mov dx,SC_INDEX
mov al,MAP_MASK
out dx,al
inc dx
mov al,0fh
out dx,al
;
; Select write mode 2.
;
mov dx,GC_INDEX
mov al,GRAPHICS_MODE
out dx,al
inc dx
mov al,02h
out dx,al
;
; Vector to proper routine.
;
pop dx ;get back line length
shl di,1
jmp cs:[LineUpVectors+di]
;
; Horizontal line to right.
;
LineUp0:
MLineUp 1, 0
;
; Diagonal line to upper right.
;
LineUp1:
MLineUp 1, -1
;
; Vertical line to top.
;
LineUp2:
MLineUp 0, -1
;
; Diagonal line to upper left.
;
LineUp3:
MLineUp -1, -1
;
; Horizontal line to left.
;
LineUp4:
MLineUp -1, 0
;
; Diagonal line to bottom left.
;
LineUp5:
MLineUp -1, 1
;
; Vertical line to bottom.
;
LineUp6:
MLineUp 0, 1
;
; Diagonal line to bottom right.
;
LineUp7:
MLineUp 1, 1
LineUpEnd:
pop es
pop di
pop si
pop dx
pop cx
pop bx
pop ax
ret
LineUp endp
;
; Draws a dot in the specified color at the specified location.
; Assumes that the VGA is in write mode 2 with writes to all planes
; enabled and that ES points to display memory.
;
; Input:
; AL = dot color
; BX = X coordinate of dot
; CX = Y coordinate of dot
; ES = display memory segment
;
; All registers preserved.
;
DotUpInColor proc near
push bx
push cx
push dx
push di
;
; Point ES:DI to the display memory byte in which the pixel goes, with
; the bit mask set up to access that pixel within the addressed byte.
;
push ax ;preserve dot color
mov ax,SCREEN_WIDTH_IN_BYTES
mul cx ;offset of start of top scan line
mov di,ax
mov cl,bl
and cl,111b
mov dx,GC_INDEX
mov al,BIT_MASK
out dx,al
inc dx
mov al,80h
shr al,cl
out dx,al ;set the bit mask for the pixel
shr bx,1
shr bx,1
shr bx,1 ;X in bytes
add di,bx ;offset of byte pixel is in
mov al,es:[di] ;load latches
pop ax ;get back dot color
stosb ;write dot in desired color
pop di
pop dx
pop cx
pop bx
ret
DotUpInColor endp
Start endp
Code ends
end Start

192
tests/pcx86/vga/L27-3.ASM Normal file
View file

@ -0,0 +1,192 @@
; Program to illustrate flipping from bit-mapped graphics mode to
; text mode and back without losing any of the graphics bit-map.
;
; Assemble with MASM or TASM
;
; By Michael Abrash
;
Stack segment para stack 'STACK'
db 512 dup(0)
Stack ends
GRAPHICS_SEGMENT equ 0a000h ;mode 10 bit-map segment
TEXT_SEGMENT equ 0b800h ;mode 3 bit-map segment
SC_INDEX equ 3c4h ;Sequence Controller Index register
MAP_MASK equ 2 ;index of Map Mask register
GC_INDEX equ 3ceh ;Graphics Controller Index register
READ_MAP equ 4 ;index of Read Map register
Data segment para common 'DATA'
GStrikeAnyKeyMsg0 label byte
db 0dh, 0ah, 'Graphics mode', 0dh, 0ah
db 'Strike any key to continue...', 0dh, 0ah, '$'
GStrikeAnyKeyMsg1 label byte
db 0dh, 0ah, 'Graphics mode again', 0dh, 0ah
db 'Strike any key to continue...', 0dh, 0ah, '$'
TStrikeAnyKeyMsg label byte
db 0dh, 0ah, 'Text mode', 0dh, 0ah
db 'Strike any key to continue...', 0dh, 0ah, '$'
Plane2Save db 2000h dup (?) ;save area for plane 2 data
; where font gets loaded
CharAttSave db 4000 dup (?) ;save area for memory wiped
; out by character/attribute
; data in text mode
Data ends
Code segment para public 'CODE'
assume cs:Code, ds:Data
Start proc near
mov ax,10h
int 10h ;select video mode 10h (640x350)
;
; Fill the graphics bit-map with a colored pattern.
;
cld
mov ax,GRAPHICS_SEGMENT
mov es,ax
mov ah,3 ;initial fill pattern
mov cx,4 ;four planes to fill
mov dx,SC_INDEX
mov al,MAP_MASK
out dx,al ;leave the SC Index pointing to the
inc dx ; Map Mask register
FillBitMap:
mov al,10h
shr al,cl ;generate map mask for this plane
out dx,al ;set map mask for this plane
sub di,di ;start at offset 0
mov al,ah ;get the fill pattern
push cx ;preserve plane count
mov cx,8000h ;fill 32K words
rep stosw ;do fill for this plane
pop cx ;get back plane count
shl ah,1
shl ah,1
loop FillBitMap
;
; Put up "strike any key" message.
;
mov ax,Data
mov ds,ax
mov dx,offset GStrikeAnyKeyMsg0
mov ah,9
int 21h
;
; Wait for a key.
;
mov ah,01h
int 21h
;
; Save the 8K of plane 2 that will be used by the font.
;
mov dx,GC_INDEX
mov al,READ_MAP
out dx,al
inc dx
mov al,2
out dx,al ;set up to read from plane 2
mov ax,Data
mov es,ax
mov ax,GRAPHICS_SEGMENT
mov ds,ax
sub si,si
mov di,offset Plane2Save
mov cx,2000h/2 ;save 8K (length of default font)
rep movsw
;
; Go to text mode without clearing display memory.
;
mov ax,083h
int 10h
;
; Save the text mode bit-map.
;
mov ax,Data
mov es,ax
mov ax,TEXT_SEGMENT
mov ds,ax
sub si,si
mov di,offset CharAttSave
mov cx,4000/2 ;length of one text screen in words
rep movsw
;
; Fill the text mode screen with dots and put up "strike any key"
; message.
;
mov ax,TEXT_SEGMENT
mov es,ax
sub di,di
mov al,'.' ;fill character
mov ah,7 ;fill attribute
mov cx,4000/2 ;length of one text screen in words
rep stosw
mov ax,Data
mov ds,ax
mov dx,offset TStrikeAnyKeyMsg
mov ah,9
int 21h
;
; Wait for a key.
;
mov ah,01h
int 21h
;
; Restore the text mode screen to the state it was in on entering
; text mode.
;
mov ax,Data
mov ds,ax
mov ax,TEXT_SEGMENT
mov es,ax
mov si,offset CharAttSave
sub di,di
mov cx,4000/2 ;length of one text screen in words
rep movsw
;
; Return to mode 10h without clearing display memory.
;
mov ax,90h
int 10h
;
; Restore the portion of plane 2 that was wiped out by the font.
;
mov dx,SC_INDEX
mov al,MAP_MASK
out dx,al
inc dx
mov al,4
out dx,al ;set up to write to plane 2
mov ax,Data
mov ds,ax
mov ax,GRAPHICS_SEGMENT
mov es,ax
mov si,offset Plane2Save
sub di,di
mov cx,2000h/2 ;restore 8K (length of default font)
rep movsw
;
; Put up "strike any key" message.
;
mov ax,Data
mov ds,ax
mov dx,offset GStrikeAnyKeyMsg1
mov ah,9
int 21h
;
; Wait for a key before returning to text mode and ending.
;
mov ah,01h
int 21h
mov ax,03h
int 10h
mov ah,4ch
int 21h
Start endp
Code ends
end Start

235
tests/pcx86/vga/L28-1.ASM Normal file
View file

@ -0,0 +1,235 @@
; Program to illustrate the use of the Read Map register in read mode 0.
; Animates by copying a 16-color image from VGA memory to system memory,
; one plane at a time, then copying the image back to a new location
; in VGA memory.
;
; By Michael Abrash
;
stack segment word stack 'STACK'
db 512 dup (?)
stack ends
;
data segment word 'DATA'
IMAGE_WIDTH EQU 4 ;in bytes
IMAGE_HEIGHT EQU 32 ;in pixels
LEFT_BOUND EQU 10 ;in bytes
RIGHT_BOUND EQU 66 ;in bytes
VGA_SEGMENT EQU 0a000h
SCREEN_WIDTH EQU 80 ;in bytes
SC_INDEX EQU 3c4h ;Sequence Controller Index register
GC_INDEX EQU 3ceh ;Graphics Controller Index register
MAP_MASK EQU 2 ;Map Mask register index in SC
READ_MAP EQU 4 ;Read Map register index in GC
;
; Base pattern for 16-color image.
;
PatternPlane0 label byte
db 32 dup (0ffh,0ffh,0,0)
PatternPlane1 label byte
db 32 dup (0ffh,0,0ffh,0)
PatternPlane2 label byte
db 32 dup (0f0h,0f0h,0f0h,0f0h)
PatternPlane3 label byte
db 32 dup (0cch,0cch,0cch,0cch)
;
; Temporary storage for 16-color image during animation.
;
ImagePlane0 db 32*4 dup (?)
ImagePlane1 db 32*4 dup (?)
ImagePlane2 db 32*4 dup (?)
ImagePlane3 db 32*4 dup (?)
;
; Current image location & direction.
;
ImageX dw 40 ;in bytes
ImageY dw 100 ;in pixels
ImageXDirection dw 1 ;in bytes
data ends
;
code segment word 'CODE'
assume cs:code,ds:data
Start proc near
cld
mov ax,data
mov ds,ax
;
; Select graphics mode 10h.
;
mov ax,10h
int 10h
;
; Draw the initial image.
;
mov si,offset PatternPlane0
call DrawImage
;
; Loop to animate by copying the image from VGA memory to system memory,
; erasing the image, and copying the image from system memory to a new
; location in VGA memory. Ends when a key is hit.
;
AnimateLoop:
;
; Copy the image from VGA memory to system memory.
;
mov di,offset ImagePlane0
call GetImage
;
; Clear the image from VGA memory.
;
call EraseImage
;
; Advance the image X coordinate, reversing direction if either edge
; of the screen has been reached.
;
mov ax,[ImageX]
cmp ax,LEFT_BOUND
jz ReverseDirection
cmp ax,RIGHT_BOUND
jnz SetNewX
ReverseDirection:
neg [ImageXDirection]
SetNewX:
add ax,[ImageXDirection]
mov [ImageX],ax
;
; Draw the image by copying it from system memory to VGA memory.
;
mov si,offset ImagePlane0
call DrawImage
;
; Slow things down a bit for visibility (adjust as needed).
;
mov cx,0
DelayLoop:
loop DelayLoop
;
; See if a key has been hit, ending the program.
;
mov ah,1
int 16h
jz AnimateLoop
;
; Clear the key, return to text mode, and return to DOS.
;
sub ah,ah
int 16h
mov ax,3
int 10h
mov ah,4ch
int 21h
Start endp
;
; Draws the image at offset DS:SI to the current image location in
; VGA memory.
;
DrawImage proc near
mov ax,VGA_SEGMENT
mov es,ax
call GetImageOffset ;ES:DI is the destination address for the
; image in VGA memory
mov dx,SC_INDEX
mov al,1 ;do plane 0 first
DrawImagePlaneLoop:
push di ;image is drawn at the same offset in
; each plane
push ax ;preserve plane select
mov al,MAP_MASK ;Map Mask index
out dx,al ;point SC Index to the Map Mask register
pop ax ;get back plane select
inc dx ;point to SC index register
out dx,al ;set up the Map Mask to allow writes to
; the plane of interest
dec dx ;point back to SC Data register
mov bx,IMAGE_HEIGHT ;# of scan lines in image
DrawImageLoop:
mov cx,IMAGE_WIDTH ;# of bytes across image
rep movsb
add di,SCREEN_WIDTH-IMAGE_WIDTH
;point to next scan line of image
dec bx ;any more scan lines?
jnz DrawImageLoop
pop di ;get back image start offset in VGA memory
shl al,1 ;Map Mask setting for next plane
cmp al,10h ;have we done all four planes?
jnz DrawImagePlaneLoop
ret
DrawImage endp
;
; Copies the image from its current location in VGA memory into the
; buffer at DS:DI.
;
GetImage proc near
mov si,di ;move destination offset into SI
call GetImageOffset ;DI is offset of image in VGA memory
xchg si,di ;SI is offset of image, DI is destination offset
push ds
pop es ;ES:DI is destination
mov ax,VGA_SEGMENT
mov ds,ax ;DS:SI is source
;
mov dx,GC_INDEX
sub al,al ;do plane 0 first
GetImagePlaneLoop:
push si ;image comes from same offset in each plane
push ax ;preserve plane select
mov al,READ_MAP ;Read Map index
out dx,al ;point GC Index to Read Map register
pop ax ;get back plane select
inc dx ;point to GC Index register
out dx,al ;set up the Read Map to select reads from
; the plane of interest
dec dx ;point back to GC data register
mov bx,IMAGE_HEIGHT ;# of scan lines in image
GetImageLoop:
mov cx,IMAGE_WIDTH ;# of bytes across image
rep movsb
add si,SCREEN_WIDTH-IMAGE_WIDTH
;point to next scan line of image
dec bx ;any more scan lines?
jnz GetImageLoop
pop si ;get back image start offset
inc al ;Read Map setting for next plane
cmp al,4 ;have we done all four planes?
jnz GetImagePlaneLoop
push es
pop ds ;restore original DS
ret
GetImage endp
;
; Erases the image at its current location.
;
EraseImage proc near
mov dx,SC_INDEX
mov al,MAP_MASK
out dx,al ;point SC Index to the Map Mask register
inc dx ;point to SC Data register
mov al,0fh
out dx,al ;set up the Map Mask to allow writes to go to
; all 4 planes
mov ax,VGA_SEGMENT
mov es,ax
call GetImageOffset ;ES:DI points to the start address
; of the image
sub al,al ;erase with zeros
mov bx,IMAGE_HEIGHT ;# of scan lines in image
EraseImageLoop:
mov cx,IMAGE_WIDTH ;# of bytes across image
rep stosb
add di,SCREEN_WIDTH-IMAGE_WIDTH
;point to next scan line of image
dec bx ;any more scan lines?
jnz EraseImageLoop
ret
EraseImage endp
;
; Returns the current offset of the image in the VGA segment in DI.
;
GetImageOffset proc near
mov ax,SCREEN_WIDTH
mul [ImageY]
add ax,[ImageX]
mov di,ax
ret
GetImageOffset endp
code ends
end Start

161
tests/pcx86/vga/L28-2.ASM Normal file
View file

@ -0,0 +1,161 @@
; Program to illustrate use of read mode 1 (color compare mode)
; to detect collisions in display memory. Draws a yellow line on a
; blue background, then draws a perpendicular green line until the
; yellow line is reached.
;
; By Michael Abrash
;
stack segment word stack 'STACK'
db 512 dup (?)
stack ends
;
VGA_SEGMENT EQU 0a000h
SCREEN_WIDTH EQU 80 ;in bytes
GC_INDEX EQU 3ceh ;Graphics Controller Index register
SET_RESET EQU 0 ;Set/Reset register index in GC
ENABLE_SET_RESET EQU 1 ;Enable Set/Reset register index in GC
COLOR_COMPARE EQU 2 ;Color Compare register index in GC
GRAPHICS_MODE EQU 5 ;Graphics Mode register index in GC
BIT_MASK EQU 8 ;Bit Mask register index in GC
;
code segment word 'CODE'
assume cs:code
Start proc near
cld
;
; Select graphics mode 10h.
;
mov ax,10h
int 10h
;
; Fill the screen with blue.
;
mov al,1 ;blue is color 1
call SelectSetResetColor ;set to draw in blue
mov ax,VGA_SEGMENT
mov es,ax
sub di,di
mov cx,7000h
rep stosb ;the value written actually doesn't
; matter, since set/reset is providing
; the data written to display memory
;
; Draw a vertical yellow line.
;
mov al,14 ;yellow is color 14
call SelectSetResetColor ;set to draw in yellow
mov dx,GC_INDEX
mov al,BIT_MASK
out dx,al ;point GC Index to Bit Mask
inc dx ;point to GC Data
mov al,10h
out dx,al ;set Bit Mask to 10h
mov di,40 ;start in the middle of the top line
mov cx,350 ;do full height of screen
VLineLoop:
mov al,es:[di] ;load the latches
stosb ;write next pixel of yellow line (set/reset
; provides the data written to display
; memory, and AL is actually ignored)
add di,SCREEN_WIDTH-1 ;point to the next scan line
loop VLineLoop
;
; Select write mode 0 and read mode 1.
;
mov dx,GC_INDEX
mov al,GRAPHICS_MODE
out dx,al ;point GC Index to Graphics Mode register
inc dx ;point to GC Data
mov al,00001000b ;bit 3=1 is read mode 1, bits 1 & 0=00
; is write mode 0
out dx,al ;set Graphics Mode to read mode 1,
; write mode 0
;
; Draw a horizontal green line, one pixel at a time, from left
; to right until color compare reports a yellow pixel is encountered.
;
; Draw in green.
;
mov al,2 ;green is color 2
call SelectSetResetColor ;set to draw in green
;
; Set color compare to look for yellow.
;
mov dx,GC_INDEX
mov al,COLOR_COMPARE
out dx,al ;point GC Index to Color Compare register
inc dx ;point to GC Data
mov al,14 ;we're looking for yellow, color 14
out dx,al ;set color compare to look for yellow
dec dx ;point to GC Index
;
; Set up for quick access to Bit Mask register.
;
mov al,BIT_MASK
out dx,al ;point GC Index to Bit Mask register
inc dx ;point to GC Data
;
; Set initial pixel mask and display memory offset.
;
mov al,80h ;initial pixel mask
mov di,100*SCREEN_WIDTH
;start at left edge of scan line 100
HLineLoop:
mov ah,es:[di] ;do a read mode 1 (color compare) read.
; This also loads the latches.
and ah,al ;is the pixel of current interest yellow?
jnz WaitKeyAndDone ;yes-we've reached the yellow line, so we're
; done
out dx,al ;set the Bit Mask register so that we
; modify only the pixel of interest
mov es:[di],al ;draw the pixel. The value written is
; irrelevant, since set/reset is providing
; the data written to display memory
ror al,1 ;shift pixel mask to the next pixel
adc di,0 ;advance the display memory offset if
; the pixel mask wrapped
;
; Slow things down a bit for visibility (adjust as needed).
;
mov cx,0
DelayLoop:
loop DelayLoop
jmp HLineLoop
;
; Wait for a key to be pressed to end, then return to text mode and
; return to DOS.
;
WaitKeyAndDone:
WaitKeyLoop:
mov ah,1
int 16h
jz WaitKeyLoop
sub ah,ah
int 16h ;clear the key
mov ax,3
int 10h ;return to text mode
mov ah,4ch
int 21h ;done
Start endp
;
; Enables set/reset for all planes, and sets the set/reset color
; to AL.
;
SelectSetResetColor proc near
mov dx,GC_INDEX
push ax ;preserve color
mov al,SET_RESET
out dx,al ;point GC Index to Set/Reset register
inc dx ;point to GC Data
pop ax ;get back color
out dx,al ;set Set/Reset register to selected color
dec dx ;point to GC Index
mov al,ENABLE_SET_RESET
out dx,al ;point GC Index to Enable Set/Reset register
inc dx ;point to GC Data
mov al,0fh
out dx,al ;enable set/reset for all planes
ret
SelectSetResetColor endp
code ends
end Start

103
tests/pcx86/vga/L28-3.ASM Normal file
View file

@ -0,0 +1,103 @@
; Program that draws a diagonal line to illustrate the use of a
; Color Don't Care register setting of 0FFH to support fast
; read-modify-write operations to VGA memory in write mode 3 by
; drawing a diagonal line.
;
; Note: Works on VGAs only.
;
; By Michael Abrash
;
stack segment word stack 'STACK'
db 512 dup (?)
stack ends
;
VGA_SEGMENT EQU 0a000h
SCREEN_WIDTH EQU 80 ;in bytes
GC_INDEX EQU 3ceh ;Graphics Controller Index register
SET_RESET EQU 0 ;Set/Reset register index in GC
ENABLE_SET_RESET EQU 1 ;Enable Set/Reset register index in GC
GRAPHICS_MODE EQU 5 ;Graphics Mode register index in GC
COLOR_DONT_CARE EQU 7 ;Color Don't Care register index in GC
;
code segment word 'CODE'
assume cs:code
Start proc near
;
; Select graphics mode 12h.
;
mov ax,12h
int 10h
;
; Select write mode 3 and read mode 1.
;
mov dx,GC_INDEX
mov al,GRAPHICS_MODE
out dx,al
inc dx
in al,dx ;VGA registers are readable, bless them!
or al,00001011b ;bit 3=1 selects read mode 1, and
; bits 1 & 0=11 selects write mode 3
jmp $+2 ;delay between IN and OUT to same port
out dx,al
dec dx
;
; Set up set/reset to always draw in white.
;
mov al,SET_RESET
out dx,al
inc dx
mov al,0fh
out dx,al
dec dx
mov al,ENABLE_SET_RESET
out dx,al
inc dx
mov al,0fh
out dx,al
dec dx
;
; Set Color Don't Care to 0, so reads of VGA memory always return 0FFH.
;
mov al,COLOR_DONT_CARE
out dx,al
inc dx
sub al,al
out dx,al
;
; Set up the initial memory pointer and pixel mask.
;
mov ax,VGA_SEGMENT
mov ds,ax
sub bx,bx
mov al,80h
;
; Draw 400 points on a diagonal line sloping down and to the right.
;
mov cx,400
DrawDiagonalLoop:
and [bx],al ;reads display memory, loading the latches,
; then writes AL to the VGA. AL becomes the
; bit mask, and set/reset provides the
; actual data written
add bx,SCREEN_WIDTH
; point to the next scan line
ror al,1 ;move the pixel mask one pixel to the right
adc bx,0 ;advance to the next byte if the pixel mask wrapped
loop DrawDiagonalLoop
;
; Wait for a key to be pressed to end, then return to text mode and
; return to DOS.
;
WaitKeyLoop:
mov ah,1
int 16h
jz WaitKeyLoop
sub ah,ah
int 16h ;clear the key
mov ax,3
int 10h ;return to text mode
mov ah,4ch
int 21h ;done
Start endp
code ends
end Start

128
tests/pcx86/vga/L29-1.ASM Normal file
View file

@ -0,0 +1,128 @@
; Program to put up a mode 10h EGA graphics screen, then save it
; to the file SNAPSHOT.SCR.
;
VGA_SEGMENT equ 0a000h
GC_INDEX equ 3ceh ;Graphics Controller Index register
READ_MAP equ 4 ;Read Map register index in GC
DISPLAYED_SCREEN_SIZE equ (640/8)*350 ;# of displayed bytes per plane in a
; hi-res graphics screen
;
stack segment para stack 'STACK'
db 512 dup (?)
stack ends
;
Data segment word 'DATA'
SampleText db 'This is bit-mapped text, drawn in hi-res '
db 'EGA graphics mode 10h.', 0dh, 0ah, 0ah
db 'Saving the screen (including this text)...'
db 0dh, 0ah, '$'
Filename db 'SNAPSHOT.SCR',0;name of file we're saving to
ErrMsg1 db "*** Couldn't open SNAPSHOT.SCR ***",0dh,0ah,'$'
ErrMsg2 db '*** Error writing to SNAPSHOT.SCR ***',0dh,0ah,'$'
WaitKeyMsg db 0dh, 0ah, 'Done. Press any key to end...',0dh,0ah,'$'
Handle dw ? ;handle of file we're saving to
Plane db ? ;plane being read
Data ends
;
Code segment
assume cs:Code, ds:Data
Start proc near
mov ax,Data
mov ds,ax
;
; Go to hi-res graphics mode.
;
mov ax,10h ;AH = 0 means mode set, AL = 10h selects
; hi-res graphics mode
int 10h ;BIOS video interrupt
;
; Put up some text, so the screen isn't empty.
;
mov ah,9 ;DOS print string function
mov dx,offset SampleText
int 21h
;
; Delete SNAPSHOT.SCR if it exists.
;
mov ah,41h ;DOS unlink file function
mov dx,offset Filename
int 21h
;
; Create the file SNAPSHOT.SCR.
;
mov ah,3ch ;DOS create file function
mov dx,offset Filename
sub cx,cx ;make it a normal file
int 21h
mov [Handle],ax ;save the handle
jnc SaveTheScreen ;we're ready to save if no error
mov ah,9 ;DOS print string function
mov dx,offset ErrMsg1
int 21h ;notify of the error
jmp short Done ;and done
;
; Loop through the 4 planes, making each readable in turn and
; writing it to disk. Note that all 4 planes are readable at
; A000:0000; the Read Map register selects which plane is readable
; at any one time.
;
SaveTheScreen:
mov [Plane],0 ;start with plane 0
SaveLoop:
mov dx,GC_INDEX
mov al,READ_MAP ;set GC Index to Read Map register
out dx,al
inc dx
mov al,[Plane] ;get the # of the plane we want
; to save
out dx,al ;set to read from the desired plane
mov ah,40h ;DOS write to file function
mov bx,[Handle]
mov cx,DISPLAYED_SCREEN_SIZE;# of bytes to save
sub dx,dx ;write all displayed bytes at A000:0000
push ds
mov si,VGA_SEGMENT
mov ds,si
int 21h ;write the displayed portion of this plane
pop ds
cmp ax,DISPLAYED_SCREEN_SIZE;did all bytes get written?
jz SaveLoopBottom
mov ah,9 ;DOS print string function
mov dx,offset ErrMsg2
int 21h ;notify about the error
jmp short DoClose ;and done
SaveLoopBottom:
mov al,[Plane]
inc ax ;point to the next plane
mov [Plane],al
cmp al,3 ;have we done all planes?
jbe SaveLoop ;no, so do the next plane
;
; Close SNAPSHOT.SCR.
;
DoClose:
mov ah,3eh ;DOS close file function
mov bx,[Handle]
int 21h
;
; Wait for a keypress.
;
mov ah,9 ;DOS print string function
mov dx,offset WaitKeyMsg
int 21h ;prompt
mov ah,8 ;DOS input without echo function
int 21h
;
; Restore text mode.
;
mov ax,3
int 10h
;
; Done.
;
Done:
mov ah,4ch ;DOS terminate function
int 21h
Start endp
Code ends
end Start

114
tests/pcx86/vga/L29-2.ASM Normal file
View file

@ -0,0 +1,114 @@
; Program to restore a mode 10h EGA graphics screen from
; the file SNAPSHOT.SCR.
;
VGA_SEGMENT equ 0a000h
SC_INDEX equ 3c4h ;Sequence Controller Index register
MAP_MASK equ 2 ;Map Mask register index in SC
DISPLAYED_SCREEN_SIZE equ (640/8)*350 ;# of displayed bytes per plane in a
; hi-res graphics screen
;
stack segment para stack 'STACK'
db 512 dup (?)
stack ends
;
Data segment word 'DATA'
Filename db 'SNAPSHOT.SCR',0;name of file we're restoring from
ErrMsg1 db "*** Couldn't open SNAPSHOT.SCR ***",0dh,0ah,'$'
ErrMsg2 db '*** Error reading from SNAPSHOT.SCR ***',0dh,0ah,'$'
WaitKeyMsg db 0dh, 0ah, 'Done. Press any key to end...',0dh,0ah,'$'
Handle dw ? ;handle of file we're restoring from
Plane db ? ;plane being written
Data ends
;
Code segment
assume cs:Code, ds:Data
Start proc near
mov ax,Data
mov ds,ax
;
; Go to hi-res graphics mode.
;
mov ax,10h ;AH = 0 means mode set, AL = 10h selects
; hi-res graphics mode
int 10h ;BIOS video interrupt
;
; Open SNAPSHOT.SCR.
;
mov ah,3dh ;DOS open file function
mov dx,offset Filename
sub al,al ;open for reading
int 21h
mov [Handle],ax ;save the handle
jnc RestoreTheScreen ;we're ready to restore if no error
mov ah,9 ;DOS print string function
mov dx,offset ErrMsg1
int 21h ;notify of the error
jmp short Done ;and done
;
; Loop through the 4 planes, making each writable in turn and
; reading it from disk. Note that all 4 planes are writable at
; A000:0000; the Map Mask register selects which planes are readable
; at any one time. We only make one plane readable at a time.
;
RestoreTheScreen:
mov [Plane],0 ;start with plane 0
RestoreLoop:
mov dx,SC_INDEX
mov al,MAP_MASK ;set SC Index to Map Mask register
out dx,al
inc dx
mov cl,[Plane] ;get the # of the plane we want
; to restore
mov al,1
shl al,cl ;set the bit enabling writes to
; only the one desired plane
out dx,al ;set to read from desired plane
mov ah,3fh ;DOS read from file function
mov bx,[Handle]
mov cx,DISPLAYED_SCREEN_SIZE;# of bytes to read
sub dx,dx ;start loading bytes at A000:0000
push ds
mov si,VGA_SEGMENT
mov ds,si
int 21h ;read the displayed portion of this plane
pop ds
jc ReadError
cmp ax,DISPLAYED_SCREEN_SIZE;did all bytes get read?
jz RestoreLoopBottom
ReadError:
mov ah,9 ;DOS print string function
mov dx,offset ErrMsg2
int 21h ;notify about the error
jmp short DoClose ;and done
RestoreLoopBottom:
mov al,[Plane]
inc ax ;point to the next plane
mov [Plane],al
cmp al,3 ;have we done all planes?
jbe RestoreLoop ;no, so do the next plane
;
; Close SNAPSHOT.SCR.
;
DoClose:
mov ah,3eh ;DOS close file function
mov bx,[Handle]
int 21h
;
; Wait for a keypress.
;
mov ah,8 ;DOS input without echo function
int 21h
;
; Restore text mode.
;
mov ax,3
int 10h
;
; Done.
;
Done:
mov ah,4ch ;DOS terminate function
int 21h
Start endp
Code ends
end Start

251
tests/pcx86/vga/L29-3.ASM Normal file
View file

@ -0,0 +1,251 @@
; Program to illustrate the color mapping capabilities of the
; EGA's palette registers.
;
VGA_SEGMENT equ 0a000h
SC_INDEX equ 3c4h ;Sequence Controller Index register
MAP_MASK equ 2 ;Map Mask register index in SC
BAR_HEIGHT equ 14 ;height of each bar
TOP_BAR equ BAR_HEIGHT*6 ;start the bars down a bit to
; leave room for text
;
stack segment para stack 'STACK'
db 512 dup (?)
stack ends
;
Data segment word 'DATA'
KeyMsg db 'Press any key to see the next color set. '
db 'There are 64 color sets in all.'
db 0dh, 0ah, 0ah, 0ah, 0ah
db 13 dup (' '), 'Attribute'
db 38 dup (' '), 'Color$'
;
; Used to label the attributes of the color bars.
;
AttributeNumbers label byte
x= 0
rept 16
if x lt 10
db '0', x+'0', 'h', 0ah, 8, 8, 8
else
db '0', x+'A'-10, 'h', 0ah, 8, 8, 8
endif
x= x+1
endm
db '$'
;
; Used to label the colors of the color bars. (Color values are
; filled in on the fly.)
;
ColorNumbers label byte
rept 16
db '000h', 0ah, 8, 8, 8, 8
endm
COLOR_ENTRY_LENGTH equ ($-ColorNumbers)/16
db '$'
;
CurrentColor db ?
;
; Space for the array of 16 colors we'll pass to the BIOS, plus
; an overscan setting of black.
;
ColorTable db 16 dup (?), 0
Data ends
;
Code segment
assume cs:Code, ds:Data
Start proc near
cld
mov ax,Data
mov ds,ax
;
; Go to hi-res graphics mode.
;
mov ax,10h ;AH = 0 means mode set, AL = 10h selects
; hi-res graphics mode
int 10h ;BIOS video interrupt
;
; Put up relevant text.
;
mov ah,9 ;DOS print string function
mov dx,offset KeyMsg
int 21h
;
; Put up the color bars, one in each of the 16 possible pixel values
; (which we'll call attributes).
;
mov cx,16 ;we'll put up 16 color bars
sub al,al ;start with attribute 0
BarLoop:
push ax
push cx
call BarUp
pop cx
pop ax
inc ax ;select the next attribute
loop BarLoop
;
; Put up the attribute labels.
;
mov ah,2 ;video interrupt set cursor position function
sub bh,bh ;page 0
mov dh,TOP_BAR/14 ;counting in character rows, match to
; top of first bar, counting in
; scan lines
mov dl,16 ;just to left of bars
int 10h
mov ah,9 ;DOS print string function
mov dx,offset AttributeNumbers
int 21h
;
; Loop through the color set, one new setting per keypress.
;
mov [CurrentColor],0 ;start with color zero
ColorLoop:
;
; Set the palette registers to the current color set, consisting
; of the current color mapped to attribute 0, current color + 1
; mapped to attribute 1, and so on.
;
mov al,[CurrentColor]
mov bx,offset ColorTable
mov cx,16 ;we have 16 colors to set
PaletteSetLoop:
and al,3fh ;limit to 6-bit color values
mov [bx],al ;built the 16-color table used for setting
inc bx ; the palette registers
inc ax
loop PaletteSetLoop
mov ah,10h ;video interrupt palette function
mov al,2 ;subfunction to set all 16 palette registers
; and overscan at once
mov dx,offset ColorTable
push ds
pop es ;ES:DX points to the color table
int 10h ;invoke the video interrupt to set the palette
;
; Put up the color numbers, so we can see how attributes map
; to color values, and so we can see how each color # looks
; (at least on this particular screen).
;
call ColorNumbersUp
;
; Wait for a keypress, so they can see this color set.
;
WaitKey:
mov ah,8 ;DOS input without echo function
int 21h
;
; Advance to the next color set.
;
mov al,[CurrentColor]
inc ax
mov [CurrentColor],al
cmp al,64
jbe ColorLoop
;
; Restore text mode.
;
mov ax,3
int 10h
;
; Done.
;
Done:
mov ah,4ch ;DOS terminate function
int 21h
;
; Puts up a bar consisting of the specified attribute (pixel value),
; at a vertical position corresponding to the attribute.
;
; Input: AL = attribute
;
BarUp proc near
mov dx,SC_INDEX
mov ah,al
mov al,MAP_MASK
out dx,al
inc dx
mov al,ah
out dx,al ;set the Map Mask register to produce
; the desired color
mov ah,BAR_HEIGHT
mul ah ;row of top of bar
add ax,TOP_BAR ;start a few lines down to leave room for
; text
mov dx,80 ;rows are 80 bytes long
mul dx ;offset in bytes of start of scan line bar
; starts on
add ax,20 ;offset in bytes of upper left corner of bar
mov di,ax
mov ax,VGA_SEGMENT
mov es,ax ;ES:DI points to offset of upper left
; corner of bar
mov dx,BAR_HEIGHT
mov al,0ffh
BarLineLoop:
mov cx,40 ;make the bars 40 wide
rep stosb ;do one scan line of the bar
add di,40 ;point to the start of the next scan line
; of the bar
dec dx
jnz BarLineLoop
ret
BarUp endp
;
; Converts AL to a hex digit in the range 0-F.
;
BinToHexDigit proc near
cmp al,9
ja IsHex
add al,'0'
ret
IsHex:
add al,'A'-10
ret
BinToHexDigit endp
;
; Displays the color values generated by the color bars given the
; current palette register settings off to the right of the color
; bars.
;
ColorNumbersUp proc near
mov ah,2 ;video interrupt set cursor position function
sub bh,bh ;page 0
mov dh,TOP_BAR/14 ;counting in character rows, match to
; top of first bar, counting in
; scan lines
mov dl,20+40+1 ;just to right of bars
int 10h
mov al,[CurrentColor] ;start with the current color
mov bx,offset ColorNumbers+1
;build the color number text string on the fly
mov cx,16 ;we've got 16 colors to do
ColorNumberLoop:
push ax ;save the color #
and al,3fh ;limit to 6-bit color values
shr al,1
shr al,1
shr al,1
shr al,1 ;isolate the high nibble of the
; color #
call BinToHexDigit ;convert the high color # nibble
mov [bx],al ; and put it into the text
pop ax ;get back the color #
push ax ;save the color #
and al,0fh ;isolate the low color # nibble
call BinToHexDigit ;convert the low nibble of the
; color # to ASCII
mov [bx+1],al ; and put it into the text
add bx,COLOR_ENTRY_LENGTH ;point to the next entry
pop ax ;get back the color #
inc ax ;next color #
loop ColorNumberLoop
mov ah,9 ;DOS print string function
mov dx,offset ColorNumbers
int 21h ;put up the attribute numbers
ret
ColorNumbersUp endp
;
Start endp
Code ends
end Start

81
tests/pcx86/vga/L29-4.ASM Normal file
View file

@ -0,0 +1,81 @@
; Program to demonstrate screen blanking via bit 5 of the
; Attribute Controller Index register.
;
AC_INDEX equ 3c0h ;Attribute Controller Index register
INPUT_STATUS_1 equ 3dah ;color-mode address of the Input
; Status 1 register
;
; Macro to wait for and clear the next keypress.
;
WAIT_KEY macro
mov ah,8 ;DOS input without echo function
int 21h
endm
;
stack segment para stack 'STACK'
db 512 dup (?)
stack ends
;
Data segment word 'DATA'
SampleText db 'This is bit-mapped text, drawn in hi-res '
db 'EGA graphics mode 10h.', 0dh, 0ah, 0ah
db 'Press any key to blank the screen, then '
db 'any key to unblank it,', 0dh, 0ah
db 'then any key to end.$'
Data ends
;
Code segment
assume cs:Code, ds:Data
Start proc near
mov ax,Data
mov ds,ax
;
; Go to hi-res graphics mode.
;
mov ax,10h ;AH = 0 means mode set, AL = 10h selects
; hi-res graphics mode
int 10h ;BIOS video interrupt
;
; Put up some text, so the screen isn't empty.
;
mov ah,9 ;DOS print string function
mov dx,offset SampleText
int 21h
;
WAIT_KEY
;
; Blank the screen.
;
mov dx,INPUT_STATUS_1
in al,dx ;reset port 3c0h to index (rather than data)
; mode
mov dx,AC_INDEX
sub al,al ;make bit 5 zero...
out dx,al ;...which blanks the screen
;
WAIT_KEY
;
; Unblank the screen.
;
mov dx,INPUT_STATUS_1
in al,dx ;reset port 3c0h to Index (rather than data)
; mode
mov dx,AC_INDEX
mov al,20h ;make bit 5 one...
out dx,al ;...which unblanks the screen
;
WAIT_KEY
;
; Restore text mode.
;
mov ax,2
int 10h
;
; Done.
;
Done:
mov ah,4ch ;DOS terminate function
int 21h
Start endp
Code ends
end Start

372
tests/pcx86/vga/L30-1.ASM Normal file
View file

@ -0,0 +1,372 @@
; Demonstrates the VGA/EGA split screen in action.
;
;*********************************************************************
IS_VGA equ 1 ;set to 0 to assemble for EGA
;
VGA_SEGMENT equ 0a000h
SCREEN_WIDTH equ 640
SCREEN_HEIGHT equ 350
CRTC_INDEX equ 3d4h ;CRT Controller Index register
OVERFLOW equ 7 ;index of Overflow reg in CRTC
MAXIMUM_SCAN_LINE equ 9 ;index of Maximum Scan Line register
; in CRTC
START_ADDRESS_HIGH equ 0ch ;index of Start Address High register
; in CRTC
START_ADDRESS_LOW equ 0dh ;index of Start Address Low register
; in CRTC
LINE_COMPARE equ 18h ;index of Line Compare reg (bits 7-0
; of split screen start scan line)
; in CRTC
INPUT_STATUS_0 equ 3dah ;Input Status 0 register
WORD_OUTS_OK equ 1 ;set to 0 to assemble for
; computers that can't handle
; word outs to indexed VGA registers
;*********************************************************************
; Macro to output a word value to a port.
;
OUT_WORD macro
if WORD_OUTS_OK
out dx,ax
else
out dx,al
inc dx
xchg ah,al
out dx,al
dec dx
xchg ah,al
endif
endm
;*********************************************************************
MyStack segment para stack 'STACK'
db 512 dup (0)
MyStack ends
;*********************************************************************
Data segment
SplitScreenLine dw ? ;line the split screen currently
; starts after
StartAddress dw ? ;display memory offset at which
; scanning for video data starts
; Message displayed in split screen.
SplitScreenMsg db 'Split screen text row #'
DigitInsert dw ?
db '...$'
Data ends
;*********************************************************************
Code segment
assume cs:Code, ds:Data
;*********************************************************************
Start proc near
mov ax,Data
mov ds,ax
;
; Select mode 10h, 640x350 16-color graphics mode.
;
mov ax,0010h ;AH=0 is select mode function
;AL=10h is mode to select,
; 640x350 16-color graphics mode
int 10h
;
; Put text into display memory starting at offset 0, with each row
; labelled as to number. This is the part of memory that will be
; displayed in the split screen portion of the display.
;
mov cx,25 ;# of lines of text we'll draw into
; the split screen part of memory
FillSplitScreenLoop:
mov ah,2 ;set cursor location function #
sub bh,bh ;set cursor in page 0
mov dh,25
sub dh,cl ;calculate row to draw in
sub dl,dl ;start in column 0
int 10h ;set the cursor location
mov al,25
sub al,cl ;calculate row to draw in again
sub ah,ah ;make the value a word for division
mov dh,10
div dh ;split the row # into two digits
add ax,'00' ;convert the digits to ASCII
mov [DigitInsert],ax ;put the digits into the text
; to be displayed
mov ah,9
mov dx,offset SplitScreenMsg
int 21h ;print the text
loop FillSplitScreenLoop
;
; Fill display memory starting at 8000h with a diagonally striped
; pattern.
;
mov ax,VGA_SEGMENT
mov es,ax
mov di,8000h
mov dx,SCREEN_HEIGHT ;fill all lines
mov ax,8888h ;starting fill pattern
cld
RowLoop:
mov cx,SCREEN_WIDTH/8/2 ;fill 1 scan line a word at a time
rep stosw ;fill the scan line
ror ax,1 ;shift pattern word
dec dx
jnz RowLoop
;
; Set the start address to 8000h and display that part of memory.
;
mov [StartAddress],8000h
call SetStartAddress
;
; Slide the split screen half way up the screen and then back down
; a quarter of the screen.
;
mov [SplitScreenLine],SCREEN_HEIGHT-1
;set the initial line just off
; the bottom of the screen
mov cx,SCREEN_HEIGHT/2
call SplitScreenUp
mov cx,SCREEN_HEIGHT/4
call SplitScreenDown
;
; Now move up another half a screen and then back down a quarter.
;
mov cx,SCREEN_HEIGHT/2
call SplitScreenUp
mov cx,SCREEN_HEIGHT/4
call SplitScreenDown
;
; Finally move up to the top of the screen.
;
mov cx,SCREEN_HEIGHT/2-2
call SplitScreenUp
;
; Wait for a key press (don't echo character).
;
mov ah,8 ;DOS console input without echo function
int 21h
;
; Turn the split screen off.
;
mov [SplitScreenLine],0ffffh
call SetSplitScreenScanLine
;
; Wait for a key press (don't echo character).
;
mov ah,8 ;DOS console input without echo function
int 21h
;
; Display the memory at 0 (the same memory the split screen displays).
;
mov [StartAddress],0
call SetStartAddress
;
; Flip between the split screen and the normal screen every 10th
; frame until a key is pressed.
;
FlipLoop:
xor [SplitScreenLine],0ffffh
call SetSplitScreenScanLine
mov cx,10
CountVerticalSyncsLoop:
call WaitForVerticalSyncEnd
loop CountVerticalSyncsLoop
mov ah,0bh ;DOS character available status
int 21h
and al,al ;character available?
jz FlipLoop ;no, toggle split screen on/off status
mov ah,1
int 21h ;clear the character
;
; Return to text mode and DOS.
;
mov ax,0003h ;AH=0 is select mode function
;AL=3 is mode to select, text mode
int 10h ;return to text mode
mov ah,4ch
int 21h ;return to DOS
Start endp
;*********************************************************************
; Waits for the leading edge of the vertical sync pulse.
;
; Input: none
;
; Output: none
;
; Registers altered: AL, DX
;
WaitForVerticalSyncStart proc near
mov dx,INPUT_STATUS_0
WaitNotVerticalSync:
in al,dx
test al,08h
jnz WaitNotVerticalSync
WaitVerticalSync:
in al,dx
test al,08h
jz WaitVerticalSync
ret
WaitForVerticalSyncStart endp
;*********************************************************************
; Waits for the trailing edge of the vertical sync pulse.
;
; Input: none
;
; Output: none
;
; Registers altered: AL, DX
;
WaitForVerticalSyncEnd proc near
mov dx,INPUT_STATUS_0
WaitVerticalSync2:
in al,dx
test al,08h
jz WaitVerticalSync2
WaitNotVerticalSync2:
in al,dx
test al,08h
jnz WaitNotVerticalSync2
ret
WaitForVerticalSyncEnd endp
;*********************************************************************
; Sets the start address to the value specifed by StartAddress.
; Wait for the trailing edge of vertical sync before setting so that
; one half of the address isn't loaded before the start of the frame
; and the other half after, resulting in flicker as one frame is
; displayed with mismatched halves. The new start address won't be
; loaded until the start of the next frame; that is, one full frame
; will be displayed before the new start address takes effect.
;
; Input: none
;
; Output: none
;
; Registers altered: AX, DX
;
SetStartAddress proc near
call WaitForVerticalSyncEnd
mov dx,CRTC_INDEX
mov al,START_ADDRESS_HIGH
mov ah,byte ptr [StartAddress+1]
cli ;make sure both registers get set at once
OUT_WORD
mov al,START_ADDRESS_LOW
mov ah,byte ptr [StartAddress]
OUT_WORD
sti
ret
SetStartAddress endp
;*********************************************************************
; Sets the scan line the split screen starts after to the scan line
; specified by SplitScreenLine.
;
; Input: none
;
; Output: none
;
; All registers preserved
;
SetSplitScreenScanLine proc near
push ax
push cx
push dx
;
; Wait for the leading edge of the vertical sync pulse. This ensures
; that we don't get mismatched portions of the split screen setting
; while setting the two or three split screen registers (register 18h
; set but register 7 not yet set when a match occurs, for example),
; which could produce brief flickering.
;
call WaitForVerticalSyncStart
;
; Set the split screen scan line.
;
mov dx,CRTC_INDEX
mov ah,byte ptr [SplitScreenLine]
mov al,LINE_COMPARE
cli ;make sure all the registers get set at once
OUT_WORD ;set bits 7-0 of the split screen scan line
mov ah,byte ptr [SplitScreenLine+1]
and ah,1
mov cl,4
shl ah,cl ;move bit 8 of the split split screen scan
; line into position for the Overflow reg
mov al,OVERFLOW
if IS_VGA
;
; The Split Screen, Overflow, and Line Compare registers all contain
; part of the split screen start scan line on the VGA. We'll take
; advantage of the readable registers of the VGA to leave other bits
; in the registers we access undisturbed.
;
out dx,al ;set CRTC Index reg to point to Overflow
inc dx ;point to CRTC Data reg
in al,dx ;get the current Overflow reg setting
and al,not 10h ;turn off split screen bit 8
or al,ah ;insert the new split screen bit 8
; (works in any mode)
out dx,al ;set the new split screen bit 8
dec dx ;point to CRTC Index reg
mov ah,byte ptr [SplitScreenLine+1]
and ah,2
mov cl,3
ror ah,cl ;move bit 9 of the split split screen scan
; line into position for the Maximum Scan
; Line register
mov al,MAXIMUM_SCAN_LINE
out dx,al ;set CRTC Index reg to point to Maximum
; Scan Line
inc dx ;point to CRTC Data reg
in al,dx ;get the current Maximum Scan Line setting
and al,not 40h ;turn off split screen bit 9
or al,ah ;insert the new split screen bit 9
; (works in any mode)
out dx,al ;set the new split screen bit 9
else
;
; Only the Split Screen and Overflow registers contain part of the
; Split Screen start scan line and need to be set on the EGA.
; EGA registers are not readable, so we have to set the non-split
; screen bits of the Overflow register to a preset value, in this
; case the value for 350-scan-line modes.
;
or ah,0fh ;insert the new split screen bit 8
; (only works in 350-scan-line EGA modes)
OUT_WORD ;set the new split screen bit 8
endif
sti
pop dx
pop cx
pop ax
ret
SetSplitScreenScanLine endp
;*********************************************************************
; Moves the split screen up the specified number of scan lines.
;
; Input: CX = # of scan lines to move the split screen up by
;
; Output: none
;
; Registers altered: CX
;
SplitScreenUp proc near
SplitScreenUpLoop:
dec [SplitScreenLine]
call SetSplitScreenScanLine
loop SplitScreenUpLoop
ret
SplitScreenUp endp
;*********************************************************************
; Moves the split screen down the specified number of scan lines.
;
; Input: CX = # of scan lines to move the split screen down by
;
; Output: none
;
; Registers altered: CX
;
SplitScreenDown proc near
SplitScreenDownLoop:
inc [SplitScreenLine]
call SetSplitScreenScanLine
loop SplitScreenDownLoop
ret
SplitScreenDown endp
;*********************************************************************
Code ends
end Start

419
tests/pcx86/vga/L30-2.ASM Normal file
View file

@ -0,0 +1,419 @@
; Demonstrates the interaction of the split screen and
; horizontal pel panning. On a VGA, first pans right in the top
; half while the split screen jerks around, because split screen
; pel panning suppression is disabled, then enables split screen
; pel panning suppression and pans right in the top half while the
; split screen remains stable. On an EGA, the split screen jerks
; around in both cases, because the EGA doesn't support split
; screen pel panning suppression.
;
; The jerking in the split screen occurs because the split screen
; is being pel panned (panned by single pixels--intrabyte panning),
; but is not and cannot be byte panned (panned by single bytes--
; "extrabyte" panning) because the start address of the split screen
; is forever fixed at 0.
;*********************************************************************
IS_VGA equ 1 ;set to 0 to assemble for EGA
;
VGA_SEGMENT equ 0a000h
LOGICAL_SCREEN_WIDTH equ 1024 ;# of pixels across virtual
; screen that we'll pan across
SCREEN_HEIGHT equ 350
SPLIT_SCREEN_START equ 200 ;start scan line for split screen
SPLIT_SCREEN_HEIGHT equ SCREEN_HEIGHT-SPLIT_SCREEN_START-1
CRTC_INDEX equ 3d4h ;CRT Controller Index register
AC_INDEX equ 3c0h ;Attribute Controller Index reg
OVERFLOW equ 7 ;index of Overflow reg in CRTC
MAXIMUM_SCAN_LINE equ 9 ;index of Maximum Scan Line register
; in CRTC
START_ADDRESS_HIGH equ 0ch ;index of Start Address High register
; in CRTC
START_ADDRESS_LOW equ 0dh ;index of Start Address Low register
; in CRTC
HOFFSET equ 13h ;index of Horizontal Offset register
; in CRTC
LINE_COMPARE equ 18h ;index of Line Compare reg (bits 7-0
; of split screen start scan line)
; in CRTC
AC_MODE_CONTROL equ 10h ;index of Mode Control reg in AC
PEL_PANNING equ 13h ;index of Pel Panning reg in AC
INPUT_STATUS_0 equ 3dah ;Input Status 0 register
WORD_OUTS_OK equ 1 ;set to 0 to assemble for
; computers that can't handle
; word outs to indexed VGA registers
;*********************************************************************
; Macro to output a word value to a port.
;
OUT_WORD macro
if WORD_OUTS_OK
out dx,ax
else
out dx,al
inc dx
xchg ah,al
out dx,al
dec dx
xchg ah,al
endif
endm
;*********************************************************************
MyStack segment para stack 'STACK'
db 512 dup (0)
MyStack ends
;*********************************************************************
Data segment
SplitScreenLine dw ? ;line the split screen currently
; starts after
StartAddress dw ? ;display memory offset at which
; scanning for video data starts
PelPan db ? ;current intrabyte horizontal pel
; panning setting
Data ends
;*********************************************************************
Code segment
assume cs:Code, ds:Data
;*********************************************************************
Start proc near
mov ax,Data
mov ds,ax
;
; Select mode 10h, 640x350 16-color graphics mode.
;
mov ax,0010h ;AH=0 is select mode function
;AL=10h is mode to select,
; 640x350 16-color graphics mode
int 10h
;
; Set the Offset register to make the offset from the start of one
; scan line to the start of the next the desired number of pixels.
; This gives us a virtual screen wider than the actual screen to
; pan across.
; Note that the Offset register is programmed with the logical
; screen width in words, not bytes, hence the final division by 2.
;
mov dx,CRTC_INDEX
mov ax,(LOGICAL_SCREEN_WIDTH/8/2 shl 8) or HOFFSET
OUT_WORD
;
; Set the start address to display the memory just past the split
; screen memory.
;
mov [StartAddress],SPLIT_SCREEN_HEIGHT*(LOGICAL_SCREEN_WIDTH/8)
call SetStartAddress
;
; Set the split screen start scan line.
;
mov [SplitScreenLine],SPLIT_SCREEN_START
call SetSplitScreenScanLine
;
; Fill the split screen portion of display memory (starting at
; offset 0) with a choppy diagonal pattern sloping left.
;
mov ax,VGA_SEGMENT
mov es,ax
sub di,di
mov dx,SPLIT_SCREEN_HEIGHT
;fill all lines in the split screen
mov ax,0FF0h ;starting fill pattern
cld
RowLoop:
mov cx,LOGICAL_SCREEN_WIDTH/8/4
;fill 1 scan line
ColumnLoop:
stosw ;draw part of a diagonal line
mov word ptr es:[di],0 ;make vertical blank spaces so
; panning effects can be seen easily
inc di
inc di
loop ColumnLoop
rol ax,1 ;shift pattern word
dec dx
jnz RowLoop
;
; Fill the portion of display memory that will be displayed in the
; normal screen (the non-split screen part of the display) with a
; choppy diagonal pattern sloping right.
;
mov di,SPLIT_SCREEN_HEIGHT*(LOGICAL_SCREEN_WIDTH/8)
mov dx,SCREEN_HEIGHT ;fill all lines
mov ax,0c510h ;starting fill pattern
cld
RowLoop2:
mov cx,LOGICAL_SCREEN_WIDTH/8/4
;fill 1 scan line
ColumnLoop2:
stosw ;draw part of a diagonal line
mov word ptr es:[di],0 ;make vertical blank spaces so
; panning effects can be seen easily
inc di
inc di
loop ColumnLoop2
ror ax,1 ;shift pattern word
dec dx
jnz RowLoop2
;
; Pel pan the non-split screen portion of the display; because
; split screen pel panning suppression is not turned on, the split
; screen jerks back and forth as the pel panning setting cycles.
;
mov cx,200 ;pan 200 pixels to the left
call PanRight
;
; Wait for a key press (don't echo character).
;
mov ah,8 ;DOS console input without echo function
int 21h
;
; Return to the original screen location, with pel panning turned off.
;
mov [StartAddress],SPLIT_SCREEN_HEIGHT*(LOGICAL_SCREEN_WIDTH/8)
call SetStartAddress
mov [PelPan],0
call SetPelPan
;
; Turn on split screen pel panning suppression, so the split screen
; won't be affected by pel panning. Not done on EGA because both
; readable registers and the split screen pel panning suppression bit
; aren't supported by EGAs.
;
if IS_VGA
mov dx,INPUT_STATUS_0
in al,dx ;reset the AC Index/Data toggle to
; Index state
mov al,20h+AC_MODE_CONTROL
;bit 5 set to 1 to keep video on
mov dx,AC_INDEX ;point to AC Index/Data register
out dx,al
inc dx ;point to AC Data reg (for reads only)
in al,dx ;get the current AC Mode Control reg
or al,20h ;enable split screen pel panning
; suppression
dec dx ;point to AC Index/Data reg (Data for
; writes only)
out dx,al ;write the new AC Mode Control setting
; with split screen pel panning
; suppression turned on
endif
;
; Pel pan the non-split screen portion of the display; because
; split screen pel panning suppression is turned on, the split
; screen will not move as the pel panning setting cycles.
;
mov cx,200 ;pan 200 pixels to the left
call PanRight
;
; Wait for a key press (don't echo character).
;
mov ah,8 ;DOS console input without echo function
int 21h
;
; Return to text mode and DOS.
;
mov ax,0003h ;AH=0 is select mode function
;AL=3 is mode to select, text mode
int 10h ;return to text mode
mov ah,4ch
int 21h ;return to DOS
Start endp
;*********************************************************************
; Waits for the leading edge of the vertical sync pulse.
;
; Input: none
;
; Output: none
;
; Registers altered: AL, DX
;
WaitForVerticalSyncStart proc near
mov dx,INPUT_STATUS_0
WaitNotVerticalSync:
in al,dx
test al,08h
jnz WaitNotVerticalSync
WaitVerticalSync:
in al,dx
test al,08h
jz WaitVerticalSync
ret
WaitForVerticalSyncStart endp
;*********************************************************************
; Waits for the trailing edge of the vertical sync pulse.
;
; Input: none
;
; Output: none
;
; Registers altered: AL, DX
;
WaitForVerticalSyncEnd proc near
mov dx,INPUT_STATUS_0
WaitVerticalSync2:
in al,dx
test al,08h
jz WaitVerticalSync2
WaitNotVerticalSync2:
in al,dx
test al,08h
jnz WaitNotVerticalSync2
ret
WaitForVerticalSyncEnd endp
;*********************************************************************
; Sets the start address to the value specifed by StartAddress.
; Wait for the trailing edge of vertical sync before setting so that
; one half of the address isn't loaded before the start of the frame
; and the other half after, resulting in flicker as one frame is
; displayed with mismatched halves. The new start address won't be
; loaded until the start of the next frame; that is, one full frame
; will be displayed before the new start address takes effect.
;
; Input: none
;
; Output: none
;
; Registers altered: AX, DX
;
SetStartAddress proc near
call WaitForVerticalSyncEnd
mov dx,CRTC_INDEX
mov al,START_ADDRESS_HIGH
mov ah,byte ptr [StartAddress+1]
cli ;make sure both registers get set at once
OUT_WORD
mov al,START_ADDRESS_LOW
mov ah,byte ptr [StartAddress]
OUT_WORD
sti
ret
SetStartAddress endp
;*********************************************************************
; Sets the horizontal pel panning setting to the value specified
; by PelPan. Waits until the start of vertical sync to do so, so
; the new pel pan setting can be loaded during non-display time
; and can be ready by the start of the next frame.
;
; Input: none
;
; Output: none
;
; Registers altered: AL, DX
;
SetPelPan proc near
call WaitForVerticalSyncStart ;also resets the AC
; Index/Data toggle
; to Index state
mov dx,AC_INDEX
mov al,PEL_PANNING+20h
;bit 5 set to 1 to keep video on
out dx,al ;point the AC Index to Pel Pan reg
mov al,[PelPan]
out dx,al ;load the new Pel Pan setting
ret
SetPelPan endp
;*********************************************************************
; Sets the scan line the split screen starts after to the scan line
; specified by SplitScreenLine.
;
; Input: none
;
; Output: none
;
; All registers preserved
;
SetSplitScreenScanLine proc near
push ax
push cx
push dx
;
; Wait for the leading edge of the vertical sync pulse. This ensures
; that we don't get mismatched portions of the split screen setting
; while setting the two or three split screen registers (register 18h
; set but register 7 not yet set when a match occurs, for example),
; which could produce brief flickering.
;
call WaitForVerticalSyncStart
;
; Set the split screen scan line.
;
mov dx,CRTC_INDEX
mov ah,byte ptr [SplitScreenLine]
mov al,LINE_COMPARE
cli ;make sure all the registers get set at once
OUT_WORD ;set bits 7-0 of the split screen scan line
mov ah,byte ptr [SplitScreenLine+1]
and ah,1
mov cl,4
shl ah,cl ;move bit 8 of the split split screen scan
; line into position for the Overflow reg
mov al,OVERFLOW
if IS_VGA
;
; The Split Screen, Overflow, and Line Compare registers all contain
; part of the split screen start scan line on the VGA. We'll take
; advantage of the readable registers of the VGA to leave other bits
; in the registers we access undisturbed.
;
out dx,al ;set CRTC Index reg to point to Overflow
inc dx ;point to CRTC Data reg
in al,dx ;get the current Overflow reg setting
and al,not 10h ;turn off split screen bit 8
or al,ah ;insert the new split screen bit 8
; (works in any mode)
out dx,al ;set the new split screen bit 8
dec dx ;point to CRTC Index reg
mov ah,byte ptr [SplitScreenLine+1]
and ah,2
mov cl,3
ror ah,cl ;move bit 9 of the split split screen scan
; line into position for the Maximum Scan
; Line register
mov al,MAXIMUM_SCAN_LINE
out dx,al ;set CRTC Index reg to point to Maximum
; Scan Line
inc dx ;point to CRTC Data reg
in al,dx ;get the current Maximum Scan Line setting
and al,not 40h ;turn off split screen bit 9
or al,ah ;insert the new split screen bit 9
; (works in any mode)
out dx,al ;set the new split screen bit 9
else
;
; Only the Split Screen and Overflow registers contain part of the
; Split Screen start scan line and need to be set on the EGA.
; EGA registers are not readable, so we have to set the non-split
; screen bits of the Overflow register to a preset value, in this
; case the value for 350-scan-line modes.
;
or ah,0fh ;insert the new split screen bit 8
; (only works in 350-scan-line EGA modes)
OUT_WORD ;set the new split screen bit 8
endif
sti
pop dx
pop cx
pop ax
ret
SetSplitScreenScanLine endp
;*********************************************************************
; Pan horizontally to the right the number of pixels specified by CX.
;
; Input: CX = # of pixels by which to pan horizontally
;
; Output: none
;
; Registers altered: AX, CX, DX
;
PanRight proc near
PanLoop:
inc [PelPan]
and [PelPan],07h
jnz DoSetStartAddress
inc [StartAddress]
DoSetStartAddress:
call SetStartAddress
call SetPelPan
loop PanLoop
ret
PanRight endp
;*********************************************************************
Code ends
end Start

326
tests/pcx86/vga/L31-1.ASM Normal file
View file

@ -0,0 +1,326 @@
; Program to demonstrate pixel drawing in 320x400 256-color
; mode on the VGA. Draws 8 lines to form an octagon, a pixel
; at a time. Draws 8 octagons in all, one on top of the other,
; each in a different color set. Although it's not used, a
; pixel read function is also provided.
;
VGA_SEGMENT equ 0a000h
SC_INDEX equ 3c4h ;Sequence Controller Index register
GC_INDEX equ 3ceh ;Graphics Controller Index register
CRTC_INDEX equ 3d4h ;CRT Controller Index register
MAP_MASK equ 2 ;Map Mask register index in SC
MEMORY_MODE equ 4 ;Memory Mode register index in SC
MAX_SCAN_LINE equ 9 ;Maximum Scan Line reg index in CRTC
START_ADDRESS_HIGH equ 0ch ;Start Address High reg index in CRTC
UNDERLINE equ 14h ;Underline Location reg index in CRTC
MODE_CONTROL equ 17h ;Mode Control register index in CRTC
READ_MAP equ 4 ;Read Map register index in GC
GRAPHICS_MODE equ 5 ;Graphics Mode register index in GC
MISCELLANEOUS equ 6 ;Miscellaneous register index in GC
SCREEN_WIDTH equ 320 ;# of pixels across screen
SCREEN_HEIGHT equ 400 ;# of scan lines on screen
WORD_OUTS_OK equ 1 ;set to 0 to assemble for
; computers that can't handle
; word outs to indexed VGA registers
;
stack segment para stack 'STACK'
db 512 dup (?)
stack ends
;
Data segment word 'DATA'
;
BaseColor db 0
;
; Structure used to control drawing of a line.
;
LineControl struc
StartX dw ?
StartY dw ?
LineXInc dw ?
LineYInc dw ?
BaseLength dw ?
LineColor db ?
LineControl ends
;
; List of descriptors for lines to draw.
;
LineList label LineControl
LineControl <130,110,1,0,60,0>
LineControl <190,110,1,1,60,1>
LineControl <250,170,0,1,60,2>
LineControl <250,230,-1,1,60,3>
LineControl <190,290,-1,0,60,4>
LineControl <130,290,-1,-1,60,5>
LineControl <70,230,0,-1,60,6>
LineControl <70,170,1,-1,60,7>
LineControl <-1,0,0,0,0,0>
Data ends
;
; Macro to output a word value to a port.
;
OUT_WORD macro
if WORD_OUTS_OK
out dx,ax
else
out dx,al
inc dx
xchg ah,al
out dx,al
dec dx
xchg ah,al
endif
endm
;
; Macro to output a constant value to an indexed VGA register.
;
CONSTANT_TO_INDEXED_REGISTER macro ADDRESS, INDEX, VALUE
mov dx,ADDRESS
mov ax,(VALUE shl 8) + INDEX
OUT_WORD
endm
;
Code segment
assume cs:Code, ds:Data
Start proc near
mov ax,Data
mov ds,ax
;
; Set 320x400 256-color mode.
;
call Set320By400Mode
;
; We're in 320x400 256-color mode. Draw each line in turn.
;
ColorLoop:
mov si,offset LineList ;point to the start of the
; line descriptor list
LineLoop:
mov cx,[si+StartX] ;set the initial X coordinate
cmp cx,-1
jz LinesDone ;a descriptor with a -1 X
; coordinate marks the end
; of the list
mov dx,[si+StartY] ;set the initial Y coordinate,
mov bl,[si+LineColor] ; line color,
mov bp,[si+BaseLength] ; and pixel count
add bl,[BaseColor] ;adjust the line color according
; to BaseColor
PixelLoop:
push cx ;save the coordinates
push dx
call WritePixel ;draw this pixel
pop dx ;retrieve the coordinates
pop cx
add cx,[si+LineXInc];set the coordinates of the
add dx,[si+LineYInc]; next point of the line
dec bp ;any more points?
jnz PixelLoop ;yes, draw the next
add si,size LineControl ;point to the next line descriptor
jmp LineLoop ; and draw the next line
LinesDone:
call GetNextKey ;wait for a key, then
inc [BaseColor] ; bump the color selection and
cmp [BaseColor],8 ; see if we're done
jb ColorLoop ;not done yet
;
; Wait for a key and return to text mode and end when
; one is pressed.
;
call GetNextKey
mov ax,0003h
int 10h ;text mode
mov ah,4ch
int 21h ;done
;
Start endp
;
; Sets up 320x400 256-color modes.
;
; Input: none
;
; Output: none
;
Set320By400Mode proc near
;
; First, go to normal 320x200 256-color mode, which is really a
; 320x400 256-color mode with each line scanned twice.
;
mov ax,0013h ;AH = 0 means mode set, AL = 13h selects
; 256-color graphics mode
int 10h ;BIOS video interrupt
;
; Change CPU addressing of video memory to linear (not odd/even,
; chain, or chain 4), to allow us to access all 256K of display
; memory. When this is done, VGA memory will look just like memory
; in modes 10h and 12h, except that each byte of display memory will
; control one 256-color pixel, with 4 adjacent pixels at any given
; address, one pixel per plane.
;
mov dx,SC_INDEX
mov al,MEMORY_MODE
out dx,al
inc dx
in al,dx
and al,not 08h ;turn off chain 4
or al,04h ;turn off odd/even
out dx,al
mov dx,GC_INDEX
mov al,GRAPHICS_MODE
out dx,al
inc dx
in al,dx
and al,not 10h ;turn off odd/even
out dx,al
dec dx
mov al,MISCELLANEOUS
out dx,al
inc dx
in al,dx
and al,not 02h ;turn off chain
out dx,al
;
; Now clear the whole screen, since the mode 13h mode set only
; cleared 64K out of the 256K of display memory. Do this before
; we switch the CRTC out of mode 13h, so we don't see garbage
; on the screen when we make the switch.
;
CONSTANT_TO_INDEXED_REGISTER SC_INDEX,MAP_MASK,0fh
;enable writes to all planes, so
; we can clear 4 pixels at a time
mov ax,VGA_SEGMENT
mov es,ax
sub di,di
mov ax,di
mov cx,8000h ;# of words in 64K
cld
rep stosw ;clear all of display memory
;
; Tweak the mode to 320x400 256-color mode by not scanning each
; line twice.
;
mov dx,CRTC_INDEX
IF 1 ; the following code can be disabled to test memory access in 320x200 mode -JP
mov al,MAX_SCAN_LINE
out dx,al
inc dx
in al,dx
and al,not 1fh ;set maximum scan line = 0
out dx,al
dec dx
ENDIF
;
; Change CRTC scanning from doubleword mode to byte mode, allowing
; the CRTC to scan more than 64K of video data.
;
mov al,UNDERLINE
out dx,al
inc dx
in al,dx
and al,not 40h ;turn off doubleword
out dx,al
dec dx
mov al,MODE_CONTROL
out dx,al
inc dx
in al,dx
or al,40h ;turn on the byte mode bit, so memory is
; scanned for video data in a purely
; linear way, just as in modes 10h and 12h
out dx,al
ret
Set320By400Mode endp
;
; Draws a pixel in the specified color at the specified
; location in 320x400 256-color mode.
;
; Input:
; CX = X coordinate of pixel
; DX = Y coordinate of pixel
; BL = pixel color
;
; Output: none
;
; Registers altered: AX, CX, DX, DI, ES
;
WritePixel proc near
mov ax,VGA_SEGMENT
mov es,ax ;point to display memory
mov ax,SCREEN_WIDTH/4
;there are 4 pixels at each address, so
; each 320-pixel row is 80 bytes wide
; in each plane
mul dx ;point to start of desired row
push cx ;set aside the X coordinate
shr cx,1 ;there are 4 pixels at each address
shr cx,1 ; so divide the X coordinate by 4
add ax,cx ;point to the pixel's address
mov di,ax
pop cx ;get back the X coordinate
and cl,3 ;get the plane # of the pixel
mov ah,1
shl ah,cl ;set the bit corresponding to the plane
; the pixel is in
mov al,MAP_MASK
mov dx,SC_INDEX
OUT_WORD ;set to write to the proper plane for
; the pixel
mov es:[di],bl ;draw the pixel
ret
WritePixel endp
;
; Reads the color of the pixel at the specified location in 320x400
; 256-color mode.
;
; Input:
; CX = X coordinate of pixel to read
; DX = Y coordinate of pixel to read
;
; Output:
; AL = pixel color
;
; Registers altered: AX, CX, DX, SI, ES
;
ReadPixel proc near
mov ax,VGA_SEGMENT
mov es,ax ;point to display memory
mov ax,SCREEN_WIDTH/4
;there are 4 pixels at each address, so
; each 320-pixel row is 80 bytes wide
; in each plane
mul dx ;point to start of desired row
push cx ;set aside the X coordinate
shr cx,1 ;there are 4 pixels at each address
shr cx,1 ; so divide the X coordinate by 4
add ax,cx ;point to the pixel's address
mov si,ax
pop ax ;get back the X coordinate
and al,3 ;get the plane # of the pixel
mov ah,al
mov al,READ_MAP
mov dx,GC_INDEX
OUT_WORD ;set to read from the proper plane for
; the pixel
lods byte ptr es:[si];read the pixel
ret
ReadPixel endp
;
; Waits for the next key and returns it in AX.
;
; Input: none
;
; Output:
; AX = full 16-bit code for key pressed
;
GetNextKey proc near
WaitKey:
mov ah,1
int 16h
jz WaitKey ;wait for a key to become available
sub ah,ah
int 16h ;read the key
ret
GetNextKey endp
;
Code ends
;
end Start

259
tests/pcx86/vga/L31-2.ASM Normal file
View file

@ -0,0 +1,259 @@
; Program to demonstrate the two pages available in 320x400
; 256-color modes on a VGA. Draws diagonal color bars in all
; 256 colors in page 0, then does the same in page 1 (but with
; the bars tilted the other way), and finally draws vertical
; color bars in page 0.
;
VGA_SEGMENT equ 0a000h
SC_INDEX equ 3c4h ;Sequence Controller Index register
GC_INDEX equ 3ceh ;Graphics Controller Index register
CRTC_INDEX equ 3d4h ;CRT Controller Index register
MAP_MASK equ 2 ;Map Mask register index in SC
MEMORY_MODE equ 4 ;Memory Mode register index in SC
MAX_SCAN_LINE equ 9 ;Maximum Scan Line reg index in CRTC
START_ADDRESS_HIGH equ 0ch ;Start Address High reg index in CRTC
UNDERLINE equ 14h ;Underline Location reg index in CRTC
MODE_CONTROL equ 17h ;Mode Control register index in CRTC
GRAPHICS_MODE equ 5 ;Graphics Mode register index in GC
MISCELLANEOUS equ 6 ;Miscellaneous register index in GC
SCREEN_WIDTH equ 320 ;# of pixels across screen
SCREEN_HEIGHT equ 400 ;# of scan lines on screen
WORD_OUTS_OK equ 1 ;set to 0 to assemble for
; computers that can't handle
; word outs to indexed VGA registers
;
stack segment para stack 'STACK'
db 512 dup (?)
stack ends
;
; Macro to output a word value to a port.
;
OUT_WORD macro
if WORD_OUTS_OK
out dx,ax
else
out dx,al
inc dx
xchg ah,al
out dx,al
dec dx
xchg ah,al
endif
endm
;
; Macro to output a constant value to an indexed VGA register.
;
CONSTANT_TO_INDEXED_REGISTER macro ADDRESS, INDEX, VALUE
mov dx,ADDRESS
mov ax,(VALUE shl 8) + INDEX
OUT_WORD
endm
;
Code segment
assume cs:Code
Start proc near
;
; Set 320x400 256-color mode.
;
call Set320By400Mode
;
; We're in 320x400 256-color mode, with page 0 displayed.
; Let's fill page 0 with color bars slanting down and to the right.
;
sub di,di ;page 0 starts at address 0
mov bl,1 ;make color bars slant down and
; to the right
call ColorBarsUp ;draw the color bars
;
; Now do the same for page 1, but with the color bars
; tilting the other way.
;
mov di,8000h ;page 1 starts at address 8000h
mov bl,-1 ;make color bars slant down and
; to the left
call ColorBarsUp ;draw the color bars
;
; Wait for a key and flip to page 1 when one is pressed.
;
call GetNextKey
CONSTANT_TO_INDEXED_REGISTER CRTC_INDEX,START_ADDRESS_HIGH,80h
;set the Start Address High register
; to 80h, for a start address of 8000h
;
; Draw vertical bars in page 0 while page 1 is displayed.
;
sub di,di ;page 0 starts at address 0
sub bl,bl ;make color bars vertical
call ColorBarsUp ;draw the color bars
;
; Wait for another key and flip back to page 0 when one is pressed.
;
call GetNextKey
CONSTANT_TO_INDEXED_REGISTER CRTC_INDEX,START_ADDRESS_HIGH,00h
;set the Start Address High register
; to 00h, for a start address of 0000h
;
; Wait for yet another key and return to text mode and end when
; one is pressed.
;
call GetNextKey
mov ax,0003h
int 10h ;text mode
mov ah,4ch
int 21h ;done
;
Start endp
;
; Sets up 320x400 256-color modes.
;
; Input: none
;
; Output: none
;
Set320By400Mode proc near
;
; First, go to normal 320x200 256-color mode, which is really a
; 320x400 256-color mode with each line scanned twice.
;
mov ax,0013h ;AH = 0 means mode set, AL = 13h selects
; 256-color graphics mode
int 10h ;BIOS video interrupt
;
; Change CPU addressing of video memory to linear (not odd/even,
; chain, or chain 4), to allow us to access all 256K of display
; memory. When this is done, VGA memory will look just like memory
; in modes 10h and 12h, except that each byte of display memory will
; control one 256-color pixel, with 4 adjacent pixels at any given
; address, one pixel per plane.
;
mov dx,SC_INDEX
mov al,MEMORY_MODE
out dx,al
inc dx
in al,dx
and al,not 08h ;turn off chain 4
or al,04h ;turn off odd/even
out dx,al
mov dx,GC_INDEX
mov al,GRAPHICS_MODE
out dx,al
inc dx
in al,dx
and al,not 10h ;turn off odd/even
out dx,al
dec dx
mov al,MISCELLANEOUS
out dx,al
inc dx
in al,dx
and al,not 02h ;turn off chain
out dx,al
;
; Now clear the whole screen, since the mode 13h mode set only
; cleared 64K out of the 256K of display memory. Do this before
; we switch the CRTC out of mode 13h, so we don't see garbage
; on the screen when we make the switch.
;
CONSTANT_TO_INDEXED_REGISTER SC_INDEX,MAP_MASK,0fh
;enable writes to all planes, so
; we can clear 4 pixels at a time
mov ax,VGA_SEGMENT
mov es,ax
sub di,di
mov ax,di
mov cx,8000h ;# of words in 64K
cld
rep stosw ;clear all of display memory
;
; Tweak the mode to 320x400 256-color mode by not scanning each
; line twice.
;
mov dx,CRTC_INDEX
mov al,MAX_SCAN_LINE
out dx,al
inc dx
in al,dx
and al,not 1fh ;set maximum scan line = 0
out dx,al
dec dx
;
; Change CRTC scanning from doubleword mode to byte mode, allowing
; the CRTC to scan more than 64K of video data.
;
mov al,UNDERLINE
out dx,al
inc dx
in al,dx
and al,not 40h ;turn off doubleword
out dx,al
dec dx
mov al,MODE_CONTROL
out dx,al
inc dx
in al,dx
or al,40h ;turn on the byte mode bit, so memory is
; scanned for video data in a purely
; linear way, just as in modes 10h and 12h
out dx,al
ret
Set320By400Mode endp
;
; Draws a full screen of slanting color bars in the specified page.
;
; Input:
; DI = page start address
; BL = 1 to make the bars slant down and to the right, -1 to
; make them slant down and to the left, 0 to make
; them vertical.
;
ColorBarsUp proc near
mov ax,VGA_SEGMENT
mov es,ax ;point to display memory
sub bh,bh ;start with color 0
mov si,SCREEN_HEIGHT;# of rows to do
mov dx,SC_INDEX
mov al,MAP_MASK
out dx,al ;point the SC Index reg to the Map Mask reg
inc dx ;point DX to the SC Data register
RowLoop:
mov cx,SCREEN_WIDTH/4
;there are 4 pixels at each address, so
; each 320-pixel row is 80 bytes wide
; in each plane
push bx ;save the row-start color
ColumnLoop:
MAP_SELECT = 1
rept 4 ;do all 4 pixels at this address with
; in-line code
mov al,MAP_SELECT
out dx,al ;select planes 0, 1, 2, and 3 in turn
mov es:[di],bh ;write this plane's pixel
inc bh ;set the color for the next pixel
MAP_SELECT = MAP_SELECT shl 1
endm
inc di ;point to the address containing the next
; 4 pixels
loop ColumnLoop ;do any remaining pixels on this line
pop bx ;get back the row-start color
add bh,bl ;select next row-start color (controls
; slanting of color bars)
dec si ;count down lines on the screen
jnz RowLoop
ret
ColorBarsUp endp
;
; Waits for the next key and returns it in AX.
;
GetNextKey proc near
WaitKey:
mov ah,1
int 16h
jz WaitKey ;wait for a key to become available
sub ah,ah
int 16h ;read the key
ret
GetNextKey endp
;
Code ends
;
end Start

211
tests/pcx86/vga/L32-1.ASM Normal file
View file

@ -0,0 +1,211 @@
; C tiny/small/medium model-callable assembler
; subroutines to:
; * Set 360x480 256-color VGA mode
; * Draw a dot in 360x480 256-color VGA mode
; * Read the color of a dot in 360x480 256-color VGA mode
;
; Assembled with TASM
;
; The 360x480 256-color mode set code and parameters were provided
; by John Bridges, who has placed them into the public domain.
;
VGA_SEGMENT equ 0a000h ;display memory segment
SC_INDEX equ 3c4h ;Sequence Controller Index register
GC_INDEX equ 3ceh ;Graphics Controller Index register
MAP_MASK equ 2 ;Map Mask register index in SC
READ_MAP equ 4 ;Read Map register index in GC
SCREEN_WIDTH equ 360 ;# of pixels across screen
WORD_OUTS_OK equ 1 ;set to 0 to assemble for
; computers that can't handle
; word outs to indexed VGA registers
;
_DATA segment public byte 'DATA'
;
; 360x480 256-color mode CRT Controller register settings.
; (Courtesy of John Bridges.)
;
vptbl dw 06b00h ; horz total
dw 05901h ; horz displayed
dw 05a02h ; start horz blanking
dw 08e03h ; end horz blanking
dw 05e04h ; start h sync
dw 08a05h ; end h sync
dw 00d06h ; vertical total
dw 03e07h ; overflow
dw 04009h ; cell height
dw 0ea10h ; v sync start
dw 0ac11h ; v sync end and protect cr0-cr7
dw 0df12h ; vertical displayed
dw 02d13h ; offset
dw 00014h ; turn off dword mode
dw 0e715h ; v blank start
dw 00616h ; v blank end
dw 0e317h ; turn on byte mode
vpend label word
_DATA ends
;
; Macro to output a word value to a port.
;
OUT_WORD macro
if WORD_OUTS_OK
out dx,ax
else
out dx,al
inc dx
xchg ah,al
out dx,al
dec dx
xchg ah,al
endif
endm
;
_TEXT segment byte public 'CODE'
assume cs:_TEXT, ds:_DATA
;
; Sets up 360x480 256-color mode.
; (Courtesy of John Bridges.)
;
; Call as: void Set360By480Mode()
;
; Returns: nothing
;
public _Set360x480Mode
_Set360x480Mode proc near
push si ;preserve C register vars
push di
mov ax,12h ; start with mode 12h
int 10h ; let the bios clear the video memory
mov ax,13h ; start with standard mode 13h
int 10h ; let the bios set the mode
mov dx,3c4h ; alter sequencer registers
mov ax,0604h ; disable chain 4
out dx,ax
mov ax,0100h ; synchronous reset
out dx,ax ; asserted
mov dx,3c2h ; misc output
mov al,0e7h ; use 28 mHz dot clock
out dx,al ; select it
mov dx,3c4h ; sequencer again
mov ax,0300h ; restart sequencer
out dx,ax ; running again
mov dx,3d4h ; alter crtc registers
mov al,11h ; cr11
out dx,al ; current value
inc dx ; point to data
in al,dx ; get cr11 value
and al,7fh ; remove cr0 -> cr7
out dx,al ; write protect
dec dx ; point to index
cld
mov si,offset vptbl
mov cx,((offset vpend)-(offset vptbl)) shr 1
@b: lodsw
out dx,ax
loop @b
pop di ;restore C register vars
pop si
ret
_Set360x480Mode endp
;
; Draws a pixel in the specified color at the specified
; location in 360x480 256-color mode.
;
; Call as: void Draw360x480Dot(int X, int Y, int Color)
;
; Returns: nothing
;
DParms struc
dw ? ;pushed BP
dw ? ;return address
DrawX dw ? ;X coordinate at which to draw
DrawY dw ? ;Y coordinate at which to draw
Color dw ? ;color in which to draw (in the
; range 0-255; upper byte ignored)
DParms ends
;
public _Draw360x480Dot
_Draw360x480Dot proc near
push bp ;preserve caller's BP
mov bp,sp ;point to stack frame
push si ;preserve C register vars
push di
mov ax,VGA_SEGMENT
mov es,ax ;point to display memory
mov ax,SCREEN_WIDTH/4
;there are 4 pixels at each address, so
; each 360-pixel row is 90 bytes wide
; in each plane
mul [bp+DrawY] ;point to start of desired row
mov di,[bp+DrawX] ;get the X coordinate
shr di,1 ;there are 4 pixels at each address
shr di,1 ; so divide the X coordinate by 4
add di,ax ;point to the pixel's address
mov cl,byte ptr [bp+DrawX] ;get the X coordinate again
and cl,3 ;get the plane # of the pixel
mov ah,1
shl ah,cl ;set the bit corresponding to the plane
; the pixel is in
mov al,MAP_MASK
mov dx,SC_INDEX
OUT_WORD ;set to write to the proper plane for
; the pixel
mov al,byte ptr [bp+Color] ;get the color
stosb ;draw the pixel
pop di ;restore C register vars
pop si
pop bp ;restore caller's BP
ret
_Draw360x480Dot endp
;
; Reads the color of the pixel at the specified
; location in 360x480 256-color mode.
;
; Call as: int Read360x480Dot(int X, int Y)
;
; Returns: pixel color
;
RParms struc
dw ? ;pushed BP
dw ? ;return address
ReadX dw ? ;X coordinate from which to read
ReadY dw ? ;Y coordinate from which to read
RParms ends
;
public _Read360x480Dot
_Read360x480Dot proc near
push bp ;preserve caller's BP
mov bp,sp ;point to stack frame
push si ;preserve C register vars
push di
mov ax,VGA_SEGMENT
mov es,ax ;point to display memory
mov ax,SCREEN_WIDTH/4
;there are 4 pixels at each address, so
; each 360-pixel row is 90 bytes wide
; in each plane
mul [bp+DrawY] ;point to start of desired row
mov si,[bp+DrawX] ;get the X coordinate
shr si,1 ;there are 4 pixels at each address
shr si,1 ; so divide the X coordinate by 4
add si,ax ;point to the pixel's address
mov ah,byte ptr [bp+DrawX]
;get the X coordinate again
and ah,3 ;get the plane # of the pixel
mov al,READ_MAP
mov dx,GC_INDEX
OUT_WORD ;set to read from the proper plane for
; the pixel
lods byte ptr es:[si];read the pixel
sub ah,ah ;make the return value a word for C
pop di ;restore C register vars
pop si
pop bp ;restore caller's BP
ret
_Read360x480Dot endp
_TEXT ends
end

201
tests/pcx86/vga/L32-2.C Normal file
View file

@ -0,0 +1,201 @@
/*
* Sample program to illustrate VGA line drawing in 360x480
* 256-color mode.
*
* Compiled with Borland C/C++.
*
* Must be linked with Listing 32.1 with a command line like:
*
* bcc l32-2.c l32-1.asm
*
* By Michael Abrash
*/
#include <dos.h> /* contains geninterrupt */
#define TEXT_MODE 0x03
#define BIOS_VIDEO_INT 0x10
#define X_MAX 360 /* working screen width */
#define Y_MAX 480 /* working screen height */
extern void Draw360x480Dot();
extern void Set360x480Mode();
/*
* Draws a line in octant 0 or 3 ( |DeltaX| >= DeltaY ).
* |DeltaX|+1 points are drawn.
*/
void Octant0(X0, Y0, DeltaX, DeltaY, XDirection, Color)
unsigned int X0, Y0; /* coordinates of start of the line */
unsigned int DeltaX, DeltaY; /* length of the line */
int XDirection; /* 1 if line is drawn left to right,
-1 if drawn right to left */
int Color; /* color in which to draw line */
{
int DeltaYx2;
int DeltaYx2MinusDeltaXx2;
int ErrorTerm;
/* Set up initial error term and values used inside drawing loop */
DeltaYx2 = DeltaY * 2;
DeltaYx2MinusDeltaXx2 = DeltaYx2 - (int) ( DeltaX * 2 );
ErrorTerm = DeltaYx2 - (int) DeltaX;
/* Draw the line */
Draw360x480Dot(X0, Y0, Color); /* draw the first pixel */
while ( DeltaX-- ) {
/* See if it's time to advance the Y coordinate */
if ( ErrorTerm >= 0 ) {
/* Advance the Y coordinate & adjust the error term
back down */
Y0++;
ErrorTerm += DeltaYx2MinusDeltaXx2;
} else {
/* Add to the error term */
ErrorTerm += DeltaYx2;
}
X0 += XDirection; /* advance the X coordinate */
Draw360x480Dot(X0, Y0, Color); /* draw a pixel */
}
}
/*
* Draws a line in octant 1 or 2 ( |DeltaX| < DeltaY ).
* |DeltaY|+1 points are drawn.
*/
void Octant1(X0, Y0, DeltaX, DeltaY, XDirection, Color)
unsigned int X0, Y0; /* coordinates of start of the line */
unsigned int DeltaX, DeltaY; /* length of the line */
int XDirection; /* 1 if line is drawn left to right,
-1 if drawn right to left */
int Color; /* color in which to draw line */
{
int DeltaXx2;
int DeltaXx2MinusDeltaYx2;
int ErrorTerm;
/* Set up initial error term and values used inside drawing loop */
DeltaXx2 = DeltaX * 2;
DeltaXx2MinusDeltaYx2 = DeltaXx2 - (int) ( DeltaY * 2 );
ErrorTerm = DeltaXx2 - (int) DeltaY;
Draw360x480Dot(X0, Y0, Color); /* draw the first pixel */
while ( DeltaY-- ) {
/* See if it's time to advance the X coordinate */
if ( ErrorTerm >= 0 ) {
/* Advance the X coordinate & adjust the error term
back down */
X0 += XDirection;
ErrorTerm += DeltaXx2MinusDeltaYx2;
} else {
/* Add to the error term */
ErrorTerm += DeltaXx2;
}
Y0++; /* advance the Y coordinate */
Draw360x480Dot(X0, Y0,Color); /* draw a pixel */
}
}
/*
* Draws a line on the EGA or VGA.
*/
void EVGALine(X0, Y0, X1, Y1, Color)
int X0, Y0; /* coordinates of one end of the line */
int X1, Y1; /* coordinates of the other end of the line */
unsigned char Color; /* color in which to draw line */
{
int DeltaX, DeltaY;
int Temp;
/* Save half the line-drawing cases by swapping Y0 with Y1
and X0 with X1 if Y0 is greater than Y1. As a result, DeltaY
is always > 0, and only the octant 0-3 cases need to be
handled. */
if ( Y0 > Y1 ) {
Temp = Y0;
Y0 = Y1;
Y1 = Temp;
Temp = X0;
X0 = X1;
X1 = Temp;
}
/* Handle as four separate cases, for the four octants in which
Y1 is greater than Y0 */
DeltaX = X1 - X0; /* calculate the length of the line
in each coordinate */
DeltaY = Y1 - Y0;
if ( DeltaX > 0 ) {
if ( DeltaX > DeltaY ) {
Octant0(X0, Y0, DeltaX, DeltaY, 1, Color);
} else {
Octant1(X0, Y0, DeltaX, DeltaY, 1, Color);
}
} else {
DeltaX = -DeltaX; /* absolute value of DeltaX */
if ( DeltaX > DeltaY ) {
Octant0(X0, Y0, DeltaX, DeltaY, -1, Color);
} else {
Octant1(X0, Y0, DeltaX, DeltaY, -1, Color);
}
}
}
/*
* Subroutine to draw a rectangle full of vectors, of the
* specified length and in varying colors, around the
* specified rectangle center.
*/
void VectorsUp(XCenter, YCenter, XLength, YLength)
int XCenter, YCenter; /* center of rectangle to fill */
int XLength, YLength; /* distance from center to edge
of rectangle */
{
int WorkingX, WorkingY, Color = 1;
/* Lines from center to top of rectangle */
WorkingX = XCenter - XLength;
WorkingY = YCenter - YLength;
for ( ; WorkingX < ( XCenter + XLength ); WorkingX++ )
EVGALine(XCenter, YCenter, WorkingX, WorkingY, Color++);
/* Lines from center to right of rectangle */
WorkingX = XCenter + XLength - 1;
WorkingY = YCenter - YLength;
for ( ; WorkingY < ( YCenter + YLength ); WorkingY++ )
EVGALine(XCenter, YCenter, WorkingX, WorkingY, Color++);
/* Lines from center to bottom of rectangle */
WorkingX = XCenter + XLength - 1;
WorkingY = YCenter + YLength - 1;
for ( ; WorkingX >= ( XCenter - XLength ); WorkingX-- )
EVGALine(XCenter, YCenter, WorkingX, WorkingY, Color++);
/* Lines from center to left of rectangle */
WorkingX = XCenter - XLength;
WorkingY = YCenter + YLength - 1;
for ( ; WorkingY >= ( YCenter - YLength ); WorkingY-- )
EVGALine(XCenter, YCenter, WorkingX, WorkingY, Color++);
}
/*
* Sample program to draw four rectangles full of lines.
*/
void main()
{
char temp;
Set360x480Mode();
/* Draw each of four rectangles full of vectors */
VectorsUp(X_MAX / 4, Y_MAX / 4, X_MAX / 4, Y_MAX / 4, 1);
VectorsUp(X_MAX * 3 / 4, Y_MAX / 4, X_MAX / 4, Y_MAX / 4, 2);
VectorsUp(X_MAX / 4, Y_MAX * 3 / 4, X_MAX / 4, Y_MAX / 4, 3);
VectorsUp(X_MAX * 3 / 4, Y_MAX * 3 / 4, X_MAX / 4, Y_MAX / 4, 4);
/* Wait for the enter key to be pressed */
scanf("%c", &temp);
/* Back to text mode */
_AX = TEXT_MODE;
geninterrupt(BIOS_VIDEO_INT);
}

176
tests/pcx86/vga/L33-1.ASM Normal file
View file

@ -0,0 +1,176 @@
; Program to demonstrate use of the DAC registers by selecting a
; smoothly contiguous set of 256 colors, then filling the screen
; with concentric diamonds in all 256 colors so that they blend
; into one another to form a continuum of color.
;
.model small
.stack 200h
.data
; Table used to set all 256 DAC entries.
;
; Table format:
; Byte 0: DAC register 0 red value
; Byte 1: DAC register 0 green value
; Byte 2: DAC register 0 blue value
; Byte 3: DAC register 1 red value
; Byte 4: DAC register 1 green value
; Byte 5: DAC register 1 blue value
; :
; Byte 765: DAC register 255 red value
; Byte 766: DAC register 255 green value
; Byte 767: DAC register 255 blue value
ColorTable label byte
; The first 64 entries are increasingly dim pure green.
X=0
REPT 64
db 0,63-X,0
X=X+1
ENDM
; The next 64 entries are increasingly strong pure blue.
X=0
REPT 64
db 0,0,X
X=X+1
ENDM
; The next 64 entries fade through violet to red.
X=0
REPT 64
db X,0,63-X
X=X+1
ENDM
; The last 64 entries are increasingly dim pure red.
X=0
REPT 64
db 63-X,0,0
X=X+1
ENDM
.code
Start:
mov ax,0013h ;AH=0 selects set mode function,
; AL=13h selects 320x200 256-color
int 10h ; mode
;load the DAC registers with the
; color settings
mov ax,@data ;point ES to the default
mov es,ax ; data segment
mov dx,offset ColorTable
;point ES:DX to the start of the
; block of RGB three-byte values
; to load into the DAC registers
mov ax,1012h ;AH=10h selects set color function,
; AL=12h selects set block of DAC
; registers subfunction
sub bx,bx ;load the block of registers
; starting at DAC register #0
mov cx,100h ;set all 256 registers
int 10h ;load the DAC registers
;now fill the screen with
; concentric diamonds in all 256
; color attributes
mov ax,0a000h ;point DS to the display memory
mov ds,ax ; segment
;
;draw diagonal lines in the upper
; left quarter of the screen
mov al,2 ;start with color attribute #2
mov ah,-1 ;cycle down through the colors
mov bx,320 ;draw top to bottom (distance from
; one line to the next)
mov dx,160 ;width of rectangle
mov si,100 ;height of rectangle
sub di,di ;start at (0,0)
mov bp,1 ;draw left to right (distance from
; one column to the next)
call FillBlock ;draw it
;
;draw diagonal lines in the upper
; right quarter of the screen
mov al,2 ;start with color attribute #2
mov ah,-1 ;cycle down through the colors
mov bx,320 ;draw top to bottom (distance from
; one line to the next)
mov dx,160 ;width of rectangle
mov si,100 ;height of rectangle
mov di,319 ;start at (319,0)
mov bp,-1 ;draw right to left (distance from
; one column to the next)
call FillBlock ;draw it
;draw diagonal lines in the lower
; left quarter of the screen
mov al,2 ;start with color attribute #2
mov ah,-1 ;cycle down through the colors
mov bx,-320 ;draw bottom to top (distance from
; one line to the next)
mov dx,160 ;width of rectangle
mov si,100 ;height of rectangle
mov di,199*320 ;start at (0,199)
mov bp,1 ;draw left to right (distance from
; one column to the next)
call FillBlock ;draw it
;
;draw diagonal lines in the lower
; right quarter of the screen
mov al,2 ;start with color attribute #2
mov ah,-1 ;cycle down through the colors
mov bx,-320 ;draw bottom to top (distance from
; one line to the next)
mov dx,160 ;width of rectangle
mov si,100 ;height of rectangle
mov di,199*320+319 ;start at (319,199)
mov bp,-1 ;draw right to left (distance from
; one column to the next)
call FillBlock ;draw it
mov ah,1 ;wait for a key
int 21h ;
mov ax,0003h ;return to text mode
int 10h ;
mov ah,4ch ;done--return to DOS
int 21h
; Fills the specified rectangular area of the screen with diagonal
; lines.
;
; Input:
; AL = initial attribute with which to draw
; AH = amount by which to advance the attribute from
; one pixel to the next
; BX = distance to advance from one pixel to the next
; DX = width of rectangle to fill
; SI = height of rectangle to fill
; DS:DN = screen address of first pixel to draw
; BP = offset from the start of one column to the start of
; the next
FillBlock:
FillHorzLoop:
push di ;preserve pointer to top of column
push ax ;preserve initial attribute
mov cx,si ;column height
FillVertLoop:
mov [di],al ;set the pixel
add di,bx ;point to the next row in the column
add al,ah ;advance the attribute
loop FillVertLoop ;
pop ax ;restore initial attribute
add al,ah ;advance to the next attribute to
; start the next column
pop di ;retrieve pointer to top of column
add di,bp ;point to next column
dec dx ;have we done all columns?
jnz FillHorzLoop ;no, do the next column
ret ;
end Start

250
tests/pcx86/vga/L34-1.ASM Normal file
View file

@ -0,0 +1,250 @@
; Fills a band across the screen with vertical bars in all 256
; attributes, then cycles a portion of the palette until a key is
; pressed.
; Assemble with MASM or TASM
;
USE_BIOS equ 1 ;set to 1 to use BIOS functions to access the
; DAC, 0 to read and write the DAC directly
GUARD_AGAINST_INTS equ 1 ;1 to turn off interrupts and set write index
; before loading each DAC location, 0 to rely
; on the DAC auto-incrementing
WAIT_VSYNC equ 1 ;set to 1 to wait for the leading edge of
; vertical sync before accessing the DAC, 0
; not to wait
NOT_8088 equ 0 ;set to 1 to use REP INSB and REP OUTSB when
; accessing the DAC directly, 0 to use
; IN/STOSB and LODSB/OUT
CYCLE_SIZE equ 256 ;# of DAC locations to cycle, 256 max
SCREEN_SEGMENT equ 0a000h ;mode 13h display memory segment
SCREEN_WIDTH_IN_BYTES equ 320 ;# of bytes across the screen in mode 13h
INPUT_STATUS_1 equ 03dah ;input status 1 register port
DAC_READ_INDEX equ 03c7h ;DAC Read Index register
DAC_WRITE_INDEX equ 03c8h ;DAC Write Index register
DAC_DATA equ 03c9h ;DAC Data register
if NOT_8088
.286
endif ;NOT_8088
.model small
.stack 100h
.data
;Storage for all 256 DAC locations, organized as one three-byte
; (actually three 6-bit values; upper two bits of each byte aren't
; significant) RGB triplet per color.
PaletteTemp db 256*3 dup(?)
.code
start:
mov ax,@data
mov ds,ax
;Select VGA's standard 256-color graphics mode, mode 13h.
mov ax,0013h ;AH = 0: set mode function,
int 10h ; AL = 13h: mode # to set
;Read all 256 DAC locations into PaletteTemp (3 6-bit values, one
; each for red, green, and blue, per DAC location).
if WAIT_VSYNC
;Wait for the leading edge of the vertical sync pulse; this ensures
; that we read the DAC starting during the vertical non-display
; period.
mov dx,INPUT_STATUS_1
WaitNotVSync: ;wait to be out of vertical sync
in al,dx
and al,08h
jnz WaitNotVSync
WaitVSync: ;wait until vertical sync begins
in al,dx
and al,08h
jz WaitVSync
endif ;WAIT_VSYNC
if USE_BIOS
mov ax,1017h ;AH = 10h: set DAC function,
; AL = 17h: read DAC block subfunction
sub bx,bx ;start with DAC location 0
mov cx,256 ;read out all 256 locations
mov dx,seg PaletteTemp
mov es,dx
mov dx,offset PaletteTemp ;point ES:DX to array in which
; the DAC values are to be stored
int 10h ;read the DAC
else ;!USE_BIOS
if GUARD_AGAINST_INTS
mov cx,CYCLE_SIZE ;# of DAC locations to load
mov di,seg PaletteTemp
mov es,di
mov di,offset PaletteTemp ;dump the DAC into this array
sub ah,ah ;start with DAC location 0
DACStoreLoop:
mov dx,DAC_READ_INDEX
mov al,ah
cli
out dx,al ;set the DAC location #
mov dx,DAC_DATA
in al,dx ;get the red component
stosb
in al,dx ;get the green component
stosb
in al,dx ;get the blue component
stosb
sti
inc ah
loop DACStoreLoop
else ;!GUARD_AGAINST_INTS
mov dx,DAC_READ_INDEX
sub al,al
out dx,al ;set the initial DAC location to 0
mov di,seg PaletteTemp
mov es,di
mov di,offset PaletteTemp ;dump the DAC into this array
mov dx,DAC_DATA
if NOT_8088
mov cx,CYCLE_SIZE*3
rep insb ;read CYCLE_SIZE DAC locations at once
else ;!NOT_8088
mov cx,CYCLE_SIZE ;# of DAC locations to load
DACStoreLoop:
in al,dx ;get the red component
stosb
in al,dx ;get the green component
stosb
in al,dx ;get the blue component
stosb
loop DACStoreLoop
endif ;NOT_8088
endif ;GUARD_AGAINST_INTS
endif ;USE_BIOS
;Draw a series of 1-pixel-wide vertical bars across the screen in
; attributes 1 through 255.
mov ax,SCREEN_SEGMENT
mov es,ax
mov di,50*SCREEN_WIDTH_IN_BYTES ;point ES:DI to the start
; of line 50 on the screen
cld
mov dx,100 ;draw 100 lines high
RowLoop:
mov al,1 ;start each line with attr 1
mov cx,SCREEN_WIDTH_IN_BYTES ;do a full line across
ColumnLoop:
stosb ;draw a pixel
add al,1 ;increment the attribute
adc al,0 ;if the attribute just turned
; over to 0, increment it to 1
; because we're not going to
; cycle DAC location 0, so
; attribute 0 won't change
loop ColumnLoop
dec dx
jnz RowLoop
;Cycle the specified range of DAC locations until a key is pressed.
CycleLoop:
;Rotate colors 1-255 one position in the PaletteTemp array;
; location 0 is always left unchanged so that the background
; and border don't change.
push word ptr PaletteTemp+(1*3) ;set aside PaletteTemp
push word ptr PaletteTemp+(1*3)+2 ; setting for attr 1
mov cx,254
mov si,offset PaletteTemp+(2*3)
mov di,offset PaletteTemp+(1*3)
mov ax,ds
mov es,ax
mov cx,254*3/2
rep movsw ;rotate PaletteTemp settings
; for attrs 2 through 255 to
; attrs 1 through 254
pop bx ;get back original settings
pop ax ; for attribute 1 and move
stosw ; them to the PaletteTemp
mov es:[di],bl ; location for attribute 255
if WAIT_VSYNC
;Wait for the leading edge of the vertical sync pulse; this ensures
; that we reload the DAC starting during the vertical non-display
; period.
mov dx,INPUT_STATUS_1
WaitNotVSync2: ;wait to be out of vertical sync
in al,dx
and al,08h
jnz WaitNotVSync2
WaitVSync2: ;wait until vertical sync begins
in al,dx
and al,08h
jz WaitVSync2
endif ;WAIT_VSYNC
if USE_BIOS
;Set the new, rotated palette.
mov ax,1012h ;AH = 10h: set DAC function,
; AL = 12h: set DAC block subfunction
sub bx,bx ;start with DAC location 0
mov cx,CYCLE_SIZE ;# of DAC locations to set
mov dx,seg PaletteTemp
mov es,dx
mov dx,offset PaletteTemp ;point ES:DX to array from which
; to load the DAC
int 10h ;load the DAC
else ;!USE_BIOS
if GUARD_AGAINST_INTS
mov cx,CYCLE_SIZE ;# of DAC locations to load
mov si,offset PaletteTemp ;load the DAC from this array
sub ah,ah ;start with DAC location 0
DACLoadLoop:
mov dx,DAC_WRITE_INDEX
mov al,ah
cli
out dx,al ;set the DAC location #
mov dx,DAC_DATA
lodsb
out dx,al ;set the red component
lodsb
out dx,al ;set the green component
lodsb
out dx,al ;set the blue component
sti
inc ah
loop DACLoadLoop
else ;!GUARD_AGAINST_INTS
mov dx,DAC_WRITE_INDEX
sub al,al
out dx,al ;set the initial DAC location to 0
mov si,offset PaletteTemp ;load the DAC from this array
mov dx,DAC_DATA
if NOT_8088
mov cx,CYCLE_SIZE*3
rep outsb ;load CYCLE_SIZE DAC locations at once
else ;!NOT_8088
mov cx,CYCLE_SIZE ;# of DAC locations to load
DACLoadLoop:
lodsb
out dx,al ;set the red component
lodsb
out dx,al ;set the green component
lodsb
out dx,al ;set the blue component
loop DACLoadLoop
endif ;NOT_8088
endif ;GUARD_AGAINST_INTS
endif ;USE_BIOS
;See if a key has been pressed.
mov ah,0bh ;DOS check standard input status fn
int 21h
and al,al ;is a key pending?
jz CycleLoop ;no, cycle some more
;Clear the keypress.
mov ah,1 ;DOS keyboard input fn
int 21h
;Restore text mode and done.
mov ax,0003h ;AH = 0: set mode function,
int 10h ; AL = 03h: mode # to set
mov ah,4ch ;DOS terminate process fn
int 21h
end start

191
tests/pcx86/vga/L35-1.C Normal file
View file

@ -0,0 +1,191 @@
/*
* C implementation of Bresenham's line drawing algorithm
* for the EGA and VGA. Works in modes 0xE, 0xF, 0x10, and 0x12.
*
* Compiled with Borland C++
*
* By Michael Abrash
*/
#include <dos.h> /* contains MK_FP macro */
#include "regs.h"
#define EVGA_SCREEN_WIDTH_IN_BYTES 80
/* memory offset from start of
one row to start of next */
#define EVGA_SCREEN_SEGMENT 0xA000
/* display memory segment */
#define GC_INDEX 0x3CE
/* Graphics Controller
Index register port */
#define GC_DATA 0x3CF
/* Graphics Controller
Data register port */
#define SET_RESET_INDEX 0 /* indexes of needed */
#define ENABLE_SET_RESET_INDEX 1 /* Graphics Controller */
#define BIT_MASK_INDEX 8 /* registers */
/*
* Draws a dot at (X0,Y0) in whatever color the EGA/VGA hardware is
* set up for. Leaves the bit mask set to whatever value the
* dot required.
*/
void EVGADot(X0, Y0)
unsigned int X0; /* coordinates at which to draw dot, with */
unsigned int Y0; /* (0,0) at the upper left of the screen */
{
unsigned char far *PixelBytePtr;
unsigned char PixelMask;
/* Calculate the offset in the screen segment of the byte in
which the pixel lies */
PixelBytePtr = MK_FP(EVGA_SCREEN_SEGMENT,
( Y0 * EVGA_SCREEN_WIDTH_IN_BYTES ) + ( X0 / 8 ));
/* Generate a mask with a 1 bit in the pixel's position within the
screen byte */
PixelMask = 0x80 >> ( X0 & 0x07 );
/* Set up the Graphics Controller's Bit Mask register to allow
only the bit corresponding to the pixel being drawn to
be modified */
outportb(GC_INDEX, BIT_MASK_INDEX);
outportb(GC_DATA, PixelMask);
/* Draw the pixel. Because of the operation of the set/reset
feature of the EGA/VGA, the value written doesn't matter.
The screen byte is ORed in order to perform a read to latch the
display memory, then perform a write in order to modify it. */
*PixelBytePtr |= 0xFE;
}
/*
* Draws a line in octant 0 or 3 ( |DeltaX| >= DeltaY ).
*/
void Octant0(X0, Y0, DeltaX, DeltaY, XDirection)
unsigned int X0, Y0; /* coordinates of start of the line */
unsigned int DeltaX, DeltaY; /* length of the line (both > 0) */
int XDirection; /* 1 if line is drawn left to right,
-1 if drawn right to left */
{
int DeltaYx2;
int DeltaYx2MinusDeltaXx2;
int ErrorTerm;
/* Set up initial error term and values used inside drawing loop */
DeltaYx2 = DeltaY * 2;
DeltaYx2MinusDeltaXx2 = DeltaYx2 - (int) ( DeltaX * 2 );
ErrorTerm = DeltaYx2 - (int) DeltaX;
/* Draw the line */
EVGADot(X0, Y0); /* draw the first pixel */
while ( DeltaX-- ) {
/* See if it's time to advance the Y coordinate */
if ( ErrorTerm >= 0 ) {
/* Advance the Y coordinate & adjust the error term
back down */
Y0++;
ErrorTerm += DeltaYx2MinusDeltaXx2;
} else {
/* Add to the error term */
ErrorTerm += DeltaYx2;
}
X0 += XDirection; /* advance the X coordinate */
EVGADot(X0, Y0); /* draw a pixel */
}
}
/*
* Draws a line in octant 1 or 2 ( |DeltaX| < DeltaY ).
*/
void Octant1(X0, Y0, DeltaX, DeltaY, XDirection)
unsigned int X0, Y0; /* coordinates of start of the line */
unsigned int DeltaX, DeltaY; /* length of the line (both > 0) */
int XDirection; /* 1 if line is drawn left to right,
-1 if drawn right to left */
{
int DeltaXx2;
int DeltaXx2MinusDeltaYx2;
int ErrorTerm;
/* Set up initial error term and values used inside drawing loop */
DeltaXx2 = DeltaX * 2;
DeltaXx2MinusDeltaYx2 = DeltaXx2 - (int) ( DeltaY * 2 );
ErrorTerm = DeltaXx2 - (int) DeltaY;
EVGADot(X0, Y0); /* draw the first pixel */
while ( DeltaY-- ) {
/* See if it's time to advance the X coordinate */
if ( ErrorTerm >= 0 ) {
/* Advance the X coordinate & adjust the error term
back down */
X0 += XDirection;
ErrorTerm += DeltaXx2MinusDeltaYx2;
} else {
/* Add to the error term */
ErrorTerm += DeltaXx2;
}
Y0++; /* advance the Y coordinate */
EVGADot(X0, Y0); /* draw a pixel */
}
}
/*
* Draws a line on the EGA or VGA.
*/
void EVGALine(X0, Y0, X1, Y1, Color)
int X0, Y0; /* coordinates of one end of the line */
int X1, Y1; /* coordinates of the other end of the line */
char Color; /* color to draw line in */
{
int DeltaX, DeltaY;
int Temp;
/* Set the drawing color */
/* Put the drawing color in the Set/Reset register */
outportb(GC_INDEX, SET_RESET_INDEX);
outportb(GC_DATA, Color);
/* Cause all planes to be forced to the Set/Reset color */
outportb(GC_INDEX, ENABLE_SET_RESET_INDEX);
outportb(GC_DATA, 0xF);
/* Save half the line-drawing cases by swapping Y0 with Y1
and X0 with X1 if Y0 is greater than Y1. As a result, DeltaY
is always > 0, and only the octant 0-3 cases need to be
handled. */
if ( Y0 > Y1 ) {
Temp = Y0;
Y0 = Y1;
Y1 = Temp;
Temp = X0;
X0 = X1;
X1 = Temp;
}
/* Handle as four separate cases, for the four octants in which
Y1 is greater than Y0 */
DeltaX = X1 - X0; /* calculate the length of the line
in each coordinate */
DeltaY = Y1 - Y0;
if ( DeltaX > 0 ) {
if ( DeltaX > DeltaY ) {
Octant0(X0, Y0, DeltaX, DeltaY, 1);
} else {
Octant1(X0, Y0, DeltaX, DeltaY, 1);
}
} else {
DeltaX = -DeltaX; /* absolute value of DeltaX */
if ( DeltaX > DeltaY ) {
Octant0(X0, Y0, DeltaX, DeltaY, -1);
} else {
Octant1(X0, Y0, DeltaX, DeltaY, -1);
}
}
/* Return the state of the EGA/VGA to normal */
outportb(GC_INDEX, ENABLE_SET_RESET_INDEX);
outportb(GC_DATA, 0);
outportb(GC_INDEX, BIT_MASK_INDEX);
outportb(GC_DATA, 0xFF);
}

81
tests/pcx86/vga/L35-2.C Normal file
View file

@ -0,0 +1,81 @@
/*
* Sample program to illustrate EGA/VGA line drawing routines.
*
* Compiled with Borland C++
*
* By Michael Abrash
*/
#include <dos.h>
#include "regs.h"
#define GRAPHICS_MODE 0x10
#define TEXT_MODE 0x03
#define BIOS_VIDEO_INT 0x10
#define X_MAX 640 /* working screen width */
#define Y_MAX 348 /* working screen height */
extern void EVGALine();
/*
* Subroutine to draw a rectangle full of vectors, of the specified
* length and color, around the specified rectangle center.
*/
void VectorsUp(XCenter, YCenter, XLength, YLength, Color)
int XCenter, YCenter; /* center of rectangle to fill */
int XLength, YLength; /* distance from center to edge
of rectangle */
int Color; /* color to draw lines in */
{
int WorkingX, WorkingY;
/* Lines from center to top of rectangle */
WorkingX = XCenter - XLength;
WorkingY = YCenter - YLength;
for ( ; WorkingX < ( XCenter + XLength ); WorkingX++ )
EVGALine(XCenter, YCenter, WorkingX, WorkingY, Color);
/* Lines from center to right of rectangle */
WorkingX = XCenter + XLength - 1;
WorkingY = YCenter - YLength;
for ( ; WorkingY < ( YCenter + YLength ); WorkingY++ )
EVGALine(XCenter, YCenter, WorkingX, WorkingY, Color);
/* Lines from center to bottom of rectangle */
WorkingX = XCenter + XLength - 1;
WorkingY = YCenter + YLength - 1;
for ( ; WorkingX >= ( XCenter - XLength ); WorkingX-- )
EVGALine(XCenter, YCenter, WorkingX, WorkingY, Color);
/* Lines from center to left of rectangle */
WorkingX = XCenter - XLength;
WorkingY = YCenter + YLength - 1;
for ( ; WorkingY >= ( YCenter - YLength ); WorkingY-- )
EVGALine(XCenter, YCenter, WorkingX, WorkingY, Color );
}
/*
* Sample program to draw four rectangles full of lines.
*/
void main()
{
char temp;
/* Set graphics mode */
USES_REGS;
_AX = GRAPHICS_MODE;
geninterrupt(BIOS_VIDEO_INT);
/* Draw each of four rectangles full of vectors */
VectorsUp(X_MAX / 4, Y_MAX / 4, X_MAX / 4, Y_MAX / 4, 1);
VectorsUp(X_MAX * 3 / 4, Y_MAX / 4, X_MAX / 4, Y_MAX / 4, 2);
VectorsUp(X_MAX / 4, Y_MAX * 3 / 4, X_MAX / 4, Y_MAX / 4, 3);
VectorsUp(X_MAX * 3 / 4, Y_MAX * 3 / 4, X_MAX / 4, Y_MAX / 4, 4);
/* Wait for the enter key to be pressed */
scanf("%c", &temp);
/* Back to text mode */
_AX = TEXT_MODE;
geninterrupt(BIOS_VIDEO_INT);
}

366
tests/pcx86/vga/L35-3.ASM Normal file
View file

@ -0,0 +1,366 @@
; Fast assembler implementation of Bresenham's line drawing algorithm
; for the EGA and VGA. Works in modes 0Eh, 0Fh, 10h, and 12h.
; C near-callable.
; Bit mask accumulation technique when |DeltaX| >= |DeltaY|
; suggested by Jim Mackraz.
;
; Assembled with MASM
;
; By Michael Abrash
;
;****************************************************************
; C-compatible line-drawing entry point at _EVGALine. *
; Near C-callable as: *
; EVGALine(X0, Y0, X1, Y1, Color); *
;****************************************************************
;
.model small
.code
;
; Equates.
;
EVGA_SCREEN_WIDTH_IN_BYTES equ 80 ;memory offset from start of
; one row to start of next
; in display memory
EVGA_SCREEN_SEGMENT equ 0a000h ;display memory segment
GC_INDEX equ 3ceh ;Graphics Controller
; Index register port
SET_RESET_INDEX equ 0 ;indexes of needed
ENABLE_SET_RESET_INDEX equ 1 ; Graphics Controller
BIT_MASK_INDEX equ 8 ; registers
;
; Stack frame.
;
EVGALineParms struc
dw ? ;pushed BP
dw ? ;pushed return address (make double
; word for far call)
X0 dw ? ;starting X coordinate of line
Y0 dw ? ;starting Y coordinate of line
X1 dw ? ;ending X coordinate of line
Y1 dw ? ;ending Y coordinate of line
Color db ? ;color of line
db ? ;dummy to pad to word size
EVGALineParms ends
;****************************************************************
; Line drawing macros. *
;****************************************************************
;
; Macro to loop through length of line, drawing each pixel in turn.
; Used for case of |DeltaX| >= |DeltaY|.
; Input:
; MOVE_LEFT: 1 if DeltaX < 0, 0 else
; AL: pixel mask for initial pixel
; BX: |DeltaX|
; DX: address of GC data register, with index register set to
; index of Bit Mask register
; SI: DeltaY
; ES:DI: display memory address of byte containing initial
; pixel
;
LINE1 macro MOVE_LEFT
local LineLoop, MoveXCoord, NextPixel, Line1End
local MoveToNextByte, ResetBitMaskAccumulator
mov cx,bx ;# of pixels in line
jcxz Line1End ;done if there are no more pixels
; (there's always at least the one pixel
; at the start location)
shl si,1 ;DeltaY * 2
mov bp,si ;error term
sub bp,bx ;error term starts at DeltaY * 2 - DeltaX
shl bx,1 ;DeltaX * 2
sub si,bx ;DeltaY * 2 - DeltaX * 2 (used in loop)
add bx,si ;DeltaY * 2 (used in loop)
mov ah,al ;set aside pixel mask for initial pixel
; with AL (the pixel mask accumulator) set
; for the initial pixel
LineLoop:
;
; See if it's time to advance the Y coordinate yet.
;
and bp,bp ;see if error term is negative
js MoveXCoord ;yes, stay at the same Y coordinate
;
; Advance the Y coordinate, first writing all pixels in the current
; byte, then move the pixel mask either left or right, depending
; on MOVE_LEFT.
;
out dx,al ;set up bit mask for pixels in this byte
xchg byte ptr [di],al
;load latches and write pixels, with bit mask
; preserving other latched bits. Because
; set/reset is enabled for all planes, the
; value written actually doesn't matter
add di,EVGA_SCREEN_WIDTH_IN_BYTES ;increment Y coordinate
add bp,si ;adjust error term back down
;
; Move pixel mask one pixel (either right or left, depending
; on MOVE_LEFT), adjusting display memory address when pixel mask wraps.
;
if MOVE_LEFT
rol ah,1 ;move pixel mask 1 pixel to the left
else
ror ah,1 ;move pixel mask 1 pixel to the right
endif
jnc ResetBitMaskAccumulator ;didn't wrap to next byte
jmp short MoveToNextByte ;did wrap to next byte
;
; Move pixel mask one pixel (either right or left, depending
; on MOVE_LEFT), adjusting display memory address and writing pixels
; in this byte when pixel mask wraps.
;
MoveXCoord:
add bp,bx ;increment error term & keep same
if MOVE_LEFT
rol ah,1 ;move pixel mask 1 pixel to the left
else
ror ah,1 ;move pixel mask 1 pixel to the right
endif
jnc NextPixel ;if still in same byte, no need to
; modify display memory yet
out dx,al ;set up bit mask for pixels in this byte.
xchg byte ptr [di],al
;load latches and write pixels, with bit mask
; preserving other latched bits. Because
; set/reset is enabled for all planes, the
; value written actually doesn't matter
MoveToNextByte:
if MOVE_LEFT
dec di ;next pixel is in byte to left
else
inc di ;next pixel is in byte to right
endif
ResetBitMaskAccumulator:
sub al,al ;reset pixel mask accumulator
NextPixel:
or al,ah ;add the next pixel to the pixel mask
; accumulator
loop LineLoop
;
; Write the pixels in the final byte.
;
Line1End:
out dx,al ;set up bit mask for pixels in this byte.
xchg byte ptr [di],al
;load latches and write pixels, with bit mask
; preserving other latched bits. Because
; set/reset is enabled for all planes, the
; value written actually doesn't matter
endm
;
; Macro to loop through length of line, drawing each pixel in turn.
; Used for case of DeltaX < DeltaY.
; Input:
; MOVE_LEFT: 1 if DeltaX < 0, 0 else
; AL: pixel mask for initial pixel
; BX: |DeltaX|
; DX: address of GC data register, with index register set to
; index of Bit Mask register
; SI: DeltaY
; ES:DI: display memory address of byte containing initial
; pixel
;
LINE2 macro MOVE_LEFT
local LineLoop, MoveYCoord, ETermAction, Line2End
mov cx,si ;# of pixels in line
jcxz Line2End ;done if there are no more pixels
shl bx,1 ;DeltaX * 2
mov bp,bx ;error term
sub bp,si ;error term starts at DeltaX * 2 - DeltaY
shl si,1 ;DeltaY * 2
sub bx,si ;DeltaX * 2 - DeltaY * 2 (used in loop)
add si,bx ;DeltaX * 2 (used in loop)
;
; Set up initial bit mask & write initial pixel.
;
out dx,al
xchg byte ptr [di],ah
;load latches and write pixel, with bit mask
; preserving other latched bits. Because
; set/reset is enabled for all planes, the
; value written actually doesn't matter
LineLoop:
;
; See if it's time to advance the X coordinate yet.
;
and bp,bp ;see if error term is negative
jns ETermAction ;no, advance X coordinate
add bp,si ;increment error term & keep same
jmp short MoveYCoord; X coordinate
ETermAction:
;
; Move pixel mask one pixel (either right or left, depending
; on MOVE_LEFT), adjusting display memory address when pixel mask wraps.
;
if MOVE_LEFT
rol al,1
sbb di,0
else
ror al,1
adc di,0
endif
out dx,al ;set new bit mask
add bp,bx ;adjust error term back down
;
; Advance Y coordinate.
;
MoveYCoord:
add di,EVGA_SCREEN_WIDTH_IN_BYTES
;
; Write the next pixel.
;
xchg byte ptr [di],ah
;load latches and write pixel, with bit mask
; preserving other latched bits. Because
; set/reset is enabled for all planes, the
; value written actually doesn't matter
;
loop LineLoop
Line2End:
endm
;****************************************************************
; Line drawing routine. *
;****************************************************************
public _EVGALine
_EVGALine proc near
push bp
mov bp,sp
push si ;preserve register variables
push di
push ds
;
; Point DS to display memory.
;
mov ax,EVGA_SCREEN_SEGMENT
mov ds,ax
;
; Set the Set/Reset and Set/Reset Enable registers for
; the selected color.
;
mov dx,GC_INDEX
mov al,SET_RESET_INDEX
out dx,al
inc dx
mov al,[bp+Color]
out dx,al
dec dx
mov al,ENABLE_SET_RESET_INDEX
out dx,al
inc dx
mov al,0ffh
out dx,al
;
; Get DeltaY.
;
mov si,[bp+Y1] ;line Y start
mov ax,[bp+Y0] ;line Y end, used later in
;calculating the start address
sub si,ax ;calculate DeltaY
jns CalcStartAddress;if positive, we're set
;
; DeltaY is negative -- swap coordinates so we're always working
; with a positive DeltaY.
;
mov ax,[bp+Y1] ;set line start to Y1, for use
; in calculating the start address
mov dx,[bp+X0]
xchg dx,[bp+X1]
mov [bp+X0],dx ;swap X coordinates
neg si ;convert to positive DeltaY
;
; Calculate the starting address in display memory of the line.
; Hardwired for a screen width of 80 bytes.
;
CalcStartAddress:
shl ax,1 ;Y0 * 2 ;Y0 is already in AX
shl ax,1 ;Y0 * 4
shl ax,1 ;Y0 * 8
shl ax,1 ;Y0 * 16
mov di,ax
shl ax,1 ;Y0 * 32
shl ax,1 ;Y0 * 64
add di,ax ;Y0 * 80
mov dx,[bp+X0]
mov cl,dl ;set aside lower 3 bits of column for
and cl,7 ; pixel masking
shr dx,1
shr dx,1
shr dx,1 ;get byte address of column (X0/8)
add di,dx ;offset of line start in display segment
;
; Set up GC Index register to point to the Bit Mask register.
;
mov dx,GC_INDEX
mov al,BIT_MASK_INDEX
out dx,al
inc dx ;leave DX pointing to the GC Data register
;
; Set up pixel mask (in-byte pixel address).
;
mov al,80h
shr al,cl
;
; Calculate DeltaX.
;
mov bx,[bp+X1]
sub bx,[bp+X0]
;
; Handle correct one of four octants.
;
js NegDeltaX
cmp bx,si
jb Octant1
;
; DeltaX >= DeltaY >= 0.
;
LINE1 0
jmp EVGALineDone
;
; DeltaY > DeltaX >= 0.
;
Octant1:
LINE2 0
jmp short EVGALineDone
;
NegDeltaX:
neg bx ;|DeltaX|
cmp bx,si
jb Octant2
;
; |DeltaX| >= DeltaY and DeltaX < 0.
;
LINE1 1
jmp short EVGALineDone
;
; |DeltaX| < DeltaY and DeltaX < 0.
;
Octant2:
LINE2 1
;
EVGALineDone:
;
; Restore EVGA state.
;
mov al,0ffh
out dx,al ;set Bit Mask register to 0ffh
dec dx
mov al,ENABLE_SET_RESET_INDEX
out dx,al
inc dx
sub al,al
out dx,al ;set Enable Set/Reset register to 0
;
pop ds
pop di
pop si
pop bp
ret
_EVGALine endp
end

131
tests/pcx86/vga/L42-1.C Normal file
View file

@ -0,0 +1,131 @@
/* Function to draw an antialiased line from (X0,Y0) to (X1,Y1), using an
* antialiasing approach published by Xiaolin Wu in the July 1991 issue of
* Computer Graphics. Requires that the palette be set up so that there
* are NumLevels intensity levels of the desired drawing color, starting at
* color BaseColor (100% intensity) and followed by (NumLevels-1) levels of
* evenly decreasing intensity, with color (BaseColor+NumLevels-1) being 0%
* intensity of the desired drawing color (black). This code is suitable for
* use at screen resolutions, with lines typically no more than 1K long; for
* longer lines, 32-bit error arithmetic must be used to avoid problems with
* fixed-point inaccuracy. No clipping is performed in DrawWuLine; it must be
* performed either at a higher level or in the DrawPixel function.
* Tested with Borland C++ in C compilation mode and the small model.
*/
extern void DrawPixel(int, int, int);
/* Wu antialiased line drawer.
* (X0,Y0),(X1,Y1) = line to draw
* BaseColor = color # of first color in block used for antialiasing, the
* 100% intensity version of the drawing color
* NumLevels = size of color block, with BaseColor+NumLevels-1 being the
* 0% intensity version of the drawing color
* IntensityBits = log base 2 of NumLevels; the # of bits used to describe
* the intensity of the drawing color. 2**IntensityBits==NumLevels
*/
void DrawWuLine(int X0, int Y0, int X1, int Y1, int BaseColor, int NumLevels, unsigned int IntensityBits)
{
unsigned int IntensityShift, ErrorAdj, ErrorAcc;
unsigned int ErrorAccTemp, Weighting, WeightingComplementMask;
int DeltaX, DeltaY, Temp, XDir;
/* Make sure the line runs top to bottom */
if (Y0 > Y1) {
Temp = Y0; Y0 = Y1; Y1 = Temp;
Temp = X0; X0 = X1; X1 = Temp;
}
/* Draw the initial pixel, which is always exactly intersected by
the line and so needs no weighting */
DrawPixel(X0, Y0, BaseColor);
if ((DeltaX = X1 - X0) >= 0) {
XDir = 1;
} else {
XDir = -1;
DeltaX = -DeltaX; /* make DeltaX positive */
}
/* Special-case horizontal, vertical, and diagonal lines, which
require no weighting because they go right through the center of
every pixel */
if ((DeltaY = Y1 - Y0) == 0) {
/* Horizontal line */
while (DeltaX-- != 0) {
X0 += XDir;
DrawPixel(X0, Y0, BaseColor);
}
return;
}
if (DeltaX == 0) {
/* Vertical line */
do {
Y0++;
DrawPixel(X0, Y0, BaseColor);
} while (--DeltaY != 0);
return;
}
if (DeltaX == DeltaY) {
/* Diagonal line */
do {
X0 += XDir;
Y0++;
DrawPixel(X0, Y0, BaseColor);
} while (--DeltaY != 0);
return;
}
/* Line is not horizontal, diagonal, or vertical */
ErrorAcc = 0; /* initialize the line error accumulator to 0 */
/* # of bits by which to shift ErrorAcc to get intensity level */
IntensityShift = 16 - IntensityBits;
/* Mask used to flip all bits in an intensity weighting, producing the
result (1 - intensity weighting) */
WeightingComplementMask = NumLevels - 1;
/* Is this an X-major or Y-major line? */
if (DeltaY > DeltaX) {
/* Y-major line; calculate 16-bit fixed-point fractional part of a
pixel that X advances each time Y advances 1 pixel, truncating the
result so that we won't overrun the endpoint along the X axis */
ErrorAdj = ((unsigned long) DeltaX << 16) / (unsigned long) DeltaY;
/* Draw all pixels other than the first and last */
while (--DeltaY) {
ErrorAccTemp = ErrorAcc; /* remember currrent accumulated error */
ErrorAcc += ErrorAdj; /* calculate error for next pixel */
if (ErrorAcc <= ErrorAccTemp) {
/* The error accumulator turned over, so advance the X coord */
X0 += XDir;
}
Y0++; /* Y-major, so always advance Y */
/* The IntensityBits most significant bits of ErrorAcc give us the
intensity weighting for this pixel, and the complement of the
weighting for the paired pixel */
Weighting = ErrorAcc >> IntensityShift;
DrawPixel(X0, Y0, BaseColor + Weighting);
DrawPixel(X0 + XDir, Y0, BaseColor + (Weighting ^ WeightingComplementMask));
}
/* Draw the final pixel, which is always exactly intersected by the line
and so needs no weighting */
DrawPixel(X1, Y1, BaseColor);
return;
}
/* It's an X-major line; calculate 16-bit fixed-point fractional part of a
pixel that Y advances each time X advances 1 pixel, truncating the
result to avoid overrunning the endpoint along the X axis */
ErrorAdj = ((unsigned long) DeltaY << 16) / (unsigned long) DeltaX;
/* Draw all pixels other than the first and last */
while (--DeltaX) {
ErrorAccTemp = ErrorAcc; /* remember currrent accumulated error */
ErrorAcc += ErrorAdj; /* calculate error for next pixel */
if (ErrorAcc <= ErrorAccTemp) {
/* The error accumulator turned over, so advance the Y coord */
Y0++;
}
X0 += XDir; /* X-major, so always advance X */
/* The IntensityBits most significant bits of ErrorAcc give us the
intensity weighting for this pixel, and the complement of the
weighting for the paired pixel */
Weighting = ErrorAcc >> IntensityShift;
DrawPixel(X0, Y0, BaseColor + Weighting);
DrawPixel(X0, Y0 + 1, BaseColor + (Weighting ^ WeightingComplementMask));
}
/* Draw the final pixel, which is always exactly intersected by the line
and so needs no weighting */
DrawPixel(X1, Y1, BaseColor);
}

124
tests/pcx86/vga/L42-2.C Normal file
View file

@ -0,0 +1,124 @@
/* Sample line-drawing program to demonstrate Wu antialiasing. Also draws
* non-antialiased lines for comparison.
* Tested with Borland C++ in C compilation mode and the small model.
*/
#include <dos.h>
#include <conio.h>
void SetPalette(struct WuColor *);
extern void DrawWuLine(int, int, int, int, int, int, unsigned int);
extern void DrawLine(int, int, int, int, int);
extern void SetMode(void);
extern int ScreenWidthInPixels; /* screen dimension globals */
extern int ScreenHeightInPixels;
#define NUM_WU_COLORS 2 /* # of colors we'll do antialiased drawing with */
struct WuColor { /* describes one color used for antialiasing */
int BaseColor; /* # of start of palette intensity block in DAC */
int NumLevels; /* # of intensity levels */
int IntensityBits; /* IntensityBits == log2 NumLevels */
int MaxRed; /* red component of color at full intensity */
int MaxGreen; /* green component of color at full intensity */
int MaxBlue; /* blue component of color at full intensity */
};
enum {WU_BLUE=0, WU_WHITE=1}; /* drawing colors */
struct WuColor WuColors[NUM_WU_COLORS] = /* blue and white */
{{192, 32, 5, 0, 0, 0x3F}, {224, 32, 5, 0x3F, 0x3F, 0x3F}};
void main()
{
int CurrentColor, i;
union REGS regset;
/* Draw Wu-antialiased lines in all directions */
SetMode();
SetPalette(WuColors);
for (i=5; i<ScreenWidthInPixels; i += 10) {
DrawWuLine(ScreenWidthInPixels/2-ScreenWidthInPixels/10+i/5,
ScreenHeightInPixels/5, i, ScreenHeightInPixels-1,
WuColors[WU_BLUE].BaseColor, WuColors[WU_BLUE].NumLevels,
WuColors[WU_BLUE].IntensityBits);
}
for (i=0; i<ScreenHeightInPixels; i += 10) {
DrawWuLine(ScreenWidthInPixels/2-ScreenWidthInPixels/10, i/5, 0, i,
WuColors[WU_BLUE].BaseColor, WuColors[WU_BLUE].NumLevels,
WuColors[WU_BLUE].IntensityBits);
}
for (i=0; i<ScreenHeightInPixels; i += 10) {
DrawWuLine(ScreenWidthInPixels/2+ScreenWidthInPixels/10, i/5,
ScreenWidthInPixels-1, i, WuColors[WU_BLUE].BaseColor,
WuColors[WU_BLUE].NumLevels, WuColors[WU_BLUE].IntensityBits);
}
for (i=0; i<ScreenWidthInPixels; i += 10) {
DrawWuLine(ScreenWidthInPixels/2-ScreenWidthInPixels/10+i/5,
ScreenHeightInPixels, i, 0, WuColors[WU_WHITE].BaseColor,
WuColors[WU_WHITE].NumLevels,
WuColors[WU_WHITE].IntensityBits);
}
getch(); /* wait for a key press */
/* Now clear the screen and draw non-antialiased lines */
SetMode();
SetPalette(WuColors);
for (i=0; i<ScreenWidthInPixels; i += 10) {
DrawLine(ScreenWidthInPixels/2-ScreenWidthInPixels/10+i/5,
ScreenHeightInPixels/5, i, ScreenHeightInPixels-1,
WuColors[WU_BLUE].BaseColor);
}
for (i=0; i<ScreenHeightInPixels; i += 10) {
DrawLine(ScreenWidthInPixels/2-ScreenWidthInPixels/10, i/5, 0, i,
WuColors[WU_BLUE].BaseColor);
}
for (i=0; i<ScreenHeightInPixels; i += 10) {
DrawLine(ScreenWidthInPixels/2+ScreenWidthInPixels/10, i/5,
ScreenWidthInPixels-1, i, WuColors[WU_BLUE].BaseColor);
}
for (i=0; i<ScreenWidthInPixels; i += 10) {
DrawLine(ScreenWidthInPixels/2-ScreenWidthInPixels/10+i/5,
ScreenHeightInPixels, i, 0, WuColors[WU_WHITE].BaseColor);
}
getch(); /* wait for a key press */
regset.x.ax = 0x0003; /* AL = 3 selects 80x25 text mode */
int86(0x10, &regset, &regset); /* return to text mode */
}
/* Sets up the palette for antialiasing with the specified colors.
* Intensity steps for each color are scaled from the full desired intensity
* of the red, green, and blue components for that color down to 0%
* intensity; each step is rounded to the nearest integer. Colors are
* corrected for a gamma of 2.3. The values that the palette is programmed
* with are hardwired for the VGA's 6 bit per color DAC.
*/
void SetPalette(struct WuColor * WColors)
{
int i, j;
union REGS regset;
struct SREGS sregset;
static unsigned char PaletteBlock[256][3]; /* 256 RGB entries */
/* Gamma-corrected DAC color components for 64 linear levels from 0% to 100% intensity */
static unsigned char GammaTable[] = {
0, 10, 14, 17, 19, 21, 23, 24, 26, 27, 28, 29, 31, 32, 33, 34,
35, 36, 37, 37, 38, 39, 40, 41, 41, 42, 43, 44, 44, 45, 46, 46,
47, 48, 48, 49, 49, 50, 51, 51, 52, 52, 53, 53, 54, 54, 55, 55,
56, 56, 57, 57, 58, 58, 59, 59, 60, 60, 61, 61, 62, 62, 63, 63};
for (i=0; i<NUM_WU_COLORS; i++) {
for (j=0; j<WColors[i].NumLevels; j++) {
PaletteBlock[j][0] = GammaTable[((double)WColors[i].MaxRed * (1.0 -
(double)j / (double)(WColors[i].NumLevels - 1))) + 0.5];
PaletteBlock[j][1] = GammaTable[((double)WColors[i].MaxGreen * (1.0 -
(double)j / (double)(WColors[i].NumLevels - 1))) + 0.5];
PaletteBlock[j][2] = GammaTable[((double)WColors[i].MaxBlue * (1.0 -
(double)j / (double)(WColors[i].NumLevels - 1))) + 0.5];
}
/* Now set up the palette to do Wu antialiasing for this color */
regset.x.ax = 0x1012; /* set block of DAC registers function */
regset.x.bx = WColors[i].BaseColor; /* first DAC location to load */
regset.x.cx = WColors[i].NumLevels; /* # of DAC locations to load */
regset.x.dx = (unsigned int)PaletteBlock; /* offset of array from which
to load RGB settings */
sregset.es = _DS; /* segment of array from which to load settings */
int86x(0x10, &regset, &regset, &sregset); /* load the palette block */
}
}

29
tests/pcx86/vga/L42-3.C Normal file
View file

@ -0,0 +1,29 @@
/* VGA mode 13h pixel-drawing and mode set functions.
* Tested with Borland C++ in C compilation mode and the small model.
*/
#include <dos.h>
/* Screen dimension globals, used in main program to scale. */
int ScreenWidthInPixels = 320;
int ScreenHeightInPixels = 200;
/* Mode 13h draw pixel function. */
void DrawPixel(int X, int Y, int Color)
{
#define SCREEN_SEGMENT 0xA000
unsigned char far *ScreenPtr;
FP_SEG(ScreenPtr) = SCREEN_SEGMENT;
FP_OFF(ScreenPtr) = (unsigned int) Y * ScreenWidthInPixels + X;
*ScreenPtr = Color;
}
/* Mode 13h mode-set function. */
void SetMode()
{
union REGS regset;
/* Set to 320x200 256-color graphics mode */
regset.x.ax = 0x0013;
int86(0x10, &regset, &regset);
}

64
tests/pcx86/vga/L42-4.C Normal file
View file

@ -0,0 +1,64 @@
/* Function to draw a non-antialiased line from (X0,Y0) to (X1,Y1), using a
* simple fixed-point error accumulation approach.
* Tested with Borland C++ in C compilation mode and the small model.
*/
extern void DrawPixel(int, int, int);
/* Non-antialiased line drawer.
* (X0,Y0),(X1,Y1) = line to draw, Color = color in which to draw
*/
void DrawLine(int X0, int Y0, int X1, int Y1, int Color)
{
unsigned long ErrorAcc, ErrorAdj;
int DeltaX, DeltaY, XDir, Temp;
/* Make sure the line runs top to bottom */
if (Y0 > Y1) {
Temp = Y0; Y0 = Y1; Y1 = Temp;
Temp = X0; X0 = X1; X1 = Temp;
}
DrawPixel(X0, Y0, Color); /* draw the initial pixel */
if ((DeltaX = X1 - X0) >= 0) {
XDir = 1;
} else {
XDir = -1;
DeltaX = -DeltaX; /* make DeltaX positive */
}
if ((DeltaY = Y1 - Y0) == 0) /* done if only one point in the line */
if (DeltaX == 0) return;
ErrorAcc = 0x8000; /* initialize line error accumulator to .5, so we can
advance when we get halfway to the next pixel */
/* Is this an X-major or Y-major line? */
if (DeltaY > DeltaX) {
/* Y-major line; calculate 16-bit fixed-point fractional part of a
pixel that X advances each time Y advances 1 pixel */
ErrorAdj = ((((unsigned long)DeltaX << 17) / (unsigned long)DeltaY) + 1) >> 1;
/* Draw all pixels between the first and last */
do {
ErrorAcc += ErrorAdj; /* calculate error for this pixel */
if (ErrorAcc & ~0xFFFFL) {
/* The error accumulator turned over, so advance the X coord */
X0 += XDir;
ErrorAcc &= 0xFFFFL; /* clear integer part of result */
}
Y0++; /* Y-major, so always advance Y */
DrawPixel(X0, Y0, Color);
} while (--DeltaY);
return;
}
/* It's an X-major line; calculate 16-bit fixed-point fractional part of a
pixel that Y advances each time X advances 1 pixel */
ErrorAdj = ((((unsigned long)DeltaY << 17) / (unsigned long)DeltaX) + 1) >> 1;
/* Draw all remaining pixels */
do {
ErrorAcc += ErrorAdj; /* calculate error for this pixel */
if (ErrorAcc & ~0xFFFFL) {
/* The error accumulator turned over, so advance the Y coord */
Y0++;
ErrorAcc &= 0xFFFFL; /* clear integer part of result */
}
X0 += XDir; /* X-major, so always advance X */
DrawPixel(X0, Y0, Color);
} while (--DeltaX);
}

40
tests/pcx86/vga/L42-5.C Normal file
View file

@ -0,0 +1,40 @@
/* Mode set and pixel-drawing functions for the 640x480 256-color mode of
* Tseng Labs ET4000-based SuperVGAs.
* Tested with Borland C++ in C compilation mode and the small model.
*/
#include <dos.h>
/* Screen dimension globals, used in main program to scale */
int ScreenWidthInPixels = 640;
int ScreenHeightInPixels = 480;
/* ET4000 640x480 256-color draw pixel function. */
void DrawPixel(int X, int Y, int Color)
{
#define SCREEN_SEGMENT 0xA000
#define GC_SEGMENT_SELECT 0x3CD /* ET4000 segment (bank) select reg */
unsigned char far *ScreenPtr;
unsigned int Bank;
unsigned long BitmapAddress;
/* Full bitmap address of pixel, as measured from address 0 to 0xFFFFF */
BitmapAddress = (unsigned long) Y * ScreenWidthInPixels + X;
/* Bank # is upper word of bitmap addr */
Bank = BitmapAddress >> 16;
/* Upper nibble is read bank #, lower nibble is write bank # */
outp(GC_SEGMENT_SELECT, (Bank << 4) | Bank);
/* Draw into the bank */
FP_SEG(ScreenPtr) = SCREEN_SEGMENT;
FP_OFF(ScreenPtr) = (unsigned int) BitmapAddress;
*ScreenPtr = Color;
}
/* ET4000 640x480 256-color mode-set function. */
void SetMode()
{
union REGS regset;
/* Set to 640x480 256-color graphics mode */
regset.x.ax = 0x002E;
int86(0x10, &regset, &regset);
}

269
tests/pcx86/vga/L42-6.ASM Normal file
View file

@ -0,0 +1,269 @@
; C near-callable function to draw an antialiased line from
; (X0,Y0) to (X1,Y1), in mode 13h, the VGA's standard 320x200 256-color
; mode. Uses an antialiasing approach published by Xiaolin Wu in the July
; 1991 issue of Computer Graphics. Requires that the palette be set up so
; that there are NumLevels intensity levels of the desired drawing color,
; starting at color BaseColor (100% intensity) and followed by (NumLevels-1)
; levels of evenly decreasing intensity, with color (BaseColor+NumLevels-1)
; being 0% intensity of the desired drawing color (black). No clipping is
; performed in DrawWuLine. Handles a maximum of 256 intensity levels per
; antialiased color. This code is suitable for use at screen resolutions,
; with lines typically no more than 1K long; for longer lines, 32-bit error
; arithmetic must be used to avoid problems with fixed-point inaccuracy.
; Tested with TASM.
;
; C near-callable as:
; void DrawWuLine(int X0, int Y0, int X1, int Y1, int BaseColor,
; int NumLevels, unsigned int IntensityBits);
SCREEN_WIDTH_IN_BYTES equ 320 ;# of bytes from the start of one scan line
; to the start of the next
SCREEN_SEGMENT equ 0a000h ;segment in which screen memory resides
; Parameters passed in stack frame.
parms struc
dw 2 dup (?) ;pushed BP and return address
X0 dw ? ;X coordinate of line start point
Y0 dw ? ;Y coordinate of line start point
X1 dw ? ;X coordinate of line end point
Y1 dw ? ;Y coordinate of line end point
BaseColor dw ? ;color # of first color in block used for
;antialiasing, the 100% intensity version of the
;drawing color
NumLevels dw ? ;size of color block, with BaseColor+NumLevels-1
; being the 0% intensity version of the drawing color
; (maximum NumLevels = 256)
IntensityBits dw ? ;log base 2 of NumLevels; the # of bits used to
; describe the intensity of the drawing color.
; 2**IntensityBits==NumLevels
; (maximum IntensityBits = 8)
parms ends
.model small
.code
; Screen dimension globals, used in main program to scale.
_ScreenWidthInPixels dw 320
_ScreenHeightInPixels dw 200
.code
public _DrawWuLine
_DrawWuLine proc near
push bp ;preserve caller's stack frame
mov bp,sp ;point to local stack frame
push si ;preserve C's register variables
push di
push ds ;preserve C's default data segment
cld ;make string instructions increment their pointers
; Make sure the line runs top to bottom.
mov si,[bp].X0
mov ax,[bp].Y0
cmp ax,[bp].Y1 ;swap endpoints if necessary to ensure that
jna NoSwap ; Y0 <= Y1
xchg [bp].Y1,ax
mov [bp].Y0,ax
xchg [bp].X1,si
mov [bp].X0,si
NoSwap:
; Draw the initial pixel, which is always exactly intersected by the line
; and so needs no weighting.
mov dx,SCREEN_SEGMENT
mov ds,dx ;point DS to the screen segment
mov dx,SCREEN_WIDTH_IN_BYTES
mul dx ;Y0 * SCREEN_WIDTH_IN_BYTES yields the offset
; of the start of the row start the initial
; pixel is on
add si,ax ;point DS:SI to the initial pixel
mov al,byte ptr [bp].BaseColor ;color with which to draw
mov [si],al ;draw the initial pixel
mov bx,1 ;XDir = 1; assume DeltaX >= 0
mov cx,[bp].X1
sub cx,[bp].X0 ;DeltaX; is it >= 1?
jns DeltaXSet ;yes, move left->right, all set
;no, move right->left
neg cx ;make DeltaX positive
neg bx ;XDir = -1
DeltaXSet:
; Special-case horizontal, vertical, and diagonal lines, which require no
; weighting because they go right through the center of every pixel.
mov dx,[bp].Y1
sub dx,[bp].Y0 ;DeltaY; is it 0?
jnz NotHorz ;no, not horizontal
;yes, is horizontal, special case
and bx,bx ;draw from left->right?
jns DoHorz ;yes, all set
std ;no, draw right->left
DoHorz:
lea di,[bx+si] ;point DI to next pixel to draw
mov ax,ds
mov es,ax ;point ES:DI to next pixel to draw
mov al,byte ptr [bp].BaseColor ;color with which to draw
;CX = DeltaX at this point
rep stosb ;draw the rest of the horizontal line
cld ;restore default direction flag
jmp Done ;and we're done
align 2
NotHorz:
and cx,cx ;is DeltaX 0?
jnz NotVert ;no, not a vertical line
;yes, is vertical, special case
mov al,byte ptr [bp].BaseColor ;color with which to draw
VertLoop:
add si,SCREEN_WIDTH_IN_BYTES ;point to next pixel to draw
mov [si],al ;draw the next pixel
dec dx ;--DeltaY
jnz VertLoop
jmp Done ;and we're done
align 2
NotVert:
cmp cx,dx ;DeltaX == DeltaY?
jnz NotDiag ;no, not diagonal
;yes, is diagonal, special case
mov al,byte ptr [bp].BaseColor ;color with which to draw
DiagLoop:
lea si,[si+SCREEN_WIDTH_IN_BYTES+bx]
;advance to next pixel to draw by
; incrementing Y and adding XDir to X
mov [si],al ;draw the next pixel
dec dx ;--DeltaY
jnz DiagLoop
jmp Done ;and we're done
; Line is not horizontal, diagonal, or vertical.
align 2
NotDiag:
; Is this an X-major or Y-major line?
cmp dx,cx
jb XMajor ;it's X-major
; It's a Y-major line. Calculate the 16-bit fixed-point fractional part of a
; pixel that X advances each time Y advances 1 pixel, truncating the result
; to avoid overrunning the endpoint along the X axis.
xchg dx,cx ;DX = DeltaX, CX = DeltaY
sub ax,ax ;make DeltaX 16.16 fixed-point value in DX:AX
div cx ;AX = (DeltaX << 16) / DeltaY. Won't overflow
; because DeltaX < DeltaY
mov di,cx ;DI = DeltaY (loop count)
sub si,bx ;back up the start X by 1, as explained below
mov dx,-1 ;initialize the line error accumulator to -1,
; so that it will turn over immediately and
; advance X to the start X. This is necessary
; properly to bias error sums of 0 to mean
; "advance next time" rather than "advance
; this time," so that the final error sum can
; never cause drawing to overrun the final X
; coordinate (works in conjunction with
; truncating ErrorAdj, to make sure X can't
; overrun)
mov cx,8 ;CL = # of bits by which to shift
sub cx,[bp].IntensityBits ; ErrorAcc to get intensity level (8
; instead of 16 because we work only
; with the high byte of ErrorAcc)
mov ch,byte ptr [bp].NumLevels ;mask used to flip all bits in an
dec ch ; intensity weighting, producing
; result (1 - intensity weighting)
mov bp,BaseColor[bp] ;***stack frame not available***
;***from now on ***
xchg bp,ax ;BP = ErrorAdj, AL = BaseColor,
; AH = scratch register
; Draw all remaining pixels.
YMajorLoop:
add dx,bp ;calculate error for next pixel
jnc NoXAdvance ;not time to step in X yet
;the error accumulator turned over,
;so advance the X coord
add si,bx ;add XDir to the pixel pointer
NoXAdvance:
add si,SCREEN_WIDTH_IN_BYTES ;Y-major, so always advance Y
; The IntensityBits most significant bits of ErrorAcc give us the intensity
; weighting for this pixel, and the complement of the weighting for the
; paired pixel.
mov ah,dh ;msb of ErrorAcc
shr ah,cl ;Weighting = ErrorAcc >> IntensityShift;
add ah,al ;BaseColor + Weighting
mov [si],ah ;DrawPixel(X, Y, BaseColor + Weighting);
mov ah,dh ;msb of ErrorAcc
shr ah,cl ;Weighting = ErrorAcc >> IntensityShift;
xor ah,ch ;Weighting ^ WeightingComplementMask
add ah,al ;BaseColor + (Weighting ^ WeightingComplementMask)
mov [si+bx],ah ;DrawPixel(X+XDir, Y,
; BaseColor + (Weighting ^ WeightingComplementMask));
dec di ;--DeltaY
jnz YMajorLoop
jmp Done ;we're done with this line
; It's an X-major line.
align 2
XMajor:
; Calculate the 16-bit fixed-point fractional part of a pixel that Y advances
; each time X advances 1 pixel, truncating the result to avoid overrunning
; the endpoint along the X axis.
sub ax,ax ;make DeltaY 16.16 fixed-point value in DX:AX
div cx ;AX = (DeltaY << 16) / Deltax. Won't overflow
; because DeltaY < DeltaX
mov di,cx ;DI = DeltaX (loop count)
sub si,SCREEN_WIDTH_IN_BYTES;back up the start X by 1, as
; explained below
mov dx,-1 ;initialize the line error accumulator to -1,
; so that it will turn over immediately and
; advance Y to the start Y. This is necessary
; properly to bias error sums of 0 to mean
; "advance next time" rather than "advance
; this time," so that the final error sum can
; never cause drawing to overrun the final Y
; coordinate (works in conjunction with
; truncating ErrorAdj, to make sure Y can't
; overrun)
mov cx,8 ;CL = # of bits by which to shift
sub cx,[bp].IntensityBits ; ErrorAcc to get intensity level (8
; instead of 16 because we work only
; with the high byte of ErrorAcc)
mov ch,byte ptr [bp].NumLevels ;mask used to flip all bits in an
dec ch ; intensity weighting, producing
; result (1 - intensity weighting)
mov bp,BaseColor[bp] ;***stack frame not available***
;***from now on ***
xchg bp,ax ;BP = ErrorAdj, AL = BaseColor,
; AH = scratch register
; Draw all remaining pixels.
XMajorLoop:
add dx,bp ;calculate error for next pixel
jnc NoYAdvance ;not time to step in Y yet
;the error accumulator turned over,
; so advance the Y coord
add si,SCREEN_WIDTH_IN_BYTES;advance Y
NoYAdvance:
add si,bx ;X-major, so add XDir to the pixel pointer
; The IntensityBits most significant bits of ErrorAcc give us the intensity
; weighting for this pixel, and the complement of the weighting for the
; paired pixel.
mov ah,dh ;msb of ErrorAcc
shr ah,cl ;Weighting = ErrorAcc >> IntensityShift;
add ah,al ;BaseColor + Weighting
mov [si],ah ;DrawPixel(X, Y, BaseColor + Weighting);
mov ah,dh ;msb of ErrorAcc
shr ah,cl ;Weighting = ErrorAcc >> IntensityShift;
xor ah,ch ;Weighting ^ WeightingComplementMask
add ah,al ;BaseColor + (Weighting ^ WeightingComplementMask)
mov [si+SCREEN_WIDTH_IN_BYTES],ah
; DrawPixel(X, Y+SCREEN_WIDTH_IN_BYTES, BaseColor + (Weighting ^ WeightingComplementMask));
dec di ;--DeltaX
jnz XMajorLoop
Done: ;we're done with this line
pop ds ;restore C's default data segment
pop di ;restore C's register variables
pop si
pop bp ;restore caller's stack frame
ret ;done
_DrawWuLine endp
end

88
tests/pcx86/vga/L47-1.ASM Normal file
View file

@ -0,0 +1,88 @@
; Mode X (320x240, 256 colors) mode set routine. Works on all VGAs.
; ****************************************************************
; * Revised 6/19/91 to select correct clock; fixes vertical roll *
; * problems on fixed-frequency (IBM 851X-type) monitors. *
; ****************************************************************
; C near-callable as:
; void Set320x240Mode(void);
; Tested with TASM
; Modified from public-domain mode set code by John Bridges.
SC_INDEX equ 03c4h ;Sequence Controller Index
CRTC_INDEX equ 03d4h ;CRT Controller Index
MISC_OUTPUT equ 03c2h ;Miscellaneous Output register
SCREEN_SEG equ 0a000h ;segment of display memory in mode X
.model small
.data
; Index/data pairs for CRT Controller registers that differ between
; mode 13h and mode X.
CRTParms label word
dw 00d06h ;vertical total
dw 03e07h ;overflow (bit 8 of vertical counts)
dw 04109h ;cell height (2 to double-scan)
dw 0ea10h ;v sync start
dw 0ac11h ;v sync end and protect cr0-cr7
dw 0df12h ;vertical displayed
dw 00014h ;turn off dword mode
dw 0e715h ;v blank start
dw 00616h ;v blank end
dw 0e317h ;turn on byte mode
CRT_PARM_LENGTH equ (($-CRTParms)/2)
.code
public _Set320x240Mode
_Set320x240Mode proc near
push bp ;preserve caller's stack frame
push si ;preserve C register vars
push di ; (don't count on BIOS preserving anything)
mov ax,13h ;let the BIOS set standard 256-color
int 10h ; mode (320x200 linear)
mov dx,SC_INDEX
mov ax,0604h
out dx,ax ;disable chain4 mode
mov ax,0100h
out dx,ax ;synchronous reset while setting Misc Output
; for safety, even though clock unchanged
mov dx,MISC_OUTPUT
mov al,0e3h
out dx,al ;select 25 MHz dot clock & 60 Hz scanning rate
mov dx,SC_INDEX
mov ax,0300h
out dx,ax ;undo reset (restart sequencer)
mov dx,CRTC_INDEX ;reprogram the CRT Controller
mov al,11h ;VSync End reg contains register write
out dx,al ; protect bit
inc dx ;CRT Controller Data register
in al,dx ;get current VSync End register setting
and al,7fh ;remove write protect on various
out dx,al ; CRTC registers
dec dx ;CRT Controller Index
cld
mov si,offset CRTParms ;point to CRT parameter table
mov cx,CRT_PARM_LENGTH ;# of table entries
SetCRTParmsLoop:
lodsw ;get the next CRT Index/Data pair
out dx,ax ;set the next CRT Index/Data pair
loop SetCRTParmsLoop
mov dx,SC_INDEX
mov ax,0f02h
out dx,ax ;enable writes to all four planes
mov ax,SCREEN_SEG ;now clear all display memory, 8 pixels
mov es,ax ; at a time
sub di,di ;point ES:DI to display memory
sub ax,ax ;clear to zero-value pixels
mov cx,8000h ;# of words in display memory
rep stosw ;clear all of display memory
pop di ;restore C register vars
pop si
pop bp ;restore caller's stack frame
ret
_Set320x240Mode endp
end

52
tests/pcx86/vga/L47-2.ASM Normal file
View file

@ -0,0 +1,52 @@
; Mode X (320x240, 256 colors) write pixel routine. Works on all VGAs.
; No clipping is performed.
; C near-callable as:
;
; void WritePixelX(int X, int Y, unsigned int PageBase, int Color);
SC_INDEX equ 03c4h ;Sequence Controller Index
MAP_MASK equ 02h ;index in SC of Map Mask register
SCREEN_SEG equ 0a000h ;segment of display memory in mode X
SCREEN_WIDTH equ 80 ;width of screen in bytes from one scan line
; to the next
parms struc
dw 2 dup (?) ;pushed BP and return address
X dw ? ;X coordinate of pixel to draw
Y dw ? ;Y coordinate of pixel to draw
PageBase dw ? ;base offset in display memory of page in
; which to draw pixel
Color dw ? ;color in which to draw pixel
parms ends
.model small
.code
public _WritePixelX
_WritePixelX proc near
push bp ;preserve caller's stack frame
mov bp,sp ;point to local stack frame
mov ax,SCREEN_WIDTH
mul [bp+Y] ;offset of pixel's scan line in page
mov bx,[bp+X]
shr bx,1
shr bx,1 ;X/4 = offset of pixel in scan line
add bx,ax ;offset of pixel in page
add bx,[bp+PageBase] ;offset of pixel in display memory
mov ax,SCREEN_SEG
mov es,ax ;point ES:BX to the pixel's address
mov cl,byte ptr [bp+X]
and cl,011b ;CL = pixel's plane
mov ax,0100h + MAP_MASK ;AL = index in SC of Map Mask reg
shl ah,cl ;set only the bit for the pixel's plane to 1
mov dx,SC_INDEX ;set the Map Mask to enable only the
out dx,ax ; pixel's plane
mov al,byte ptr [bp+Color]
mov es:[bx],al ;draw the pixel in the desired color
pop bp ;restore caller's stack frame
ret
_WritePixelX endp
end

49
tests/pcx86/vga/L47-3.ASM Normal file
View file

@ -0,0 +1,49 @@
; Mode X (320x240, 256 colors) read pixel routine. Works on all VGAs.
; No clipping is performed.
; C near-callable as:
;
; unsigned int ReadPixelX(int X, int Y, unsigned int PageBase);
GC_INDEX equ 03ceh ;Graphics Controller Index
READ_MAP equ 04h ;index in GC of the Read Map register
SCREEN_SEG equ 0a000h ;segment of display memory in mode X
SCREEN_WIDTH equ 80 ;width of screen in bytes from one scan line
; to the next
parms struc
dw 2 dup (?) ;pushed BP and return address
X dw ? ;X coordinate of pixel to read
Y dw ? ;Y coordinate of pixel to read
PageBase dw ? ;base offset in display memory of page from
; which to read pixel
parms ends
.model small
.code
public _ReadPixelX
_ReadPixelX proc near
push bp ;preserve caller's stack frame
mov bp,sp ;point to local stack frame
mov ax,SCREEN_WIDTH
mul [bp+Y] ;offset of pixel's scan line in page
mov bx,[bp+X]
shr bx,1
shr bx,1 ;X/4 = offset of pixel in scan line
add bx,ax ;offset of pixel in page
add bx,[bp+PageBase] ;offset of pixel in display memory
mov ax,SCREEN_SEG
mov es,ax ;point ES:BX to the pixel's address
mov ah,byte ptr [bp+X]
and ah,011b ;AH = pixel's plane
mov al,READ_MAP ;AL = index in GC of the Read Map reg
mov dx,GC_INDEX ;set the Read Map to read the pixel's
out dx,ax ; plane
mov al,es:[bx] ;read the pixel's color
sub ah,ah ;convert it to an unsigned int
pop bp ;restore caller's stack frame
ret
_ReadPixelX endp
end

90
tests/pcx86/vga/L47-4.ASM Normal file
View file

@ -0,0 +1,90 @@
; Mode X (320x240, 256 colors) rectangle fill routine. Works on all
; VGAs. Uses slow approach that selects the plane explicitly for each
; pixel. Fills up to but not including the column at EndX and the row
; at EndY. No clipping is performed.
; C near-callable as:
;
; void FillRectangleX(int StartX, int StartY, int EndX, int EndY,
; unsigned int PageBase, int Color);
SC_INDEX equ 03c4h ;Sequence Controller Index
MAP_MASK equ 02h ;index in SC of Map Mask register
SCREEN_SEG equ 0a000h ;segment of display memory in mode X
SCREEN_WIDTH equ 80 ;width of screen in bytes from one scan line
; to the next
parms struc
dw 2 dup (?) ;pushed BP and return address
StartX dw ? ;X coordinate of upper left corner of rect
StartY dw ? ;Y coordinate of upper left corner of rect
EndX dw ? ;X coordinate of lower right corner of rect
; (the row at EndX is not filled)
EndY dw ? ;Y coordinate of lower right corner of rect
; (the column at EndY is not filled)
PageBase dw ? ;base offset in display memory of page in
; which to fill rectangle
Color dw ? ;color in which to draw pixel
parms ends
.model small
.code
public _FillRectangleX
_FillRectangleX proc near
push bp ;preserve caller's stack frame
mov bp,sp ;point to local stack frame
push si ;preserve caller's register variables
push di
mov ax,SCREEN_WIDTH
mul [bp+StartY] ;offset in page of top rectangle scan line
mov di,[bp+StartX]
shr di,1
shr di,1 ;X/4 = offset of first rectangle pixel in scan
; line
add di,ax ;offset of first rectangle pixel in page
add di,[bp+PageBase] ;offset of first rectangle pixel in
; display memory
mov ax,SCREEN_SEG
mov es,ax ;point ES:DI to the first rectangle pixel's
; address
mov dx,SC_INDEX ;set the Sequence Controller Index to
mov al,MAP_MASK ; point to the Map Mask register
out dx,al
inc dx ;point DX to the SC Data register
mov cl,byte ptr [bp+StartX]
and cl,011b ;CL = first rectangle pixel's plane
mov al,01h
shl al,cl ;set only the bit for the pixel's plane to 1
mov ah,byte ptr [bp+Color] ;color with which to fill
mov bx,[bp+EndY]
sub bx,[bp+StartY] ;BX = height of rectangle
jle FillDone ;skip if 0 or negative height
mov si,[bp+EndX]
sub si,[bp+StartX] ;CX = width of rectangle
jle FillDone ;skip if 0 or negative width
FillRowsLoop:
push ax ;remember the plane mask for the left edge
push di ;remember the start offset of the scan line
mov cx,si ;set count of pixels in this scan line
FillScanLineLoop:
out dx,al ;set the plane for this pixel
mov es:[di],ah ;draw the pixel
shl al,1 ;adjust the plane mask for the next pixel's
and al,01111b ; bit, modulo 4
jnz AddressSet ;advance address if we turned over from
inc di ; plane 3 to plane 0
mov al,00001b ;set plane mask bit for plane 0
AddressSet:
loop FillScanLineLoop
pop di ;retrieve the start offset of the scan line
add di,SCREEN_WIDTH ;point to the start of the next scan
; line of the rectangle
pop ax ;retrieve the plane mask for the left edge
dec bx ;count down scan lines
jnz FillRowsLoop
FillDone:
pop di ;restore caller's register variables
pop si
pop bp ;restore caller's stack frame
ret
_FillRectangleX endp
end

127
tests/pcx86/vga/L47-5.ASM Normal file
View file

@ -0,0 +1,127 @@
; Mode X (320x240, 256 colors) rectangle fill routine. Works on all
; VGAs. Uses medium-speed approach that selects each plane only once
; per rectangle; this results in a fade-in effect for large
; rectangles. Fills up to but not including the column at EndX and the
; row at EndY. No clipping is performed.
; C near-callable as:
;
; void FillRectangleX(int StartX, int StartY, int EndX, int EndY,
; unsigned int PageBase, int Color);
SC_INDEX equ 03c4h ;Sequence Controller Index
MAP_MASK equ 02h ;index in SC of Map Mask register
SCREEN_SEG equ 0a000h ;segment of display memory in mode X
SCREEN_WIDTH equ 80 ;width of screen in bytes from one scan line
; to the next
parms struc
dw 2 dup (?) ;pushed BP and return address
StartX dw ? ;X coordinate of upper left corner of rect
StartY dw ? ;Y coordinate of upper left corner of rect
EndX dw ? ;X coordinate of lower right corner of rect
; (the row at EndX is not filled)
EndY dw ? ;Y coordinate of lower right corner of rect
; (the column at EndY is not filled)
PageBase dw ? ;base offset in display memory of page in
; which to fill rectangle
Color dw ? ;color in which to draw pixel
parms ends
_StartOffset equ -2 ;local storage for start offset of rectangle
_Width equ -4 ;local storage for address width of rectangle
_Height equ -6 ;local storage for height of rectangle
_PlaneInfo equ -8 ;local storage for plane # and plane mask
STACK_FRAME_SIZE equ 8
.model small
.code
public _FillRectangleX
_FillRectangleX proc near
push bp ;preserve caller's stack frame
mov bp,sp ;point to local stack frame
sub sp,STACK_FRAME_SIZE ;allocate space for local vars
push si ;preserve caller's register variables
push di
cld
mov ax,SCREEN_WIDTH
mul [bp+StartY] ;offset in page of top rectangle scan line
mov di,[bp+StartX]
shr di,1
shr di,1 ;X/4 = offset of first rectangle pixel in scan
; line
add di,ax ;offset of first rectangle pixel in page
add di,[bp+PageBase] ;offset of first rectangle pixel in
; display memory
mov ax,SCREEN_SEG
mov es,ax ;point ES:DI to the first rectangle pixel's
mov [bp+_StartOffset],di ; address
mov dx,SC_INDEX ;set the Sequence Controller Index to
mov al,MAP_MASK ; point to the Map Mask register
out dx,al
mov bx,[bp+EndY]
sub bx,[bp+StartY] ;BX = height of rectangle
jle FillDone ;skip if 0 or negative height
mov [bp+_Height],bx
mov dx,[bp+EndX]
mov cx,[bp+StartX]
cmp dx,cx
jle FillDone ;skip if 0 or negative width
dec dx
and cx,not 011b
sub dx,cx
shr dx,1
shr dx,1
inc dx ;# of addresses across rectangle to fill
mov [bp+_Width],dx
mov word ptr [bp+_PlaneInfo],0001h
;lower byte = plane mask for plane 0,
; upper byte = plane # for plane 0
FillPlanesLoop:
mov ax,word ptr [bp+_PlaneInfo]
mov dx,SC_INDEX+1 ;point DX to the SC Data register
out dx,al ;set the plane for this pixel
mov di,[bp+_StartOffset] ;point ES:DI to rectangle start
mov dx,[bp+_Width]
mov cl,byte ptr [bp+StartX]
and cl,011b ;plane # of first pixel in initial byte
cmp ah,cl ;do we draw this plane in the initial byte?
jae InitAddrSet ;yes
dec dx ;no, so skip the initial byte
jz FillLoopBottom ;skip this plane if no pixels in it
inc di
InitAddrSet:
mov cl,byte ptr [bp+EndX]
dec cl
and cl,011b ;plane # of last pixel in final byte
cmp ah,cl ;do we draw this plane in the final byte?
jbe WidthSet ;yes
dec dx ;no, so skip the final byte
jz FillLoopBottom ;skip this planes if no pixels in it
WidthSet:
mov si,SCREEN_WIDTH
sub si,dx ;distance from end of one scan line to start
; of next
mov bx,[bp+_Height] ;# of lines to fill
mov al,byte ptr [bp+Color] ;color with which to fill
FillRowsLoop:
mov cx,dx ;# of bytes across scan line
rep stosb ;fill the scan line in this plane
add di,si ;point to the start of the next scan
; line of the rectangle
dec bx ;count down scan lines
jnz FillRowsLoop
FillLoopBottom:
mov ax,word ptr [bp+_PlaneInfo]
shl al,1 ;set the plane bit to the next plane
inc ah ;increment the plane #
mov word ptr [bp+_PlaneInfo],ax
cmp ah,4 ;have we done all planes?
jnz FillPlanesLoop ;continue if any more planes
FillDone:
pop di ;restore caller's register variables
pop si
mov sp,bp ;discard storage for local variables
pop bp ;restore caller's stack frame
ret
_FillRectangleX endp
end

113
tests/pcx86/vga/L47-6.ASM Normal file
View file

@ -0,0 +1,113 @@
; Mode X (320x240, 256 colors) rectangle fill routine. Works on all
; VGAs. Uses fast approach that fans data out to up to four planes at
; once to draw up to four pixels at once. Fills up to but not
; including the column at EndX and the row at EndY. No clipping is
; performed.
; C near-callable as:
; void FillRectangleX(int StartX, int StartY, int EndX, int EndY,
; unsigned int PageBase, int Color);
SC_INDEX equ 03c4h ;Sequence Controller Index
MAP_MASK equ 02h ;index in SC of Map Mask register
SCREEN_SEG equ 0a000h ;segment of display memory in mode X
SCREEN_WIDTH equ 80 ;width of screen in bytes from one scan line
; to the next
parms struc
dw 2 dup (?) ;pushed BP and return address
StartX dw ? ;X coordinate of upper left corner of rect
StartY dw ? ;Y coordinate of upper left corner of rect
EndX dw ? ;X coordinate of lower right corner of rect
; (the row at EndX is not filled)
EndY dw ? ;Y coordinate of lower right corner of rect
; (the column at EndY is not filled)
PageBase dw ? ;base offset in display memory of page in
; which to fill rectangle
Color dw ? ;color in which to draw pixel
parms ends
.model small
.data
; Plane masks for clipping left and right edges of rectangle.
LeftClipPlaneMask db 00fh,00eh,00ch,008h
RightClipPlaneMask db 00fh,001h,003h,007h
.code
public _FillRectangleX
_FillRectangleX proc near
push bp ;preserve caller's stack frame
mov bp,sp ;point to local stack frame
push si ;preserve caller's register variables
push di
cld
mov ax,SCREEN_WIDTH
mul [bp+StartY] ;offset in page of top rectangle scan line
mov di,[bp+StartX]
shr di,1 ;X/4 = offset of first rectangle pixel in scan
shr di,1 ; line
add di,ax ;offset of first rectangle pixel in page
add di,[bp+PageBase] ;offset of first rectangle pixel in
; display memory
mov ax,SCREEN_SEG ;point ES:DI to the first rectangle
mov es,ax ; pixel's address
mov dx,SC_INDEX ;set the Sequence Controller Index to
mov al,MAP_MASK ; point to the Map Mask register
out dx,al
inc dx ;point DX to the SC Data register
mov si,[bp+StartX]
and si,0003h ;look up left edge plane mask
mov bh,LeftClipPlaneMask[si] ; to clip & put in BH
mov si,[bp+EndX]
and si,0003h ;look up right edge plane
mov bl,RightClipPlaneMask[si] ; mask to clip & put in BL
mov cx,[bp+EndX] ;calculate # of addresses across rect
mov si,[bp+StartX]
cmp cx,si
jle FillDone ;skip if 0 or negative width
dec cx
and si,not 011b
sub cx,si
shr cx,1
shr cx,1 ;# of addresses across rectangle to fill - 1
jnz MasksSet ;there's more than one byte to draw
and bh,bl ;there's only one byte, so combine the left
; and right edge clip masks
MasksSet:
mov si,[bp+EndY]
sub si,[bp+StartY] ;BX = height of rectangle
jle FillDone ;skip if 0 or negative height
mov ah,byte ptr [bp+Color] ;color with which to fill
mov bp,SCREEN_WIDTH ;stack frame isn't needed any more
sub bp,cx ;distance from end of one scan line to start
dec bp ; of next
FillRowsLoop:
push cx ;remember width in addresses - 1
mov al,bh ;put left-edge clip mask in AL
out dx,al ;set the left-edge plane (clip) mask
mov al,ah ;put color in AL
stosb ;draw the left edge
dec cx ;count off left edge byte
js FillLoopBottom ;that's the only byte
jz DoRightEdge ;there are only two bytes
mov al,00fh ;middle addresses are drawn 4 pixels at a pop
out dx,al ;set the middle pixel mask to no clip
mov al,ah ;put color in AL
rep stosb ;draw the middle addresses four pixels apiece
DoRightEdge:
mov al,bl ;put right-edge clip mask in AL
out dx,al ;set the right-edge plane (clip) mask
mov al,ah ;put color in AL
stosb ;draw the right edge
FillLoopBottom:
add di,bp ;point to the start of the next scan line of
; the rectangle
pop cx ;retrieve width in addresses - 1
dec si ;count down scan lines
jnz FillRowsLoop
FillDone:
pop di ;restore caller's register variables
pop si
pop bp ;restore caller's stack frame
ret
_FillRectangleX endp
end

25
tests/pcx86/vga/L47-7.C Normal file
View file

@ -0,0 +1,25 @@
/* Program to demonstrate mode X (320x240, 256-colors) rectangle
fill by drawing adjacent 20x20 rectangles in successive colors from
0 on up across and down the screen
*/
#include <conio.h>
#include <dos.h>
void Set320x240Mode(void);
void FillRectangleX(int, int, int, int, unsigned int, int);
void main() {
int i,j;
union REGS regset;
Set320x240Mode();
FillRectangleX(0,0,320,240,0,0); /* clear the screen to black */
for (j = 1; j < 220; j += 21) {
for (i = 1; i < 300; i += 21) {
FillRectangleX(i, j, i+20, j+20, 0, ((j/21*15)+i/21) & 0xFF);
}
}
getch();
regset.x.ax = 0x0003; /* switch back to text mode and done */
int86(0x10, &regset, &regset);
}

44
tests/pcx86/vga/L48-1.C Normal file
View file

@ -0,0 +1,44 @@
/* Program to demonstrate Mode X (320x240, 256 colors) patterned
rectangle fills by filling the screen with adjacent 80x60
rectangles in a variety of patterns. Tested with Borland C++
in C compilation mode and the small model */
#include <conio.h>
#include <dos.h>
void Set320x240Mode(void);
void FillPatternX(int, int, int, int, unsigned int, char*);
/* 16 4x4 patterns */
static char Patt0[]={10,0,10,0,0,10,0,10,10,0,10,0,0,10,0,10};
static char Patt1[]={9,0,0,0,0,9,0,0,0,0,9,0,0,0,0,9};
static char Patt2[]={5,0,0,0,0,0,5,0,5,0,0,0,0,0,5,0};
static char Patt3[]={14,0,0,14,0,14,14,0,0,14,14,0,14,0,0,14};
static char Patt4[]={15,15,15,1,15,15,1,1,15,1,1,1,1,1,1,1};
static char Patt5[]={12,12,12,12,6,6,6,12,6,6,6,12,6,6,6,12};
static char Patt6[]={80,80,80,80,80,80,80,80,80,80,80,80,80,80,80,15};
static char Patt7[]={78,78,78,78,80,80,80,80,82,82,82,82,84,84,84,84};
static char Patt8[]={78,80,82,84,80,82,84,78,82,84,78,80,84,78,80,82};
static char Patt9[]={78,80,82,84,78,80,82,84,78,80,82,84,78,80,82,84};
static char Patt10[]={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
static char Patt11[]={0,1,2,3,0,1,2,3,0,1,2,3,0,1,2,3};
static char Patt12[]={14,14,9,9,14,9,9,14,9,9,14,14,9,14,14,9};
static char Patt13[]={15,8,8,8,15,15,15,8,15,15,15,8,15,8,8,8};
static char Patt14[]={3,3,3,3,3,7,7,3,3,7,7,3,3,3,3,3};
static char Patt15[]={0,0,0,0,0,64,0,0,0,0,0,0,0,0,0,89};
/* Table of pointers to the 16 4x4 patterns with which to draw */
static char* PattTable[] = {Patt0,Patt1,Patt2,Patt3,Patt4,Patt5,Patt6,
Patt7,Patt8,Patt9,Patt10,Patt11,Patt12,Patt13,Patt14,Patt15};
void main() {
int i,j;
union REGS regset;
Set320x240Mode();
for (j = 0; j < 4; j++) {
for (i = 0; i < 4; i++) {
FillPatternX(i*80,j*60,i*80+80,j*60+60,0,PattTable[j*4+i]);
}
}
getch();
regset.x.ax = 0x0003; /* switch back to text mode and done */
int86(0x10, &regset, &regset);
}

174
tests/pcx86/vga/L48-2.ASM Normal file
View file

@ -0,0 +1,174 @@
; Mode X (320x240, 256 colors) rectangle 4x4 pattern fill routine.
; Upper left corner of pattern is always aligned to a multiple-of-4
; row and column. Works on all VGAs. Uses approach of copying the
; pattern to off-screen display memory, then loading the latches with
; the pattern for each scan line and filling each scan line four
; pixels at a time. Fills up to but not including the column at EndX
; and the row at EndY. No clipping is performed. All ASM code tested
; with TASM. C near-callable as:
;
; void FillPatternX(int StartX, int StartY, int EndX, int EndY,
; unsigned int PageBase, char* Pattern);
SC_INDEX equ 03c4h ;Sequence Controller Index register port
MAP_MASK equ 02h ;index in SC of Map Mask register
GC_INDEX equ 03ceh ;Graphics Controller Index register port
BIT_MASK equ 08h ;index in GC of Bit Mask register
PATTERN_BUFFER equ 0fffch ;offset in screen memory of the buffer used
; to store each pattern during drawing
SCREEN_SEG equ 0a000h ;segment of display memory in Mode X
SCREEN_WIDTH equ 80 ;width of screen in addresses from one scan
; line to the next
parms struc
dw 2 dup (?) ;pushed BP and return address
StartX dw ? ;X coordinate of upper left corner of rect
StartY dw ? ;Y coordinate of upper left corner of rect
EndX dw ? ;X coordinate of lower right corner of rect
; (the row at EndX is not filled)
EndY dw ? ;Y coordinate of lower right corner of rect
; (the column at EndY is not filled)
PageBase dw ? ;base offset in display memory of page in
; which to fill rectangle
Pattern dw ? ;4x4 pattern with which to fill rectangle
parms ends
NextScanOffset equ -2 ;local storage for distance from end of one
; scan line to start of next
RectAddrWidth equ -4 ;local storage for address width of rectangle
Height equ -6 ;local storage for height of rectangle
STACK_FRAME_SIZE equ 6
.model small
.data
; Plane masks for clipping left and right edges of rectangle.
LeftClipPlaneMask db 00fh,00eh,00ch,008h
RightClipPlaneMask db 00fh,001h,003h,007h
.code
public _FillPatternX
_FillPatternX proc near
push bp ;preserve caller's stack frame
mov bp,sp ;point to local stack frame
sub sp,STACK_FRAME_SIZE ;allocate space for local vars
push si ;preserve caller's register variables
push di
cld
mov ax,SCREEN_SEG ;point ES to display memory
mov es,ax
;copy pattern to display memory buffer
mov si,[bp+Pattern] ;point to pattern to fill with
mov di,PATTERN_BUFFER ;point ES:DI to pattern buffer
mov dx,SC_INDEX ;point Sequence Controller Index to
mov al,MAP_MASK ; Map Mask
out dx,al
inc dx ;point to SC Data register
mov cx,4 ;4 pixel quadruplets in pattern
DownloadPatternLoop:
mov al,1 ;
out dx,al ;select plane 0 for writes
movsb ;copy over next plane 0 pattern pixel
dec di ;stay at same address for next plane
mov al,2 ;
out dx,al ;select plane 1 for writes
movsb ;copy over next plane 1 pattern pixel
dec di ;stay at same address for next plane
mov al,4 ;
out dx,al ;select plane 2 for writes
movsb ;copy over next plane 2 pattern pixel
dec di ;stay at same address for next plane
mov al,8 ;
out dx,al ;select plane 3 for writes
movsb ;copy over next plane 3 pattern pixel
; and advance address
loop DownloadPatternLoop
mov dx,GC_INDEX ;set the bit mask to select all bits
mov ax,00000h+BIT_MASK ; from the latches and none from
out dx,ax ; the CPU, so that we can write the
; latch contents directly to memory
mov ax,[bp+StartY] ;top rectangle scan line
mov si,ax
and si,011b ;top rect scan line modulo 4
add si,PATTERN_BUFFER ;point to pattern scan line that
; maps to top line of rect to draw
mov dx,SCREEN_WIDTH
mul dx ;offset in page of top rectangle scan line
mov di,[bp+StartX]
mov bx,di
shr di,1 ;X/4 = offset of first rectangle pixel in scan
shr di,1 ; line
add di,ax ;offset of first rectangle pixel in page
add di,[bp+PageBase] ;offset of first rectangle pixel in
; display memory
and bx,0003h ;look up left edge plane mask
mov ah,LeftClipPlaneMask[bx] ; to clip
mov bx,[bp+EndX]
and bx,0003h ;look up right edge plane
mov al,RightClipPlaneMask[bx] ; mask to clip
mov bx,ax ;put the masks in BX
mov cx,[bp+EndX] ;calculate # of addresses across rect
mov ax,[bp+StartX]
cmp cx,ax
jle FillDone ;skip if 0 or negative width
dec cx
and ax,not 011b
sub cx,ax
shr cx,1
shr cx,1 ;# of addresses across rectangle to fill - 1
jnz MasksSet ;there's more than one pixel to draw
and bh,bl ;there's only one pixel, so combine the left
; and right edge clip masks
MasksSet:
mov ax,[bp+EndY]
sub ax,[bp+StartY] ;AX = height of rectangle
jle FillDone ;skip if 0 or negative height
mov [bp+Height],ax
mov ax,SCREEN_WIDTH
sub ax,cx ;distance from end of one scan line to start
dec ax ; of next
mov [bp+NextScanOffset],ax
mov [bp+RectAddrWidth],cx ;remember width in addresses - 1
mov dx,SC_INDEX+1 ;point to Sequence Controller Data reg
; (SC Index still points to Map Mask)
FillRowsLoop:
mov cx,[bp+RectAddrWidth] ;width across - 1
mov al,es:[si] ;read display memory to latch this scan
; line's pattern
inc si ;point to the next pattern scan line, wrapping
jnz short NoWrap ; back to the start of the pattern if
sub si,4 ; we've run off the end
NoWrap:
mov al,bh ;put left-edge clip mask in AL
out dx,al ;set the left-edge plane (clip) mask
stosb ;draw the left edge (pixels come from latches;
; value written by CPU doesn't matter)
dec cx ;count off left edge address
js FillLoopBottom ;that's the only address
jz DoRightEdge ;there are only two addresses
mov al,00fh ;middle addresses are drawn 4 pixels at a pop
out dx,al ;set the middle pixel mask to no clip
rep stosb ;draw the middle addresses four pixels apiece
; (from latches; value written doesn't matter)
DoRightEdge:
mov al,bl ;put right-edge clip mask in AL
out dx,al ;set the right-edge plane (clip) mask
stosb ;draw the right edge (from latches; value
; written doesn't matter)
FillLoopBottom:
add di,[bp+NextScanOffset] ;point to the start of the next scan
; line of the rectangle
dec word ptr [bp+Height] ;count down scan lines
jnz FillRowsLoop
FillDone:
mov dx,GC_INDEX+1 ;restore the bit mask to its default,
mov al,0ffh ; which selects all bits from the CPU
out dx,al ; and none from the latches (the GC
; Index still points to Bit Mask)
pop di ;restore caller's register variables
pop si
mov sp,bp ;discard storage for local variables
pop bp ;restore caller's stack frame
ret
_FillPatternX endp
end

173
tests/pcx86/vga/L48-3.ASM Normal file
View file

@ -0,0 +1,173 @@
; Mode X (320x240, 256 colors) display memory to display memory copy
; routine. Left edge of source rectangle modulo 4 must equal left edge
; of destination rectangle modulo 4. Works on all VGAs. Uses approach
; of reading 4 pixels at a time from the source into the latches, then
; writing the latches to the destination. Copies up to but not
; including the column at SourceEndX and the row at SourceEndY. No
; clipping is performed. Results are not guaranteed if the source and
; destination overlap. C near-callable as:
;
; void CopyScreenToScreenX(int SourceStartX, int SourceStartY,
; int SourceEndX, int SourceEndY, int DestStartX,
; int DestStartY, unsigned int SourcePageBase,
; unsigned int DestPageBase, int SourceBitmapWidth,
; int DestBitmapWidth);
SC_INDEX equ 03c4h ;Sequence Controller Index register port
MAP_MASK equ 02h ;index in SC of Map Mask register
GC_INDEX equ 03ceh ;Graphics Controller Index register port
BIT_MASK equ 08h ;index in GC of Bit Mask register
SCREEN_SEG equ 0a000h ;segment of display memory in Mode X
parms struc
dw 2 dup (?) ;pushed BP and return address
SourceStartX dw ? ;X coordinate of upper left corner of source
SourceStartY dw ? ;Y coordinate of upper left corner of source
SourceEndX dw ? ;X coordinate of lower right corner of source
; (the row at SourceEndX is not copied)
SourceEndY dw ? ;Y coordinate of lower right corner of source
; (the column at SourceEndY is not copied)
DestStartX dw ? ;X coordinate of upper left corner of dest
DestStartY dw ? ;Y coordinate of upper left corner of dest
SourcePageBase dw ? ;base offset in display memory of page in
; which source resides
DestPageBase dw ? ;base offset in display memory of page in
; which dest resides
SourceBitmapWidth dw ? ;# of pixels across source bitmap
; (must be a multiple of 4)
DestBitmapWidth dw ? ;# of pixels across dest bitmap
; (must be a multiple of 4)
parms ends
SourceNextScanOffset equ -2 ;local storage for distance from end of
; one source scan line to start of next
DestNextScanOffset equ -4 ;local storage for distance from end of
; one dest scan line to start of next
RectAddrWidth equ -6 ;local storage for address width of rectangle
Height equ -8 ;local storage for height of rectangle
STACK_FRAME_SIZE equ 8
.model small
.data
; Plane masks for clipping left and right edges of rectangle.
LeftClipPlaneMask db 00fh,00eh,00ch,008h
RightClipPlaneMask db 00fh,001h,003h,007h
.code
public _CopyScreenToScreenX
_CopyScreenToScreenX proc near
push bp ;preserve caller's stack frame
mov bp,sp ;point to local stack frame
sub sp,STACK_FRAME_SIZE ;allocate space for local vars
push si ;preserve caller's register variables
push di
push ds
cld
mov dx,GC_INDEX ;set the bit mask to select all bits
mov ax,00000h+BIT_MASK ; from the latches and none from
out dx,ax ; the CPU, so that we can write the
; latch contents directly to memory
mov ax,SCREEN_SEG ;point ES to display memory
mov es,ax
mov ax,[bp+DestBitmapWidth]
shr ax,1 ;convert to width in addresses
shr ax,1
mul [bp+DestStartY] ;top dest rect scan line
mov di,[bp+DestStartX]
shr di,1 ;X/4 = offset of first dest rect pixel in
shr di,1 ; scan line
add di,ax ;offset of first dest rect pixel in page
add di,[bp+DestPageBase] ;offset of first dest rect pixel
; in display memory
mov ax,[bp+SourceBitmapWidth]
shr ax,1 ;convert to width in addresses
shr ax,1
mul [bp+SourceStartY] ;top source rect scan line
mov si,[bp+SourceStartX]
mov bx,si
shr si,1 ;X/4 = offset of first source rect pixel in
shr si,1 ; scan line
add si,ax ;offset of first source rect pixel in page
add si,[bp+SourcePageBase] ;offset of first source rect
; pixel in display memory
and bx,0003h ;look up left edge plane mask
mov ah,LeftClipPlaneMask[bx]; to clip
mov bx,[bp+SourceEndX]
and bx,0003h ;look up right edge plane
mov al,RightClipPlaneMask[bx] ; mask to clip
mov bx,ax ;put the masks in BX
mov cx,[bp+SourceEndX] ;calculate # of addresses across
mov ax,[bp+SourceStartX] ; rect
cmp cx,ax
jle CopyDone ;skip if 0 or negative width
dec cx
and ax,not 011b
sub cx,ax
shr cx,1
shr cx,1 ;# of addresses across rectangle to copy - 1
jnz MasksSet ;there's more than one address to draw
and bh,bl ;there's only one address, so combine the left
; and right edge clip masks
MasksSet:
mov ax,[bp+SourceEndY]
sub ax,[bp+SourceStartY] ;AX = height of rectangle
jle CopyDone ;skip if 0 or negative height
mov [bp+Height],ax
mov ax,[bp+DestBitmapWidth]
shr ax,1 ;convert to width in addresses
shr ax,1
sub ax,cx ;distance from end of one dest scan line to
dec ax ; start of next
mov [bp+DestNextScanOffset],ax
mov ax,[bp+SourceBitmapWidth]
shr ax,1 ;convert to width in addresses
shr ax,1
sub ax,cx ;distance from end of one source scan line to
dec ax ; start of next
mov [bp+SourceNextScanOffset],ax
mov [bp+RectAddrWidth],cx ;remember width in addresses - 1
;-----------------------BUG FIX
mov dx,SC_INDEX
mov al,MAP_MASK
out dx,al ;point SC Index reg to Map Mask
inc dx ;point to SC Data reg
;-----------------------BUG FIX
mov ax,es ;DS=ES=screen segment for MOVS
mov ds,ax
CopyRowsLoop:
mov cx,[bp+RectAddrWidth] ;width across - 1
mov al,bh ;put left-edge clip mask in AL
out dx,al ;set the left-edge plane (clip) mask
movsb ;copy the left edge (pixels go through
; latches)
dec cx ;count off left edge address
js CopyLoopBottom ;that's the only address
jz DoRightEdge ;there are only two addresses
mov al,00fh ;middle addresses are drawn 4 pixels at a pop
out dx,al ;set the middle pixel mask to no clip
rep movsb ;draw the middle addresses four pixels apiece
; (pixels copied through latches)
DoRightEdge:
mov al,bl ;put right-edge clip mask in AL
out dx,al ;set the right-edge plane (clip) mask
movsb ;draw the right edge (pixels copied through
; latches)
CopyLoopBottom:
add si,[bp+SourceNextScanOffset] ;point to the start of
add di,[bp+DestNextScanOffset] ; next source & dest lines
dec word ptr [bp+Height] ;count down scan lines
jnz CopyRowsLoop
CopyDone:
mov dx,GC_INDEX+1 ;restore the bit mask to its default,
mov al,0ffh ; which selects all bits from the CPU
out dx,al ; and none from the latches (the GC
; Index still points to Bit Mask)
pop ds
pop di ;restore caller's register variables
pop si
mov sp,bp ;discard storage for local variables
pop bp ;restore caller's stack frame
ret
_CopyScreenToScreenX endp
end

118
tests/pcx86/vga/L48-4.ASM Normal file
View file

@ -0,0 +1,118 @@
; Mode X (320x240, 256 colors) system memory to display memory copy
; routine. Uses approach of changing the plane for each pixel copied;
; this is slower than copying all pixels in one plane, then all pixels
; in the next plane, and so on, but it is simpler; besides, images for
; which performance is critical should be stored in off-screen memory
; and copied to the screen via the latches. Copies up to but not
; including the column at SourceEndX and the row at SourceEndY. No
; clipping is performed. C near-callable as:
;
; void CopySystemToScreenX(int SourceStartX, int SourceStartY,
; int SourceEndX, int SourceEndY, int DestStartX,
; int DestStartY, char* SourcePtr, unsigned int DestPageBase,
; int SourceBitmapWidth, int DestBitmapWidth);
SC_INDEX equ 03c4h ;Sequence Controller Index register port
MAP_MASK equ 02h ;index in SC of Map Mask register
SCREEN_SEG equ 0a000h ;segment of display memory in Mode X
parms struc
dw 2 dup (?) ;pushed BP and return address
SourceStartX dw ? ;X coordinate of upper left corner of source
SourceStartY dw ? ;Y coordinate of upper left corner of source
SourceEndX dw ? ;X coordinate of lower right corner of source
; (the row at EndX is not copied)
SourceEndY dw ? ;Y coordinate of lower right corner of source
; (the column at EndY is not copied)
DestStartX dw ? ;X coordinate of upper left corner of dest
DestStartY dw ? ;Y coordinate of upper left corner of dest
SourcePtr dw ? ;pointer in DS to start of bitmap in which
; source resides
DestPageBase dw ? ;base offset in display memory of page in
; which dest resides
SourceBitmapWidth dw ? ;# of pixels across source bitmap
DestBitmapWidth dw ? ;# of pixels across dest bitmap
; (must be a multiple of 4)
parms ends
RectWidth equ -2 ;local storage for width of rectangle
LeftMask equ -4 ;local storage for left rect edge plane mask
STACK_FRAME_SIZE equ 4
.model small
.code
public _CopySystemToScreenX
_CopySystemToScreenX proc near
push bp ;preserve caller's stack frame
mov bp,sp ;point to local stack frame
sub sp,STACK_FRAME_SIZE ;allocate space for local vars
push si ;preserve caller's register variables
push di
cld
mov ax,SCREEN_SEG ;point ES to display memory
mov es,ax
mov ax,[bp+SourceBitmapWidth]
mul [bp+SourceStartY] ;top source rect scan line
add ax,[bp+SourceStartX]
add ax,[bp+SourcePtr] ;offset of first source rect pixel
mov si,ax ; in DS
mov ax,[bp+DestBitmapWidth]
shr ax,1 ;convert to width in addresses
shr ax,1
mov [bp+DestBitmapWidth],ax ;remember address width
mul [bp+DestStartY] ;top dest rect scan line
mov di,[bp+DestStartX]
mov cx,di
shr di,1 ;X/4 = offset of first dest rect pixel in
shr di,1 ; scan line
add di,ax ;offset of first dest rect pixel in page
add di,[bp+DestPageBase] ;offset of first dest rect pixel
; in display memory
and cl,011b ;CL = first dest pixel's plane
mov al,11h ;upper nibble comes into play when plane wraps
; from 3 back to 0
shl al,cl ;set the bit for the first dest pixel's plane
mov [bp+LeftMask],al ; in each nibble to 1
mov cx,[bp+SourceEndX] ;calculate # of pixels across
sub cx,[bp+SourceStartX] ; rect
jle CopyDone ;skip if 0 or negative width
mov [bp+RectWidth],cx
mov bx,[bp+SourceEndY]
sub bx,[bp+SourceStartY] ;BX = height of rectangle
jle CopyDone ;skip if 0 or negative height
mov dx,SC_INDEX ;point to SC Index register
mov al,MAP_MASK
out dx,al ;point SC Index reg to the Map Mask
inc dx ;point DX to SC Data reg
CopyRowsLoop:
mov ax,[bp+LeftMask]
mov cx,[bp+RectWidth]
push si ;remember the start offset in the source
push di ;remember the start offset in the dest
CopyScanLineLoop:
out dx,al ;set the plane for this pixel
movsb ;copy the pixel to the screen
rol al,1 ;set mask for next pixel's plane
cmc ;advance destination address only when
sbb di,0 ; wrapping from plane 3 to plane 0
; (else undo INC DI done by MOVSB)
loop CopyScanLineLoop
pop di ;retrieve the dest start offset
add di,[bp+DestBitmapWidth] ;point to the start of the
; next scan line of the dest
pop si ;retrieve the source start offset
add si,[bp+SourceBitmapWidth] ;point to the start of the
; next scan line of the source
dec bx ;count down scan lines
jnz CopyRowsLoop
CopyDone:
pop di ;restore caller's register variables
pop si
mov sp,bp ;discard storage for local variables
pop bp ;restore caller's stack frame
ret
_CopySystemToScreenX endp
end

37
tests/pcx86/vga/MAKEFILE Normal file
View file

@ -0,0 +1,37 @@
CL=C:\MSDEV\BIN\WIN95\C816\BIN\CL.EXE
INCLUDE=C:\MSDEV\BIN\WIN95\C816\INC
LIB=C:\MSDEV\BIN\WIN95\C816\LIB
MASM=C:\MSDEV\BIN\WIN95\MASM\MASM.EXE
LINK=C:\MSDEV\BIN\WIN95\C816\BIN\LINK.EXE
.C.OBJ:
$(CL) /c $<
.ASM.OBJ:
$(MASM) $<;
.ASM.EXE:
$(MASM) $*;
$(LINK) $*;
ALL: L23-1.EXE L24-1.EXE L25-1.EXE L25-2.EXE L25-3.EXE L25-4.EXE L26-1.EXE L26-2.EXE L27-1.EXE L27-2.EXE L27-3.EXE \
L28-1.EXE L28-2.EXE L28-3.EXE L29-1.EXE L29-2.EXE L29-3.EXE L29-4.EXE L30-1.EXE L30-2.EXE L31-1.EXE L31-2.EXE \
L33-1.EXE L34-1.EXE L35-1.EXE L35-3.EXE L47-4.EXE L47-5.EXE L47-6.EXE L48-2.EXE
L35-1.EXE: L35-1.OBJ L35-2.OBJ
$(LINK) $**,$@;
L35-3.EXE: L35-3.OBJ L35-2.OBJ
$(LINK) $**,$@;
L47-4.EXE: L47-1.OBJ L47-2.OBJ L47-3.OBJ L47-4.OBJ L47-7.OBJ
$(LINK) $**,$@;
L47-5.EXE: L47-1.OBJ L47-2.OBJ L47-3.OBJ L47-5.OBJ L47-7.OBJ
$(LINK) $**,$@;
L47-6.EXE: L47-1.OBJ L47-2.OBJ L47-3.OBJ L47-6.OBJ L47-7.OBJ
$(LINK) $**,$@;
L48-2.EXE: L48-1.OBJ L47-1.OBJ L48-2.OBJ
$(LINK) $**,$@;

131
tests/pcx86/vga/README.md Normal file
View file

@ -0,0 +1,131 @@
---
layout: page
title: VGA "Black Book" Tests
permalink: /tests/pcx86/vga/
redirect_from:
- /tests/pc/vga/
machines:
- type: pcx86
id: deskpro386
debugger: true
config: /devices/pcx86/machine/compaq/deskpro386/vga/2048kb/debugger/machine.xml
autoMount:
A:
path: /disks/pcx86/dos/compaq/3.31/COMPAQ-DOS331-DISK2.json
B:
path: /tests/pcx86/vga/VGABIN.json
---
VGA "Black Book" Tests
---
To aid in the development of PCjs VGA support, I've added some VGA tests to the project.
For now, the only "tests" are samples taken directly from
[Michael Abrash's Graphics Programming Black Book](https://github.com/jeffpar/abrash-black-book), which you
can run in the [VGA "Black Book" Test Machine](#vga-black-book-test-machine) below.
Abrash's book is available on many sites, but I'm partial to the Markdown version that [James Gregory](https://github.com/jagregory)
has made available on GitHub, because (a) it's a brilliant way to render and share the text, and (b) it apparently has
Abrash's blessing, so I feel more comfortable forking it, using it, and resharing it.
The main reasons for my [fork](https://github.com/jeffpar/abrash-black-book) are to make the book's
images display properly on GitHub, and to extract and add assorted source code listings as I need them. Since that
project's [/src](https://github.com/jeffpar/abrash-black-book/tree/master/src) folder contains just the book's text,
I've added a [/code](https://github.com/jeffpar/abrash-black-book/tree/master/code) folder for the source code listings.
The name of each source code file matches the name displayed in the text (eg, [L23-1.ASM](L23-1.ASM) is Listing 23.1
from [Chapter 23](https://github.com/jeffpar/abrash-black-book/blob/master/src/chapter-23.md)).
I assume something similar was done on the CD-ROM that accompanied the Black Book, but since I don't have the original
book or its CD-ROM, I'm extracting the source code directly from the Markdown text, and then "tabifying" it with 8-column
tab stops.
Development of PCjs VGA support has just begun (June 2015), so don't expect everything here to to run properly yet.
---
List of VGA Samples from [Michael Abrash's Graphics Programming Black Book](https://github.com/jeffpar/abrash-black-book):
* [Chapter 23: Bones and Sinew](https://github.com/jeffpar/abrash-black-book/blob/master/src/chapter-23.md)
* [L23-1.ASM: Animates four balls bouncing around a playfield by using page flipping and panning](L23-1.ASM)
* [Chapter 24: Parallel Processing with the VGA](https://github.com/jeffpar/abrash-black-book/blob/master/src/chapter-24.md)
* [L24-1.ASM: Illustrates operation of ALUs and latches of the VGA's Graphics Controller](L24-1.ASM)
* [Chapter 25: VGA Data Machinery](https://github.com/jeffpar/abrash-black-book/blob/master/src/chapter-25.md)
* [L25-1.ASM: Illustrates operation of data rotate and bit mask features of Graphics Controller](L25-1.ASM)
* [L25-2.ASM: Illustrates operation of Map Mask register when drawing to memory that already contains data](L25-2.ASM)
* [L25-3.ASM: Illustrates operation of set/reset circuitry to force setting of memory that already contains data](L25-3.ASM)
* [L25-4.ASM: Illustrates operation of set/reset circuitry in conjunction with CPU data](L25-4.ASM)
* [Chapter 26: VGA Write Mode 3](https://github.com/jeffpar/abrash-black-book/blob/master/src/chapter-26.md)
* [L26-1.ASM: Illustrates operation of write mode 3 of the VGA](L26-1.ASM)
* [L26-2.ASM: Illustrates high-speed text-drawing operation of write mode 3 of the VGA](L26-2.ASM)
* [Chapter 27: Yet Another VGA Write Mode](https://github.com/jeffpar/abrash-black-book/blob/master/src/chapter-27.md)
* [L27-1.ASM: Illustrates one use of write mode 2 of the VGA and EGA by animating the image of an "A"](L27-1.ASM)
* [L27-2.ASM: Illustrates one use of write mode 2 of the VGA and EGA by drawing lines in color patterns](L27-2.ASM)
* [L27-3.ASM: Illustrates flipping from bit-mapped graphics mode to text mode and back](L27-3.ASM)
* [Chapter 28: Reading VGA Memory](https://github.com/jeffpar/abrash-black-book/blob/master/src/chapter-28.md)
* [L28-1.ASM: Illustrates the use of the Read Map register in read mode 0](L28-1.ASM)
* [L28-2.ASM: Illustrates use of read mode 1 (color compare mode) to detect collisions in display memory](L28-2.ASM)
* [L28-3.ASM: Illustrates the use of Color Don't Care to support fast read-modify-write operations](L28-3.ASM)
* [Chapter 29: Saving Screens and Other VGA Mysteries](https://github.com/jeffpar/abrash-black-book/blob/master/src/chapter-29.md)
* [L29-1.ASM: Puts up a mode 10h EGA graphics screen, then saves it to the file SNAPSHOT.SCR](L29-1.ASM)
* [L29-2.ASM: Restores a mode 10h EGA graphics screen from the file SNAPSHOT.SCR](L29-2.ASM)
* [L29-3.ASM: Illustrates the color mapping capabilities of the EGA's palette registers](L29-3.ASM)
* [L29-4.ASM: Demonstrates screen blanking via bit 5 of the Attribute Controller Index register](L29-4.ASM)
* [Chapter 30: Video Est Omnis Divisa](https://github.com/jeffpar/abrash-black-book/blob/master/src/chapter-30.md)
* [L30-1.ASM: Demonstrates the VGA/EGA split screen in action](L30-1.ASM)
* [L30-2.ASM: Demonstrates the interaction of the split screen and horizontal pel panning](L30-2.ASM)
* [Chapter 31: Higher 256-Color Resolution on the VGA](https://github.com/jeffpar/abrash-black-book/blob/master/src/chapter-31.md)
* [L31-1.ASM: Demonstrates pixel drawing in 320x400 256-color mode on the VGA](L31-1.ASM)
* [L31-2.ASM: Demonstrates the two pages available in 320x400 256-color modes on a VGA](L31-2.ASM)
* [Chapter 32: Be It Resolved: 360x480](https://github.com/jeffpar/abrash-black-book/blob/master/src/chapter-32.md)
* [L32-1.ASM: Illustrates VGA line drawing in 360x480 256-color mode](L32-1.ASM)
* [L32-2.C: Sample program to illustrate VGA line drawing in 360x480 256-color mode](L32-2.C)
* [Chapter 33: Yogi Bear and Eurythmics Confront VGA Colors](https://github.com/jeffpar/abrash-black-book/blob/master/src/chapter-33.md)
* [L33-1.ASM: Demonstrates use of the DAC registers by selecting a smoothly contiguous set of 256 colors](L33-1.ASM)
* [Chapter 34: Changing Colors without Writing Pixels](https://github.com/jeffpar/abrash-black-book/blob/master/src/chapter-34.md)
* [L34-1.ASM: Fills a band across the screen with vertical bars in all 256 attributes](L34-1.ASM)
* [Chapter 35: Bresenham Is Fast, and Fast Is Good](https://github.com/jeffpar/abrash-black-book/blob/master/src/chapter-35.md)
* [L35-1.C: C implementation of Bresenham's line drawing algorithm](L35-1.C)
* [L35-2.C: Sample program to illustrate EGA/VGA line drawing routines](L35-2.C)
* [L35-3.ASM: Fast assembler implementation of Bresenham's line drawing algorithm](L35-3.ASM)
* [Chapter 47: Mode X: 256-Color VGA Magic](https://github.com/jeffpar/abrash-black-book/blob/master/src/chapter-35.md)
* [L47-1.ASM: Mode X (320x240, 256 colors) mode set routine](L47-1.ASM)
* [L47-2.ASM: Mode X (320x240, 256 colors) write pixel routine](L47-2.ASM)
* [L47-3.ASM: Mode X (320x240, 256 colors) read pixel routine](L47-3.ASM)
* [L47-4.ASM: Mode X (320x240, 256 colors) rectangle fill routine (slow)](L47-4.ASM)
* [L47-5.ASM: Mode X (320x240, 256 colors) rectangle fill routine (medium)](L47-5.ASM)
* [L47-6.ASM: Mode X (320x240, 256 colors) rectangle fill routine (fast)](L47-6.ASM)
* [L47-7.C: Program to demonstrate mode X (320x240, 256-colors) rectangle fill](L47-7.C)
---
Also, I've updated the PCjs [Library](/disks/pcx86/library.xml) disk collection to include a disk containing executables
built from the sources in this directory:
```xml
<disk path="/tests/pcx86/vga/VGABIN.json">VGA Tests (Black Book)</disk>
```
The "VGA Tests (Black Book)" disk image (VGABIN) was built with this command:
diskdump --dir=bin --format=json --output=VGABIN.json
Alternatively, if *path* refers to a directory (ending with a slash) instead of a disk image, the PCjs client will ask
the PCjs web server to enumerate the contents of that directory and send back a JSON-encoded disk image containing all
the files in that directory (including any subdirectories) every time that disk is requested. Since this puts an added
burden on the server, it's best to do this only when running PCjs from a local PCjs web server.
```xml
<disk path="/tests/pcx86/vga/">VGA Tests (Black Book)</disk>
```
One advantage of using [DiskDump](/modules/diskdump/) is that it automatically converts linefeeds in known text files
(including ASM files) into DOS-compatible CR/LF sequences.
VGA "Black Book" Test Machine
---
The [Compaq DeskPro 386](/devices/pcx86/machine/compaq/deskpro386/vga/2048kb/) machine below loads the
"VGA Tests (Black Book)" disk from the PCjs [Library](/disks/pcx86/library.xml) disk collection into Drive B.
Click the "Run" button to start the machine.
{% include machine.html id="deskpro386" %}

27
tests/pcx86/vga/REGS.H Normal file
View file

@ -0,0 +1,27 @@
#ifdef __TURBOC__
#define USES_REGS
#else
#define USES_REGS union REGS reg; struct SREGS sreg;
#define _AH reg.h.ah
#define _AL reg.h.al
#define _BH reg.h.bh
#define _BL reg.h.bl
#define _CH reg.h.ch
#define _CL reg.h.cl
#define _DH reg.h.dh
#define _DL reg.h.dl
#define _AX reg.x.ax
#define _BX reg.x.bx
#define _CX reg.x.cx
#define _DX reg.x.dx
#define _SI reg.x.si
#define _DI reg.x.di
#define _DS sreg.ds
#define _ES sreg.es
#define _SS sreg.ss
#define geninterrupt(n) int86x(n,&reg,&reg,&sreg)
#define inport(port) _inpw(port)
#define inportb(port) _inp(port)
#define outport(port,data) _outpw(port,data)
#define outportb(port,data) _outp(port,data)
#endif

File diff suppressed because one or more lines are too long