📄 elkplan4.c
字号:
/*
* Copyright (c) 1999, 2000 Greg Haerr <greg@censoft.com>
*
* Microwindows
* ELKS EGA/VGA Screen Driver 16 color 4 planes - 16-bit assembly version
*
* This file is an adapation of the asmplan4.s MSC asm driver for ELKS
*/
#include "device.h"
#include "vgaplan4.h"
/* assumptions for speed: NOTE: psd is ignored in these routines*/
#define SCREENSEG $0a000
#define SCREENBASE MK_FP(0xa000, 0)
#define BYTESPERLINE 80
/* extern data*/
extern int gr_mode; /* temp kluge*/
static unsigned char mode_table[MWMODE_MAX + 1] = {
0x00, 0x18, 0x10, 0x08, /* COPY, XOR, AND, OR implemented*/
0x00, 0x00, 0x00, 0x00, /* no VGA HW for other modes*/
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
};
int
ega_init(PSD psd)
{
/* fill in screendevice struct*/
psd->addr = SCREENBASE;
psd->linelen = BYTESPERLINE;
/* Set up some default values for the VGA Graphics Registers. */
set_enable_sr (0x0f);
set_op (0);
set_mode (0);
return 1;
}
/*
* Routine to draw a horizontal line.
* ega_drawhine(psd, x1, x2, y, color);
*
* works in the following EGA and VGA modes:
* 200 line 16 colors modes
* 350 line modes
* 640x480 16 color
*/
/* Draw horizontal line from x1,y to x2,y including final point*/
void
ega_drawhorzline(PSD psd, int x1, int x2, int y, int color)
{
#asm
push bp ; setup stack frame and preserve registers
mov bp, sp
push si
push di
push es
; configure the graphics controller
mov dx, #$03ce ; DX := Graphics Controller port address
mov al, #3 ; set data rotate register
lea bx, _mode_table
add bx, _gr_mode
mov ah, [bx]
out dx, ax
mov ah, [bp+12] ; pixel value
xor al, al ; Set/Reset register number (0)
out dx, ax
mov ax, #$0f01 ; AH := bit plane mask for Enable Set/Reset
out dx, ax ; AL := Enable Set/Reset register number
push ds ; preserve DS
mov ax, [bp+10] ; y
mov bx, [bp+6] ; x1
; compute pixel address
mov dx, #BYTESPERLINE ; AX := [row * BYTESPERLINE]
mul dx
mov cl, bl ; save low order column bits
shr bx, #1 ; BX := [col / 8]
shr bx, #1
shr bx, #1
add bx, ax ; BX := [row * BYTESPERLINE] + [col / 8]
and cl, #$07 ; CL := [col % 8]
xor cl, #$07 ; CL := 7 - [col % 8]
mov ah, #$01 ; AH := 1 << [7 - [col % 8]] [mask]
mov dx, #SCREENSEG ; ES := EGA buffer segment address
mov es, dx
; AH := bit mask
; ES:BX -> video buffer
; CL := number bits to shift left
mov di, bx ; ES:DI -> buffer
mov dh, ah ; DH := unshifted bit mask for left byte
not dh
shl dh, cl ; DH := reverse bit mask for first byte
not dh ; DH := bit mask for first byte
mov cx, [bp+8] ; x2
and cl, #7
xor cl, #7 ; CL := number of bits to shift left
mov dl, #$0ff ; DL := unshifted bit mask for right byte
shl dl, cl ; DL := bit mask for last byte
; determine byte offset of first and last pixel in the line
mov ax, [bp+8] ; AX := x2
mov bx, [bp+6] ; BX := x1
mov cl, #3 ; bits to convert pixels to bytes
shr ax, cl ; AX := byte offset of X2
shr bx, cl ; BX := byte offset of X1
mov cx, ax
sub cx, bx ; CX := [number of bytes in line] - 1
; get Graphics Controller port address into DX
mov bx, dx ; BH := bit mask for first byte
; BL := bit mask for last byte
mov dx, #$03ce ; DX := Graphics Controller port
mov al, #8 ; AL := Bit mask Register number
; make video buffer addressable through DS:SI
push es
pop ds
mov si, di ; DS:SI -> video buffer
; set pixels in leftmost byte of the line
or bh, bh
js L43 ; jump if byte-aligned [x1 is leftmost]
or cx, cx
jnz L42 ; jump if more than one byte in the line
and bl, bh ; BL := bit mask for the line
jmp near L44
L42: mov ah, bh ; AH := bit mask for first byte
out dx, ax ; update graphics controller
movsb ; update bit planes
dec cx
; use a fast 8086 machine instruction to draw the remainder of the line
L43: mov ah, #$0ff ; AH := bit mask
out dx, ax ; update Bit Mask register
rep
movsb ; update all pixels in the line
; set pixels in the rightmost byte of the line
L44: mov ah, bl ; AH := bit mask for last byte
out dx, ax ; update Graphics Controller
movsb ; update bit planes
pop ds ; restore ds
; restore default Graphics Controller state and return to caller
;;xor ax, ax ; AH := 0, AL := 0
;;out dx, ax ; restore Set/Reset register
;;inc ax ; AH := 0, AL := 1
;;out dx, ax ; restore Enable Set/Reset register
;;mov ax, #$0ff08 ; AH := 0xff, AL := 0
;;out dx, ax ; restore Bit Mask register
pop es
pop di
pop si
pop bp
#endasm
}
/*
* Routine to draw a vertical line.
* Called from C:
* ega_drawvline(psd, x, y1, y2, color);
*
* works in the following EGA and VGA modes:
* 200 line 16 colors modes
* 350 line modes
* 640x480 16 color
*/
/* Draw a vertical line from x,y1 to x,y2 including final point*/
void
ega_drawvertline(PSD psd, int x,int y1, int y2, int color)
{
#asm
push bp ; setup stack frame and preserve registers
mov bp, sp
push ds
; configure the graphics controller
mov dx, #$03ce ; DX := Graphics Controller port address
mov al, #3 ; set data rotate register
lea bx, _mode_table
add bx, _gr_mode
mov ah, [bx]
out dx, ax
mov ah, [bp+12] ; color pixel value
xor al, al ; Set/Reset register number (0)
out dx, ax
mov ax, #$0f01 ; AH := bit plane mask for Enable Set/Reset
out dx, ax ; AL := Enable Set/Reset register number
; prepare to draw vertical line
mov ax, [bp+8] ; AX := y1
mov cx, [bp+10] ; BX := y2
;;mov cx, bx
sub cx, ax ; CX := dy
;;jge L311 ; jump if dy >= 0
;;neg cx ; force dy >= 0
;;mov ax, bx ; AX := y2
L311: inc cx ; CX := number of pixels to draw
mov bx, [bp+6] ; BX := x
push cx ; save register
; compute pixel address
push dx
mov dx, #BYTESPERLINE ; AX := [row * BYTESPERLINE]
mul dx
mov cl, bl ; save low order column bits
shr bx, #1 ; BX := [col / 8]
shr bx, #1
shr bx, #1
add bx, ax ; BX := [row * BYTESPERLINE] + [col / 8]
and cl, #$07 ; CL := [col % 8]
xor cl, #$07 ; CL := 7 - [col % 8]
mov ah, #$01 ; AH := 1 << [7 - [col % 8]] [mask]
mov dx, #SCREENSEG ; DS := EGA buffer segment address
mov ds, dx
pop dx
; AH := bit mask
; DS:BX -> video buffer
; CL := number bits to shift left
; set up Graphics controller
shl ah, cl ; AH := bit mask in proper position
mov al, #$08 ; AL := Bit Mask register number
out dx, ax
pop cx ; restore register
; draw the line
mov dx, #BYTESPERLINE ; increment for video buffer
L1111: or [bx], al ; set pixel
add bx, dx ; increment to next line
loop L1111
; restore default Graphics Controller state and return to caller
;;xor ax, ax ; AH := 0, AL := 0
;;out dx, ax ; restore Set/Reset register
;;inc ax ; AH := 0, AL := 1
;;out dx, ax ; restore Enable Set/Reset register
;;mov ax, #$0ff08 ; AH := 0xff, AL := 0
;;out dx, ax ; restore Bit Mask register
pop ds
pop bp
#endasm
}
/*
* Routine to set an individual pixel value.
* Called from C like:
* ega_drawpixel(psd, x, y, color);
*/
void
ega_drawpixel(PSD psd, int x, int y, int color)
{
#asm
push bp
mov bp, sp
mov dx, #$03ce ; graphics controller port address
mov al, #3 ; set data rotate register
lea bx, _mode_table
add bx, _gr_mode
mov ah, [bx]
out dx, ax
mov cx, [bp+6] ; ECX := x
mov ax, [bp+8] ; EAX := y
mov dx, #BYTESPERLINE ; AX := [y * BYTESPERLINE]
mul dx
mov bx, cx ; BX := [x / 8]
shr bx, #1
shr bx, #1
shr bx, #1
add bx, ax ; BX := [y * BYTESPERLINE] + [x / 8]
and cl, #$07 ; CL := [x % 8]
xor cl, #$07 ; CL := 7 - [x % 8]
mov ch, #$01 ; CH := 1 << [7 - [x % 8]] [mask]
shl ch, cl
mov dx, #$03ce ; graphics controller port address
;;required for old code
mov ax, #$0205 ; select write mode 2
out dx, ax ; [load value 2 into mode register 5]
; new code
;;xor ax,ax ; set color register 0
;;mov ah,[bp+10] ; color pixel value
;;out dx,ax
; original code
mov al, #$08 ; set the bit mask register
mov ah, ch ; [load bit mask into register 8]
out dx, ax
push ds
mov ax, #SCREENSEG ; DS := EGA buffer segment address
mov ds, ax
; new code
;;or [bx],al ; quick rmw to set pixel
;;the following fails under ELKS without cli/sti
;;using ES works though. Code changed to use single
;;rmw above rather than write mode 2, but the
;;reason for this failure is still unknown...
;;cli
mov al, [bx] ; dummy read to latch bit planes
mov al, [bp+10] ; pixel value
mov [bx], al ; write pixel back to bit planes
;;sti
pop ds ; restore registers and return
mov ax, #$0005 ; restore default write mode 0
out dx, ax ; [load value 0 into mode register 5]
;;mov ax, #$0ff08 ; restore default bit mask
;;out dx, ax ; [load value ff into register 8]
pop bp
#endasm
}
/*
* Routine to read the value of an individual pixel.
* Called from C like:
* color = ega_readpixel(psd, x, y);
*/
PIXELVAL
ega_readpixel(PSD psd, int x, int y)
{
#asm
push bp
mov bp, sp
push si
push ds
mov ax, [bp+8] ; EAX := y
mov bx, [bp+6] ; EBX := x
mov dx, #BYTESPERLINE ; AX := [y * BYTESPERLINE]
mul dx
mov cl, bl ; save low order column bits
shr bx, #1 ; BX := [x / 8]
shr bx, #1
shr bx, #1
add bx, ax ; BX := [y * BYTESPERLINE] + [x / 8]
and cl, #$07 ; CL := [x % 8]
xor cl, #$07 ; CL := 7 - [x % 8]
mov dx, #SCREENSEG ; DS := EGA buffer segment address
mov ds, dx
mov ch, #$01 ; CH := 1 << [7 - [col % 8]] [mask]
shl ch, cl ; CH := bit mask in proper position
mov si, bx ; DS:SI -> region buffer byte
xor bl, bl ; BL is used to accumulate the pixel value
mov dx, #$03ce ; DX := Graphics Controller port
mov ax, #$0304 ; AH := initial bit plane number
; AL := Read Map Select register number
L112: out dx, ax ; select bit plane
mov bh, [si] ; BH := byte from current bit plane
and bh, ch ; mask one bit
neg bh ; bit 7 of BH := 1 if masked bit = 1
; bit 7 of BH := 0 if masked bit = 0
rol bx, #1 ; bit 0 of BL := next bit from pixel value
dec ah ; AH := next bit plane number
jge L112
xor ax, ax ; AL := pixel value
mov al, bl
pop ds
pop si
pop bp
#endasm
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -