32#pragma GCC optimize ("O2")
39static const RGB222 CGAPalette[16] = {
60static const RGB222 CGAGraphics4ColorsPalette[4][4] = {
92void GraphicsAdapter::setupEmulation(Emulation emulation)
96 FontInfo
const * font;
99 uint8_t scanlinesPerCallback;
101 char const * modeline;
102 int16_t viewportWidth;
103 int16_t viewportHeight;
104 } emulationInfo[] = {
107 {
true, &FONT_8x8, 5, 7, 4, drawScanline_PC_Text_40x25_16Colors,
QVGA_320x240_60Hz, 320, 200 },
110 {
true, &FONT_8x16, 13, 15, 8, drawScanline_PC_Text_80x25_16Colors,
VGA_640x480_60Hz, 640, 400 },
113 {
false,
nullptr, 0, 0, 1, drawScanline_PC_Graphics_320x200_4Colors,
QVGA_320x240_60Hz, 320, 200 },
116 {
false,
nullptr, 0, 0, 1, drawScanline_PC_Graphics_640x200_2Colors,
VGA_640x240_60Hz, 640, 200 },
119 {
false,
nullptr, 0, 0, 2, drawScanline_PC_Graphics_HGC_720x348,
VGA_720x350_70Hz, -1, -1 },
123 if (emulation != Emulation::None) {
124 auto info = emulationInfo + (int)emulation - 1;
126 m_VGADCtrl.setDrawScanlineCallback(info->scanlineCallback,
this);
127 m_VGADCtrl.setScanlinesPerCallBack(info->scanlinesPerCallback);
128 m_VGADCtrl.setResolution(info->modeline, info->viewportWidth, info->viewportHeight);
132 setCursorShape(info->cursorStart, info->cursorEnd);
133 m_columns = m_VGADCtrl.getViewPortWidth() / m_font.width;
134 m_rows = m_VGADCtrl.getViewPortHeight() / m_font.height;
140GraphicsAdapter::GraphicsAdapter()
142 m_emulation(Emulation::None),
143 m_videoBuffer(nullptr),
149 m_cursorVisible(false),
150 m_cursorGlyph(nullptr),
152 m_PCGraphicsBackgroundColorIndex(0),
153 m_PCGraphicsForegroundColorIndex(15),
154 m_PCGraphicsPaletteInUse(0),
157 m_font.data =
nullptr;
162GraphicsAdapter::~GraphicsAdapter()
168 heap_caps_free(m_cursorGlyph);
172bool GraphicsAdapter::enableVideo(
bool value)
174 if (value == m_videoEnabled)
175 return m_videoEnabled;
177 m_videoEnabled = value;
179 if (m_videoEnabled) {
180 setupEmulation(m_emulation);
186 return !m_videoEnabled;
190void GraphicsAdapter::setEmulation(Emulation emulation)
192 if (m_emulation != emulation) {
194 bool videoWasEnabled = enableVideo(
false);
197 m_emulation = emulation;
198 if (m_emulation != Emulation::None) {
199 setupEmulation(emulation);
201 enableVideo(videoWasEnabled);
207void GraphicsAdapter::freeLUT()
210 heap_caps_free(m_rawLUT);
215void GraphicsAdapter::setupLUT()
217 switch (m_emulation) {
219 case Emulation::None:
222 case Emulation::PC_Text_80x25_16Colors:
223 case Emulation::PC_Text_40x25_16Colors:
226 m_rawLUT = (uint8_t*) heap_caps_malloc(16, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
227 for (
int i = 0; i < 16; ++i)
228 m_rawLUT[i] = m_VGADCtrl.createRawPixel(CGAPalette[i]);
231 case Emulation::PC_Graphics_320x200_4Colors:
234 m_rawLUT = (uint8_t*) heap_caps_malloc(256 * 4, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
235 for (
int i = 0; i < 256; ++i) {
236 for (
int j = 0; j < 4; ++j) {
237 int pixel = (i >> (6 - j * 2)) & 0b11;
238 uint8_t rawPixel = m_VGADCtrl.createRawPixel(pixel == 0 ? CGAPalette[m_PCGraphicsBackgroundColorIndex] : CGAGraphics4ColorsPalette[m_PCGraphicsPaletteInUse][pixel]);
239 m_rawLUT[(i * 4) + (j ^ 2)] = rawPixel;
244 case Emulation::PC_Graphics_640x200_2Colors:
247 m_rawLUT = (uint8_t*) heap_caps_malloc(256 * 8, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
248 for (
int i = 0; i < 256; ++i) {
249 for (
int j = 0; j < 8; ++j) {
250 bool pixel = (i >> (7 - j)) & 1;
251 uint8_t rawPixel = m_VGADCtrl.createRawPixel(pixel ? CGAPalette[m_PCGraphicsForegroundColorIndex] :
RGB222(0, 0, 0));
252 m_rawLUT[(i * 8) + (j ^ 2)] = rawPixel;
257 case Emulation::PC_Graphics_HGC_720x348:
260 m_rawLUT = (uint8_t*) heap_caps_malloc(256 * 8, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
261 for (
int i = 0; i < 256; ++i) {
262 for (
int j = 0; j < 8; ++j) {
263 bool pixel = (i >> (7 - j)) & 1;
264 uint8_t rawPixel = m_VGADCtrl.createRawPixel(
RGB222(pixel * 3, pixel * 3, pixel * 3));
265 m_rawLUT[(i * 8) + (j ^ 2)] = rawPixel;
274void GraphicsAdapter::setPCGraphicsBackgroundColorIndex(
int colorIndex)
276 m_PCGraphicsBackgroundColorIndex = colorIndex;
281void GraphicsAdapter::setPCGraphicsForegroundColorIndex(
int colorIndex)
283 m_PCGraphicsForegroundColorIndex = colorIndex;
288void GraphicsAdapter::setPCGraphicsPaletteInUse(
int paletteIndex)
290 m_PCGraphicsPaletteInUse = paletteIndex;
295void GraphicsAdapter::setVideoBuffer(
void const * videoBuffer)
297 m_videoBuffer = (uint8_t*) videoBuffer;
301void GraphicsAdapter::cleanupFont()
304 heap_caps_free((
void*)m_font.data);
305 m_font.data =
nullptr;
310void GraphicsAdapter::setFont(FontInfo
const * font)
316 auto size = 256 * ((m_font.width + 7) / 8) * m_font.
height;
317 m_font.data = (uint8_t
const *) heap_caps_malloc(size, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
318 memcpy((
void*)m_font.data, font->data, size);
323void GraphicsAdapter::setCursorShape(
int start,
int end)
325 if (start != m_cursorStart || end != m_cursorEnd) {
326 m_cursorStart = start;
330 if (start <= end && end >= m_font.height) {
332 end = m_font.height - 1;
336 int charWidthInBytes = (m_font.width + 7) / 8;
338 m_cursorGlyph = (uint8_t*) heap_caps_malloc(charWidthInBytes * m_font.height, MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
339 memset(m_cursorGlyph, 0, charWidthInBytes * m_font.height);
340 if (end >= start && start >= 0 && start < m_font.height && end < m_font.height)
341 memset(m_cursorGlyph + (start * charWidthInBytes), 0xff, (end - start + 1) * charWidthInBytes);
346void GraphicsAdapter::setCursorPos(
int row,
int column)
349 m_cursorCol = column;
353void IRAM_ATTR GraphicsAdapter::drawScanline_PC_Text_40x25_16Colors(
void * arg, uint8_t * dest,
int scanLine)
355 auto ga = (GraphicsAdapter*) arg;
357 constexpr int CHARWIDTH = 8;
358 constexpr int CHARHEIGHT = 8;
359 constexpr int CHARWIDTHINBYTES = (CHARWIDTH + 7) / 8;
360 constexpr int CHARSIZEINBYTES = CHARWIDTHINBYTES * CHARHEIGHT;
361 constexpr int COLUMNS = 40;
362 constexpr int SCREENWIDTH = 320;
363 constexpr int LINES = 8;
366 ++ga->m_frameCounter;
368 int charScanline = scanLine & (CHARHEIGHT - 1);
369 int textRow = scanLine / CHARHEIGHT;
371 uint8_t
const * fontData = ga->m_font.data + (charScanline * CHARWIDTHINBYTES);
373 uint8_t * rawLUT = ga->m_rawLUT;
375 uint8_t
const * curItem = ga->m_videoBuffer + (textRow * COLUMNS * 2);
377 bool showCursor = ga->m_cursorVisible && ga->m_cursorRow == textRow && ((ga->m_frameCounter & 0x1f) < 0xf);
378 int cursorCol = ga->m_cursorCol;
380 bool bit7blink = ga->m_bit7blink;
381 bool blinktime = bit7blink && !((ga->m_frameCounter & 0x3f) < 0x1f);
383 for (
int textCol = 0; textCol < COLUMNS; ++textCol) {
385 int charIdx = *curItem++;
386 int charAttr = *curItem++;
390 blink = blinktime && (charAttr & 0x80);
394 uint8_t bg = rawLUT[charAttr >> 4];
395 uint8_t fg = blink ? bg : rawLUT[charAttr & 0xf];
397 const uint8_t colors[2] = { bg, fg };
399 uint8_t
const * charBitmapPtr = fontData + charIdx * CHARSIZEINBYTES;
403 if (showCursor && textCol == cursorCol) {
405 uint8_t
const * cursorBitmapPtr = ga->m_cursorGlyph + (charScanline * CHARWIDTHINBYTES);
407 for (
int charRow = 0; charRow < LINES / 2; ++charRow) {
409 uint32_t charBitmap = *charBitmapPtr | *cursorBitmapPtr;
411 *(destptr + 0) = colors[(
bool)(charBitmap & 0x20)];
412 *(destptr + 1) = colors[(
bool)(charBitmap & 0x10)];
413 *(destptr + 2) = colors[(
bool)(charBitmap & 0x80)];
414 *(destptr + 3) = colors[(
bool)(charBitmap & 0x40)];
415 *(destptr + 4) = colors[(
bool)(charBitmap & 0x02)];
416 *(destptr + 5) = colors[(
bool)(charBitmap & 0x01)];
417 *(destptr + 6) = colors[(
bool)(charBitmap & 0x08)];
418 *(destptr + 7) = colors[(
bool)(charBitmap & 0x04)];
420 destptr += SCREENWIDTH;
421 charBitmapPtr += CHARWIDTHINBYTES;
422 cursorBitmapPtr += CHARWIDTHINBYTES;
427 for (
int charRow = 0; charRow < LINES / 2; ++charRow) {
429 uint32_t charBitmap = *charBitmapPtr;
431 *(destptr + 0) = colors[(
bool)(charBitmap & 0x20)];
432 *(destptr + 1) = colors[(
bool)(charBitmap & 0x10)];
433 *(destptr + 2) = colors[(
bool)(charBitmap & 0x80)];
434 *(destptr + 3) = colors[(
bool)(charBitmap & 0x40)];
435 *(destptr + 4) = colors[(
bool)(charBitmap & 0x02)];
436 *(destptr + 5) = colors[(
bool)(charBitmap & 0x01)];
437 *(destptr + 6) = colors[(
bool)(charBitmap & 0x08)];
438 *(destptr + 7) = colors[(
bool)(charBitmap & 0x04)];
440 destptr += SCREENWIDTH;
441 charBitmapPtr += CHARWIDTHINBYTES;
452void IRAM_ATTR GraphicsAdapter::drawScanline_PC_Text_80x25_16Colors(
void * arg, uint8_t * dest,
int scanLine)
454 auto ga = (GraphicsAdapter*) arg;
456 constexpr int CHARWIDTH = 8;
457 constexpr int CHARHEIGHT = 16;
458 constexpr int CHARWIDTHINBYTES = (CHARWIDTH + 7) / 8;
459 constexpr int CHARSIZEINBYTES = CHARWIDTHINBYTES * CHARHEIGHT;
460 constexpr int COLUMNS = 80;
461 constexpr int SCREENWIDTH = 640;
462 constexpr int LINES = 16;
465 ++ga->m_frameCounter;
467 int charScanline = scanLine & (CHARHEIGHT - 1);
468 int textRow = scanLine / CHARHEIGHT;
470 uint8_t
const * fontData = ga->m_font.data + (charScanline * CHARWIDTHINBYTES);
472 uint8_t * rawLUT = ga->m_rawLUT;
474 uint8_t
const * curItem = ga->m_videoBuffer + (textRow * COLUMNS * 2);
476 bool showCursor = ga->m_cursorVisible && ga->m_cursorRow == textRow && ((ga->m_frameCounter & 0x1f) < 0xf);
477 int cursorCol = ga->m_cursorCol;
479 bool bit7blink = ga->m_bit7blink;
480 bool blinktime = bit7blink && !((ga->m_frameCounter & 0x3f) < 0x1f);
482 for (
int textCol = 0; textCol < COLUMNS; ++textCol) {
484 int charIdx = *curItem++;
485 int charAttr = *curItem++;
489 blink = blinktime && (charAttr & 0x80);
493 uint8_t bg = rawLUT[charAttr >> 4];
494 uint8_t fg = blink ? bg : rawLUT[charAttr & 0xf];
496 const uint8_t colors[2] = { bg, fg };
498 uint8_t
const * charBitmapPtr = fontData + charIdx * CHARSIZEINBYTES;
502 if (showCursor && textCol == cursorCol) {
504 uint8_t
const * cursorBitmapPtr = ga->m_cursorGlyph + (charScanline * CHARWIDTHINBYTES);
506 for (
int charRow = 0; charRow < LINES / 2; ++charRow) {
508 uint32_t charBitmap = *charBitmapPtr | *cursorBitmapPtr;
510 *(destptr + 0) = colors[(
bool)(charBitmap & 0x20)];
511 *(destptr + 1) = colors[(
bool)(charBitmap & 0x10)];
512 *(destptr + 2) = colors[(
bool)(charBitmap & 0x80)];
513 *(destptr + 3) = colors[(
bool)(charBitmap & 0x40)];
514 *(destptr + 4) = colors[(
bool)(charBitmap & 0x02)];
515 *(destptr + 5) = colors[(
bool)(charBitmap & 0x01)];
516 *(destptr + 6) = colors[(
bool)(charBitmap & 0x08)];
517 *(destptr + 7) = colors[(
bool)(charBitmap & 0x04)];
519 destptr += SCREENWIDTH;
520 charBitmapPtr += CHARWIDTHINBYTES;
521 cursorBitmapPtr += CHARWIDTHINBYTES;
526 for (
int charRow = 0; charRow < LINES / 2; ++charRow) {
528 uint32_t charBitmap = *charBitmapPtr;
530 *(destptr + 0) = colors[(
bool)(charBitmap & 0x20)];
531 *(destptr + 1) = colors[(
bool)(charBitmap & 0x10)];
532 *(destptr + 2) = colors[(
bool)(charBitmap & 0x80)];
533 *(destptr + 3) = colors[(
bool)(charBitmap & 0x40)];
534 *(destptr + 4) = colors[(
bool)(charBitmap & 0x02)];
535 *(destptr + 5) = colors[(
bool)(charBitmap & 0x01)];
536 *(destptr + 6) = colors[(
bool)(charBitmap & 0x08)];
537 *(destptr + 7) = colors[(
bool)(charBitmap & 0x04)];
539 destptr += SCREENWIDTH;
540 charBitmapPtr += CHARWIDTHINBYTES;
553void IRAM_ATTR GraphicsAdapter::drawScanline_PC_Graphics_320x200_4Colors(
void * arg, uint8_t * dest,
int scanLine)
555 constexpr int WIDTH = 320;
556 constexpr int PIXELSPERBYTE = 4;
557 constexpr int WIDTHINBYTES = WIDTH / PIXELSPERBYTE;
559 auto ga = (GraphicsAdapter*) arg;
561 auto src = ga->m_videoBuffer + ((scanLine & 1) << 13) + WIDTHINBYTES * (scanLine >> 1);
563 auto dest32 = (uint32_t*) dest;
565 auto LUT32 = (uint32_t*) ga->m_rawLUT;
567 for (
int col = 0; col < WIDTH; col += PIXELSPERBYTE) {
568 *dest32++ = LUT32[*src++];
575void IRAM_ATTR GraphicsAdapter::drawScanline_PC_Graphics_640x200_2Colors(
void * arg, uint8_t * dest,
int scanLine)
577 constexpr int WIDTH = 640;
578 constexpr int PIXELSPERBYTE = 8;
579 constexpr int WIDTHINBYTES = WIDTH / PIXELSPERBYTE;
581 auto ga = (GraphicsAdapter*) arg;
583 auto src = ga->m_videoBuffer + ((scanLine & 1) << 13) + WIDTHINBYTES * (scanLine >> 1);
585 auto dest64 = (uint64_t*) dest;
587 auto LUT64 = (uint64_t*) ga->m_rawLUT;
589 for (
int col = 0; col < WIDTH; col += PIXELSPERBYTE) {
590 *dest64++ = LUT64[*src++];
599void IRAM_ATTR GraphicsAdapter::drawScanline_PC_Graphics_HGC_720x348(
void * arg, uint8_t * dest,
int scanLine)
601 constexpr int WIDTH = 720;
602 constexpr int PIXELSPERBYTE = 8;
603 constexpr int WIDTHINBYTES = WIDTH / PIXELSPERBYTE;
605 auto ga = (GraphicsAdapter*) arg;
607 auto src = ga->m_videoBuffer + ((scanLine & 0b11) << 13) + WIDTHINBYTES * (scanLine >> 2);
609 auto dest64 = (uint64_t*) dest;
611 auto LUT64 = (uint64_t*) ga->m_rawLUT;
613 for (
int col = 0; col < WIDTH; col += PIXELSPERBYTE)
614 *dest64++ = LUT64[*src++];
617 src = ga->m_videoBuffer + ((scanLine & 0b11) << 13) + WIDTHINBYTES * (scanLine >> 2);
619 for (
int col = 0; col < WIDTH; col += PIXELSPERBYTE)
620 *dest64++ = LUT64[*src++];
#define QVGA_320x240_60Hz
This file contains fabgl::GraphicsAdapter definition.
Represents a 6 bit RGB color.
void(* DrawScanlineCallback)(void *arg, uint8_t *dest, int scanLine)
Callback used when VGADirectController needs to prepare a new scanline to be sent to the VGA output.