2021-11-04 14:17:40 +00:00
|
|
|
#include "MOS6561.h"
|
|
|
|
|
|
|
|
extern "C" {
|
|
|
|
#include "emuapi.h"
|
|
|
|
#include "platform_config.h"
|
|
|
|
}
|
|
|
|
|
2023-04-20 21:21:15 +00:00
|
|
|
#include "pico_dsp.h"
|
2021-11-04 14:17:40 +00:00
|
|
|
typedef uint16_t Pixel;
|
|
|
|
|
|
|
|
#define WIN_W TFT_WIDTH
|
|
|
|
#define WIN_H TFT_HEIGHT
|
|
|
|
|
|
|
|
|
|
|
|
// definitions for easy access to registers
|
|
|
|
#define REG_COLPAGE_BASE (0x9400) // 0x9400+0x200 = 0x9600 unexpanded
|
|
|
|
|
|
|
|
#define REG_SCRPAGE_LO ((readWord(0x9002) & 0x80)<< 2) // 0x0200
|
|
|
|
#define REG_COLPAGE_LO ((readWord(0x9002) & 0x80)<< 2) // 0x0200
|
|
|
|
#define REG_NB_COLUMNS (readWord(0x9002) & 0x7F)
|
|
|
|
|
|
|
|
#define REG_RASTER_LO_RD (0x9003) // 0x80 >> 7
|
|
|
|
#define REG_DOUBLE_HEIGHT (readWord(0x9003) & 0x01)
|
|
|
|
#define REG_NB_ROWS ((readWord(0x9003) & 0x7E) >> 1) //x2 => 46
|
|
|
|
|
|
|
|
#define REG_RASTER_HI_RD (0x9004) // << 1 // 1E00 unexpanded?
|
|
|
|
|
|
|
|
#define REG_CHRMAP_PT (readWord(0x9005) & 0x0F)
|
|
|
|
#define REG_SCRPAGE_HIGH ((readWord(0x9005) & 0xF0) << 6) // 0x2C00
|
|
|
|
|
|
|
|
#define REG_AUXILIARY_COLOUR ((readWord(0x900E) & 0xF0) >> 4)
|
|
|
|
#define REG_BORDER_COLOUR (readWord(0x900F) & 0x7)
|
|
|
|
#define REG_BACKGROUND_COLOUR ((readWord(0x900F) & 0xF0) >> 4)
|
|
|
|
|
|
|
|
#define REG_HORI_OFFSET ((readWord(0x9000) & 0x7F)
|
|
|
|
#define REG_VERT_OFFSET ((readWord(0x9001) & 0xFF)
|
|
|
|
|
|
|
|
// C R H V
|
|
|
|
// 16 16 +6 +16
|
|
|
|
// 22 23 0 0
|
|
|
|
// 24 28 -3 -9
|
|
|
|
// 25 30 -3 -12
|
|
|
|
// 27 33 -5 -19
|
|
|
|
|
|
|
|
|
2022-02-11 22:58:34 +00:00
|
|
|
static Pixel vicPalette[16];
|
2021-11-04 14:17:40 +00:00
|
|
|
static uint16_t remap[16] = {
|
|
|
|
0x8000,
|
|
|
|
0x8400,
|
|
|
|
0x8800,
|
|
|
|
0x8c00,
|
|
|
|
0x0000,
|
|
|
|
0x0000,
|
|
|
|
0x0000,
|
|
|
|
0x0000,
|
|
|
|
0x0000,
|
|
|
|
0x0000,
|
|
|
|
0x0000,
|
|
|
|
0x0000,
|
|
|
|
0x1000,
|
|
|
|
0x1400,
|
|
|
|
0x1800,
|
|
|
|
0x1c00
|
|
|
|
};
|
|
|
|
|
2023-04-20 21:21:15 +00:00
|
|
|
static Pixel linebuf[WIN_W];
|
|
|
|
|
2021-11-04 14:17:40 +00:00
|
|
|
MOS6561::MOS6561() : IC(), curRow(0), frameReady(true) {
|
|
|
|
// Set clock speed
|
|
|
|
this->setClockSpeed(1108000);
|
|
|
|
}
|
|
|
|
|
|
|
|
MOS6561::~MOS6561() {
|
|
|
|
}
|
|
|
|
|
|
|
|
void MOS6561::initialize() {
|
|
|
|
// Initialize VIC palette
|
|
|
|
vicPalette[0] = RGBVAL16(0, 0, 0);
|
|
|
|
vicPalette[1] = RGBVAL16(255, 255, 255);
|
|
|
|
vicPalette[2] = RGBVAL16((182), (31), (33));
|
|
|
|
vicPalette[3] = RGBVAL16((77), (240), (255));
|
|
|
|
vicPalette[4] = RGBVAL16((180), (63), (255));
|
|
|
|
vicPalette[5] = RGBVAL16((68), (226), (55));
|
|
|
|
vicPalette[6] = RGBVAL16((15), (87), (247));
|
|
|
|
vicPalette[7] = RGBVAL16((220), (215), (27));
|
|
|
|
vicPalette[8] = RGBVAL16((202), (84), (0));
|
|
|
|
vicPalette[9] = RGBVAL16((233), (176), (114));
|
|
|
|
vicPalette[10] = RGBVAL16((231), (146), (147));
|
|
|
|
vicPalette[11] = RGBVAL16((154), (247), (253));
|
|
|
|
vicPalette[12] = RGBVAL16((224), (159), (255));
|
|
|
|
vicPalette[13] = RGBVAL16((143), (228), (147));
|
|
|
|
vicPalette[14] = RGBVAL16((130), (144), (255));
|
|
|
|
vicPalette[15] = RGBVAL16((229), (222), (133));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void MOS6561::renderBorder(uint16_t raster){
|
|
|
|
if (raster < WIN_H) {
|
|
|
|
Pixel bcol = vicPalette[REG_BORDER_COLOUR];
|
2023-04-20 21:21:15 +00:00
|
|
|
Pixel * dst = &linebuf[0];
|
2021-11-04 14:17:40 +00:00
|
|
|
for (int x=0; x < WIN_W; x++) {
|
|
|
|
*dst++ = bcol;
|
2023-04-20 21:21:15 +00:00
|
|
|
}
|
|
|
|
emu_DrawLine16(&linebuf[0], WIN_W, WIN_H, raster);
|
2021-11-04 14:17:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void MOS6561::renderLine(uint16_t raster, uint16_t row, uint8_t rowHeight, uint8_t chrLine) {
|
|
|
|
int curRow = row;
|
|
|
|
int nbRow = REG_NB_ROWS;
|
|
|
|
|
|
|
|
if ( (rowHeight == 16) && (nbRow >= 23) ) {
|
|
|
|
curRow = curRow/2;
|
|
|
|
nbRow = nbRow/2;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (raster < WIN_H)
|
|
|
|
{
|
|
|
|
int nbCol = REG_NB_COLUMNS;
|
|
|
|
int bWidth = (WIN_W - nbCol*8)/2;
|
|
|
|
#define bakcol 0
|
|
|
|
#define borcol 1
|
|
|
|
#define forcol 2
|
|
|
|
#define auxcol 3
|
|
|
|
Pixel cols[4];
|
|
|
|
uint16_t screenPage = (REG_SCRPAGE_HIGH & ~0x2000) + REG_SCRPAGE_LO;
|
|
|
|
uint8_t * charPointer = &vicmemory[screenPage + (curRow * nbCol)];
|
|
|
|
uint16_t colourPage = REG_COLPAGE_BASE + REG_COLPAGE_LO;
|
|
|
|
uint8_t * colPointer = &vicmemory[colourPage + (curRow * nbCol)];
|
|
|
|
|
|
|
|
cols[borcol] = vicPalette[REG_BORDER_COLOUR];
|
|
|
|
cols[bakcol] = vicPalette[REG_BACKGROUND_COLOUR];
|
|
|
|
|
|
|
|
// Border Left
|
2023-04-20 21:21:15 +00:00
|
|
|
Pixel * dst = &linebuf[0];
|
2021-11-04 14:17:40 +00:00
|
|
|
for (int x=0; x < bWidth; x++) {
|
|
|
|
*dst++ = cols[borcol];
|
|
|
|
}
|
|
|
|
|
|
|
|
uint16_t chardefbase = remap[REG_CHRMAP_PT];
|
|
|
|
for (int x = 0; x < nbCol; x +=1)
|
|
|
|
{
|
|
|
|
uint16_t charpt = chardefbase + charPointer[x]*rowHeight + chrLine;
|
|
|
|
if ( (charpt > 0x2000) && (charpt < 0x3000) )
|
|
|
|
charpt += 0x6000;
|
|
|
|
uint8_t characterByte = vicmemory[charpt];
|
|
|
|
uint8_t colour = colPointer[x] & 0x7;
|
|
|
|
uint8_t multiColour = colPointer[x] & 0x8;
|
|
|
|
cols[forcol] = vicPalette[colour];
|
|
|
|
if (!multiColour) {
|
|
|
|
Pixel * dest = dst;
|
|
|
|
for (int a = 0; a < 8; a++) {
|
|
|
|
if ((characterByte << a) & 0x80) {
|
|
|
|
*dest++ = cols[forcol];
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
*dest++ = cols[bakcol];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
Pixel * dest = dst;
|
|
|
|
cols[auxcol] = vicPalette[REG_AUXILIARY_COLOUR];
|
|
|
|
for (int a = 0; a < 8; a += 2) {
|
|
|
|
// Set colour
|
|
|
|
Pixel col = cols[((characterByte << a) & 0xC0) >> 6];
|
|
|
|
*dest++ = col;
|
|
|
|
*dest++ = col;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dst +=8;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Border Right
|
|
|
|
for (int x=0; x < bWidth; x++) {
|
|
|
|
*dst++ = cols[borcol];
|
2023-04-20 21:21:15 +00:00
|
|
|
}
|
|
|
|
emu_DrawLine16(&linebuf[0], WIN_W, 1, raster);
|
2021-11-04 14:17:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void MOS6561::renderRow(uint16_t raster, uint16_t row, uint8_t rowHeight)
|
|
|
|
{
|
|
|
|
int curRow = row;
|
|
|
|
int nbRow = REG_NB_ROWS;
|
|
|
|
|
|
|
|
//printf("rows %d of %d, cols=%d, rowHeight:%d\n", curRow, nbRow, nbCol, rowHeight);
|
|
|
|
|
|
|
|
if ( (rowHeight == 16) && (nbRow >= 23) ) {
|
|
|
|
curRow = curRow/2;
|
|
|
|
nbRow = nbRow/2;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((raster+curRow*rowHeight) < WIN_H)
|
|
|
|
{
|
|
|
|
int nbCol = REG_NB_COLUMNS;
|
|
|
|
int bWidth = (WIN_W - nbCol*8)/2;
|
|
|
|
#define bakcol 0
|
|
|
|
#define borcol 1
|
|
|
|
#define forcol 2
|
|
|
|
#define auxcol 3
|
|
|
|
Pixel cols[4];
|
|
|
|
uint16_t screenPage = (REG_SCRPAGE_HIGH & ~0x2000) + REG_SCRPAGE_LO;
|
|
|
|
uint8_t * charPointer = &vicmemory[screenPage + (curRow * nbCol)];
|
|
|
|
uint16_t colourPage = REG_COLPAGE_BASE + REG_COLPAGE_LO;
|
|
|
|
uint8_t * colPointer = &vicmemory[colourPage + (curRow * nbCol)];
|
|
|
|
|
|
|
|
cols[borcol] = vicPalette[REG_BORDER_COLOUR];
|
|
|
|
cols[bakcol] = vicPalette[REG_BACKGROUND_COLOUR];
|
|
|
|
|
|
|
|
for (int line=0; line < rowHeight; line++) {
|
|
|
|
// Border Left
|
2023-04-20 21:21:15 +00:00
|
|
|
Pixel * dst = &linebuf[0];
|
2021-11-04 14:17:40 +00:00
|
|
|
for (int x=0; x < bWidth; x++) {
|
|
|
|
*dst++ = cols[borcol];
|
|
|
|
}
|
|
|
|
|
|
|
|
uint16_t chardefbase = remap[REG_CHRMAP_PT];
|
|
|
|
for (int x = 0; x < nbCol; x +=1)
|
|
|
|
{
|
|
|
|
uint16_t charpt = chardefbase + charPointer[x]*rowHeight + line;
|
|
|
|
if ( (charpt > 0x2000) && (charpt < 0x3000) )
|
|
|
|
charpt += 0x6000;
|
|
|
|
uint8_t characterByte = vicmemory[charpt];
|
|
|
|
uint8_t colour = colPointer[x] & 0x7;
|
|
|
|
uint8_t multiColour = colPointer[x] & 0x8;
|
|
|
|
cols[forcol] = vicPalette[colour];
|
|
|
|
if (!multiColour) {
|
2023-04-20 21:21:15 +00:00
|
|
|
Pixel * dest = dst;
|
|
|
|
for (int a = 0; a < 8; a++) {
|
|
|
|
if ((characterByte << a) & 0x80) {
|
|
|
|
*dest++ = cols[forcol];
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
*dest++ = cols[bakcol];
|
|
|
|
}
|
|
|
|
}
|
2021-11-04 14:17:40 +00:00
|
|
|
}
|
|
|
|
else {
|
2023-04-20 21:21:15 +00:00
|
|
|
Pixel * dest = dst;
|
|
|
|
cols[auxcol] = vicPalette[REG_AUXILIARY_COLOUR];
|
|
|
|
for (int a = 0; a < 8; a += 2) {
|
|
|
|
// Set colour
|
|
|
|
Pixel col = cols[((characterByte << a) & 0xC0) >> 6];
|
|
|
|
*dest++ = col;
|
|
|
|
*dest++ = col;
|
|
|
|
}
|
2021-11-04 14:17:40 +00:00
|
|
|
}
|
|
|
|
dst +=8;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Border Right
|
|
|
|
for (int x=0; x < bWidth; x++) {
|
|
|
|
*dst++ = cols[borcol];
|
2023-04-20 21:21:15 +00:00
|
|
|
}
|
|
|
|
emu_DrawLine16(&linebuf[0], WIN_W, 1, curRow*rowHeight+line+raster);
|
2021-11-04 14:17:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void MOS6561::renderFrame() {
|
|
|
|
this->frameReady = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool MOS6561::isFrameReady() {
|
|
|
|
return this->frameReady;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void MOS6561::tick(int nbcycles) {
|
|
|
|
// Increment raster counter
|
|
|
|
uint16_t raster = readWord(REG_RASTER_HI_RD) << 1 | readWord(REG_RASTER_LO_RD) >> 7;
|
|
|
|
if (cycles % cyclesPerScanline == 0) {
|
|
|
|
uint8_t rowHeight = (REG_DOUBLE_HEIGHT?16:8);
|
|
|
|
if (raster <= lastScanline) {
|
|
|
|
raster += 1;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
raster = 0;
|
|
|
|
visScanlines = REG_NB_ROWS * rowHeight;
|
|
|
|
if ( (rowHeight == 16) && (REG_NB_ROWS >= 23) ) {
|
|
|
|
visScanlines = REG_NB_ROWS * 8;
|
|
|
|
}
|
|
|
|
firstVisibleScanline = (WIN_H-visScanlines)/2;
|
|
|
|
}
|
|
|
|
writeWord(REG_RASTER_LO_RD, (raster & 0x1) << 7 | (readWord(REG_RASTER_LO_RD) & 0x7F));
|
|
|
|
writeWord(REG_RASTER_HI_RD, raster >> 1);
|
|
|
|
|
|
|
|
if (visScanlines) {
|
|
|
|
if (raster < firstVisibleScanline) {
|
|
|
|
renderBorder(raster);
|
|
|
|
}
|
|
|
|
// If not blanking, update screen
|
|
|
|
else if (raster >= firstVisibleScanline && raster < (firstVisibleScanline+visScanlines)) {
|
|
|
|
// Update video
|
|
|
|
uint8_t chrLine = (raster-firstVisibleScanline) % rowHeight;
|
|
|
|
//renderLine(raster, this->curRow, rowHeight, chrLine);
|
|
|
|
if (chrLine == rowHeight-1) {
|
|
|
|
renderRow(firstVisibleScanline, this->curRow, rowHeight);
|
|
|
|
this->curRow++;
|
|
|
|
// End of screen reached
|
|
|
|
if (this->curRow >= REG_NB_ROWS) {
|
|
|
|
this->curRow = 0;
|
|
|
|
frameReady = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
renderBorder(raster);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Increment cycle counter
|
|
|
|
this->cycles += nbcycles;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|