analog/v2-analog-rev1/vga/render_text.c
David Kuder d9be2ed9cd Firmware Release 23-01-16-119
New autodetect routines for Apple II+, IIe (Platinum), IIgs (ROM03).
Timing tweaks to improve compatibility on IIgs and IIe.
Corrected text rendering on all supported machines.
Initial Super HiRes support (not recommended to use at this time)
Monochrome DHGR and HGR support activated with IIgs MONOCOLOR or NEWVID registers.
80 Column mode on IIe and IIgs.
2023-01-16 20:36:00 -05:00

166 lines
6.1 KiB
C

#include <pico/stdlib.h>
#include "vgabuf.h"
#include "render.h"
#include "character_rom.h"
#include "vgaout.h"
uint_fast32_t text_flasher_mask = 0;
static uint64_t next_flash_tick = 0;
void update_text_flasher() {
uint64_t now = time_us_64();
if(now > next_flash_tick) {
text_flasher_mask ^= 0x7f;
next_flash_tick = now + 250000u;
}
}
static inline uint_fast8_t __time_critical_func(char_text_bits)(uint_fast8_t ch, uint_fast8_t glyph_line) {
uint_fast8_t bits = character_rom[((uint_fast16_t)ch << 3) | glyph_line] & 0x7f;
if((soft_switches & SOFTSW_ALTCHAR) || (ch & 0x80)) {
// normal character
return bits;
}
if(ch & 0x40) {
// flashing character
return (bits ^ text_flasher_mask);
}
// inverse character
return bits;
}
// Skip 48 lines to center vertically
void __time_critical_func(render_border)() {
struct vga_scanline *sl = vga_prepare_scanline();
uint sl_pos = 0;
while(sl_pos < VGA_WIDTH/16) {
sl->data[sl_pos] = (text_border|THEN_EXTEND_7) | ((text_border|THEN_EXTEND_7) << 16); // 8 pixels per word
sl_pos++;
}
sl->length = sl_pos;
sl->repeat_count = 47;
vga_submit_scanline(sl);
}
void __time_critical_func(render_text)() {
vga_prepare_frame();
render_border();
bool p2 = !(soft_switches & SOFTSW_80STORE) && (soft_switches & SOFTSW_PAGE_2);
if(soft_switches & SOFTSW_80COL) {
for(uint line=0; line < 24; line++) {
render_text80_line(p2, line);
}
} else {
for(uint line=0; line < 24; line++) {
render_text40_line(p2, line);
}
}
render_border();
}
void __time_critical_func(render_text40_line)(bool p2, unsigned int line) {
const uint8_t *page = (const uint8_t *)(p2 ? text_p2 : text_p1);
const uint8_t *line_buf = (const uint8_t *)(page + ((line & 0x7) << 7) + (((line >> 3) & 0x3) * 40));
for(uint glyph_line=0; glyph_line < 8; glyph_line++) {
struct vga_scanline *sl = vga_prepare_scanline();
uint sl_pos = 0;
// 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
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_3) << 16); // 8 pixels per word
for(uint col=0; col < 40; ) {
// Grab 14 pixels from the next two characters
uint32_t bits_a = char_text_bits(line_buf[col], glyph_line);
col++;
uint32_t bits_b = char_text_bits(line_buf[col], glyph_line);
col++;
uint32_t bits = (bits_a << 7) | bits_b;
// Translate each pair of bits into a pair of pixels
for(int i=0; i < 7; i++) {
uint32_t pixeldata = (bits & 0x2000) ? (text_back|THEN_EXTEND_1) : (text_fore|THEN_EXTEND_1);
pixeldata |= (bits & 0x1000) ?
((uint32_t)text_back|THEN_EXTEND_1) << 16 :
((uint32_t)text_fore|THEN_EXTEND_1) << 16;
bits <<= 2;
sl->data[sl_pos] = pixeldata;
sl_pos++;
}
}
// 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
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_3) << 16); // 8 pixels per word
sl->length = sl_pos;
sl->repeat_count = 1;
vga_submit_scanline(sl);
}
}
void __time_critical_func(render_text80_line)(bool p2, unsigned int line) {
const uint8_t *page_a = (const uint8_t *)(p2 ? text_p2 : text_p1);
const uint8_t *page_b = (const uint8_t *)(p2 ? text_p4 : text_p3);
const uint8_t *line_buf_a = page_a + ((line & 0x7) << 7) + (((line >> 3) & 0x3) * 40);
const uint8_t *line_buf_b = page_b + ((line & 0x7) << 7) + (((line >> 3) & 0x3) * 40);
for(uint glyph_line=0; glyph_line < 8; glyph_line++) {
struct vga_scanline *sl = vga_prepare_scanline();
uint sl_pos = 0;
// 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
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_3) << 16); // 8 pixels per word
for(uint col=0; col < 40; ) {
// Grab 14 pixels from the next four characters
uint32_t bits_a = char_text_bits(line_buf_a[col], glyph_line);
uint32_t bits_b = char_text_bits(line_buf_b[col], glyph_line);
col++;
uint32_t bits = (bits_b << 7) | bits_a;
// Translate each pair of bits into a pair of pixels
for(int i=0; i < 7; i++) {
uint32_t pixeldata = (bits & 0x2000) ? (text_back) : (text_fore);
pixeldata |= (bits & 0x1000) ?
((uint32_t)text_back) << 16 :
((uint32_t)text_fore) << 16;
bits <<= 2;
sl->data[sl_pos] = pixeldata;
sl_pos++;
}
}
// 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
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_3) << 16); // 8 pixels per word
sl->length = sl_pos;
sl->repeat_count = 1;
vga_submit_scanline(sl);
}
}