commit 0d6bed04f741b9906b01adc8226bec51c0b57471 Author: John Bintz Date: Wed Feb 14 20:15:55 2024 -0500 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d9ab992 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +*.zip +*.OBJ +*.ERR +*.EXE +.ccls-cache/ diff --git a/bmp_loader.c b/bmp_loader.c new file mode 100644 index 0000000..74429e0 --- /dev/null +++ b/bmp_loader.c @@ -0,0 +1,135 @@ +#include +#include +#include +#include + +#include "bmp_loader.h" +#include "pc_stuff.h" +#include "vga.h" + +int readBMPHeader(FILE *fh, struct BMPImage *info) { + int sizeOfHeader; + 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(word), 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, 16, SEEK_CUR); + fread(&numberOfUsedColors, sizeof(unsigned int), 1, fh); + 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; +} + +int readBMPIntoMemory(FILE *fh, struct BMPImage *info) { + int x, y, plane; + int result; + byte *currentPointer; + + result = readBMPHeader(fh, info); + if (result) return result; + + currentPointer = info->memoryStart; + + for (y = 0; y < info->height; ++y) { + for (x = 0; x < info->width; ++x) { + currentPointer[(info->height - 1 - y) * info->width + x] = fgetc(fh); + } + } + + return 0; +} + +int readBMPIntoUnchainedMemory(FILE *fh, struct BMPImage* info) { + int x, y, plane; + int writeX, writeY, offset; + int unchainedLineWidth; + int result; + byte *rowHolder; + + result = readBMPHeader(fh, info); + if (result) return result; + + unchainedLineWidth = info->width / 4; + + // reserve a row's worth of data + // read the bitmap data into the row, interleaving for planes + // for each plane, memcpy the data to the plane + for (y = 0; y < info->height; ++y) { + writeY = ((info->height - 1) - y) * unchainedLineWidth; + + for (x = 0; x < info->width; ++x) { + plane = x & 0x03; + writeX = (x >> 2); + + offset = plane * (unchainedLineWidth * info->height); + + info->memoryStart[offset + writeY + writeX] = fgetc(fh); + } + } + + return 0; +} + +int readBMPIntoUnchainedVGAMemory(FILE *fh, struct BMPImage* info) { + int x, y, plane; + int unchainedLineWidth; + int result; + byte *rowHolder; + + result = readBMPHeader(fh, info); + if (result) return result; + + rowHolder = malloc(info->width); + unchainedLineWidth = info->width / 4; + + // reserve a row's worth of data + // read the bitmap data into the row, interleaving for planes + // for each plane, memcpy the data to the plane + for (y = 0; y < info->height; ++y) { + for (x = 0; x < info->width; ++x) { + rowHolder[((x & 3) * 80) + (x >> 2)] = fgetc(fh); + } + + for (plane = 0; plane < 4; ++plane) { + setActiveVGAMemoryPlanes(1 << plane); + + memcpy(info->memoryStart + ((info->height - 1) - y) * info->width / 4, rowHolder + unchainedLineWidth * plane, unchainedLineWidth); + } + } + + free(rowHolder); + + return 0; +} + +void bmp256ColorPaletteToVGAColorPalette(struct BMPImage* bmpImage, struct VGAColor 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; + } +} diff --git a/bmp_loader.h b/bmp_loader.h new file mode 100644 index 0000000..b216371 --- /dev/null +++ b/bmp_loader.h @@ -0,0 +1,23 @@ +#include + +#include "types.h" + +struct BMPColor { + byte blue; + byte green; + byte red; + byte _a; +}; + +struct BMPImage { + int width; + int height; + word bpp; + struct BMPColor colors[256]; + byte *memoryStart; +}; + +int readBMPIntoUnchainedMemory(FILE *, struct BMPImage *); +int readBMPIntoMemory(FILE *, struct BMPImage *); +void bmp256ColorPaletteToVGAColorPalette(struct BMPImage*, struct VGAColor[]); + diff --git a/build.bat b/build.bat new file mode 100644 index 0000000..112a3aa --- /dev/null +++ b/build.bat @@ -0,0 +1,6 @@ +wcc386.exe -q bmp_loader.c +wcc386.exe -q mouse_io.c +wcc386.exe -q pc_stuff.c +wcc386.exe -q vga.c +wcc386.exe -q keyboard.c +wcl386.exe -q unch1.c bmp_load.obj mouse_io.obj pc_stuff.obj vga.obj keyboard.obj diff --git a/chicken.bmp b/chicken.bmp new file mode 100644 index 0000000..f0db92f Binary files /dev/null and b/chicken.bmp differ diff --git a/game.c b/game.c new file mode 100644 index 0000000..d303251 --- /dev/null +++ b/game.c @@ -0,0 +1,38 @@ +#include "vga.h" +#include "keyboard.h" +#include "mouse_io.h" + +struct VGAColor colors[4] = { + { 0, 0, 0 }, { 255, 255, 255 }, { 255, 0, 0 }, { 0, 0, 255 } +} + +int main(void) { + int keepRunning = 1; + struct MouseStatus mouseStatus; + + installKeyboardHandler(); + activateMouse(&mouseStatus); + + while (keepRunning) { + + } + // states: + // * main menu + // * play + // * quit + // * play + // * draw base map + // * draw sidebar + // * Game loop + // * get mouse + // * get keyboard + // * set character position + // * check for enemy spawn and spawn enemy if needed + // * check for character firing and able to fire, spawn bullet if allowed + // * check for each enemy firing and able to fire, spawn enemy bullet if allowed + // * check for bullet collisions + // * enemies are destroyed + // * character is damaged + // * check for bullets hitting the edges of the screen and remove + // * +} diff --git a/in_progress.md b/in_progress.md new file mode 100644 index 0000000..7f8f974 --- /dev/null +++ b/in_progress.md @@ -0,0 +1,2 @@ +* Preserve video mode +* Rename files to 8.3 diff --git a/keyboard.c b/keyboard.c new file mode 100644 index 0000000..2581197 --- /dev/null +++ b/keyboard.c @@ -0,0 +1,56 @@ +#include +#include + +#include "keyboard.h" + +unsigned char keystateBits[16]; +struct KeyboardKeydownState keyboardKeydownState; + +void far (interrupt *int9Save)(); + +void populateKeyboardKeydownState() { + keyboardKeydownState.KEY_W = keystateBits[2] & 0x02; + keyboardKeydownState.KEY_A = keystateBits[3] & 0x40; + keyboardKeydownState.KEY_S = keystateBits[3] & 0x80; + keyboardKeydownState.KEY_D = keystateBits[4] & 0x01; +} + +void far interrupt keyboardHandler(void) { + unsigned char rawcode, scancode, ksbByte, temp; + unsigned char mask = 1; + + _disable(); + + rawcode = inp(0x60); + + scancode = rawcode & 0x7f; + mask = 1 << (scancode & 0x07); + ksbByte = scancode >> 3; + + // https://github.com/id-Software/wolf3d/blob/05167784ef009d0d0daefe8d012b027f39dc8541/WOLFSRC/ID_IN.C#L152-L154 + outp(0x61, (temp = inp(0x61)) | 0x80); + outp(0x61, temp); + + /* break */ + if (rawcode & 0x80) { + mask = 0xff - mask; + keystateBits[ksbByte] &= mask; + /* make */ + } else { + keystateBits[ksbByte] |= mask; + } + + // acknowledge we're done with the interrupt + outp(0x20, 0x20); + + _enable(); +} + +void installKeyboardHandler() { + int9Save = _dos_getvect(9); + _dos_setvect(9, keyboardHandler); +} + +void uninstallKeyboardHandler() { + _dos_setvect(9, int9Save); +} diff --git a/keyboard.h b/keyboard.h new file mode 100644 index 0000000..28e145e --- /dev/null +++ b/keyboard.h @@ -0,0 +1,21 @@ +#ifndef __KEYBOARD_H__ +#define __KEYBOARD_H__ 1 + +#include "types.h" + +extern unsigned char keystateBits[]; + +struct KeyboardKeydownState { + byte KEY_W; + byte KEY_A; + byte KEY_S; + byte KEY_D; +}; + +extern struct KeyboardKeydownState keyboardKeydownState; + +void installKeyboardHandler(); +void uninstallKeyboardHandler(); +void populateKeyboardKeydownState(); + +#endif diff --git a/mouse_io.c b/mouse_io.c new file mode 100644 index 0000000..8bf4eb3 --- /dev/null +++ b/mouse_io.c @@ -0,0 +1,48 @@ +#include +#include + +#include "mouse_io.h" + +int activateMouse(struct MouseStatus *status) { + union REGS regs; + int mouseActivated; + + regs.w.ax = MOUSE_DRIVER_RESET; + int386(MOUSE_DRIVER_INTERRUPT, ®s, ®s); + + mouseActivated = regs.w.ax; + + if (mouseActivated) { + status->isActive = regs.w.ax; + status->buttonCount = regs.w.bx; + + // set horiz and vert range + regs.w.ax = 0x07; + regs.w.cx = 0; + regs.w.dx = VGA_DISPLAY_WIDTH - 1; + int386(MOUSE_DRIVER_INTERRUPT, ®s, ®s); + + regs.w.ax = 0x08; + regs.w.cx = 0; + regs.w.dx = VGA_DISPLAY_HEIGHT - 1; + int386(MOUSE_DRIVER_INTERRUPT, ®s, ®s); + } + + return mouseActivated; +} + +void readMouse(struct MouseStatus *status) { + union REGS regs; + int buttonStatus; + + regs.w.ax = MOUSE_DRIVER_READ_STATE; + int386(MOUSE_DRIVER_INTERRUPT, ®s, ®s); + + buttonStatus = regs.w.bx; + status->xPosition = regs.w.cx; + status->yPosition = regs.w.dx; + + status->leftButtonDown = buttonStatus & 1; + status->rightButtonDown = (buttonStatus >> 1) & 1; +} + diff --git a/mouse_io.h b/mouse_io.h new file mode 100644 index 0000000..4759877 --- /dev/null +++ b/mouse_io.h @@ -0,0 +1,18 @@ +#include "vga.h" + +typedef struct MouseStatus { + int isActive; + int buttonCount; + int xPosition; + int yPosition; + int leftButtonDown; + int rightButtonDown; +}; + +#define MOUSE_DRIVER_INTERRUPT (0x33) +#define MOUSE_DRIVER_RESET (0x00) +#define MOUSE_DRIVER_READ_DELTA_MOTION (0x0B) +#define MOUSE_DRIVER_READ_STATE (0x03); + +int activateMouse(struct MouseStatus *); +void readMouse(struct MouseStatus *); diff --git a/pc_stuff.c b/pc_stuff.c new file mode 100644 index 0000000..d28eb77 --- /dev/null +++ b/pc_stuff.c @@ -0,0 +1,23 @@ +#include "pc_stuff.h" + +#define INPUT_STATUS (0x03da) +#define VBLANK (0x08) +#define BIOS_SET_VIDEO_MODE (0x00) + +byte *VGA = (byte *)0xA0000; + +void setVideoMode(byte videoMode) { + union REGS regs; + + regs.h.ah = BIOS_SET_VIDEO_MODE; + regs.h.al = videoMode; + int386(BIOS_VIDEO_INTERRUPT, ®s, ®s); +} + +void waitStartVbl() { + while (inp(INPUT_STATUS) & VBLANK); +} + +void waitEndVbl() { + while (!(inp(INPUT_STATUS) & VBLANK)); +} diff --git a/pc_stuff.h b/pc_stuff.h new file mode 100644 index 0000000..b12718b --- /dev/null +++ b/pc_stuff.h @@ -0,0 +1,25 @@ +#ifndef __PC_STUFF_H__ +#define __PC_STUFF_H__ + +#include +#include + +#include "types.h" + +extern byte *VGA; + +#define BIOS_GET_VIDEO_MODE (0x0F) +#define BIOS_VIDEO_INTERRUPT (0x10) + +#define VIDEO_MODE_VGA_256 (0x13) +#define VIDEO_MODE_80x25_TEXT (0x03) + +// remember, little endian, so the "first" value is "last" +#define outpw_to_register(indexPort, dataRegister, data) \ + outpw(indexPort, (((word)(data) << 8)) + (dataRegister)) + +#endif + +void setVideoMode(byte); +void waitStartVbl(); +void waitEndVbl(); diff --git a/sprtsht.bmp b/sprtsht.bmp new file mode 100644 index 0000000..534d3a4 Binary files /dev/null and b/sprtsht.bmp differ diff --git a/types.h b/types.h new file mode 100644 index 0000000..a445af9 --- /dev/null +++ b/types.h @@ -0,0 +1,3 @@ +typedef unsigned char byte; +typedef unsigned short int word; +typedef unsigned long int ulong; diff --git a/unch1.c b/unch1.c new file mode 100644 index 0000000..d39fb0d --- /dev/null +++ b/unch1.c @@ -0,0 +1,155 @@ +#include +#include +#include +#include +#include +#include + +#include "types.h" +#include "mouse_io.h" +#include "pc_stuff.h" +#include "bmp_loader.h" +#include "vga.h" +#include "keyboard.h" + +byte MOUSE_POINTER[8][8] = { + { 2, 2, 2, 2, 2, 0, 0, 0 }, + { 2, 1, 1, 1, 2, 0, 0, 0 }, + { 2, 1, 1, 1, 2, 0, 0, 0 }, + { 2, 1, 1, 1, 2, 0, 0, 0 }, + { 2, 2, 2, 255, 1, 2, 0, 0 }, + { 0, 0, 0, 0, 2, 1, 2, 0 }, + { 0, 0, 0, 0, 0, 2, 1, 2 }, + { 0, 0, 0, 0, 0, 0, 2, 0 }, +}; + +byte WALKER[9][8] = { + { 0, 0, 0, 4, 4, 0, 0, 0 }, + { 0, 0, 4, 5, 5, 4, 0, 0 }, + { 0, 4, 5, 5, 5, 5, 4, 0 }, + { 0, 0, 0, 4, 4, 0, 0, 0 }, + { 0, 0, 4, 5, 255, 4, 0, 0 }, + { 0, 4, 5, 5, 5, 5, 4, 0 }, + { 0, 0, 0, 4, 4, 0, 0, 0 }, + { 0, 0, 4, 5, 5, 4, 0, 0 }, + { 0, 4, 5, 5, 5, 5, 4, 0 } +}; + +int main(void) { + FILE *fh; + struct BMPImage bmpImage, spriteSheetImage; + struct MouseStatus mouseStatus = { .xPosition = 0, .yPosition = 0, .leftButtonDown = 0 }; + struct VGAColor colors[256]; + byte *spriteSheet, *chicken, *drawBuffer; + int i, x, y; + + int currentMouseSprite = 0; + int mouseSpriteDelayCount = 0; + + float measuredTime; + + int walkerX = 0, walkerY = 0; + int yOffset; + + struct SpriteRender mousePointer = { + .data = (byte *)MOUSE_POINTER, + .transparentColor = 0, + .width = 8, + .height = 8, + .modulo = 48 + }; + + struct SpriteRender walker = { + .data = (byte *)WALKER, + .transparentColor = 0, + .width = 8, + .height = 9, + .modulo = 0 + }; + + installKeyboardHandler(); + + initializeDrawBuffer(); + + spriteSheet = malloc(64 * 64); + spriteSheetImage.memoryStart = spriteSheet; + + fh = fopen("sprtsht.bmp", "rb"); + if (readBMPIntoMemory(fh, &spriteSheetImage)) return 1; + fclose(fh); + + setVideoMode(VIDEO_MODE_VGA_256); + + chicken = malloc(320 * 256); + bmpImage.memoryStart = chicken; + + fh = fopen("chicken.bmp", "rb"); + readBMPIntoMemory(fh, &bmpImage); + fclose(fh); + + mousePointer.data = spriteSheet; + mousePointer.width = 16; + mousePointer.height = 16; + mousePointer.modulo = 48; + + bmp256ColorPaletteToVGAColorPalette(&bmpImage, colors); + setVGAColors(colors); + + activateMouse(&mouseStatus); + readMouse(&mouseStatus); + + while (!mouseStatus.leftButtonDown) { + readMouse(&mouseStatus); + + populateKeyboardKeydownState(); + + if (keyboardKeydownState.KEY_W) walkerY -= 1; + if (keyboardKeydownState.KEY_S) walkerY += 1; + if (keyboardKeydownState.KEY_A) walkerX -= 1; + if (keyboardKeydownState.KEY_D) walkerX += 1; + + + drawBuffer = getDrawBuffer(); + + memcpy(drawBuffer, chicken, 320 * 200); + + mousePointer.x = mouseStatus.xPosition; + mousePointer.y = mouseStatus.yPosition; + drawSprite(&mousePointer); + + for (i = 0; i < 100; ++i) { + walker.x = walkerX + i; + walker.y = walkerY + i; + drawSprite(&walker); + } + + waitStartVbl(); + memcpy((byte *)0xa0000, drawBuffer, 320 * 200); + waitEndVbl(); + + //drawBufferToUnchainedMemory(); + + + //copyUnchainedMemoryToActive(chicken); + + //swapPlanes(); + + mouseSpriteDelayCount += 1; + if (mouseSpriteDelayCount >= 15) { + currentMouseSprite = 1 - currentMouseSprite; + mousePointer.data = spriteSheet + (currentMouseSprite * 16); + mouseSpriteDelayCount = 0; + } + } + + uninstallKeyboardHandler(); + setVideoMode(VIDEO_MODE_80x25_TEXT); + free(spriteSheet); + free(chicken); + + freeDrawBuffer(); + + fprintf(stderr, "%lu %lu %lu\n", startTime, endTime, (clock_t)(endTime - startTime)); + + return 0; +} diff --git a/vga.c b/vga.c new file mode 100644 index 0000000..d13360a --- /dev/null +++ b/vga.c @@ -0,0 +1,456 @@ +#include "vga.h" +#include "pc_stuff.h" +#include +#include +#include +#include +#include + +#define VGA_SEQUENCE_CONTROLLER_INDEX (0x03C4) +#define VGA_SEQUENCE_CONTROLLER_DATA (0x03C5) + +#define VGA_SEQUENCE_CONTROLLER_MEMORY_MODE (0x04) +#define VGA_SEQUENCE_CONTROLLER_ODD_EVEN_SEQUENTIAL_ACCESS (0x04) +#define VGA_SEQUENCE_CONTROLLER_EXTENDED_MEMORY (0x02) + +#define VGA_SEQUENCE_CONTROLLER_MAP_MASK_MODE (0x02) +#define VGA_SEQUENCE_CONTROLLER_ALL_PLANES (0xff) + +#define VGA_GRAPHICS_REGISTERS_INDEX (0x03CE) +#define VGA_GRAPHICS_REGISTERS_DATA (0x03CF) + +#define VGA_GRAPHICS_DATA_ROTATE (0x03) + +#define VGA_CRT_CONTROLLER_INDEX (0x03d4) +#define VGA_CRT_CONTROLLER_DATA (0x03d5) + +#define VGA_CRT_CONTROLLER_UNDERLINE_MODE (0x14) +#define VGA_CRT_CONTROLLER_MODE_CONTROL (0x17) + +#define VGA_CRT_CONTROLLER_DISPLAY_HIGH_ADDRESS (0x0c) +#define VGA_CRT_CONTROLLER_DISPLAY_LOW_ADDRESS (0x0d) + +#define VGA_CRT_CONTROLLER_VERTICAL_RETRACE_START (0x10) +#define VGA_CRT_CONTROLLER_VERTICAL_RETRACE_END (0x11) +#define VGA_CRT_CONTROLLER_VERTICAL_DISPLAY_END (0x12) +#define VGA_CRT_CONTROLLER_VERTICAL_BLANK_START (0x15) +#define VGA_CRT_CONTROLLER_VERTICAL_BLANK_END (0x16) +#define VGA_CRT_CONTROLLER_VERTICAL_TOTAL (0x06) +#define VGA_CRT_CONTROLLER_OVERFLOW (0x07) + +int activePage = 0; +byte *drawBuffer, *displayMirrorBuffer; + +void initializeDrawBuffer() { + int i; + + drawBuffer = (byte *)malloc(VGA_DISPLAY_WIDTH * VGA_DISPLAY_HEIGHT); + displayMirrorBuffer = (byte *)malloc(VGA_DISPLAY_WIDTH * VGA_DISPLAY_HEIGHT); + + for (i = 0; i < VGA_DISPLAY_WIDTH * VGA_DISPLAY_HEIGHT; ++i) { + drawBuffer[i] = 0; + displayMirrorBuffer[i] = 0; + } +} + +void freeDrawBuffer() { + free(drawBuffer); + free(displayMirrorBuffer); +} + +void setActiveVGAMemoryPlanes(int planes) { + outpw_to_register( + VGA_SEQUENCE_CONTROLLER_INDEX, + VGA_SEQUENCE_CONTROLLER_MAP_MASK_MODE, + planes + ); +} + +byte *getDrawBuffer() { + return drawBuffer; +} + +clock_t startTime, endTime; + +void drawBufferToUnchainedMemory() { + int x, y, plane, pageOffset, yOffset, yDisplayOffset; + byte *vgaStart; + unsigned long data; + + int rowsToCopy[VGA_DISPLAY_HEIGHT], minRow = -1, maxRow = 0; + + unsigned long *drawBufferLong = (unsigned long *)drawBuffer, + *displayMirrorBufferLong = (unsigned long *)displayMirrorBuffer; + + byte copyRow[VGA_DISPLAY_WIDTH]; + + startTime = clock(); + + pageOffset = activePage * PAGE_SIZE; + vgaStart = (byte *)(VGA + pageOffset); + + for (y = 0; y < VGA_DISPLAY_HEIGHT; ++y) { + rowsToCopy[y] = 0; + yOffset = y * VGA_UNCHAINED_LINE_WIDTH; + + for (x = 0; x < VGA_UNCHAINED_LINE_WIDTH; x++) { + data = *(drawBufferLong++); + + if (displayMirrorBufferLong[yOffset] != data) { + rowsToCopy[y] = 1; + + break; + } + + yOffset++; + } + } + + memcpy(displayMirrorBuffer, drawBuffer, VGA_DISPLAY_WIDTH * VGA_DISPLAY_HEIGHT); + + for (y = 0; y < VGA_DISPLAY_HEIGHT; ++y) { + if (rowsToCopy[y]) { + if (minRow == -1) minRow = y; + maxRow = y; + } + } + + + if (minRow == -1) return; + + drawBufferLong = (unsigned long *)drawBuffer; + + for (y = minRow; y <= maxRow; ++y) { + if (!rowsToCopy[y]) continue; + + yOffset = y * VGA_UNCHAINED_LINE_WIDTH; + + for (x = 0; x < VGA_UNCHAINED_LINE_WIDTH; ++x) { + data = drawBufferLong[yOffset + x]; + + for (plane = 0; plane < 4; plane++) { + copyRow[x + (plane * VGA_UNCHAINED_LINE_WIDTH)] = data & 0xff; + data >>= 8; + } + } + + for (plane = 0; plane < 4; ++plane) { + setActiveVGAMemoryPlanes(1 << plane); + + memcpy(vgaStart + yOffset, copyRow + (plane * VGA_UNCHAINED_LINE_WIDTH), VGA_UNCHAINED_LINE_WIDTH); + } + } + + endTime = clock(); + +} + +void enableUnchainedVGAMode() { + word clearOffset; + + // convert VGA pointer into a word sized + ulong *ptr = (ulong *)VGA; + + // The VGA registers are (mostly) byte sized, and are grouped + // logically behind a pair of index/data ports. + // To access them, you prep the index port on the VGA card + // for the register you want to access, then do that access + // via the data port. + // + // Visual: VGA has pouches which contain organization boxes of + // registers. + + // target the Sequence Memory Mode Register for Memory Mode + // enable sequential access and extended memory + outpw_to_register( + VGA_SEQUENCE_CONTROLLER_INDEX, + VGA_SEQUENCE_CONTROLLER_MEMORY_MODE, + VGA_SEQUENCE_CONTROLLER_EXTENDED_MEMORY | VGA_SEQUENCE_CONTROLLER_ODD_EVEN_SEQUENTIAL_ACCESS + ); + + // target the Sequence Memory Mode Register for map mask + outpw_to_register( + VGA_SEQUENCE_CONTROLLER_INDEX, + VGA_SEQUENCE_CONTROLLER_MAP_MASK_MODE, + VGA_SEQUENCE_CONTROLLER_ALL_PLANES + ); + + for (clearOffset = 0; clearOffset < PAGE_SIZE; ++clearOffset) { + *ptr++ = 0; + } + + // there's a way to address memory as words or longs, but we're + // going to set it to byte addressing because it makes mroe sense. + outpw_to_register( + VGA_CRT_CONTROLLER_INDEX, + VGA_CRT_CONTROLLER_UNDERLINE_MODE, + 0x00 + ); + + // * enable hsync and vsync + // * activate byte Mode + // * ...something with address wrapping that I don't quite understand yet... + // * ...even more with address funkiness that I don't understand + outpw_to_register( + VGA_CRT_CONTROLLER_INDEX, + VGA_CRT_CONTROLLER_MODE_CONTROL, + 0xe3 + ); + + outp(0x3c2, 0xe3); +} + +void enable320x256VGAMode() { + outpw_to_register( + VGA_CRT_CONTROLLER_INDEX, + VGA_CRT_CONTROLLER_VERTICAL_RETRACE_END, + 0x2c + ); + outpw_to_register( + VGA_CRT_CONTROLLER_INDEX, + VGA_CRT_CONTROLLER_VERTICAL_TOTAL, + 0x0d + ); + outpw_to_register( + VGA_CRT_CONTROLLER_INDEX, + VGA_CRT_CONTROLLER_OVERFLOW, + 0x3e + ); + outpw_to_register( + VGA_CRT_CONTROLLER_INDEX, + VGA_CRT_CONTROLLER_VERTICAL_RETRACE_START, + 0xea + ); + outpw_to_register( + VGA_CRT_CONTROLLER_INDEX, + VGA_CRT_CONTROLLER_VERTICAL_RETRACE_END, + 0xac + ); + outpw_to_register( + VGA_CRT_CONTROLLER_INDEX, + VGA_CRT_CONTROLLER_VERTICAL_DISPLAY_END, + 0xdf + ); + outpw_to_register( + VGA_CRT_CONTROLLER_INDEX, + VGA_CRT_CONTROLLER_VERTICAL_BLANK_START, + 0xe7 + ); + outpw_to_register( + VGA_CRT_CONTROLLER_INDEX, + VGA_CRT_CONTROLLER_VERTICAL_BLANK_END, + 0x06 + ); +} + +void clearWithColor(byte color) { + int x, y; + int pageOffset, drawY; + long *start; + long newColor = (color << 24) + (color << 16) + (color << 8) + (color); + + // write to all planes + outpw_to_register( + VGA_SEQUENCE_CONTROLLER_INDEX, + VGA_SEQUENCE_CONTROLLER_MAP_MASK_MODE, + 0xff + ); + + pageOffset = activePage * PAGE_SIZE; + start = (long *)(VGA + pageOffset); + + for (y = 0; y < VGA_DISPLAY_HEIGHT; ++y) { + for (x = 0; x < VGA_UNCHAINED_LINE_WIDTH / 4; ++x) { + *(start++) = newColor; + } + } +} + +/** + * this is very temporary + */ +void copyUnchainedMemoryToActive(byte *src) { + int y, plane, _testSize, _testOffset; + + _testSize = 80 * 240; + _testOffset = 80 * 256; + + for (plane = 0; plane < 4; ++plane) { + // copying to/from VGA memory requires setting the plane to write to... + outpw_to_register( + VGA_SEQUENCE_CONTROLLER_INDEX, + VGA_SEQUENCE_CONTROLLER_MAP_MASK_MODE, + 1 << plane + ); + + memcpy(VGA + activePage * PAGE_SIZE, src + plane * _testOffset, _testSize); + } +} + +void copyScratchPlanesToActive() { + int y, plane; + + for (plane = 0; plane < 4; ++plane) { + // copying to/from VGA memory requires setting the plane to write to... + outpw_to_register( + VGA_SEQUENCE_CONTROLLER_INDEX, + VGA_SEQUENCE_CONTROLLER_MAP_MASK_MODE, + 1 << plane + ); + + // and to read from + outpw_to_register( + 0x3ce, + 0x04, + plane + ); + + memcpy(VGA + activePage * PAGE_SIZE, VGA + SCRATCH_PAGE, PAGE_SIZE); + } +} + +void copyScratchPlanesToActiveViaLatches() { + int pixel; + byte *src, *dest; + + // TODO: VGA latches + outpw_to_register( + VGA_SEQUENCE_CONTROLLER_INDEX, + VGA_SEQUENCE_CONTROLLER_MAP_MASK_MODE, + 0xff + ); + + outpw_to_register( + VGA_GRAPHICS_REGISTERS_INDEX, + VGA_GRAPHICS_DATA_ROTATE, + 0x10 + ); + + src = VGA + SCRATCH_PAGE; + dest = VGA + activePage * PAGE_SIZE; + + for (pixel = 0; pixel < PAGE_SIZE; ++pixel) { + volatile char _temp = *(src++); + *(dest++) = 0; + } + + outpw_to_register( + VGA_GRAPHICS_REGISTERS_INDEX, + VGA_GRAPHICS_DATA_ROTATE, + 0x00 + ); +} + +void drawSprite(struct SpriteRender* sprite) { + int x, y; + byte pixel; + + byte* spriteData = sprite->data; + byte* drawBufferPos = drawBuffer + sprite->x + (sprite->y * VGA_DISPLAY_WIDTH); + + for (y = 0; y < sprite->height; ++y) { + for (x = 0; x < sprite->width; ++x) { + pixel = *(spriteData++); + if (pixel != sprite->transparentColor) { + *(drawBufferPos) = pixel; + } + + drawBufferPos++; + } + + drawBufferPos += (VGA_DISPLAY_WIDTH - sprite->width); + spriteData += sprite->modulo; + } +} + +void drawUnchainedSprite(struct SpriteRender* sprite) { + int dataX, dataY, currentPlane; + int drawX, drawY, vgaWriteYStart; + int startY, endY; + int nextStartX = 1; + int planeSampleX = 0, readPositionOffset; + int vgaWritePosition, readPosition, spriteAdvance; + int activePageOffset, alwaysVGAWriteStart, startVGAWritePosition; + byte readResult; + + // precalculate sprite sheet handling and VGA plane + currentPlane = sprite->x & 3; + spriteAdvance = sprite->width + sprite->modulo; + + outpw_to_register( + VGA_SEQUENCE_CONTROLLER_INDEX, + VGA_SEQUENCE_CONTROLLER_MAP_MASK_MODE, + 1 << currentPlane + ); + + // precalculate x pixel drawing + activePageOffset = activePage * PAGE_SIZE; + + alwaysVGAWriteStart = sprite->y; + if (alwaysVGAWriteStart < 0) alwaysVGAWriteStart = 0; + alwaysVGAWriteStart *= VGA_DISPLAY_WIDTH; + + // precalculate y pixel drawing + startY = sprite->y; + endY = sprite->y + sprite->height; + readPositionOffset = 0; + + if (startY < 0) { + readPositionOffset = (spriteAdvance * (-startY)); + startY = 0; + } + if (endY > VGA_DISPLAY_HEIGHT) { endY = VGA_DISPLAY_HEIGHT; } + + startVGAWritePosition = activePageOffset + (sprite->x + alwaysVGAWriteStart) / PLANE_PIXEL_DISTANCE; + + for (dataX = 0; dataX < sprite->width; ++dataX) { + if (planeSampleX >= sprite->width) { + planeSampleX = nextStartX; + nextStartX++; + currentPlane = (currentPlane + 1) & 3; + + startVGAWritePosition = activePageOffset + (planeSampleX + sprite->x + alwaysVGAWriteStart) / PLANE_PIXEL_DISTANCE; + + // save a second out + outp( + VGA_SEQUENCE_CONTROLLER_DATA, + 1 << currentPlane + ); + } + + drawX = planeSampleX + sprite->x; + if (drawX >= 0 && drawX < VGA_DISPLAY_WIDTH) { + vgaWritePosition = startVGAWritePosition; + readPosition = planeSampleX + readPositionOffset; + + for (drawY = startY; drawY < endY; ++drawY) { + readResult = sprite->data[readPosition]; + if (readResult != sprite->transparentColor) { + VGA[vgaWritePosition] = readResult; + } + vgaWritePosition += VGA_UNCHAINED_LINE_WIDTH; + readPosition += spriteAdvance; + } + } + + startVGAWritePosition++; + planeSampleX += 4; + } +} +void swapPlanes() { + outpw(VGA_CRT_CONTROLLER_INDEX, VGA_CRT_CONTROLLER_DISPLAY_HIGH_ADDRESS | ((PAGE_SIZE * activePage) & 0xff00)); + outpw(VGA_CRT_CONTROLLER_INDEX, VGA_CRT_CONTROLLER_DISPLAY_LOW_ADDRESS | ((PAGE_SIZE * activePage) << 8)); + + activePage = 1 - activePage; +} + +void setVGAColors(struct VGAColor colors[], int totalColors) { + int i; + + outp(0x3c8,0); + for (i = 0; i < totalColors; ++i) { + outp(0x3c9, colors[i].red); + outp(0x3c9, colors[i].green); + outp(0x3c9, colors[i].blue); + } +} diff --git a/vga.h b/vga.h new file mode 100644 index 0000000..cf9837a --- /dev/null +++ b/vga.h @@ -0,0 +1,51 @@ +#ifndef __VGA_H__ +#define __VGA_H__ + +#include "types.h" +#include + +#define PLANE_PIXEL_DISTANCE (4) +#define VGA_DISPLAY_WIDTH (320) +#define VGA_DISPLAY_HEIGHT (200) +#define VGA_UNCHAINED_LINE_WIDTH (VGA_DISPLAY_WIDTH / PLANE_PIXEL_DISTANCE) + +// there are four of these in a row +#define PAGE_SIZE (VGA_DISPLAY_WIDTH*VGA_DISPLAY_HEIGHT/4) +#define SCRATCH_PAGE (PAGE_SIZE * 2) + +struct VGAColor { + byte red; + byte green; + byte blue; +}; + +struct SpriteRender { + byte* data; + int x; + int y; + unsigned int width; + unsigned int height; + int transparentColor; + unsigned int modulo; +}; + +extern clock_t startTime, endTime; + +void enableUnchainedVGAMode(); +void enable320x256VGAMode(); +void setVGAColors(struct VGAColor[], int); +void copyUnchainedMemoryToActive(); +void copyScratchPlanesToActive(); +void copyScratchPlanesToActiveViaLatches(); +void drawSprite(struct SpriteRender *sprite); +void swapPlanes(); +void setActiveVGAMemoryPlanes(int); +void clearWithColor(byte); + +void initializeDrawBuffer(); +void freeDrawBuffer(); +void drawBufferToUnchainedMemory(); + +byte *getDrawBuffer(); + +#endif