MOS6561.cpp
1 #include "MOS6561.h" 2 3 extern "C" { 4 #include "emuapi.h" 5 #include "platform_config.h" 6 } 7 8 typedef uint16_t Pixel; 9 10 #define WIN_W 320 //TFT_WIDTH 11 #define WIN_H 240 //TFT_HEIGHT 12 13 14 // definitions for easy access to registers 15 #define REG_COLPAGE_BASE (0x9400) // 0x9400+0x200 = 0x9600 unexpanded 16 17 #define REG_SCRPAGE_LO ((readWord(0x9002) & 0x80)<< 2) // 0x0200 18 #define REG_COLPAGE_LO ((readWord(0x9002) & 0x80)<< 2) // 0x0200 19 #define REG_NB_COLUMNS (readWord(0x9002) & 0x7F) 20 21 #define REG_RASTER_LO_RD (0x9003) // 0x80 >> 7 22 #define REG_DOUBLE_HEIGHT (readWord(0x9003) & 0x01) 23 #define REG_NB_ROWS ((readWord(0x9003) & 0x7E) >> 1) //x2 => 46 24 25 #define REG_RASTER_HI_RD (0x9004) // << 1 // 1E00 unexpanded? 26 27 #define REG_CHRMAP_PT (readWord(0x9005) & 0x0F) 28 #define REG_SCRPAGE_HIGH ((readWord(0x9005) & 0xF0) << 6) // 0x2C00 29 30 #define REG_AUXILIARY_COLOUR ((readWord(0x900E) & 0xF0) >> 4) 31 #define REG_BORDER_COLOUR (readWord(0x900F) & 0x7) 32 #define REG_BACKGROUND_COLOUR ((readWord(0x900F) & 0xF0) >> 4) 33 34 #define REG_HORI_OFFSET ((readWord(0x9000) & 0x7F) 35 #define REG_VERT_OFFSET ((readWord(0x9001) & 0xFF) 36 37 // C R H V 38 // 16 16 +6 +16 39 // 22 23 0 0 40 // 24 28 -3 -9 41 // 25 30 -3 -12 42 // 27 33 -5 -19 43 44 45 static Pixel vicPalette[16]; 46 static uint16_t remap[16] = { 47 0x8000, 48 0x8400, 49 0x8800, 50 0x8c00, 51 0x0000, 52 0x0000, 53 0x0000, 54 0x0000, 55 0x0000, 56 0x0000, 57 0x0000, 58 0x0000, 59 0x1000, 60 0x1400, 61 0x1800, 62 0x1c00 63 }; 64 65 static Pixel linebuf[WIN_W]; 66 67 MOS6561::MOS6561() : IC(), curRow(0), frameReady(true) { 68 // Set clock speed 69 this->setClockSpeed(1108000); 70 } 71 72 MOS6561::~MOS6561() { 73 } 74 75 void MOS6561::initialize() { 76 // Initialize VIC palette 77 vicPalette[0] = RGBVAL16(0, 0, 0); 78 vicPalette[1] = RGBVAL16(255, 255, 255); 79 vicPalette[2] = RGBVAL16((182), (31), (33)); 80 vicPalette[3] = RGBVAL16((77), (240), (255)); 81 vicPalette[4] = RGBVAL16((180), (63), (255)); 82 vicPalette[5] = RGBVAL16((68), (226), (55)); 83 vicPalette[6] = RGBVAL16((15), (87), (247)); 84 vicPalette[7] = RGBVAL16((220), (215), (27)); 85 vicPalette[8] = RGBVAL16((202), (84), (0)); 86 vicPalette[9] = RGBVAL16((233), (176), (114)); 87 vicPalette[10] = RGBVAL16((231), (146), (147)); 88 vicPalette[11] = RGBVAL16((154), (247), (253)); 89 vicPalette[12] = RGBVAL16((224), (159), (255)); 90 vicPalette[13] = RGBVAL16((143), (228), (147)); 91 vicPalette[14] = RGBVAL16((130), (144), (255)); 92 vicPalette[15] = RGBVAL16((229), (222), (133)); 93 } 94 95 96 void MOS6561::renderBorder(uint16_t raster){ 97 if (raster < WIN_H) { 98 Pixel bcol = vicPalette[REG_BORDER_COLOUR]; 99 Pixel * dst = &linebuf[0]; 100 for (int x=0; x < WIN_W; x++) { 101 *dst++ = bcol; 102 } 103 emu_DrawLine16(&linebuf[0], WIN_W, WIN_H, raster); 104 } 105 } 106 107 108 void MOS6561::renderLine(uint16_t raster, uint16_t row, uint8_t rowHeight, uint8_t chrLine) { 109 int curRow = row; 110 int nbRow = REG_NB_ROWS; 111 112 if ( (rowHeight == 16) && (nbRow >= 23) ) { 113 curRow = curRow/2; 114 nbRow = nbRow/2; 115 } 116 117 if (raster < WIN_H) 118 { 119 int nbCol = REG_NB_COLUMNS; 120 int bWidth = (WIN_W - nbCol*8)/2; 121 #define bakcol 0 122 #define borcol 1 123 #define forcol 2 124 #define auxcol 3 125 Pixel cols[4]; 126 uint16_t screenPage = (REG_SCRPAGE_HIGH & ~0x2000) + REG_SCRPAGE_LO; 127 uint8_t * charPointer = &vicmemory[screenPage + (curRow * nbCol)]; 128 uint16_t colourPage = REG_COLPAGE_BASE + REG_COLPAGE_LO; 129 uint8_t * colPointer = &vicmemory[colourPage + (curRow * nbCol)]; 130 131 cols[borcol] = vicPalette[REG_BORDER_COLOUR]; 132 cols[bakcol] = vicPalette[REG_BACKGROUND_COLOUR]; 133 134 // Border Left 135 Pixel * dst = &linebuf[0]; 136 for (int x=0; x < bWidth; x++) { 137 *dst++ = cols[borcol]; 138 } 139 140 uint16_t chardefbase = remap[REG_CHRMAP_PT]; 141 for (int x = 0; x < nbCol; x +=1) 142 { 143 uint16_t charpt = chardefbase + charPointer[x]*rowHeight + chrLine; 144 if ( (charpt > 0x2000) && (charpt < 0x3000) ) 145 charpt += 0x6000; 146 uint8_t characterByte = vicmemory[charpt]; 147 uint8_t colour = colPointer[x] & 0x7; 148 uint8_t multiColour = colPointer[x] & 0x8; 149 cols[forcol] = vicPalette[colour]; 150 if (!multiColour) { 151 Pixel * dest = dst; 152 for (int a = 0; a < 8; a++) { 153 if ((characterByte << a) & 0x80) { 154 *dest++ = cols[forcol]; 155 } 156 else { 157 *dest++ = cols[bakcol]; 158 } 159 } 160 } 161 else { 162 Pixel * dest = dst; 163 cols[auxcol] = vicPalette[REG_AUXILIARY_COLOUR]; 164 for (int a = 0; a < 8; a += 2) { 165 // Set colour 166 Pixel col = cols[((characterByte << a) & 0xC0) >> 6]; 167 *dest++ = col; 168 *dest++ = col; 169 } 170 } 171 dst +=8; 172 } 173 174 // Border Right 175 for (int x=0; x < bWidth; x++) { 176 *dst++ = cols[borcol]; 177 } 178 emu_DrawLine16(&linebuf[0], WIN_W, 1, raster); 179 } 180 } 181 182 void MOS6561::renderRow(uint16_t raster, uint16_t row, uint8_t rowHeight) 183 { 184 int curRow = row; 185 int nbRow = REG_NB_ROWS; 186 187 //printf("rows %d of %d, cols=%d, rowHeight:%d\n", curRow, nbRow, nbCol, rowHeight); 188 189 if ( (rowHeight == 16) && (nbRow >= 23) ) { 190 curRow = curRow/2; 191 nbRow = nbRow/2; 192 } 193 194 if ((raster+curRow*rowHeight) < WIN_H) 195 { 196 int nbCol = REG_NB_COLUMNS; 197 int bWidth = (WIN_W - nbCol*8)/2; 198 #define bakcol 0 199 #define borcol 1 200 #define forcol 2 201 #define auxcol 3 202 Pixel cols[4]; 203 uint16_t screenPage = (REG_SCRPAGE_HIGH & ~0x2000) + REG_SCRPAGE_LO; 204 uint8_t * charPointer = &vicmemory[screenPage + (curRow * nbCol)]; 205 uint16_t colourPage = REG_COLPAGE_BASE + REG_COLPAGE_LO; 206 uint8_t * colPointer = &vicmemory[colourPage + (curRow * nbCol)]; 207 208 cols[borcol] = vicPalette[REG_BORDER_COLOUR]; 209 cols[bakcol] = vicPalette[REG_BACKGROUND_COLOUR]; 210 211 for (int line=0; line < rowHeight; line++) { 212 // Border Left 213 Pixel * dst = &linebuf[0]; 214 for (int x=0; x < bWidth; x++) { 215 *dst++ = cols[borcol]; 216 } 217 218 uint16_t chardefbase = remap[REG_CHRMAP_PT]; 219 for (int x = 0; x < nbCol; x +=1) 220 { 221 uint16_t charpt = chardefbase + charPointer[x]*rowHeight + line; 222 if ( (charpt > 0x2000) && (charpt < 0x3000) ) 223 charpt += 0x6000; 224 uint8_t characterByte = vicmemory[charpt]; 225 uint8_t colour = colPointer[x] & 0x7; 226 uint8_t multiColour = colPointer[x] & 0x8; 227 cols[forcol] = vicPalette[colour]; 228 if (!multiColour) { 229 Pixel * dest = dst; 230 for (int a = 0; a < 8; a++) { 231 if ((characterByte << a) & 0x80) { 232 *dest++ = cols[forcol]; 233 } 234 else { 235 *dest++ = cols[bakcol]; 236 } 237 } 238 } 239 else { 240 Pixel * dest = dst; 241 cols[auxcol] = vicPalette[REG_AUXILIARY_COLOUR]; 242 for (int a = 0; a < 8; a += 2) { 243 // Set colour 244 Pixel col = cols[((characterByte << a) & 0xC0) >> 6]; 245 *dest++ = col; 246 *dest++ = col; 247 } 248 } 249 dst +=8; 250 } 251 252 // Border Right 253 for (int x=0; x < bWidth; x++) { 254 *dst++ = cols[borcol]; 255 } 256 emu_DrawLine16(&linebuf[0], WIN_W, 1, curRow*rowHeight+line+raster); 257 } 258 } 259 } 260 261 262 void MOS6561::renderFrame() { 263 this->frameReady = false; 264 } 265 266 bool MOS6561::isFrameReady() { 267 return this->frameReady; 268 } 269 270 271 void MOS6561::tick(int nbcycles) { 272 // Increment raster counter 273 uint16_t raster = readWord(REG_RASTER_HI_RD) << 1 | readWord(REG_RASTER_LO_RD) >> 7; 274 if (cycles % cyclesPerScanline == 0) { 275 uint8_t rowHeight = (REG_DOUBLE_HEIGHT?16:8); 276 if (raster <= lastScanline) { 277 raster += 1; 278 } 279 else { 280 raster = 0; 281 visScanlines = REG_NB_ROWS * rowHeight; 282 if ( (rowHeight == 16) && (REG_NB_ROWS >= 23) ) { 283 visScanlines = REG_NB_ROWS * 8; 284 } 285 firstVisibleScanline = (WIN_H-visScanlines)/2; 286 } 287 writeWord(REG_RASTER_LO_RD, (raster & 0x1) << 7 | (readWord(REG_RASTER_LO_RD) & 0x7F)); 288 writeWord(REG_RASTER_HI_RD, raster >> 1); 289 290 if (visScanlines) { 291 if (raster < firstVisibleScanline) { 292 renderBorder(raster); 293 } 294 // If not blanking, update screen 295 else if (raster >= firstVisibleScanline && raster < (firstVisibleScanline+visScanlines)) { 296 // Update video 297 uint8_t chrLine = (raster-firstVisibleScanline) % rowHeight; 298 //renderLine(raster, this->curRow, rowHeight, chrLine); 299 if (chrLine == rowHeight-1) { 300 renderRow(firstVisibleScanline, this->curRow, rowHeight); 301 this->curRow++; 302 // End of screen reached 303 if (this->curRow >= REG_NB_ROWS) { 304 this->curRow = 0; 305 frameReady = true; 306 } 307 } 308 } 309 else { 310 renderBorder(raster); 311 } 312 } 313 } 314 315 // Increment cycle counter 316 this->cycles += nbcycles; 317 }