Compare commits

...

17 Commits

Author SHA1 Message Date
David Kuder 813118b3c8 Update build.h 2023-05-02 20:15:15 -04:00
David Kuder b2326d63e1 Update buffers.c 2023-05-02 19:28:59 -04:00
David Kuder 175e08e54c Default Video 7 shift register to being set (140x192 DHGR) 2023-05-02 19:28:42 -04:00
David Kuder 9469594990 Live palette editing 2023-05-02 19:28:08 -04:00
David Kuder 4871a12993 Flashing Text Fix 2023-05-02 19:27:52 -04:00
David Kuder f67a365804 Video 7 rendering fixes 2023-05-02 19:27:43 -04:00
David Kuder d915710a2a Merge branch 'main' of github.com:V2RetroComputing/analog-firmware 2023-05-02 16:23:46 -04:00
David Kuder dd3a58f51c Video 7 Color Text Fixes
Removed color 80 column (for now)
2023-05-02 16:23:41 -04:00
David Kuder 9c5804c33d Video 7 F/B HiRes mode 2023-05-02 16:19:35 -04:00
David Kuder 138440b31b Video 7 Monochrome and 160x192 mode fixes 2023-05-02 16:19:10 -04:00
David Kuder dd9fa40678 Disable the test screen on any bus activity
May need to make this on consistent bus activity.
The card was remaining in the test pattern if reset and no soft switches were toggled afterward.
2023-05-02 16:18:10 -04:00
David Kuder 594af38236 Move SHR to Analog GS firmware
Even with overclocking SHR pushes the limits of the RP2040 if it has to bit-shift the 12-bit RGB to 9-bit RGB.  May investigate using the hardware math acceleration to re-enable this at a later time.
2023-05-02 16:16:09 -04:00
David Kuder 1928fe010c
Update README.md 2023-05-02 15:09:08 -04:00
David Kuder 073a139514 Add builds for Analog GS 2023-05-02 15:08:23 -04:00
David Kuder d34706c47b Update vgaout.c 2023-05-02 15:08:11 -04:00
David Kuder bc17d64651 Revert extended PIO pixel hold values
due to how the PIO program counter operates, the pixel holds are also used to shift the active area after a HSYNC. the larger values of pixel holds caused problems and had to be removed.
2023-05-02 15:08:04 -04:00
David Kuder d8ef103650 Jumper reading for Analog GS 2023-05-02 15:04:36 -04:00
16 changed files with 165 additions and 91 deletions

View File

