/* * reinette, a french Apple II emulator, using SDL2 * and powered by puce65c02 - a WDS 65c02 cpu emulator by the same author * Last modified 1st of July 2021 * Copyright (c) 2021 Arthur Ferreira (arthur.ferreira2@gmail.com) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include "reinette.h" Video::Video() { memset(ramHeatmap, 0xFF000000, sizeof(ramHeatmap)); memset(auxHeatmap, 0xFF000000, sizeof(auxHeatmap)); // array from https://github.com/Michaelangel007/apple2_hgr_font_tutorial/ const char FONT[] = { 0x10, 0x08, 0x36, 0x7F, 0x3F, 0x3F, 0x7E, 0x36, // 0x00 ^@ 0x10, 0x08, 0x36, 0x41, 0x21, 0x21, 0x4A, 0x36, // 0x01 ^A 0x00, 0x00, 0x02, 0x06, 0x0E, 0x1E, 0x36, 0x42, // 0x02 ^B 0x7F, 0x22, 0x14, 0x08, 0x08, 0x14, 0x2A, 0x7F, // 0x03 ^C 0x00, 0x40, 0x20, 0x11, 0x0A, 0x04, 0x04, 0x00, // 0x04 ^D 0x7F, 0x3F, 0x5F, 0x6C, 0x75, 0x7B, 0x7B, 0x7F, // 0x05 ^E 0x70, 0x60, 0x7E, 0x31, 0x79, 0x30, 0x3F, 0x02, // 0x06 ^F 0x00, 0x18, 0x07, 0x00, 0x07, 0x0C, 0x08, 0x70, // 0x07 ^G 0x08, 0x04, 0x02, 0x7F, 0x02, 0x04, 0x08, 0x00, // 0x08 ^H 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, // 0x09 ^I 0x08, 0x08, 0x08, 0x08, 0x49, 0x2A, 0x1C, 0x08, // 0x0A ^J 0x08, 0x1C, 0x2A, 0x49, 0x08, 0x08, 0x08, 0x08, // 0x0B ^K 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0C ^L 0x40, 0x40, 0x40, 0x44, 0x46, 0x7F, 0x06, 0x04, // 0x0D ^M 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, // 0x0E ^N 0x13, 0x18, 0x1C, 0x7E, 0x1C, 0x18, 0x10, 0x6F, // 0x0F ^O 0x64, 0x0C, 0x1C, 0x3F, 0x1C, 0x0C, 0x04, 0x7B, // 0x10 ^P 0x40, 0x48, 0x08, 0x7F, 0x3E, 0x1C, 0x48, 0x40, // 0x11 ^Q 0x40, 0x48, 0x1C, 0x3E, 0x7E, 0x08, 0x48, 0x40, // 0x12 ^R 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, // 0x13 ^S 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x7F, // 0x14 ^T 0x08, 0x10, 0x20, 0x7F, 0x20, 0x10, 0x08, 0x00, // 0x15 ^U 0x2A, 0x55, 0x2A, 0x55, 0x2A, 0x55, 0x2A, 0x55, // 0x16 ^V 0x55, 0x2A, 0x55, 0x2A, 0x55, 0x2A, 0x55, 0x2A, // 0x17 ^W 0x00, 0x3E, 0x41, 0x01, 0x01, 0x01, 0x7F, 0x00, // 0x18 ^X 0x00, 0x00, 0x3F, 0x40, 0x40, 0x40, 0x7F, 0x00, // 0x19 ^Y 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, // 0x1A ^Z 0x08, 0x1C, 0x3E, 0x7F, 0x3E, 0x1C, 0x08, 0x00, // 0x1B ^[ 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, // 0x1C ^ 0x14, 0x14, 0x77, 0x00, 0x77, 0x14, 0x14, 0x00, // 0x1D ^] 0x7F, 0x40, 0x40, 0x4C, 0x4C, 0x40, 0x40, 0x7F, // 0x1E ^^ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, // 0x1F ^_ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x20 space 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x08, 0x00, // 0x21 ! 0x14, 0x14, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x22 " 0x14, 0x14, 0x3E, 0x14, 0x3E, 0x14, 0x14, 0x00, // 0x23 # 0x08, 0x3C, 0x0A, 0x1C, 0x28, 0x1E, 0x08, 0x00, // 0x24 $ 0x06, 0x26, 0x10, 0x08, 0x04, 0x32, 0x30, 0x00, // 0x25 % 0x04, 0x0A, 0x0A, 0x04, 0x2A, 0x12, 0x2C, 0x00, // 0x26 & 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x27 ' 0x08, 0x04, 0x02, 0x02, 0x02, 0x04, 0x08, 0x00, // 0x28 ( 0x08, 0x10, 0x20, 0x20, 0x20, 0x10, 0x08, 0x00, // 0x29 ) 0x08, 0x2A, 0x1C, 0x08, 0x1C, 0x2A, 0x08, 0x00, // 0x2A * 0x00, 0x08, 0x08, 0x3E, 0x08, 0x08, 0x00, 0x00, // 0x2B + 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x04, 0x00, // 0x2C , 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, // 0x2D - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, // 0x2E . 0x00, 0x20, 0x10, 0x08, 0x04, 0x02, 0x00, 0x00, // 0x2F / 0x1C, 0x22, 0x32, 0x2A, 0x26, 0x22, 0x1C, 0x00, // 0x30 0 0x08, 0x0C, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, // 0x31 1 0x1C, 0x22, 0x20, 0x18, 0x04, 0x02, 0x3E, 0x00, // 0x32 2 0x3E, 0x20, 0x10, 0x18, 0x20, 0x22, 0x1C, 0x00, // 0x33 3 0x10, 0x18, 0x14, 0x12, 0x3E, 0x10, 0x10, 0x00, // 0x34 4 0x3E, 0x02, 0x1E, 0x20, 0x20, 0x22, 0x1C, 0x00, // 0x35 5 0x38, 0x04, 0x02, 0x1E, 0x22, 0x22, 0x1C, 0x00, // 0x36 6 0x3E, 0x20, 0x10, 0x08, 0x04, 0x04, 0x04, 0x00, // 0x37 7 0x1C, 0x22, 0x22, 0x1C, 0x22, 0x22, 0x1C, 0x00, // 0x38 8 0x1C, 0x22, 0x22, 0x3C, 0x20, 0x10, 0x0E, 0x00, // 0x39 9 0x00, 0x00, 0x08, 0x00, 0x08, 0x00, 0x00, 0x00, // 0x3A : 0x00, 0x00, 0x08, 0x00, 0x08, 0x08, 0x04, 0x00, // 0x3B ; 0x10, 0x08, 0x04, 0x02, 0x04, 0x08, 0x10, 0x00, // 0x3C < 0x00, 0x00, 0x3E, 0x00, 0x3E, 0x00, 0x00, 0x00, // 0x3D = 0x04, 0x08, 0x10, 0x20, 0x10, 0x08, 0x04, 0x00, // 0x3E > 0x1C, 0x22, 0x10, 0x08, 0x08, 0x00, 0x08, 0x00, // 0x3F ? 0x1C, 0x22, 0x2A, 0x3A, 0x1A, 0x02, 0x3C, 0x00, // 0x40 @ 0x08, 0x14, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x00, // 0x41 A 0x1E, 0x22, 0x22, 0x1E, 0x22, 0x22, 0x1E, 0x00, // 0x42 B 0x1C, 0x22, 0x02, 0x02, 0x02, 0x22, 0x1C, 0x00, // 0x43 C 0x1E, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1E, 0x00, // 0x44 D 0x3E, 0x02, 0x02, 0x1E, 0x02, 0x02, 0x3E, 0x00, // 0x45 E 0x3E, 0x02, 0x02, 0x1E, 0x02, 0x02, 0x02, 0x00, // 0x46 F 0x3C, 0x02, 0x02, 0x02, 0x32, 0x22, 0x3C, 0x00, // 0x47 G 0x22, 0x22, 0x22, 0x3E, 0x22, 0x22, 0x22, 0x00, // 0x48 H 0x1C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, // 0x49 I 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x1C, 0x00, // 0x4A J 0x22, 0x12, 0x0A, 0x06, 0x0A, 0x12, 0x22, 0x00, // 0x4B K 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x3E, 0x00, // 0x4C L 0x22, 0x36, 0x2A, 0x2A, 0x22, 0x22, 0x22, 0x00, // 0x4D M 0x22, 0x22, 0x26, 0x2A, 0x32, 0x22, 0x22, 0x00, // 0x4E N 0x1C, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, // 0x4F O 0x1E, 0x22, 0x22, 0x1E, 0x02, 0x02, 0x02, 0x00, // 0x50 P 0x1C, 0x22, 0x22, 0x22, 0x2A, 0x12, 0x2C, 0x00, // 0x51 Q 0x1E, 0x22, 0x22, 0x1E, 0x0A, 0x12, 0x22, 0x00, // 0x52 R 0x1C, 0x22, 0x02, 0x1C, 0x20, 0x22, 0x1C, 0x00, // 0x53 S 0x3E, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, // 0x54 T 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x1C, 0x00, // 0x55 U 0x22, 0x22, 0x22, 0x22, 0x22, 0x14, 0x08, 0x00, // 0x56 V 0x22, 0x22, 0x22, 0x2A, 0x2A, 0x36, 0x22, 0x00, // 0x57 W 0x22, 0x22, 0x14, 0x08, 0x14, 0x22, 0x22, 0x00, // 0x58 X 0x22, 0x22, 0x14, 0x08, 0x08, 0x08, 0x08, 0x00, // 0x59 Y 0x3E, 0x20, 0x10, 0x08, 0x04, 0x02, 0x3E, 0x00, // 0x5A Z 0x3E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x3E, 0x00, // 0x5B [ 0x00, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00, 0x00, // 0x5C back slash 0x3E, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3E, 0x00, // 0x5D ] 0x00, 0x00, 0x08, 0x14, 0x22, 0x00, 0x00, 0x00, // 0x5E ^ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, // 0x5F _ 0x04, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x60 ` 0x00, 0x00, 0x1C, 0x20, 0x3C, 0x22, 0x3C, 0x00, // 0x61 a 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x1E, 0x00, // 0x62 b 0x00, 0x00, 0x3C, 0x02, 0x02, 0x02, 0x3C, 0x00, // 0x63 c 0x20, 0x20, 0x3C, 0x22, 0x22, 0x22, 0x3C, 0x00, // 0x64 d 0x00, 0x00, 0x1C, 0x22, 0x3E, 0x02, 0x3C, 0x00, // 0x65 e 0x18, 0x24, 0x04, 0x1E, 0x04, 0x04, 0x04, 0x00, // 0x66 f 0x00, 0x00, 0x1C, 0x22, 0x22, 0x3C, 0x20, 0x1C, // 0x67 g 0x02, 0x02, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x00, // 0x68 h 0x08, 0x00, 0x0C, 0x08, 0x08, 0x08, 0x1C, 0x00, // 0x69 i 0x10, 0x00, 0x18, 0x10, 0x10, 0x10, 0x12, 0x0C, // 0x6A j 0x02, 0x02, 0x22, 0x12, 0x0E, 0x12, 0x22, 0x00, // 0x6B k 0x0C, 0x08, 0x08, 0x08, 0x08, 0x08, 0x1C, 0x00, // 0x6C l 0x00, 0x00, 0x36, 0x2A, 0x2A, 0x2A, 0x22, 0x00, // 0x6D m 0x00, 0x00, 0x1E, 0x22, 0x22, 0x22, 0x22, 0x00, // 0x6E n 0x00, 0x00, 0x1C, 0x22, 0x22, 0x22, 0x1C, 0x00, // 0x6F o 0x00, 0x00, 0x1E, 0x22, 0x22, 0x1E, 0x02, 0x02, // 0x70 p 0x00, 0x00, 0x3C, 0x22, 0x22, 0x3C, 0x20, 0x20, // 0x71 q 0x00, 0x00, 0x3A, 0x06, 0x02, 0x02, 0x02, 0x00, // 0x72 r 0x00, 0x00, 0x3C, 0x02, 0x1C, 0x20, 0x1E, 0x00, // 0x73 s 0x04, 0x04, 0x1E, 0x04, 0x04, 0x24, 0x18, 0x00, // 0x74 t 0x00, 0x00, 0x22, 0x22, 0x22, 0x32, 0x2C, 0x00, // 0x75 u 0x00, 0x00, 0x22, 0x22, 0x22, 0x14, 0x08, 0x00, // 0x76 v 0x00, 0x00, 0x22, 0x22, 0x2A, 0x2A, 0x36, 0x00, // 0x77 w 0x00, 0x00, 0x22, 0x14, 0x08, 0x14, 0x22, 0x00, // 0x78 x 0x00, 0x00, 0x22, 0x22, 0x22, 0x3C, 0x20, 0x1C, // 0x79 y 0x00, 0x00, 0x3E, 0x10, 0x08, 0x04, 0x3E, 0x00, // 0x7A z 0x38, 0x0C, 0x0C, 0x06, 0x0C, 0x0C, 0x38, 0x00, // 0x7B { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, // 0x7C | 0x0E, 0x18, 0x18, 0x30, 0x18, 0x18, 0x0E, 0x00, // 0x7D } 0x2C, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x7E ~ 0x00, 0x2A, 0x14, 0x2A, 0x14, 0x2A, 0x00, 0x00 // 0x7F DEL }; // Generate the font arrays from the above array int raw; for (int c=0; c<128; c++) { for (int y=0; y<8; y++) { raw = FONT[c*8 + y]; for (int x=0; x<7; x++) { if (raw & (1<TEXT && mmu->HIRES && !mmu->DHIRES) { gfxmode = 40; uint8_t colorIdx; // index to the color array uint16_t word; uint8_t bits[16], bit, pbit, colorSet, even; int vRamBase = 0x2000 + mmu->PAGE2 * 0x2000; int endRaw = mmu->MIXED ? 160 : 192; for (int line = 0; line < endRaw; line++) { // for every line for (int col = 0; col < 40; col += 2) { // for every 7 horizontal dots word = (uint16_t)(mmu->ram[vRamBase + offsetHGR[line] + col + 1]) << 8; // store the two next bytes into 'word' word += mmu->ram[vRamBase + offsetHGR[line] + col]; // in reverse order if (previousDots[line][col] != word) { // check if this group of 7 dots need a redraw int x = col * 7; even = 0; for (bit = 0; bit < 16; bit++) // store all bits 'word' into 'bits' bits[bit] = (word >> bit) & 1; colorSet = bits[7] * 4; // select the right color set pbit = previousBit[line][col]; // the bit value of the left dot bit = 0; // starting at 1st bit of 1st byte while (bit < 15) { // until we reach bit7 of 2nd byte if (bit == 7) { // moving into the second byte colorSet = bits[15] * 4; // update the color set bit++; // skip bit 7 } colorIdx = even + colorSet + (bits[bit] << 1) + (pbit); screenPixels[line * 280 + x] = hcolor[colorIdx]; x++; pbit = bits[bit++]; // proceed to the next pixel even = even ? 0 : 8; // one pixel every two is darker } previousDots[line][col] = word; // update the video cache if ((col < 37) && (previousBit[line][col + 2] != pbit)) { // check color franging effect on the dot after previousBit[line][col + 2] = pbit; // set pbit and clear the previousDots[line][col + 2] = -1; // video cache for next dot } } // if (previousDots[line][col] ... } } } // DOUBLE HIGH RES GRAPHICS else if (!mmu->TEXT && mmu->HIRES && mmu->DHIRES) { gfxmode = 80; // int vRamBase = 0x2000 + PAGE2 * 0x2000; int vRamBase = mmu->STORE80 ? 0x2000 : mmu->PAGE2 * 0x2000 + 0x2000; // TODO : CHECK THIS ! int endRaw = mmu->MIXED ? 160 : 192; uint32_t color; uint32_t dword; for (int line = 0; line < endRaw; line++) { for (int col = 0, x = 0; col < 39; col+=2) { int address = vRamBase + offsetHGR[line] + col; // combine 4 bytes of memory, from MAIN and AUX dword = (uint32_t)(mmu->aux[address] & 0x7F); dword |= (uint32_t)(mmu->ram[address] & 0x7F) << 7; address++; dword |= (uint32_t)(mmu->aux[address] & 0x7F) << 14; dword |= (uint32_t)(mmu->ram[address] & 0x7F) << 21; int offset1 = 560 * line * 2; int offset2 = 560 + offset1; for (int p=0; p<7; p++, dword >>= 4) { color = dhcolor[dword & 0x0F]; for (int a=0; a<4; a++, x++) { // draw 4x2 pixels screenPixels80[offset1 + x] = color; screenPixels80[offset2 + x] = color; } } } } } // lOW RES GRAPHICS else if (!mmu->TEXT && !mmu->HIRES && !mmu->DHIRES) { // and not in HIRES gfxmode = 40; int vRamBase = mmu->PAGE2 * 0x0400 + 0x400; int endRaw = mmu->MIXED ? 20 : 24; uint32_t color; for (int col = 0; col < 40; col++) { // for each column int x = col * 7; for (int line = 0; line < endRaw; line++) { // for each row glyph = mmu->ram[vRamBase + offsetGR[line] + col]; // read video memory if (previousBlocks[line][col] != glyph) { // check if this block need a redraw previousBlocks[line][col] = glyph; int y = line * 8; // first block color = lcolor[glyph & 0x0F]; // first nibble for (int a=0; a<7; a++) for (int b=0; b<4; b++) screenPixels[(y+b) * 280 + x + a] = color; y += 4; // second block color = lcolor[(glyph & 0xF0) >> 4]; // second nibble for (int a=0; a<7; a++) for (int b=0; b<4; b++) screenPixels[(y+b) * 280 + x + a] = color; } } } } // DOUBLE lOW RES GRAPHICS else if (!mmu->TEXT && !mmu->HIRES && mmu->DHIRES) { gfxmode = 80; int vRamBase = mmu->PAGE2 * 0x0400 + 0x400; // TODO : CHECK THIS ! int endRaw = mmu->MIXED ? 20 : 24; int x, y; uint32_t color; for (int col = 0; col < 40; col++) { // for each column x = col * 14; for (int line = 0; line < endRaw; line++) { // for each row y = line * 16; // first block glyph = mmu->aux[vRamBase + offsetGR[line] + col]; // read AUX video memory color = lcolor[glyph & 0x0F]; // first nibble for (int a=0; a<7; a++) for (int b=0; b<8; b++) screenPixels80[(y+b) * 560 + (x+a)] = color; color = lcolor[(glyph & 0xF0) >> 4]; // second nibble for (int a=0; a<7; a++) for (int b=8; b<16; b++) screenPixels80[(y+b) * 560 + (x+a)] = color; glyph = mmu->ram[vRamBase + offsetGR[line] + col]; // read MAIN video memory color = lcolor[glyph & 0x0F]; // first nibble for (int a=7; a<14; a++) for (int b=0; b<8; b++) screenPixels80[(y+b) * 560 + (x+a)] = color; color = lcolor[(glyph & 0xF0) >> 4]; // second nibble for (int a=7; a<14; a++) for (int b=8; b<16; b++) screenPixels80[(y+b) * 560 + (x+a)] = color; } } } // TEXT 40 COLUMNS if (!mmu->COL80 && (mmu->TEXT || mmu->MIXED)) { gfxmode = 40; int vRamBase = mmu->PAGE2 * 0x0400 + 0x400; int startRaw = mmu->TEXT ? 0 : 20; for (int col = 0; col < 40; col++) { // for each column for (int line = startRaw; line < 24; line++) { // for each row glyph = mmu->ram[vRamBase + offsetGR[line] + col]; // read video memory if (glyph > 0x7F) glyphAttr = A_NORMAL; // is NORMAL ? else if (glyph < 0x40) glyphAttr = A_INVERSE; // is INVERSE ? else glyphAttr = A_FLASH; // it's FLASH ! if (previousChars[line][col] != (glyph <<(8*mmu->ALTCHARSET)) || glyphAttr == A_FLASH) { // check if this char need a redraw previousChars[line][col] = (glyph <<(8*mmu->ALTCHARSET)); glyph &= 0x7F; // unset bit 7 if (glyph < 0x20) glyph |= 0x40; // shifts to match the ASCII codes if ((!mmu->ALTCHARSET) && (glyphAttr == A_FLASH) && (glyph > 0x5F)) glyph &= 0x3F; if (mmu->ALTCHARSET && (glyphAttr == A_FLASH)) { if (glyph >= 0x40 && glyph < 0x60) { glyph &= 0x3F; glyphAttr = A_NORMAL; } else if (glyph >= 0x60) glyphAttr = A_INVERSE; } if (glyphAttr == A_NORMAL || ((glyphAttr == A_FLASH) && (gui->frameNumber % 30 < 16))) { for (int yy=0; yy<8; yy++) for (int xx=0; xx<7; xx++) screenPixels[(yy+line*8)*280 + (xx+(col*7))] = fontNormal[glyph][yy][xx]; } else { for (int yy=0; yy<8; yy++) for (int xx=0; xx<7; xx++) screenPixels[(yy+line*8)*280 + (xx+(col*7))] = fontInverse[glyph][yy][xx]; } } } } } // TEXT 80 COLUMNS else if (mmu->COL80 && (mmu->TEXT || mmu->MIXED)) { gfxmode = 80; int startRaw = mmu->TEXT ? 0 : 20; int vRamBase = 0x0400; // + PAGE2 * 0x400; for (int col = 0; col < 40; col++) { // for each column for (int line = startRaw; line < 24; line++) { // for each row glyph = mmu->ram[vRamBase + offsetGR[line] + col]; // read video memory in Main memory if (glyph > 0x7F) glyphAttr = A_NORMAL; // is NORMAL ? else if (glyph < 0x40) glyphAttr = A_INVERSE; // is INVERSE ? else glyphAttr = A_FLASH; // it's FLASH ! if (previousChars[line][col] != (glyph <<(8*mmu->ALTCHARSET)) || glyphAttr == A_FLASH) { // check if this char need a redraw previousChars[line][col] = (glyph <<(8*mmu->ALTCHARSET)); glyph &= 0x7F; // unset bit 7 if (glyph < 0x20) glyph |= 0x40; // shifts to match the ASCII codes if ((!mmu->ALTCHARSET) && (glyphAttr == A_FLASH) && (glyph > 0x5F)) glyph &= 0x3F; if (mmu->ALTCHARSET && (glyphAttr == A_FLASH)) { if (glyph >= 0x40 && glyph < 0x60) { glyph &= 0x3F; glyphAttr = A_NORMAL; } else if (glyph >= 0x60) glyphAttr = A_INVERSE; } if (glyphAttr == A_NORMAL || ((glyphAttr == A_FLASH) && (gui->frameNumber % 30 < 16))) { for (int yy=0; yy<16; yy+=2) for (int xx=0; xx<7; xx++){ screenPixels80[(yy+line*16)*560 + (xx+(((col*2)+1)*7))] = fontNormal[glyph][yy/2][xx]; screenPixels80[(1+yy+line*16)*560 + (xx+(((col*2)+1)*7))] = fontNormal[glyph][yy/2][xx]; } } else { for (int yy=0; yy<16; yy+=2) for (int xx=0; xx<7; xx++){ screenPixels80[(yy+line*16)*560 + (xx+(((col*2)+1)*7))] = fontInverse[glyph][yy/2][xx]; screenPixels80[(1+yy+line*16)*560 + (xx+(((col*2)+1)*7))] = fontInverse[glyph][yy/2][xx]; } } } glyph = mmu->aux[vRamBase + offsetGR[line] + col]; // read video memory in Auxiliary Memory if (glyph > 0x7F) glyphAttr = A_NORMAL; // is NORMAL ? else if (glyph < 0x40) glyphAttr = A_INVERSE; // is INVERSE ? else glyphAttr = A_FLASH; // it's FLASH ! if (previousChars80[line][col] != (glyph <<(8*mmu->ALTCHARSET)) || glyphAttr == A_FLASH) { // check if this char need a redraw previousChars80[line][col] = (glyph <<(8*mmu->ALTCHARSET)); glyph &= 0x7F; // unset bit 7 if (glyph < 0x20) glyph |= 0x40; // shifts to match the ASCII codes if ((!mmu->ALTCHARSET) && (glyphAttr == A_FLASH) && (glyph > 0x5F)) glyph &= 0x3F; if (mmu->ALTCHARSET && (glyphAttr == A_FLASH)) { if (glyph >= 0x40 && glyph < 0x60) { glyph &= 0x3F; glyphAttr = A_NORMAL; } else if (glyph >= 0x60) glyphAttr = A_INVERSE; } if (glyphAttr == A_NORMAL || ((glyphAttr == A_FLASH) && (gui->frameNumber % 30 < 16))) { for (int yy=0; yy<16; yy+=2) for (int xx=0; xx<7; xx++) { screenPixels80[(yy+line*16)*560 + (xx+(col*2*7))] = fontNormal[glyph][yy/2][xx]; screenPixels80[(1+yy+line*16)*560 + (xx+(col*2*7))] = fontNormal[glyph][yy/2][xx]; } } else { for (int yy=0; yy<16; yy+=2) for (int xx=0; xx<7; xx++) { screenPixels80[(yy+line*16)*560 + (xx+(col*2*7))] = fontInverse[glyph][yy/2][xx]; screenPixels80[(1+yy+line*16)*560 + (xx+(col*2*7))] = fontInverse[glyph][yy/2][xx]; } } } } } } // update ram & aux HEATMAPS (fade out green and red components) for (int address=0; address<65536; address++){ if (ramHeatmap[address] & 0x000000FF) ramHeatmap[address]--; if (ramHeatmap[address] & 0x0000FF00) ramHeatmap[address] -= 256; if (auxHeatmap[address] & 0x000000FF) auxHeatmap[address]--; if (auxHeatmap[address] & 0x0000FF00) auxHeatmap[address] -= 256; } }