#include #include #include "../Video.h" #include "../../util/Asm.h" #include "../../util/Log.h" #define VGA_DAC_ADDR_RD 0x3c7 #define VGA_DAC_ADDR_WR 0x3c8 #define VGA_DAC_DATA 0x3c9 #define VGA_EXT_INPUT_STATUS 0x3da #define VGA_EXT_INPUT_STATUS_VRETRACE 8 Video video; Video::Video() { __djgpp_nearptr_enable(); // Store current video mode _oldMode = GetMode(); // Store current palette for (auto i = 0; i < 256; i++) { GetPaletteEntry(i, &_oldPalette[i]); } } Video::~Video() { exit(); } void Video::enter() { SetMode(0x13); } void Video::exit() { // Restore old palette for (auto i = 0; i < 256; i++) { SetPaletteEntry(i, &_oldPalette[i]); } // Restore old video mode SetMode(_oldMode); } void Video::SetMode(uint8_t mode) { union REGS regs; regs.h.ah = 0x00; regs.h.al = mode; int86(0x10, ®s, ®s); //__djgpp_nearptr_enable(); switch (mode) { default: case 0x13: _fb = (uint8_t *)(0xA0000 + __djgpp_conventional_base); } } uint8_t Video::GetMode() { union REGS regs; regs.h.ah = 0x0f; regs.h.al = 0; int86(0x10, ®s, ®s); return regs.h.al; } void Video::SetPaletteEntry(uint8_t index, PaletteEntry entry) { SetPaletteEntry(index, &entry); } Video::PaletteEntry Video::GetPaletteEntry(uint8_t index) { PaletteEntry entry; GetPaletteEntry(index, &entry); return entry; } void Video::SetPaletteEntry(uint8_t index, PaletteEntry *entry) { outb(VGA_DAC_ADDR_WR, index); outb(VGA_DAC_DATA, entry->r >> 2); outb(VGA_DAC_DATA, entry->g >> 2); outb(VGA_DAC_DATA, entry->b >> 2); } void Video::GetPaletteEntry(uint8_t index, PaletteEntry *entry) { outb(VGA_DAC_ADDR_RD, index); entry->r = inb(VGA_DAC_DATA); entry->g = inb(VGA_DAC_DATA); entry->b = inb(VGA_DAC_DATA); } void *Video::GetFB() { return _renderBuffer; } void Video::WaitForVerticalSync() { auto state = inb(VGA_EXT_INPUT_STATUS) & VGA_EXT_INPUT_STATUS_VRETRACE; if (!state) { // We started before a retrace, so wait until it begins while (!(inb(VGA_EXT_INPUT_STATUS) & VGA_EXT_INPUT_STATUS_VRETRACE)) {} } // Wait until it ends while (inb(VGA_EXT_INPUT_STATUS) & VGA_EXT_INPUT_STATUS_VRETRACE); } void Video::UpdateRect(const Rect &rect) { // Merge rect if it overlaps with an existing rect for (auto i = 0; i < _updateRectIndex; i++) { auto &r = _updatedRects[i]; if (r.overlaps(rect)) { r += rect; return; } } // Add a new rect to the list _updatedRects[_updateRectIndex++] = rect; } void Video::Flip() { for (auto i = 0; i <= MAX_UPDATE_RECT_INDEX; i++) { auto &rect = _updatedRects[i]; for (auto y = rect.y1; y <= rect.y2; y++) { auto yOffset = y * SCREEN_WIDTH; for (auto x = rect.x1; x <= rect.x2; x++) { _fb[yOffset + x] = _renderBuffer[yOffset + x]; } } } _updateRectIndex = 0; }