@ -4,8 +4,7 @@ This firmware is now modular and has builds for V2 Analog LC, V2 Analog WiFi and
There are separate builds for the Z80 Applicard and VGA functions now.
Please note which card you have as the firmware is not cross compatible between hardware.
The VGA firmware currently requires the font data to be uploaded separately when migrating from the old monolithic firmware builds.
You can find the firmware data preloader [here](https://github.com/V2RetroComputing/analog-preload).
The VGA firmware currently requires the font data to be uploaded separately when migrating from the old monolithic firmware builds or bringing up a card for the first time. You can find the firmware data preloader [here](https://github.com/V2RetroComputing/analog-preload).
Full details are available at [∀2 Retro Computing](https://www.v2retrocomputing.com/).

View File

@ -21,3 +21,6 @@ build_firmware v2-analog-wifi-4ns-z80
build_firmware v2-analog-wifi-8ns-z80
build_firmware v2-analog-wifi-4ns-vga
build_firmware v2-analog-wifi-8ns-vga
build_firmware v2-analog-gs-4ns-z80
build_firmware v2-analog-gs-8ns-z80
build_firmware v2-analog-gs-4ns-vga

View File

@ -1,7 +1,7 @@
#include "buffers.h"
volatile uint32_t soft_switches = 0;
volatile uint32_t internal_flags = 0;
volatile uint32_t internal_flags = IFLAGS_V7_MODE3;
volatile uint8_t reset_state = 0;
@ -11,6 +11,8 @@ volatile uint32_t busactive = 0;
volatile uint8_t __attribute__((section (".appledata."))) apple_memory[64*1024];
volatile uint8_t __attribute__((section (".appledata."))) private_memory[64*1024];
volatile uint8_t jumpers = 0;
#ifdef FUNCTION_VGA
volatile uint8_t *text_p1 = apple_memory + 0x0400;
volatile uint8_t *text_p2 = apple_memory + 0x0800;

View File

@ -16,6 +16,8 @@ extern volatile uint8_t cfbuf[4096];
#define config_cmdbuf ((uint8_t*)(apple_memory+0xC0F0+(cardslot<<8)))
#define config_rpybuf ((uint8_t*)(apple_memory+0xC0F8+(cardslot<<8)))
extern volatile uint8_t jumpers;
#ifdef FUNCTION_VGA
extern volatile uint8_t *text_p1;
extern volatile uint8_t *text_p2;

View File

@ -1,6 +1,6 @@
#define BUILDDATE 0x20230403
#define BUILDID 0x0171
#define BUILDSTR " 3 Apr 2023 Build 0171"
#define BUILDDATE 0x20230502
#define BUILDID 0x0182
#define BUILDSTR " 2 May 2023 Build 0182"
#ifdef ANALOG_GS
#define HWSTRING " V2 Analog GS Rev1"

View File

@ -112,7 +112,7 @@ bool DELAYED_COPY_CODE(parse_config)(uint32_t address) {
break;
case CFGTOKEN_VIDEO7:
if((config[i] >> 16) & 1) {
internal_flags |= IFLAGS_VIDEO7;
internal_flags |= IFLAGS_VIDEO7 | IFLAGS_V7_MODE3;
} else {
internal_flags &= ~IFLAGS_VIDEO7;
}
@ -201,7 +201,7 @@ void DELAYED_COPY_CODE(default_config)() {
current_machine = MACHINE_AUTO;
internal_flags |= (IFLAGS_IIE_REGS | IFLAGS_IIGS_REGS);
#endif
internal_flags |= IFLAGS_VIDEO7;
internal_flags |= IFLAGS_VIDEO7 | IFLAGS_V7_MODE3;
}
int DELAYED_COPY_CODE(make_config)(uint32_t rev) {
@ -808,6 +808,12 @@ void DELAYED_COPY_CODE(config_handler)() {
config_rpybuf[rs++] = HWREV; // '1'
break;
case 'j':
// Read jumpers
retval = REPLY_OK;
config_rpybuf[rs++] = jumpers;
break;
case 'd':
// Identify Date Request
retval = REPLY_OK;

View File

@ -35,6 +35,9 @@ static void __noinline __time_critical_func(core1_loop)() {
}
}
#ifdef ANALOG_GS
jumpers = (value >> 26) & 0x3f;
#endif
busactive = 1;
if(CARD_SELECT) {

View File

@ -4,6 +4,18 @@
#include "common/abus.h"
#include "vga/businterface.h"
#include "vga/vgabuf.h"
#include "vga/render.h"
uint8_t lores_to_dhgr[16] = {
0x0, 0x8,
0x1, 0x9,
0x2, 0xA,
0x3, 0xB,
0x4, 0xC,
0x5, 0xD,
0x6, 0xE,
0x7, 0xF
};
volatile uint8_t *terminal_page = terminal_memory;
@ -157,7 +169,7 @@ void __time_critical_func(vga_businterface)(uint32_t address, uint32_t value) {
break;
case 0x5f:
// Video 7 shift register
if(!soft_switches & SOFTSW_DGR) {
if(soft_switches & SOFTSW_DGR) {
internal_flags = (internal_flags & 0xfffffffc) | ((internal_flags & 0x1) << 1) | ((soft_switches & SOFTSW_80COL) ? 1 : 0);
}
@ -240,6 +252,8 @@ void __time_critical_func(vga_businterface)(uint32_t address, uint32_t value) {
// Card Registers
if(ACCESS_WRITE) {
if(CARD_SELECT && CARD_DEVSEL) {
uint8_t color_index;
cardslot = (address >> 4) & 0x7;
switch(address & 0x0F) {
case 0x01:
@ -280,6 +294,51 @@ void __time_critical_func(vga_businterface)(uint32_t address, uint32_t value) {
romx_changed = 1;
}
break;
case 0x0C:
apple_memory[address] = value;
break;
case 0x0D:
color_index = (apple_memory[address-1] >> 4) & 0xF;
#ifdef ANALOG_GS
switch(apple_memory[address-1] & 3) {
case 1:
lores_palette[color_index] =
(lores_palette[color_index] & 0x00FF) |
(((uint16_t)(value & 0xF)) << 8);
break;
case 2:
lores_palette[color_index] =
(lores_palette[color_index] & 0x0F0F) |
(((uint16_t)(value & 0xF)) << 4);
break;
case 3:
lores_palette[color_index] =
(lores_palette[color_index] & 0x0FF0) |
(((uint16_t)(value & 0xF)) << 0);
break;
}
#else
switch(apple_memory[address-1] & 3) {
case 1:
lores_palette[color_index] =
(lores_palette[color_index] & 0x003F) |
(((uint16_t)(value & 0x7)) << 6);
break;
case 2:
lores_palette[color_index] =
(lores_palette[color_index] & 0x01C7) |
(((uint16_t)(value & 0x7)) << 3);
break;
case 3:
lores_palette[color_index] =
(lores_palette[color_index] & 0x01F8) |
(((uint16_t)(value & 0x7)) << 0);
break;
}
#endif
dhgr_palette[lores_to_dhgr[color_index]] = lores_palette[color_index];
apple_memory[address] = value;
break;
}
}
} else if(CARD_SELECT && CARD_DEVSEL) {

View File

@ -202,13 +202,14 @@ void DELAYED_COPY_CODE(render_loop)() {
if(internal_flags & IFLAGS_TEST) {
render_testpattern();
// Automatically dismiss the test pattern when the Apple II is seen.
if(((soft_switches & SOFTSW_MODE_MASK) != 0) && (testdone == 0)) {
// Assume the RP2040 has been hard reset and try to default to text display
if(busactive && (testdone == 0)) { // was ((soft_switches & SOFTSW_MODE_MASK) != 0)
soft_switches |= SOFTSW_TEXT_MODE;
internal_flags &= ~IFLAGS_TEST;
testdone = 1;
render_about_init();
}
#if defined(ANALOG_GS) || defined(OVERCLOCKED)
#if defined(ANALOG_GS)
} else if(soft_switches & SOFTSW_SHR) {
vga_prepare_frame();
render_shr();

View File

@ -6,7 +6,7 @@
#include "vga/render.h"
#include "vga/vgaout.h"
static void render_dhgr_line(bool p2, uint line);
static void render_dhgr_line(bool p2, uint line, bool mono);
uint16_t DELAYED_COPY_DATA(dhgr_palette)[16] = {
RGB_BLACK, RGB_DBLUE, RGB_DGREEN, RGB_HBLUE,
@ -25,26 +25,28 @@ static inline uint dhgr_line_to_mem_offset(uint line) {
void DELAYED_COPY_CODE(render_dhgr)() {
if((internal_flags & IFLAGS_VIDEO7) && (internal_flags & IFLAGS_V7_MODE3 == IFLAGS_V7_MODE3)) {
mono_rendering = true;
bool mono = mono_rendering;
if((internal_flags & IFLAGS_VIDEO7) && ((internal_flags & IFLAGS_V7_MODE3) == IFLAGS_V7_MODE0)) {
mono = true;
}
for(uint line=0; line < 192; line++) {
render_dhgr_line(PAGE2SEL, line);
render_dhgr_line(PAGE2SEL, line, mono);
}
}
void DELAYED_COPY_CODE(render_mixed_dhgr)() {
if((internal_flags & IFLAGS_VIDEO7) && (internal_flags & IFLAGS_V7_MODE3 == IFLAGS_V7_MODE3)) {
mono_rendering = true;
bool mono = mono_rendering;
if((internal_flags & IFLAGS_VIDEO7) && ((internal_flags & IFLAGS_V7_MODE3) == IFLAGS_V7_MODE0)) {
mono = true;
}
for(uint line=0; line < 160; line++) {
render_dhgr_line(PAGE2SEL, line);
render_dhgr_line(PAGE2SEL, line, mono);
}
render_mixed_text();
}
static void DELAYED_COPY_CODE(render_dhgr_line)(bool p2, uint line) {
static void DELAYED_COPY_CODE(render_dhgr_line)(bool p2, uint line, bool mono) {
struct vga_scanline *sl = vga_prepare_scanline();
uint sl_pos = 0;
uint i;
@ -52,11 +54,8 @@ static void DELAYED_COPY_CODE(render_dhgr_line)(bool p2, uint line) {
const uint8_t *line_mema = (const uint8_t *)((p2 ? hgr_p2 : hgr_p1) + dhgr_line_to_mem_offset(line));
const uint8_t *line_memb = (const uint8_t *)((p2 ? hgr_p4 : hgr_p3) + dhgr_line_to_mem_offset(line));
if((internal_flags & IFLAGS_VIDEO7) && ((internal_flags & IFLAGS_V7_MODE3) == IFLAGS_V7_MODE1)) {
// Pad 30 pixels on the left to center horizontally
sl->data[sl_pos++] = (text_border|THEN_EXTEND_7) | ((text_border|THEN_EXTEND_7) << 16); // 16 pixels per word
sl->data[sl_pos++] = (text_border|THEN_EXTEND_3) | ((text_border|THEN_EXTEND_7) << 16); // 12 pixels per word
sl->data[sl_pos++] = (text_border) | ((text_border) << 16); // 2 pixels per word
if((internal_flags & IFLAGS_VIDEO7) && ((internal_flags & IFLAGS_V7_MODE3) == IFLAGS_V7_MODE2)) {
// Pad 0 pixels on the left to center horizontally
} else {
// Pad 40 pixels on the left to center horizontally
sl->data[sl_pos++] = (text_border|THEN_EXTEND_7) | ((text_border|THEN_EXTEND_7) << 16); // 16 pixels per word
@ -64,13 +63,13 @@ static void DELAYED_COPY_CODE(render_dhgr_line)(bool p2, uint line) {
sl->data[sl_pos++] = (text_border|THEN_EXTEND_3) | ((text_border|THEN_EXTEND_3) << 16); // 16 pixels per word
}
// DHGR is weird. Nuff said.
// DHGR is weird. Video-7 just makes it weirder. Nuff said.
uint32_t dots = 0;
uint_fast8_t dotc = 0;
uint32_t pixeldata;
i = 0;
if(mono_rendering) {
if(mono) {
while(i < 40) {
// Load in as many subpixels as possible
while((dotc < 28) && (i < 40)) {
@ -91,7 +90,46 @@ static void DELAYED_COPY_CODE(render_dhgr_line)(bool p2, uint line) {
dotc -= 2;
}
}
} else if((internal_flags & IFLAGS_VIDEO7) && ((internal_flags & IFLAGS_V7_MODE3) == IFLAGS_V7_MODE1)) {
} else if((internal_flags & IFLAGS_VIDEO7) && ((soft_switches & (SOFTSW_80STORE | SOFTSW_80COL)) == (SOFTSW_80STORE))) {
uint32_t color1, color2, color3, color4;
int j;
// Video 7 F/B HiRes
while(i < 40) {
dots = (line_mema[i] & 0x7f);
color1 = lores_palette[(line_memb[i] >> 4) & 0xF];
color2 = lores_palette[(line_memb[i] >> 0) & 0xF];
i++;
dots |= (line_mema[i] & 0x7f) << 7;
color3 = lores_palette[(line_memb[i] >> 4) & 0xF];
color4 = lores_palette[(line_memb[i] >> 0) & 0xF];
i++;
for(j = 0; j < 3; j++) {
pixeldata = ((dots & 1) ? (color1) : (color2)) | THEN_EXTEND_1;
dots >>= 1;
pixeldata |= (((dots & 1) ? (color1) : (color2)) | THEN_EXTEND_1) << 16;
dots >>= 1;
sl->data[sl_pos++] = pixeldata;
}
pixeldata = ((dots & 1) ? (color1) : (color2)) | THEN_EXTEND_1;
dots >>= 1;
pixeldata |= (((dots & 1) ? (color3) : (color4)) | THEN_EXTEND_1) << 16;
dots >>= 1;
sl->data[sl_pos++] = pixeldata;
for(j = 0; j < 3; j++) {
pixeldata = ((dots & 1) ? (color3) : (color4)) | THEN_EXTEND_1;
dots >>= 1;
pixeldata |= (((dots & 1) ? (color3) : (color4)) | THEN_EXTEND_1) << 16;
dots >>= 1;
sl->data[sl_pos++] = pixeldata;
}
}
} else if((internal_flags & IFLAGS_VIDEO7) && ((internal_flags & IFLAGS_V7_MODE3) == IFLAGS_V7_MODE2)) {
// 160x192 Video-7
while(i < 40) {
// Load in as many subpixels as possible
while((dotc <= 18) && (i < 40)) {
@ -104,9 +142,9 @@ static void DELAYED_COPY_CODE(render_dhgr_line)(bool p2, uint line) {
// Consume pixels
while(dotc >= 8) {
pixeldata = (dhgr_palette[dots & 0xf] | THEN_EXTEND_3);
pixeldata = (lores_palette[dots & 0xf] | THEN_EXTEND_3);
dots >>= 4;
pixeldata |= (dhgr_palette[dots & 0xf] | THEN_EXTEND_3) << 16;
pixeldata |= (lores_palette[dots & 0xf] | THEN_EXTEND_3) << 16;
dots >>= 4;
sl->data[sl_pos++] = pixeldata;
dotc -= 8;
@ -175,11 +213,8 @@ static void DELAYED_COPY_CODE(render_dhgr_line)(bool p2, uint line) {
sl->data[sl_pos++] = pixeldata;
}
if((internal_flags & IFLAGS_VIDEO7) && ((internal_flags & IFLAGS_V7_MODE3) == IFLAGS_V7_MODE1)) {
// Pad 30 pixels on the right to center horizontally
sl->data[sl_pos++] = (text_border|THEN_EXTEND_7) | ((text_border|THEN_EXTEND_7) << 16); // 16 pixels per word
sl->data[sl_pos++] = (text_border|THEN_EXTEND_3) | ((text_border|THEN_EXTEND_7) << 16); // 12 pixels per word
sl->data[sl_pos++] = (text_border) | ((text_border) << 16); // 2 pixels per word
if((internal_flags & IFLAGS_VIDEO7) && ((internal_flags & IFLAGS_V7_MODE3) == IFLAGS_V7_MODE2)) {
// Pad 0 pixels on the right to center horizontally
} else {
// Pad 40 pixels on the right to center horizontally
sl->data[sl_pos++] = (text_border|THEN_EXTEND_7) | ((text_border|THEN_EXTEND_7) << 16); // 16 pixels per word

View File

@ -10,7 +10,6 @@
#define PAGE2SEL ((soft_switches & (SOFTSW_80STORE | SOFTSW_PAGE_2)) == SOFTSW_PAGE_2)
static void render_hires_line(bool p2, uint line);
extern uint16_t dhgr_palette[16];
static inline uint hires_line_to_mem_offset(uint line) {
return ((line & 0x07) << 10) | ((line & 0x38) << 4) | (((line & 0xc0) >> 6) * 40);
@ -52,12 +51,10 @@ static void DELAYED_COPY_CODE(render_hires_line)(bool p2, uint line) {
if(mono_rendering) {
while(i < 40) {
// Load in as many subpixels as possible
while((dotc < 18) && (i < 40)) {
dots |= (hires_dot_patterns2[lastmsb | line_mem[i]]) << dotc;
lastmsb = (dotc>0) ? ((line_mem[i] & 0x40)<<2) : 0;
i++;
dotc += 14;
}
dots |= (hires_dot_patterns2[lastmsb | line_mem[i]]) << dotc;
lastmsb = (dotc>0) ? ((line_mem[i] & 0x40)<<2) : 0;
i++;
dotc += 14;
// Consume pixels
while(dotc) {

View File

@ -4,8 +4,6 @@
#include "vga/render.h"
#include "vga/vgaout.h"
extern uint16_t lores_palette[16];
//#define PAGE2SEL (!(soft_switches & SOFTSW_80STORE) && (soft_switches & SOFTSW_PAGE_2))
#define PAGE2SEL ((soft_switches & (SOFTSW_80STORE | SOFTSW_PAGE_2)) == SOFTSW_PAGE_2)
@ -44,23 +42,17 @@ static inline uint_fast8_t char_text_bits(uint_fast8_t ch, uint_fast8_t glyph_li
ch = (ch & 0x3f) | 0x80;
}
bits = character_rom[((uint_fast16_t)ch << 3) | glyph_line] & 0x7f;
bits = character_rom[((uint_fast16_t)ch << 3) | glyph_line];
return bits ^ invert;
return (bits ^ invert) & 0x7f;
}
void DELAYED_COPY_CODE(render_text)() {
uint line;
if((internal_flags & IFLAGS_VIDEO7) && !(soft_switches & SOFTSW_DGR)) {
if(soft_switches & SOFTSW_80COL) {
for(line=0; line < 24; line++) {
render_color_text80_line(line);
}
} else {
for(line=0; line < 24; line++) {
render_color_text40_line(line);
}
if((internal_flags & IFLAGS_VIDEO7) && ((soft_switches & (SOFTSW_80STORE | SOFTSW_80COL | SOFTSW_DGR)) == (SOFTSW_80STORE | SOFTSW_DGR))) {
for(line=0; line < 24; line++) {
render_color_text40_line(line);
}
} else {
if(soft_switches & SOFTSW_80COL) {
@ -78,15 +70,9 @@ void DELAYED_COPY_CODE(render_text)() {
void DELAYED_COPY_CODE(render_mixed_text)() {
uint line;
if((internal_flags & IFLAGS_VIDEO7) && !(soft_switches & SOFTSW_DGR)) {
if(soft_switches & SOFTSW_80COL) {
for(line=20; line < 24; line++) {
render_color_text80_line(line);
}
} else {
for(line=20; line < 24; line++) {
render_color_text40_line(line);
}
if((internal_flags & IFLAGS_VIDEO7) && ((soft_switches & (SOFTSW_80STORE | SOFTSW_80COL | SOFTSW_DGR)) == (SOFTSW_80STORE | SOFTSW_DGR))) {
for(line=20; line < 24; line++) {
render_color_text40_line(line);
}
} else {
if(soft_switches & SOFTSW_80COL) {

View File

@ -37,12 +37,6 @@ public wait_vsync:
wait 0 irq VSYNC_IRQ_NUM
public wait_hsync:
wait 0 irq HSYNC_IRQ_NUM [1]
public extend_31: ; 32 pixels / an extra 62 clocks (32+4+12+2+6+4+2)
nop [31]
public extend_15: ; 16 pixels / an extra 30 clocks (4+12+2+6+4+2)
nop [3]
public extend_13: ; 14 pixels / an extra 26 clocks (12+2+6+4+2)
nop [11]
public extend_7: ; 8 pixels / an extra 14 clocks (2+6+4+2)
nop [1]
public extend_6: ; 7 pixels / an extra 12 clocks (6+4+2)

View File

@ -37,12 +37,6 @@ public wait_vsync:
wait 0 irq VSYNC_IRQ_NUM
public wait_hsync:
wait 0 irq HSYNC_IRQ_NUM [1]
public extend_31: ;public extend_31: ; 32 pixels / an extra 62 clocks (32+4+12+2+6+4+2)
nop [31]
public extend_15: ; 16 pixels / an extra 30 clocks (4+12+2+6+4+2)
nop [3]
public extend_13: ; 14 pixels / an extra 26 clocks (12+2+6+4+2)
nop [11]
public extend_7: ; 8 pixels / an extra 14 clocks (2+6+4+2)
nop [1]
public extend_6: ; 7 pixels / an extra 12 clocks (6+4+2)

View File

@ -236,7 +236,6 @@ struct vga_scanline * DELAYED_COPY_CODE(vga_prepare_scanline)() {
// Wait for the scanline buffer to become available again
while(scanline->_flags & FLAG_BUSY)
terminal_process_input();
//tight_loop_contents();
// Reinitialize the scanline struct for reuse
scanline->length = 0;

View File

@ -9,23 +9,17 @@
#ifdef ANALOG_GS
#define THEN_WAIT_VSYNC (2 << 12)
#define THEN_WAIT_HSYNC (3 << 12)
#define THEN_EXTEND_31 (4 << 12)
#define THEN_EXTEND_15 (5 << 12)
#define THEN_EXTEND_13 (6 << 12)
#define THEN_EXTEND_7 (7 << 12)
#define THEN_EXTEND_6 (8 << 12)
#define THEN_EXTEND_3 (9 << 12)
#define THEN_EXTEND_1 (10 << 12)
#define THEN_EXTEND_7 (4 << 12)
#define THEN_EXTEND_6 (5 << 12)
#define THEN_EXTEND_3 (6 << 12)
#define THEN_EXTEND_1 (7 << 12)
#else
#define THEN_WAIT_VSYNC (2 << 9)
#define THEN_WAIT_HSYNC (3 << 9)
#define THEN_EXTEND_31 (4 << 9)
#define THEN_EXTEND_15 (5 << 9)
#define THEN_EXTEND_13 (6 << 9)
#define THEN_EXTEND_7 (7 << 9)
#define THEN_EXTEND_6 (8 << 9)
#define THEN_EXTEND_3 (9 << 9)
#define THEN_EXTEND_1 (10 << 9)
#define THEN_EXTEND_7 (4 << 9)
#define THEN_EXTEND_6 (5 << 9)
#define THEN_EXTEND_3 (6 << 9)
#define THEN_EXTEND_1 (7 << 9)
#endif
struct vga_scanline {