svga works, optimize vga and clean up code
This commit is contained in:
parent
77c162002b
commit
135fa6c2e8
7
README.md
Normal file
7
README.md
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# VGA logic units in 256 colors and SVGA mode usage
|
||||||
|
|
||||||
|
Some code to exercise the four logic units of the VGA card in
|
||||||
|
256 color mode, as well as code that gets SVGA mode
|
||||||
|
working. Most of this is in C, but some of it's in assembler
|
||||||
|
to help me learn assembler better, as well as get comfortable with
|
||||||
|
C/asm interop on x86.
|
53
bmp.c
Normal file
53
bmp.c
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include "bmp.h"
|
||||||
|
#include "vesa.h"
|
||||||
|
|
||||||
|
int readBMPHeader(FILE *fh, struct BMPImage *info) {
|
||||||
|
int sizeOfHeader;
|
||||||
|
char value;
|
||||||
|
unsigned int numberOfUsedColors;
|
||||||
|
|
||||||
|
if ((fgetc(fh) != 'B') || (fgetc(fh) != 'M')) {
|
||||||
|
printf("Not a BMP file\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fseek(fh, 12, SEEK_CUR);
|
||||||
|
|
||||||
|
fread(&sizeOfHeader, sizeof(int), 1, fh);
|
||||||
|
fread(&info->width, sizeof(int), 1, fh);
|
||||||
|
fread(&info->height, sizeof(int), 1, fh);
|
||||||
|
fseek(fh, 2, SEEK_CUR);
|
||||||
|
fread(&info->bpp, sizeof(int), 1, fh);
|
||||||
|
|
||||||
|
// for gimp, you need to add colors to the color map until it hits
|
||||||
|
// 16 colors, then the image will be a 256 color, 8 bpp image
|
||||||
|
//
|
||||||
|
// https://www.gimp-forum.net/Thread-indexing-into-8-bit?pid=13233#pid13233
|
||||||
|
if (info->bpp != 8) return 1;
|
||||||
|
|
||||||
|
fseek(fh, 18, SEEK_CUR);
|
||||||
|
fread(&numberOfUsedColors, sizeof(unsigned int), 1, fh);
|
||||||
|
printf("used colors: %d\n",numberOfUsedColors);
|
||||||
|
if (numberOfUsedColors > 256) return 1;
|
||||||
|
|
||||||
|
|
||||||
|
// get down to color data
|
||||||
|
fseek(fh, 14 + sizeOfHeader, SEEK_SET);
|
||||||
|
|
||||||
|
fread(info->colors, sizeof(struct BMPColor), numberOfUsedColors, fh);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bmp256ColorPaletteToVESAColorPalette(struct BMPImage* bmpImage, struct VESAColor colors[]) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < 256; ++i) {
|
||||||
|
colors[i].red = bmpImage->colors[i].red >> 2;
|
||||||
|
colors[i].green = bmpImage->colors[i].green >> 2;
|
||||||
|
colors[i].blue = bmpImage->colors[i].blue >> 2;
|
||||||
|
}
|
||||||
|
}
|
19
bmp.h
Normal file
19
bmp.h
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#pragma pack(push,1)
|
||||||
|
struct BMPColor {
|
||||||
|
uint8_t blue;
|
||||||
|
uint8_t green;
|
||||||
|
uint8_t red;
|
||||||
|
uint8_t _a;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BMPImage {
|
||||||
|
uint16_t width;
|
||||||
|
uint16_t height;
|
||||||
|
uint16_t bpp;
|
||||||
|
struct BMPColor colors[256];
|
||||||
|
char *memoryStart;
|
||||||
|
|
||||||
|
uint16_t transparentColor;
|
||||||
|
};
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
BIN
chkn640.bmp
Normal file
BIN
chkn640.bmp
Normal file
Binary file not shown.
After Width: | Height: | Size: 301 KiB |
50
dpmi.c
Normal file
50
dpmi.c
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#include <sys/types.h>
|
||||||
|
#include <i86.h>
|
||||||
|
#include <dos.h>
|
||||||
|
|
||||||
|
#include "dpmi.h"
|
||||||
|
|
||||||
|
static uint16_t vbeDOSBufferDPMISelector = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is memory accessible to real-mode x86, so that
|
||||||
|
* interrupts can access the data.
|
||||||
|
*/
|
||||||
|
void *dpmiAllocateDOSMemory(int size) {
|
||||||
|
union REGS regs;
|
||||||
|
|
||||||
|
// a paragraph is 16 bytes because why not
|
||||||
|
unsigned short sizeParagraphs = (size + 15) >> 4UL;
|
||||||
|
|
||||||
|
regs.w.ax = 0x0100;
|
||||||
|
regs.w.bx = sizeParagraphs;
|
||||||
|
int386(0x31, ®s, ®s);
|
||||||
|
|
||||||
|
vbeDOSBufferDPMISelector = regs.w.dx;
|
||||||
|
return (void *)(((unsigned long)regs.w.ax) << 4UL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dpmiDeallocateDOSMemory() {
|
||||||
|
union REGS regs;
|
||||||
|
|
||||||
|
regs.w.ax = 0x0101;
|
||||||
|
regs.w.dx = vbeDOSBufferDPMISelector;
|
||||||
|
int386(0x31, ®s, ®s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* You only need to use this if you're passing a DPMI-allocated
|
||||||
|
* memory struture to an interrupt via ES:DI. Otherwise, a
|
||||||
|
* regular int386 call is sufficient.
|
||||||
|
*/
|
||||||
|
void dpmiInt(int interruptNo, struct dpmiRealModeRegs *rc) {
|
||||||
|
union REGS regs;
|
||||||
|
|
||||||
|
// call the DPMI real mode interrupt handler
|
||||||
|
regs.w.ax = 0x0300;
|
||||||
|
regs.w.bx = interruptNo; // real mode interrupt
|
||||||
|
regs.w.cx = 0; // don't mess with our stack
|
||||||
|
regs.x.edi = (unsigned int)rc;
|
||||||
|
|
||||||
|
int386(0x31, ®s, ®s);
|
||||||
|
}
|
18
dpmi.h
Normal file
18
dpmi.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#ifndef __DPMI_H__
|
||||||
|
#define __DPMI_H__
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#pragma pack(push,1)
|
||||||
|
struct dpmiRealModeRegs {
|
||||||
|
uint32_t edi,esi,ebp,reserved;
|
||||||
|
uint32_t ebx,edx,ecx,eax;
|
||||||
|
uint16_t flags,es,ds,fs,gs,ip,cs,sp,ss;
|
||||||
|
};
|
||||||
|
#pragma paack(pop)
|
||||||
|
|
||||||
|
void *dpmiAllocateDOSMemory(int size);
|
||||||
|
void dpmiDeallocateDOSMemory();
|
||||||
|
void dpmiInt(int interruptNo, struct dpmiRealModeRegs *);
|
||||||
|
|
||||||
|
#endif
|
60
main.c
60
main.c
@ -7,14 +7,14 @@ extern void __cdecl enableUnchainedVGAMode();
|
|||||||
extern void __cdecl enableTextMode();
|
extern void __cdecl enableTextMode();
|
||||||
extern void __cdecl fillScreen(int);
|
extern void __cdecl fillScreen(int);
|
||||||
extern void __cdecl latchCopyCube(
|
extern void __cdecl latchCopyCube(
|
||||||
char offset,
|
int offset,
|
||||||
int writtenValue,
|
int writtenValue,
|
||||||
int writeMode,
|
int writeMode,
|
||||||
int setResetRegister,
|
int setResetRegister,
|
||||||
int enableSetResetRegister
|
int enableSetResetRegister
|
||||||
);
|
);
|
||||||
extern void __cdecl writeCube(
|
extern void __cdecl writeCube(
|
||||||
char offset,
|
int offset,
|
||||||
int writeMode,
|
int writeMode,
|
||||||
int setResetRegister,
|
int setResetRegister,
|
||||||
int enableSetResetRegister
|
int enableSetResetRegister
|
||||||
@ -23,7 +23,6 @@ extern void __cdecl drawColorCube(
|
|||||||
char offset,
|
char offset,
|
||||||
int color
|
int color
|
||||||
);
|
);
|
||||||
extern int __cdecl paramTest(int wow);
|
|
||||||
|
|
||||||
extern void far interrupt keyboardHandler();
|
extern void far interrupt keyboardHandler();
|
||||||
extern char keyboardPressed;
|
extern char keyboardPressed;
|
||||||
@ -40,9 +39,9 @@ void populateExampleCube(void) {
|
|||||||
|
|
||||||
outp(0x3c4, 0x02);
|
outp(0x3c4, 0x02);
|
||||||
|
|
||||||
for (y = 0; y < 16; ++y) {
|
for (x = 0; x < 16; ++x) {
|
||||||
for (x = 0; x < 16; ++x) {
|
outp(0x3c5, 1 << (x % 4));
|
||||||
outp(0x3c5, 1 << (x % 4));
|
for (y = 0; y < 16; ++y) {
|
||||||
VGA[y * PLANE_WIDTH + x / 4] = y * 16 + x;
|
VGA[y * PLANE_WIDTH + x / 4] = y * 16 + x;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -50,20 +49,20 @@ void populateExampleCube(void) {
|
|||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
// activate unchained vga mode
|
// activate unchained vga mode
|
||||||
// set up 8 very clear colors
|
|
||||||
// place some data into video memory to be latch copied
|
// place some data into video memory to be latch copied
|
||||||
// show off the following
|
// show off the following
|
||||||
// * latch copies
|
// * latch copies with logical operators
|
||||||
// * masking
|
// * plane selection
|
||||||
// * barrel shifting
|
// * barrel shifting
|
||||||
|
// * set/reset
|
||||||
int i,j;
|
|
||||||
|
|
||||||
int9Save = _dos_getvect(9);
|
int9Save = _dos_getvect(9);
|
||||||
_dos_setvect(9, keyboardHandler);
|
_dos_setvect(9, keyboardHandler);
|
||||||
|
|
||||||
enableUnchainedVGAMode();
|
enableUnchainedVGAMode();
|
||||||
|
|
||||||
|
// set a very bright color as the last possible color to make
|
||||||
|
// set/reset usage more visible
|
||||||
outp(0x3c8, 255);
|
outp(0x3c8, 255);
|
||||||
outp(0x3c9, 63);
|
outp(0x3c9, 63);
|
||||||
outp(0x3c9, 63);
|
outp(0x3c9, 63);
|
||||||
@ -71,27 +70,28 @@ int main(void) {
|
|||||||
|
|
||||||
fillScreen(0);
|
fillScreen(0);
|
||||||
|
|
||||||
/*
|
|
||||||
populateExampleCube();
|
populateExampleCube();
|
||||||
latchCopyCube(8, 0, 0x00, 0, 0);
|
|
||||||
latchCopyCube(12, 127, 0x00, 0, 0);
|
|
||||||
latchCopyCube(16, 0, 0x08, 0, 0);
|
|
||||||
latchCopyCube(20, 127, 0x08, 0, 0);
|
|
||||||
latchCopyCube(24, 0, 0x10, 0, 0);
|
|
||||||
latchCopyCube(28, 127, 0x10, 0, 0);
|
|
||||||
latchCopyCube(32, 0, 0x18, 0, 0);
|
|
||||||
latchCopyCube(36, 127, 0x18, 0, 0);
|
|
||||||
latchCopyCube(40, 0, 0x18 + 0x04, 0, 0);
|
|
||||||
latchCopyCube(44, 127, 0x18 + 0x04, 0, 0);
|
|
||||||
latchCopyCube(64, 0, 0x10, 7, 7);
|
|
||||||
latchCopyCube(68, 0, 0x10, 7, 0);
|
|
||||||
latchCopyCube(72, 0, 0x10, 0, 7);
|
|
||||||
*/
|
|
||||||
|
|
||||||
writeCube(0, 0x10, 0, 0);
|
latchCopyCube(80 * 20, 0, 0x00, 0, 0);
|
||||||
writeCube(4, 0x10, 7, 0);
|
latchCopyCube(80 * 20 + 5, 127, 0x00, 0, 0);
|
||||||
writeCube(8, 0x10, 7, 7);
|
latchCopyCube(80 * 20 + 10, 0, 0x08, 0, 0);
|
||||||
writeCube(12, 0x10, 0, 7);
|
latchCopyCube(80 * 20 + 15, 127, 0x08, 0, 0);
|
||||||
|
latchCopyCube(80 * 20 + 20, 0, 0x10, 0, 0);
|
||||||
|
latchCopyCube(80 * 20 + 25, 127, 0x10, 0, 0);
|
||||||
|
latchCopyCube(80 * 20 + 30, 0, 0x18, 0, 0);
|
||||||
|
latchCopyCube(80 * 20 + 35, 127, 0x18, 0, 0);
|
||||||
|
latchCopyCube(80 * 20 + 40, 127, 0x10, 1, 3);
|
||||||
|
|
||||||
|
latchCopyCube(80 * 40, 0, 0x18 + 0x04, 0, 0);
|
||||||
|
latchCopyCube(80 * 40 + 5, 127, 0x18 + 0x04, 0, 0);
|
||||||
|
|
||||||
|
latchCopyCube(80 * 40 + 10, 0, 0x10, 3, 3);
|
||||||
|
latchCopyCube(80 * 40 + 15, 0, 0x10, 1, 3);
|
||||||
|
latchCopyCube(80 * 40 + 20, 0, 0x10, 0, 3);
|
||||||
|
|
||||||
|
writeCube(5, 0x00, 1, 3);
|
||||||
|
writeCube(10, 0x00, 0, 3);
|
||||||
|
writeCube(15, 0x00, 3, 0);
|
||||||
|
|
||||||
while (keyboardPressed == 0);
|
while (keyboardPressed == 0);
|
||||||
|
|
||||||
|
6
makefile
6
makefile
@ -1,4 +1,5 @@
|
|||||||
obj = vga.o main.o
|
obj = vga.o main.o
|
||||||
|
svga_obj = dpmi.o bmp.o vesa.o svga.o
|
||||||
|
|
||||||
.c.o:
|
.c.o:
|
||||||
wcc386 -q -bt=dos $<
|
wcc386 -q -bt=dos $<
|
||||||
@ -8,3 +9,8 @@ obj = vga.o main.o
|
|||||||
|
|
||||||
main.exe: $(obj)
|
main.exe: $(obj)
|
||||||
wcl386 -fe=main -bt=dos -l=dos4g $(obj)
|
wcl386 -fe=main -bt=dos -l=dos4g $(obj)
|
||||||
|
|
||||||
|
svga.exe: $(svga_obj)
|
||||||
|
wcl386 -fe=svga -bt=dos -l=dos4g $(svga_obj)
|
||||||
|
|
||||||
|
all: main.exe svga.exe
|
||||||
|
102
svga.c
Normal file
102
svga.c
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
#include <stddef.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <conio.h>
|
||||||
|
#include <dos.h>
|
||||||
|
#include <i86.h>
|
||||||
|
#include <memory.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
Set up a 640x480x256 Super VGA screen and load a bitmap image into it.
|
||||||
|
Wait 10 seconds, then exit the program.
|
||||||
|
|
||||||
|
Since this is going through DOS/4GW, we need to use the DOS Protected
|
||||||
|
Mode Interface (DPMI) to work with x86 Real Mode RAM so the VESA
|
||||||
|
interrupt functions have a place to accept and dump data.
|
||||||
|
|
||||||
|
There were a lot of scattered examples as to how to get SVGA going, but:
|
||||||
|
|
||||||
|
* most were written in assembler and, while I can read assembler, I don't
|
||||||
|
prefer to, especially in the early learning stages of a bunch of new concepts
|
||||||
|
* very few of them covered the whole pipeline of query display, then
|
||||||
|
set it up, then do something real with it
|
||||||
|
|
||||||
|
So here's this one. Enjoy.
|
||||||
|
|
||||||
|
John
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
// built from a combination of:
|
||||||
|
//
|
||||||
|
// * http://www.pennelynn.com/Documents/CUJ/HTML/14.08/KREHBIEL/KREHL1.HTM
|
||||||
|
// * https://forum.osdev.org/viewtopic.php?f=2&t=30186
|
||||||
|
// * https://github.com/joncampbell123/doslib/blob/master/hw/vesa/vesa.c
|
||||||
|
// * https://stackoverflow.com/questions/3318410/pragma-pack-effect
|
||||||
|
// * https://www.delorie.com/djgpp/doc/dpmi/ch4.4.html
|
||||||
|
// * https://delorie.com/djgpp/doc/dpmi/api/310100.html
|
||||||
|
// * http://www.monstersoft.com/tutorial1/VESA_intro.html
|
||||||
|
// * http://www.delorie.com/djgpp/doc/ug/graphics/vesa.html.en
|
||||||
|
// * https://lhlaurini.xyz/VESA_Video_Modes
|
||||||
|
// * https://stackoverflow.com/questions/58328264/retrieve-list-of-vesa-video-modes-from-int-10h-ax-4f00h
|
||||||
|
// * https://github.com/execomrt/vesa2/blob/main/vbelib/vbecore.c#L262
|
||||||
|
// * https://pdos.csail.mit.edu/6.828/2004/readings/hardware/vbe3.pdf
|
||||||
|
|
||||||
|
#include "vesa.h"
|
||||||
|
#include "bmp.h"
|
||||||
|
#include "dpmi.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
|
||||||
|
If you want to enumerate all the possible modes to inspect, you would need
|
||||||
|
something like this to turn the segment:offset data into array elements you can
|
||||||
|
address in 32-bit DOS.
|
||||||
|
|
||||||
|
uint16_t *modePtr;
|
||||||
|
|
||||||
|
modePtr = (uint16_t*)((vbeInfo.VideoModeSegment << 4) + vbeInfo.VideoModeOffset);
|
||||||
|
for (i = 0; i < 4; ++i) {
|
||||||
|
printf("Available mode %d: 0x%x\n", i, modePtr[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
int x, y;
|
||||||
|
FILE *bmp;
|
||||||
|
struct BMPImage bmpImage;
|
||||||
|
struct VESAColor colors[256];
|
||||||
|
struct VbeInfo vbeInfo;
|
||||||
|
struct VbeModeInfo vbeModeInfo;
|
||||||
|
|
||||||
|
setupVESAHandling();
|
||||||
|
|
||||||
|
vbeGetVBEInfo(&vbeInfo);
|
||||||
|
printf("VBE Version: 0x%x\n", vbeInfo.VbeVersion);
|
||||||
|
|
||||||
|
bmp = fopen("chkn640.bmp", "rb");
|
||||||
|
readBMPHeader(bmp, &bmpImage);
|
||||||
|
bmp256ColorPaletteToVESAColorPalette(&bmpImage, colors);
|
||||||
|
|
||||||
|
// we could enumerate all the modes, but in DOSBox-X's default S3 VGA card,
|
||||||
|
// 0x101 is 640x480x256, and that's where I'm running this. it's more correct
|
||||||
|
// to select the mode based on examining capabilities for all available
|
||||||
|
// modes, but...eh.
|
||||||
|
startSVGAMode(0x101, &vbeModeInfo);
|
||||||
|
setVESAColorPalette(colors);
|
||||||
|
|
||||||
|
for (y = 0; y < vbeModeInfo.YResolution; ++y) {
|
||||||
|
for (x = 0; x < vbeModeInfo.XResolution; ++x) {
|
||||||
|
setPixel(x, vbeModeInfo.YResolution - y, fgetc(bmp));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(bmp);
|
||||||
|
|
||||||
|
delay(10000);
|
||||||
|
|
||||||
|
teardownVESAHandling();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
133
vesa.c
Normal file
133
vesa.c
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
#include <memory.h>
|
||||||
|
#include <i86.h>
|
||||||
|
#include <dos.h>
|
||||||
|
|
||||||
|
#include "dpmi.h"
|
||||||
|
#include "vesa.h"
|
||||||
|
|
||||||
|
char far *VGA = (char*)0xA0000;
|
||||||
|
long windowSizeBytes, bytesPerScanLine;
|
||||||
|
void *dosmem;
|
||||||
|
int currentWindowPosition;
|
||||||
|
|
||||||
|
#define MAX_COLORS (256)
|
||||||
|
|
||||||
|
void setupVESAHandling() {
|
||||||
|
dosmem = dpmiAllocateDOSMemory(1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
void teardownVESAHandling() {
|
||||||
|
dpmiDeallocateDOSMemory();
|
||||||
|
}
|
||||||
|
|
||||||
|
void addDOSMemToRealModeRegs(struct dpmiRealModeRegs *rc) {
|
||||||
|
rc->edi = (unsigned long)dosmem & 0xf;
|
||||||
|
rc->es = (unsigned long)dosmem >> 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
void vbeGetModeInfo(
|
||||||
|
uint16_t mode,
|
||||||
|
struct VbeModeInfo *modeInfo
|
||||||
|
) {
|
||||||
|
struct dpmiRealModeRegs rc={0};
|
||||||
|
|
||||||
|
_fmemset(dosmem, 0, sizeof(struct VbeModeInfo));
|
||||||
|
|
||||||
|
rc.eax = 0x4f01;
|
||||||
|
rc.ecx = mode;
|
||||||
|
addDOSMemToRealModeRegs(&rc);
|
||||||
|
|
||||||
|
dpmiInt(0x10, &rc);
|
||||||
|
|
||||||
|
_fmemcpy(modeInfo, dosmem, sizeof(struct VbeModeInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
int vbeGetVBEInfo(
|
||||||
|
struct VbeInfo far *p
|
||||||
|
) {
|
||||||
|
struct dpmiRealModeRegs rc={0};
|
||||||
|
|
||||||
|
// request VBE2 extended info
|
||||||
|
_fmemset(p, 0, sizeof(struct VbeInfo));
|
||||||
|
_fmemcpy(p->VbeSignature,"VBE2",4);
|
||||||
|
_fmemcpy(dosmem, p, sizeof(struct VbeInfo));
|
||||||
|
|
||||||
|
rc.eax = 0x4f00;
|
||||||
|
addDOSMemToRealModeRegs(&rc);
|
||||||
|
|
||||||
|
dpmiInt(0x10, &rc);
|
||||||
|
|
||||||
|
_fmemcpy(p, dosmem, sizeof(struct VbeInfo));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void startSVGAMode(int mode, struct VbeModeInfo *vbeModeInfo) {
|
||||||
|
/**
|
||||||
|
* Side effects: sets some internal variables for window and scanline sizes
|
||||||
|
*/
|
||||||
|
union REGS regs;
|
||||||
|
|
||||||
|
vbeGetModeInfo(mode, vbeModeInfo);
|
||||||
|
|
||||||
|
windowSizeBytes = 1024L * vbeModeInfo->WinSize;
|
||||||
|
bytesPerScanLine = vbeModeInfo->BytesPerScanLine;
|
||||||
|
|
||||||
|
regs.w.ax = 0x4f02;
|
||||||
|
regs.w.bx = mode;
|
||||||
|
int386(0x10, ®s, ®s);
|
||||||
|
|
||||||
|
vbeSetWindow(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void vbeSetWindow(int window, int position) {
|
||||||
|
/**
|
||||||
|
* Side effects: tracks current window position
|
||||||
|
*/
|
||||||
|
union REGS regs;
|
||||||
|
|
||||||
|
regs.w.ax = 0x4f05;
|
||||||
|
regs.h.bh = 0;
|
||||||
|
regs.h.bl = window;
|
||||||
|
regs.w.dx = position;
|
||||||
|
int386(0x10, ®s, ®s);
|
||||||
|
|
||||||
|
currentWindowPosition = position;
|
||||||
|
}
|
||||||
|
|
||||||
|
void maybeVbeSetWindow(int window, int position) {
|
||||||
|
if (currentWindowPosition == position) return;
|
||||||
|
|
||||||
|
vbeSetWindow(window, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setVESAColorPalette(
|
||||||
|
struct VESAColor *colors
|
||||||
|
) {
|
||||||
|
struct dpmiRealModeRegs rc={0};
|
||||||
|
int i;
|
||||||
|
|
||||||
|
_fmemcpy(dosmem, colors, MAX_COLORS * 4);
|
||||||
|
|
||||||
|
// prep our real mode interrupt call
|
||||||
|
rc.eax = 0x4f09;
|
||||||
|
rc.ebx = 0;
|
||||||
|
rc.ecx = MAX_COLORS;
|
||||||
|
rc.edx = 0;
|
||||||
|
addDOSMemToRealModeRegs(&rc);
|
||||||
|
|
||||||
|
dpmiInt(0x10, &rc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setPixel(int x, int y, int color) {
|
||||||
|
long absolutePosition = (long)y * bytesPerScanLine + x;
|
||||||
|
long windowPosition = absolutePosition / windowSizeBytes;
|
||||||
|
long windowOffset = absolutePosition % windowSizeBytes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This will not shift window positions unless needed.
|
||||||
|
*/
|
||||||
|
maybeVbeSetWindow(0, windowPosition);
|
||||||
|
|
||||||
|
VGA[windowOffset] = color;
|
||||||
|
}
|
57
vesa.h
Normal file
57
vesa.h
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
#ifndef __VESA_H__
|
||||||
|
#define __VESA_H__
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#pragma pack(push,1)
|
||||||
|
struct VbeInfo {
|
||||||
|
char VbeSignature[4];
|
||||||
|
uint16_t VbeVersion;
|
||||||
|
uint32_t OemStringPtr;
|
||||||
|
uint32_t Capabilities;
|
||||||
|
|
||||||
|
uint16_t VideoModeOffset;
|
||||||
|
uint16_t VideoModeSegment;
|
||||||
|
|
||||||
|
uint16_t TotalMemory;
|
||||||
|
|
||||||
|
int OemSoftwareRev;
|
||||||
|
char far *OemVendorNamePtr;
|
||||||
|
char far *OemProductNamePtr;
|
||||||
|
char far *OenProductRevPtr;
|
||||||
|
char Reserved[222];
|
||||||
|
char OemData[256];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VbeModeInfo {
|
||||||
|
uint16_t ModeAttributes;
|
||||||
|
uint8_t WinAAttributes;
|
||||||
|
uint8_t WinBAttributes;
|
||||||
|
uint16_t WinGranularity;
|
||||||
|
uint16_t WinSize;
|
||||||
|
uint16_t WinASegment;
|
||||||
|
uint16_t WinBSegment;
|
||||||
|
void *WindowFunction;
|
||||||
|
uint16_t BytesPerScanLine;
|
||||||
|
uint16_t XResolution;
|
||||||
|
uint16_t YResolution;
|
||||||
|
uint8_t XCharSize;
|
||||||
|
uint8_t YCharSize;
|
||||||
|
uint8_t NumberOfPlanes;
|
||||||
|
uint8_t BitsPerPixel;
|
||||||
|
uint8_t NumberOfBanks;
|
||||||
|
uint8_t MemoryModel;
|
||||||
|
uint8_t BankSize;
|
||||||
|
uint8_t NumberOfImagePages;
|
||||||
|
char more[500];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VESAColor {
|
||||||
|
uint8_t blue;
|
||||||
|
uint8_t green;
|
||||||
|
uint8_t red;
|
||||||
|
uint8_t _a;
|
||||||
|
};
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
#endif
|
124
vga.asm
124
vga.asm
@ -4,7 +4,6 @@ PUBLIC _fillScreen
|
|||||||
PUBLIC _latchCopyCube
|
PUBLIC _latchCopyCube
|
||||||
PUBLIC _drawColorCube
|
PUBLIC _drawColorCube
|
||||||
PUBLIC _writeCube
|
PUBLIC _writeCube
|
||||||
PUBLIC _paramTest
|
|
||||||
PUBLIC _keyboardPressed
|
PUBLIC _keyboardPressed
|
||||||
PUBLIC keyboardHandler_
|
PUBLIC keyboardHandler_
|
||||||
|
|
||||||
@ -39,6 +38,7 @@ keyboardHandler_:
|
|||||||
push eax
|
push eax
|
||||||
push ebx
|
push ebx
|
||||||
|
|
||||||
|
; put scancode into bl
|
||||||
in al, 0x60
|
in al, 0x60
|
||||||
mov bl, al
|
mov bl, al
|
||||||
|
|
||||||
@ -50,6 +50,7 @@ keyboardHandler_:
|
|||||||
xchg ah, al
|
xchg ah, al
|
||||||
out 0x61, al
|
out 0x61, al
|
||||||
|
|
||||||
|
; a key is pressed if the topmost bit of the scancode is 0
|
||||||
and bl,0x80
|
and bl,0x80
|
||||||
jne _keyboardHandler_notKeydown
|
jne _keyboardHandler_notKeydown
|
||||||
|
|
||||||
@ -62,8 +63,10 @@ _keyboardHandler_notKeydown:
|
|||||||
pop ebx
|
pop ebx
|
||||||
pop eax
|
pop eax
|
||||||
sti
|
sti
|
||||||
|
; return from the interrupt in a way that preserves the calling stack
|
||||||
iretd
|
iretd
|
||||||
|
|
||||||
|
|
||||||
_enableUnchainedVGAMode:
|
_enableUnchainedVGAMode:
|
||||||
mov ax,0x0013;
|
mov ax,0x0013;
|
||||||
int 0x10
|
int 0x10
|
||||||
@ -85,12 +88,14 @@ _enableUnchainedVGAMode:
|
|||||||
|
|
||||||
ret
|
ret
|
||||||
|
|
||||||
|
|
||||||
_enableTextMode:
|
_enableTextMode:
|
||||||
mov ah,0h
|
mov ah,0h
|
||||||
mov al,DISPLAY_MODE_TEXT
|
mov al,DISPLAY_MODE_TEXT
|
||||||
int 10h
|
int 10h
|
||||||
ret
|
ret
|
||||||
|
|
||||||
|
|
||||||
_fillScreen:
|
_fillScreen:
|
||||||
push ebp
|
push ebp
|
||||||
mov ebp, esp
|
mov ebp, esp
|
||||||
@ -98,34 +103,45 @@ _fillScreen:
|
|||||||
; set all bitplanes
|
; set all bitplanes
|
||||||
mov dx, VGA_SEQUENCE_CONTROLLER_INDEX
|
mov dx, VGA_SEQUENCE_CONTROLLER_INDEX
|
||||||
mov al, 0x02
|
mov al, 0x02
|
||||||
mov ah, 0fh
|
mov ah, 0x0f
|
||||||
out dx, ax
|
out dx, ax
|
||||||
|
|
||||||
mov al, [ebp+8] ; requested color
|
push ebx
|
||||||
|
push ecx
|
||||||
|
push edx
|
||||||
|
sub eax,eax
|
||||||
|
mov al, [ebp+8] ; requested color
|
||||||
|
|
||||||
mov cx, 16000 ; loop index
|
; we can write a long at a time and let the bus
|
||||||
mov ebx, 0xa0000
|
; and BIU work out the details. on a 16-bit ISA
|
||||||
|
; bus with a 16-bit VGA card, this will be faster
|
||||||
|
; than byte writes
|
||||||
|
sub edx,edx
|
||||||
|
add dl,al
|
||||||
|
shl edx,8
|
||||||
|
add dl,al
|
||||||
|
shl edx,8
|
||||||
|
add dl,al
|
||||||
|
shl edx,8
|
||||||
|
add dl,al
|
||||||
|
|
||||||
|
; with all planes selected we only need to fill 16000 bytes of data
|
||||||
|
; and we're filling them four bytes at a time
|
||||||
|
mov cx, 4000
|
||||||
|
mov ebx, 0xA0000 ; VGA base address
|
||||||
_fillScreen_loop:
|
_fillScreen_loop:
|
||||||
mov BYTE PTR [ebx], al
|
mov DWORD PTR [ebx], edx
|
||||||
inc ebx
|
add ebx,4
|
||||||
loop _fillScreen_loop
|
loop _fillScreen_loop
|
||||||
|
pop edx
|
||||||
|
pop ecx
|
||||||
|
pop ebx
|
||||||
|
|
||||||
mov esp,ebp
|
mov esp,ebp
|
||||||
pop ebp
|
pop ebp
|
||||||
|
|
||||||
ret
|
ret
|
||||||
|
|
||||||
_paramTest:
|
|
||||||
push ebp
|
|
||||||
mov ebp,esp
|
|
||||||
|
|
||||||
mov eax,0
|
|
||||||
mov al, BYTE PTR [ebp+8]
|
|
||||||
|
|
||||||
mov esp,ebp
|
|
||||||
pop ebp
|
|
||||||
ret
|
|
||||||
|
|
||||||
|
|
||||||
_drawColorCube:
|
_drawColorCube:
|
||||||
push ebp
|
push ebp
|
||||||
@ -165,6 +181,7 @@ _drawColorCube:
|
|||||||
_drawColorCube_yLoop:
|
_drawColorCube_yLoop:
|
||||||
mov [ebp-4],cx
|
mov [ebp-4],cx
|
||||||
|
|
||||||
|
; put the VGA write offset into eax
|
||||||
mov eax,[ebp-4]
|
mov eax,[ebp-4]
|
||||||
dec eax
|
dec eax
|
||||||
mov bl,80
|
mov bl,80
|
||||||
@ -174,7 +191,7 @@ _drawColorCube_yLoop:
|
|||||||
|
|
||||||
mov cx, 4
|
mov cx, 4
|
||||||
_drawColorCube_xLoop:
|
_drawColorCube_xLoop:
|
||||||
mov [eax],bh ; write color
|
mov [eax],bh ; write color four bytes at a time
|
||||||
inc eax
|
inc eax
|
||||||
|
|
||||||
loop _drawColorCube_xLoop
|
loop _drawColorCube_xLoop
|
||||||
@ -190,15 +207,15 @@ _drawColorCube_xLoop:
|
|||||||
ret
|
ret
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
_writeCube:
|
_writeCube:
|
||||||
push ebp
|
push ebp
|
||||||
mov ebp,esp
|
mov ebp,esp
|
||||||
; we need a y variable
|
; we need a y variable
|
||||||
sub esp,4
|
sub esp,4
|
||||||
; and a current color
|
|
||||||
sub esp,4
|
push ebx
|
||||||
|
push ecx
|
||||||
|
push edx
|
||||||
|
|
||||||
; [ebp+8] is x offset
|
; [ebp+8] is x offset
|
||||||
; [ebp+12] is data rotate
|
; [ebp+12] is data rotate
|
||||||
@ -222,57 +239,66 @@ _writeCube:
|
|||||||
mov ah, [ebp+20]
|
mov ah, [ebp+20]
|
||||||
out dx, ax
|
out dx, ax
|
||||||
|
|
||||||
mov DWORD PTR [ebp-8],0
|
|
||||||
mov DWORD PTR [ebp-4],16
|
mov DWORD PTR [ebp-4],16
|
||||||
mov cx,[ebp-4]
|
mov cx,[ebp-4]
|
||||||
|
|
||||||
_writeCube_yLoop:
|
_writeCube_yLoop:
|
||||||
mov [ebp-4],cx
|
mov [ebp-4],cx
|
||||||
|
|
||||||
|
; determine the offset within VGA memory to write this line to
|
||||||
|
; and put it in eax
|
||||||
mov eax,[ebp-4]
|
mov eax,[ebp-4]
|
||||||
dec eax
|
dec eax
|
||||||
mov bl,80
|
mov bl,80
|
||||||
mul bl
|
mul bl
|
||||||
add eax,0xa0000
|
add eax,0xA0000
|
||||||
|
|
||||||
|
; 16 pixels across
|
||||||
mov cx,16
|
mov cx,16
|
||||||
_writeCube_xLoop:
|
_writeCube_xLoop:
|
||||||
; get the current plane
|
|
||||||
push ecx
|
|
||||||
dec cl
|
|
||||||
and cl,0x03
|
|
||||||
|
|
||||||
jne _writeCube_dontAdvanceVGA
|
|
||||||
inc eax
|
|
||||||
_writeCube_dontAdvanceVGA:
|
|
||||||
|
|
||||||
mov bl,1
|
|
||||||
sal bl,cl
|
|
||||||
pop ecx
|
|
||||||
|
|
||||||
push eax
|
push eax
|
||||||
|
; get the plane to write to in bl
|
||||||
|
push ecx
|
||||||
|
dec ecx
|
||||||
|
and ecx,0x03
|
||||||
|
mov bl,1
|
||||||
|
shl bl,cl
|
||||||
|
pop ecx
|
||||||
|
|
||||||
; write to one plane
|
; write to one plane
|
||||||
mov dx, VGA_SEQUENCE_CONTROLLER_INDEX
|
mov dx, VGA_SEQUENCE_CONTROLLER_INDEX
|
||||||
mov al, VGA_SEQUENCE_CONTROLLER_MAP_MASK_MODE
|
mov al, VGA_SEQUENCE_CONTROLLER_MAP_MASK_MODE
|
||||||
mov ah, bl
|
mov ah, bl
|
||||||
out dx, ax
|
out dx, ax
|
||||||
|
|
||||||
|
; select color to write into bl
|
||||||
|
mov ax,[ebp-4]
|
||||||
|
dec ax
|
||||||
|
mov bl,16
|
||||||
|
mul bl
|
||||||
|
add al,cl
|
||||||
|
mov bl,al
|
||||||
pop eax
|
pop eax
|
||||||
|
|
||||||
;mov ebx,[ebp+8]
|
push edx
|
||||||
|
push ecx
|
||||||
push ecx
|
; select the correct pixel offset (x / 4) and write the color
|
||||||
mov ecx,[ebp+8]
|
dec ecx
|
||||||
mov bl,[ebp-8]
|
shr ecx,2
|
||||||
mov BYTE PTR [eax+ecx],bl
|
add ecx,[ebp+8]
|
||||||
inc bl
|
mov BYTE PTR [eax+ecx],bl
|
||||||
mov [ebp-8],bl
|
pop ecx
|
||||||
pop ecx
|
pop edx
|
||||||
|
|
||||||
loop _writeCube_xLoop
|
loop _writeCube_xLoop
|
||||||
|
|
||||||
mov cx,[ebp-4]
|
mov cx,[ebp-4]
|
||||||
loop _writeCube_yLoop
|
loop _writeCube_yLoop
|
||||||
|
|
||||||
|
pop edx
|
||||||
|
pop ecx
|
||||||
|
pop ebx
|
||||||
|
|
||||||
mov esp,ebp
|
mov esp,ebp
|
||||||
pop ebp
|
pop ebp
|
||||||
ret
|
ret
|
||||||
@ -359,6 +385,8 @@ _latchCopyCube_xLoop:
|
|||||||
; loop until y is 0
|
; loop until y is 0
|
||||||
loop _latchCopyCube_yLoop
|
loop _latchCopyCube_yLoop
|
||||||
|
|
||||||
|
_done:
|
||||||
|
|
||||||
; put things back
|
; put things back
|
||||||
pop ebx
|
pop ebx
|
||||||
pop edx
|
pop edx
|
||||||
|
Loading…
Reference in New Issue
Block a user