diff --git a/MCUME_pico/bin/mcume_pico64.uf2 b/MCUME_pico/bin/mcume_pico64.uf2 index 14a630a..9589a9e 100644 Binary files a/MCUME_pico/bin/mcume_pico64.uf2 and b/MCUME_pico/bin/mcume_pico64.uf2 differ diff --git a/MCUME_pico/pico64/IntervalTimer.h b/MCUME_pico/pico64/IntervalTimer.h index 42359dd..740421e 100755 --- a/MCUME_pico/pico64/IntervalTimer.h +++ b/MCUME_pico/pico64/IntervalTimer.h @@ -54,11 +54,12 @@ private: public: MyIntervalTimer() { //channel = NULL; - nvic_priority = 128; + //nvic_priority = 128; } ~MyIntervalTimer() { end(); } + /* bool begin(void (*funct)(), unsigned int microseconds) { if (microseconds == 0 || microseconds > MAX_PERIOD) return false; uint32_t cycles = (F_BUS / 1000000) * microseconds - 1; @@ -84,61 +85,41 @@ public: bool begin(void (*funct)(), double microseconds) { return begin(funct, (float)microseconds); } - + */ void setIntervalFast(float microseconds) { /*NEW*/ uint32_t cycles = (float)(F_BUS / 1000000) * microseconds - 0.5; //channel->LDVAL = cycles; } - bool setInterval(float microseconds) { /*NEW*/ + /* + bool setInterval(float microseconds) { //if (!channel) return false; if (microseconds <= 0 || microseconds > MAX_PERIOD) return false; setIntervalFast(microseconds); return true; } - + */ void end() {}; + /* void priority(uint8_t n) { nvic_priority = n; - #if defined(KINETISK) - /* if (channel) { int index = channel - KINETISK_PIT_CHANNELS; - NVIC_SET_PRIORITY(IRQ_PIT_CH0 + index, nvic_priority); + //NVIC_SET_PRIORITY(IRQ_PIT_CH0 + index, nvic_priority); } - */ - #elif defined(KINETISL) - /* - if (channel) { - int index = channel - KINETISK_PIT_CHANNELS; - nvic_priorites[index] = nvic_priority; - if (nvic_priorites[0] <= nvic_priorites[1]) { - NVIC_SET_PRIORITY(IRQ_PIT, nvic_priorites[0]); - } else { - NVIC_SET_PRIORITY(IRQ_PIT, nvic_priorites[1]); - } - } - */ - #endif } + */ //operator IRQ_NUMBER_t() { /* if (channel) { - #if defined(KINETISK) int index = channel - KINETISK_PIT_CHANNELS; return (IRQ_NUMBER_t)(IRQ_PIT_CH0 + index); - #elif defined(KINETISL) - return IRQ_PIT; - #endif } */ //return (IRQ_NUMBER_t)NVIC_NUM_INTERRUPTS; //} private: //KINETISK_PIT_CHANNEL_t *channel; - uint8_t nvic_priority; - #if defined(KINETISL) - static uint8_t nvic_priorites[2]; - #endif + //uint8_t nvic_priority; bool beginCycles(void (*funct)(), uint32_t cycles); }; diff --git a/MCUME_pico/pico64/IntervalTimer.h_old b/MCUME_pico/pico64/IntervalTimer.h_old new file mode 100755 index 0000000..42359dd --- /dev/null +++ b/MCUME_pico/pico64/IntervalTimer.h_old @@ -0,0 +1,150 @@ +/* Teensyduino Core Library + * http://www.pjrc.com/teensy/ + * Copyright (c) 2017 PJRC.COM, LLC. + * + * 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: + * + * 1. The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * 2. If the Software is incorporated into a build system that allows + * selection among a list of target devices, then similar target + * devices manufactured by PJRC.COM must be included in the list of + * target devices and selectable in the same manner. + * + * 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. + */ + + /* + + Added : + + - void setIntervalFast(float microseconds) + - bool setInterval(float microseconds) + + F.B. + + */ + +#ifndef __INTERVALTIMERX_H__ +#define __INTERVALTIMERX_H__ + +//#include "kinetis.h" + +#ifdef __cplusplus +extern "C" { +#endif + +class MyIntervalTimer { +private: + static const uint32_t MAX_PERIOD = UINT32_MAX / (F_BUS / 1000000.0); +public: + MyIntervalTimer() { + //channel = NULL; + nvic_priority = 128; + } + ~MyIntervalTimer() { + end(); + } + bool begin(void (*funct)(), unsigned int microseconds) { + if (microseconds == 0 || microseconds > MAX_PERIOD) return false; + uint32_t cycles = (F_BUS / 1000000) * microseconds - 1; + if (cycles < 36) return false; + return beginCycles(funct, cycles); + } + bool begin(void (*funct)(), int microseconds) { + if (microseconds < 0) return false; + return begin(funct, (unsigned int)microseconds); + } + bool begin(void (*funct)(), unsigned long microseconds) { + return begin(funct, (unsigned int)microseconds); + } + bool begin(void (*funct)(), long microseconds) { + return begin(funct, (int)microseconds); + } + bool begin(void (*funct)(), float microseconds) { + if (microseconds <= 0 || microseconds > MAX_PERIOD) return false; + uint32_t cycles = (float)(F_BUS / 1000000) * microseconds - 0.5; + if (cycles < 36) return false; + return beginCycles(funct, cycles); + } + bool begin(void (*funct)(), double microseconds) { + return begin(funct, (float)microseconds); + } + + void setIntervalFast(float microseconds) { /*NEW*/ + uint32_t cycles = (float)(F_BUS / 1000000) * microseconds - 0.5; + //channel->LDVAL = cycles; + } + bool setInterval(float microseconds) { /*NEW*/ + //if (!channel) return false; + if (microseconds <= 0 || microseconds > MAX_PERIOD) return false; + setIntervalFast(microseconds); + return true; + } + + void end() {}; + void priority(uint8_t n) { + nvic_priority = n; + #if defined(KINETISK) + /* + if (channel) { + int index = channel - KINETISK_PIT_CHANNELS; + NVIC_SET_PRIORITY(IRQ_PIT_CH0 + index, nvic_priority); + } + */ + #elif defined(KINETISL) + /* + if (channel) { + int index = channel - KINETISK_PIT_CHANNELS; + nvic_priorites[index] = nvic_priority; + if (nvic_priorites[0] <= nvic_priorites[1]) { + NVIC_SET_PRIORITY(IRQ_PIT, nvic_priorites[0]); + } else { + NVIC_SET_PRIORITY(IRQ_PIT, nvic_priorites[1]); + } + } + */ + #endif + } + //operator IRQ_NUMBER_t() { + /* + if (channel) { + #if defined(KINETISK) + int index = channel - KINETISK_PIT_CHANNELS; + return (IRQ_NUMBER_t)(IRQ_PIT_CH0 + index); + #elif defined(KINETISL) + return IRQ_PIT; + #endif + } + */ + //return (IRQ_NUMBER_t)NVIC_NUM_INTERRUPTS; + //} +private: + //KINETISK_PIT_CHANNEL_t *channel; + uint8_t nvic_priority; + #if defined(KINETISL) + static uint8_t nvic_priorites[2]; + #endif + bool beginCycles(void (*funct)(), uint32_t cycles); + +}; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/MCUME_pico/pico64/c64.cpp b/MCUME_pico/pico64/c64.cpp index 7156956..d110812 100644 --- a/MCUME_pico/pico64/c64.cpp +++ b/MCUME_pico/pico64/c64.cpp @@ -27,14 +27,14 @@ static void oneRasterLine(void) { while (true) { - cpu.lineStartTime = get_ccount(); + cpu.lineStartTime = fbmicros(); //get_ccount(); cpu.lineCycles = cpu.lineCyclesAbs = 0; if (!cpu.exactTiming) { - vic_do(); - } else { - vic_do_simple(); - } + vic_do(); + } else { + vic_do_simple(); + } if (--lc == 0) { lc = LINEFREQ / 10; // 10Hz @@ -44,11 +44,10 @@ static void oneRasterLine(void) { //Switch "ExactTiming" Mode off after a while: if (!cpu.exactTiming) break; - // no exact timing !! JMH - //if (get_ccount() - cpu.exactTimingStartTime >= EXACTTIMINGDURATION * (F_CPU / 1000)) { - // cpu_disableExactTiming(); - // break; - //} + if ( (fbmicros() - cpu.exactTimingStartTime)*1000 >= EXACTTIMINGDURATION ) { + cpu_disableExactTiming(); + break; + } }; } @@ -289,11 +288,8 @@ static char textkey[1]; static bool res=false; static bool firsttime=true; - -static unsigned char dummyline[320]; - -static char * oskbtext1 = "1234567890 \"$,ABCDEFGHIJKLMNOPQRSTUVWXYZ"; -static char * oskbtext2 = "FFFFFFFF "; +static char * oskbtext1 = "FFFFFFFF RD\"$ABCDEFGHIJKLMNOPQRSTUVWXYZ"; +static char * oskbtext2 = "12345678 TL ,.* 0123456789 "; static int oskbXPos = 10; static int oskbYPos = 0; @@ -301,14 +297,20 @@ static int oskbYPos = 0; #define OSKB_BG VGA_RGB(255, 255, 255) #define OSKB_HL VGA_RGB(255, 255, 0) +int emu_oskbActive(void) { + return (oskbActive?1:0); +} + void emu_DrawVsync(void) { char sel[2]={0,0}; if (oskbActive) { - tft.drawText(0,OSKB_YPOS,oskbtext1,OSKB_TEXT,OSKB_BG,false); - tft.drawText(0,OSKB_YPOS+8,oskbtext2,OSKB_TEXT,OSKB_BG,false); + int fbwidth; + tft.get_frame_buffer_size(&fbwidth,NULL); + tft.drawText((fbwidth-320)/2,OSKB_YPOS,oskbtext1,OSKB_TEXT,OSKB_BG,false); + tft.drawText((fbwidth-320)/2,OSKB_YPOS+8,oskbtext2,OSKB_TEXT,OSKB_BG,false); sel[0]=(oskbYPos==0)?oskbtext1[oskbXPos]:oskbtext2[oskbXPos]; - tft.drawText(oskbXPos*8,OSKB_YPOS+8*oskbYPos,sel,OSKB_TEXT,OSKB_HL,false); + tft.drawText((fbwidth-320)/2+oskbXPos*8,OSKB_YPOS+8*oskbYPos,sel,OSKB_TEXT,OSKB_HL,false); } //skip += 1; //skip &= VID_FRAME_SKIP; @@ -316,20 +318,13 @@ void emu_DrawVsync(void) } -void * emu_LineBuffer(int line) -{ - if ( (line >= OSKB_YPOS) && (oskbActive) ) - return &dummyline[0]; - - return (void*)tft.getLineBuffer(line); -} - - void c64_Input(int bClick) { if (oskbActive) { - if (bClick & MASK_JOY2_BTN) { - textkey[0] = oskbtext1[oskbXPos]; - if (oskbYPos==1) if (oskbXPos<8) textkey[0] = 0x85+oskbXPos; + if (bClick & MASK_JOY2_BTN) { + if (oskbXPos == 10) textkey[0] = 13; + else if (oskbXPos == 11) textkey[0] = 157; + else if (oskbXPos < 8) textkey[0] = 0x85+oskbXPos; + else textkey[0] = (oskbYPos == 0)?oskbtext1[oskbXPos]:oskbtext2[oskbXPos]; textseq = textkey; nbkeys = 1; kcnt = 0; diff --git a/MCUME_pico/pico64/cpu.cpp b/MCUME_pico/pico64/cpu.cpp index c092ca6..a12270f 100755 --- a/MCUME_pico/pico64/cpu.cpp +++ b/MCUME_pico/pico64/cpu.cpp @@ -2677,11 +2677,11 @@ noOpcode: cia_clockt(cpu.ticks); c-= cpu.ticks; cpu.lineCycles += cpu.ticks; - - //if (cpu.exactTiming) { - //uint32_t t = cpu.lineCycles * MCU_C64_RATIO; - //while (ARM_DWT_CYCCNT - cpu.lineStartTime < t){;} - //} +#define US_C64_CYCLE (1000000.0f / CLOCKSPEED) // Duration (µs) of a C64 Cycle + if (cpu.exactTiming) { + uint32_t t = cpu.lineCycles * US_C64_CYCLE; + while (fbmicros() - cpu.lineStartTime < t){;} + } }; @@ -2696,9 +2696,9 @@ void cpu_setExactTiming() { vic_displaySimpleModeScreen(); } - //cpu.exactTiming = 1; - //cpu.exactTimingStartTime = ARM_DWT_CYCCNT; - cpu.exactTiming = 0; + cpu.exactTiming = 1; + cpu.exactTimingStartTime = fbmicros(); + //cpu.exactTiming = 0; } //Disable "ExactTiming" Mode diff --git a/MCUME_pico/pico64/emuapi.h b/MCUME_pico/pico64/emuapi.h index 352f9ff..cec076a 100644 --- a/MCUME_pico/pico64/emuapi.h +++ b/MCUME_pico/pico64/emuapi.h @@ -118,6 +118,7 @@ extern int emu_ReadAnalogJoyY(int min, int max); extern int emu_ReadI2CKeyboard(void); extern void emu_KeyboardOnUp(int keymodifer, int key); extern void emu_KeyboardOnDown(int keymodifer, int key); +extern int emu_oskbActive(void); extern void emu_sndPlaySound(int chan, int volume, int freq); extern void emu_sndPlayBuzz(int size, int val); diff --git a/MCUME_pico/pico64/pico64.cpp b/MCUME_pico/pico64/pico64.cpp index e1c2644..47e9253 100644 --- a/MCUME_pico/pico64/pico64.cpp +++ b/MCUME_pico/pico64/pico64.cpp @@ -35,7 +35,8 @@ int main(void) { stdio_init_all(); - tft.begin(VGA_MODE_320x240); + tft.begin(VGA_MODE_400x240); +// tft.begin(VGA_MODE_320x240); emu_init(); while (true) { if (menuActive()) { @@ -76,7 +77,10 @@ void emu_SetPaletteEntry(unsigned char r, unsigned char g, unsigned char b, int } } - +void * emu_LineBuffer(int line) +{ + return (void*)tft.getLineBuffer(line); +} void emu_DrawLine(unsigned char * VBuf, int width, int height, int line) { diff --git a/MCUME_pico/pico64/util.h b/MCUME_pico/pico64/util.h index 084d90a..a2aefde 100755 --- a/MCUME_pico/pico64/util.h +++ b/MCUME_pico/pico64/util.h @@ -39,12 +39,12 @@ Copyright Frank Bösing, 2017 #include "Teensy64.h" -static inline unsigned get_ccount(void) -{ - //unsigned r; - //asm volatile ("rsr %0, ccount" : "=r"(r)); - return 0; -} +//static inline unsigned get_ccount(void) +//{ +// //unsigned r; +// //asm volatile ("rsr %0, ccount" : "=r"(r)); +// return (time_us_32()); +//} void disableEventResponder(void); diff --git a/MCUME_pico/pico64/vic.cpp b/MCUME_pico/pico64/vic.cpp old mode 100644 new mode 100755 index 8c03677..1983649 --- a/MCUME_pico/pico64/vic.cpp +++ b/MCUME_pico/pico64/vic.cpp @@ -45,7 +45,6 @@ - optimize more */ - #include "Teensy64.h" #include "vic.h" #include @@ -58,22 +57,35 @@ #define PALETTE(r,g,b) (RGBVAL16(r,g,b)) //(((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3)) #include "vic_palette.h" - - -#define BORDER (240-200)/2 -#define SCREEN_HEIGHT (200+2*BORDER) -#define SCREEN_WIDTH 320 -//#define LINE_MEM_WIDTH 320 -#define FIRSTDISPLAYLINE ( 51 - BORDER ) -#define LASTDISPLAYLINE ( 250 + BORDER ) -#define BORDER_LEFT 0 -#define BORDER_RIGHT 0 - typedef uint8_t tpixel; -#define MAXCYCLESSPRITES0_2 3 -#define MAXCYCLESSPRITES3_7 5 -#define MAXCYCLESSPRITES (MAXCYCLESSPRITES0_2 + MAXCYCLESSPRITES3_7) +static tpixel * SCREENMEM=NULL; +static int LINE_MEM_WIDTH=0; +static int REAL_XRES=0; +static int REAL_YRES=0; + +#define SCREEN_WIDTH (320) + +/* +static int BORDER=0; +static int SCREEN_HEIGHT=0; +static int FIRSTDISPLAYLINE=0; +static int LASTDISPLAYLINE=0; +static int BORDER_LEFT=0; +static int BORDER_RIGHT=0; +*/ + +#define BORDER ((REAL_YRES-200)/2) +#define SCREEN_HEIGHT (200+2*BORDER) +#define FIRSTDISPLAYLINE ( 51 - BORDER ) +#define LASTDISPLAYLINE ( 250 + BORDER ) +#define BORDER_LEFT ((REAL_XRES-320)/2) +#define BORDER_RIGHT ((REAL_XRES-320)/2) + +#define MAXCYCLESSPRITES0_2 3 +#define MAXCYCLESSPRITES3_7 5 +#define MAXCYCLESSPRITES (MAXCYCLESSPRITES0_2 + MAXCYCLESSPRITES3_7) + /*****************************************************************************************************/ @@ -1271,8 +1283,6 @@ typedef void (*modes_t)( tpixel *p, const tpixel *pe, uint16_t *spl, const uint1 const modes_t modes[8] = {mode0, mode1, mode2, mode3, mode4, mode5, mode6, mode7}; -//static tpixel linebuffer[SCREEN_WIDTH]; - void vic_do(void) { uint16_t vc; @@ -1291,6 +1301,7 @@ void vic_do(void) { */ if ( cpu.vic.rasterLine >= LINECNT ) { + //reSID sound needs much time - too much to keep everything in sync and with stable refreshrate //but it is not called very often, so most of the time, we have more time than needed. //We can measure the time needed for a frame and calc a correction factor to speed things up. @@ -1302,14 +1313,14 @@ void vic_do(void) { cpu.vic.rasterLine = 0; cpu.vic.vcbase = 0; cpu.vic.denLatch = 0; - //if (cpu.vic.rasterLine == LINECNT) { - emu_DrawVsync(); - //} + emu_DrawVsync(); } else cpu.vic.rasterLine++; int r = cpu.vic.rasterLine; +// if ( (r >= LASTDISPLAYLINE) && (emu_oskbActive()) ) return; + if (r == cpu.vic.intRasterLine )//Set Rasterline-Interrupt cpu.vic.R[0x19] |= 1 | ((cpu.vic.R[0x1a] & 1) << 7); @@ -1392,18 +1403,61 @@ void vic_do(void) { } //max_x = (!cpu.vic.CSEL) ? 40:38; - //p = SCREENMEM + (r - FIRSTDISPLAYLINE) * LINE_MEM_WIDTH; - //p = &linebuffer[0]; //tft.getLineBuffer((r - FIRSTDISPLAYLINE)); - p = (tpixel*)emu_LineBuffer((r - FIRSTDISPLAYLINE)); + p = SCREENMEM + (r - FIRSTDISPLAYLINE) * LINE_MEM_WIDTH; + + + uint16_t col; +//#if !VGA +if(REAL_XRES == SCREEN_WIDTH) { pe = p + SCREEN_WIDTH; //Left Screenborder: Cycle 10 spl = &cpu.vic.spriteLine[24]; cpu_clock(6); +} +else { + pe = p + SCREEN_WIDTH + BORDER_LEFT; + col = cpu.vic.colors[0]; + + //Left Screenborder: Cycle 10 + for (int i = 0; i <2; i++) { + cpu_clock(1); + *p++ = col;*p++ = col;*p++ = col;*p++ = col; + *p++ = col;*p++ = col;*p++ = col;*p++ = col; + } + + //Left Screenborder: Cycle 13 +#if 0 //mit Sprites + spl = &cpu.vic.spriteLine[0]; + uint16_t sprite; + for (int i=0; i<3; i++) { + cpu_clock(1); + SPRITEORFIXEDCOLOR(); + SPRITEORFIXEDCOLOR(); + SPRITEORFIXEDCOLOR(); + SPRITEORFIXEDCOLOR(); + SPRITEORFIXEDCOLOR(); + SPRITEORFIXEDCOLOR(); + SPRITEORFIXEDCOLOR(); + SPRITEORFIXEDCOLOR(); + } +#else //ohne sprites + spl = &cpu.vic.spriteLine[24]; + for (int i=0; i<3; i++) { + cpu_clock(1); + *p++ = col;*p++ = col;*p++ = col;*p++ = col; + *p++ = col;*p++ = col;*p++ = col;*p++ = col; + } +#endif +} if (cpu.vic.borderFlag) { - cpu_clock(5); - fastFillLineNoSprites(p, pe + BORDER_RIGHT, cpu.vic.colors[0]); +//#if !VGA + if(REAL_XRES == SCREEN_WIDTH) { + cpu_clock(5); + } + if ( (!emu_oskbActive()) ) + fastFillLineNoSprites(p, pe + BORDER_RIGHT, cpu.vic.colors[0]); goto noDisplayIncRC ; } @@ -1558,9 +1612,7 @@ g-Zugriff cpu_clock(1); uint16_t col = cpu.vic.colors[0]; //p = &screen[r - FIRSTDISPLAYLINE][0]; - //p = SCREENMEM + (r - FIRSTDISPLAYLINE) * LINE_MEM_WIDTH + BORDER_LEFT; - //p = &linebuffer[0]; // tft.getLineBuffer((r - FIRSTDISPLAYLINE)); - p = (tpixel*)emu_LineBuffer((r - FIRSTDISPLAYLINE)); + p = SCREENMEM + (r - FIRSTDISPLAYLINE) * LINE_MEM_WIDTH + BORDER_LEFT; #if 0 // Sprites im Rand uint16_t sprite; @@ -1583,10 +1635,8 @@ g-Zugriff //Rand rechts: //p = &screen[r - FIRSTDISPLAYLINE][SCREEN_WIDTH - 9]; - //p = SCREENMEM + (r - FIRSTDISPLAYLINE) * LINE_MEM_WIDTH + SCREEN_WIDTH - 9 + BORDER_LEFT; - //p = &linebuffer[SCREEN_WIDTH - 9 + BORDER_LEFT]; //tft.getLineBuffer((r - FIRSTDISPLAYLINE)) + SCREEN_WIDTH - 9 + BORDER_LEFT; - p = (tpixel*)emu_LineBuffer((r - FIRSTDISPLAYLINE)) + SCREEN_WIDTH - 9 + BORDER_LEFT; - pe = p + 9; + p = SCREENMEM + (r - FIRSTDISPLAYLINE) * LINE_MEM_WIDTH + SCREEN_WIDTH - 9 + BORDER_LEFT; + pe = p + 9; #if 0 // Sprites im Rand @@ -1596,20 +1646,32 @@ g-Zugriff } #else //keine Sprites im Rand - //while (p < pe) { - // *p++ = col; - //} + while (p < pe) { + *p++ = col; + } #endif - - } -// emu_DrawLine8(&linebuffer[0], SCREEN_WIDTH, SCREEN_HEIGHT, (r - FIRSTDISPLAYLINE)); - //Rechter Rand nach CSEL, im Textbereich cpu_clock(5); +//#if VGA +if(REAL_XRES != SCREEN_WIDTH) { + p = SCREENMEM + (r - FIRSTDISPLAYLINE) * LINE_MEM_WIDTH + SCREEN_WIDTH + BORDER_LEFT; + pe += BORDER_RIGHT; +#if 0 + // Sprites im Rand + while (p < pe) { + SPRITEORFIXEDCOLOR(); + } +#else + //keine Sprites im Rand + while (p < pe) { + *p++ = col; + } +#endif +} noDisplayIncRC: @@ -1891,7 +1953,7 @@ if ( cpu.vic.rasterLine >= LINECNT ) { cpu.vic.rasterLine = 0; cpu.vic.vcbase = 0; cpu.vic.denLatch = 0; - + emu_DrawVsync(); } else { cpu.vic.rasterLine++; cpu_clock(1); @@ -1899,6 +1961,8 @@ if ( cpu.vic.rasterLine >= LINECNT ) { } int r = cpu.vic.rasterLine; + +// if ( (r >= LASTDISPLAYLINE) && (emu_oskbActive()) ) return; if (r == cpu.vic.intRasterLine )//Set Rasterline-Interrupt cpu.vic.R[0x19] |= 1 | ((cpu.vic.R[0x1a] & 1) << 7); @@ -1964,16 +2028,6 @@ if ( cpu.vic.rasterLine >= LINECNT ) { } - -/*****************************************************************************************************/ -/*****************************************************************************************************/ -/*****************************************************************************************************/ - -void installPalette(void) { - memcpy(cpu.vic.palette, (void*)palette, sizeof(cpu.vic.palette)); -} - - /*****************************************************************************************************/ /*****************************************************************************************************/ /*****************************************************************************************************/ @@ -2002,17 +2056,17 @@ void vic_write(uint32_t address, uint8_t value) { switch (address) { case 0x11 : - cpu.vic.R[address] = value; + cpu.vic.R[address] = value; cpu.vic.intRasterLine = (cpu.vic.intRasterLine & 0xff) | ((((uint16_t) value) << 1) & 0x100); if (cpu.vic.rasterLine == 0x30 ) cpu.vic.denLatch |= value & 0x10; cpu.vic.badline = (cpu.vic.denLatch && (cpu.vic.rasterLine >= 0x30) && (cpu.vic.rasterLine <= 0xf7) && ( (cpu.vic.rasterLine & 0x07) == (value & 0x07))); - if (cpu.vic.badline) { - cpu.vic.idle = 0; - } + if (cpu.vic.badline) { + cpu.vic.idle = 0; + } - vic_adrchange(); + vic_adrchange(); break; case 0x12 : @@ -2109,7 +2163,40 @@ uint8_t vic_read(uint32_t address) { /*****************************************************************************************************/ /*****************************************************************************************************/ +void installPalette(void) { + memcpy(cpu.vic.palette, (void*)palette, sizeof(cpu.vic.palette)); +} + + +/*****************************************************************************************************/ +/*****************************************************************************************************/ +/*****************************************************************************************************/ +#define XOFFSET 4 +#define YOFFSET 16 + + void resetVic(void) { + LINE_MEM_WIDTH = tft.get_frame_buffer_size(&REAL_XRES,&REAL_YRES); + SCREENMEM=(tpixel*)emu_LineBuffer(0); + /* + if (REAL_XRES == SCREEN_WIDTH) { + BORDER = (REAL_YRES-200)/2; + SCREEN_HEIGHT = (200+2*BORDER); + FIRSTDISPLAYLINE = ( 51 - BORDER ); + LASTDISPLAYLINE = ( 250 + BORDER ); + BORDER_LEFT = 0; + BORDER_RIGHT = 0; + } + else { + BORDER = (REAL_YRES-200)/2; + SCREEN_HEIGHT = (200+2*BORDER); + FIRSTDISPLAYLINE = ( 51 - BORDER ); + LASTDISPLAYLINE = ( 250 + BORDER ); + BORDER_LEFT = (REAL_XRES-320)/2; + BORDER_RIGHT = (REAL_XRES-320)/2; + } + */ + enableCycleCounter(); cpu.vic.intRasterLine = 0; @@ -2117,8 +2204,6 @@ void resetVic(void) { cpu.vic.lineHasSprites = 0; memset(&cpu.RAM[0x400], 0, 1000); memset(&cpu.vic, 0, sizeof(cpu.vic)); - - installPalette(); diff --git a/MCUME_pico/pico64/vic.cpp_old b/MCUME_pico/pico64/vic.cpp_old new file mode 100644 index 0000000..eea3065 --- /dev/null +++ b/MCUME_pico/pico64/vic.cpp_old @@ -0,0 +1,2159 @@ +/* + Copyright Frank Bösing, 2017 + + This file is part of Teensy64. + + Teensy64 is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Teensy64 is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Teensy64. If not, see . + + Diese Datei ist Teil von Teensy64. + + Teensy64 ist Freie Software: Sie können es unter den Bedingungen + der GNU General Public License, wie von der Free Software Foundation, + Version 3 der Lizenz oder (nach Ihrer Wahl) jeder späteren + veröffentlichten Version, weiterverbreiten und/oder modifizieren. + + Teensy64 wird in der Hoffnung, dass es nützlich sein wird, aber + OHNE JEDE GEWÄHRLEISTUNG, bereitgestellt; sogar ohne die implizite + Gewährleistung der MARKTFÄHIGKEIT oder EIGNUNG FÜR EINEN BESTIMMTEN ZWECK. + Siehe die GNU General Public License für weitere Details. + + Sie sollten eine Kopie der GNU General Public License zusammen mit diesem + Programm erhalten haben. Wenn nicht, siehe . + +*/ + +/* + TODOs: + - Fix Bugs.. + - FLD - (OK 08/17) test this more.. + - Sprite Stretching (requires "MOBcounter") + - BA Signal -> CPU + - xFLI + - ... + - DMA Delay (?) - needs partial rewrite (idle - > badline in middle of line. Is the 3.6 fast enough??) + - optimize more +*/ + + +#include "Teensy64.h" +#include "vic.h" +#include +#include +#include + +#define min(a, b) (((a) < (b)) ? (a) : (b)) +#define max(a, b) (((a) > (b)) ? (a) : (b)) + +#define PALETTE(r,g,b) (RGBVAL16(r,g,b)) //(((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3)) +#include "vic_palette.h" + + + +#define BORDER (240-200)/2 +#define SCREEN_HEIGHT (200+2*BORDER) +#define SCREEN_WIDTH 320 +//#define LINE_MEM_WIDTH 320 +#define FIRSTDISPLAYLINE ( 51 - BORDER ) +#define LASTDISPLAYLINE ( 250 + BORDER ) +#define BORDER_LEFT (400-320)/2 +#define BORDER_RIGHT 0 + +typedef uint8_t tpixel; + +#define MAXCYCLESSPRITES0_2 3 +#define MAXCYCLESSPRITES3_7 5 +#define MAXCYCLESSPRITES (MAXCYCLESSPRITES0_2 + MAXCYCLESSPRITES3_7) + + +/*****************************************************************************************************/ +/*****************************************************************************************************/ +/*****************************************************************************************************/ + +inline __attribute__((always_inline)) +void fastFillLine(tpixel * p, const tpixel * pe, const uint16_t col, uint16_t * spl); +inline __attribute__((always_inline)) +void fastFillLineNoSprites(tpixel * p, const tpixel * pe, const uint16_t col); + + +/*****************************************************************************************************/ +/*****************************************************************************************************/ +/*****************************************************************************************************/ + +#define SPRITENUM(data) (1 << ((data >> 8) & 0x07)) +#define CHARSETPTR() cpu.vic.charsetPtr = cpu.vic.charsetPtrBase + cpu.vic.rc; +#define CYCLES(x) {if (cpu.vic.badline) {cia_clockt(x);} else {cpu_clock(x);} } + +#define BADLINE(x) {if (cpu.vic.badline) { \ + cpu.vic.lineMemChr[x] = cpu.RAM[cpu.vic.videomatrix + vc + x]; \ + cpu.vic.lineMemCol[x] = cpu.vic.COLORRAM[vc + x]; \ + cia1_clock(1); \ + cia2_clock(1); \ + } else { \ + cpu_clock(1); \ + } \ + }; + +#define SPRITEORFIXEDCOLOR() \ + sprite = *spl++; \ + if (sprite) { \ + *p++ = cpu.vic.palette[sprite & 0x0f]; \ + } else { \ + *p++ = col; \ + } + + +#if 0 +#define PRINTOVERFLOW \ + if (p>pe) { \ + Serial.print("VIC overflow Mode "); \ + Serial.println(mode); \ + } + +#define PRINTOVERFLOWS \ + if (p>pe) { \ + Serial.print("VIC overflow (Sprite) Mode "); \ + Serial.println(mode); \ + } +#else +#define PRINTOVERFLOW +#define PRINTOVERFLOWS +#endif + +/*****************************************************************************************************/ +void mode0 (tpixel *p, const tpixel *pe, uint16_t *spl, const uint16_t vc) { + // Standard-Textmodus(ECM/BMM/MCM=0/0/0) + /* + Standard-Textmodus (ECM / BMM / MCM = 0/0/0) + In diesem Modus (wie in allen Textmodi) liest der VIC aus der videomatrix 8-Bit-Zeichenzeiger, + die die Adresse der Punktmatrix des Zeichens im Zeichengenerator angibt. Damit ist ein Zeichensatz + von 256 Zeichen verfügbar, die jeweils aus 8×8 Pixeln bestehen, die in 8 aufeinanderfolgenden Bytes + im Zeichengenerator abgelegt sind. Mit den Bits VM10-13 und CB11-13 aus Register $d018 lassen sich + videomatrix und Zeichengenerator im Speicher verschieben. Im Standard-Textmodus entspricht jedes Bit + im Zeichengenerator direkt einem Pixel auf dem Bildschirm. Die Vordergrundfarbe ist für jedes Zeichen + im Farbnibble aus der videomatrix angegeben, die Hintergrundfarbe wird global durch Register $d021 festgelegt. + + +----+----+----+----+----+----+----+----+ + | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + +----+----+----+----+----+----+----+----+ + | 8 Pixel (1 Bit/Pixel) | + | | + | "0": Hintergrundfarbe 0 ($d021) | + | "1": Farbe aus Bits 8-11 der c-Daten | + +---------------------------------------+ + + */ + + uint8_t chr, pixel; + uint16_t fgcol; + uint16_t bgcol; + uint16_t x = 0; + + CHARSETPTR(); + + if (cpu.vic.lineHasSprites) { + + do { + + BADLINE(x); + + chr = cpu.vic.charsetPtr[cpu.vic.lineMemChr[x] * 8]; + fgcol = cpu.vic.lineMemCol[x]; + x++; + unsigned m = min(8, pe - p); + for (unsigned i = 0; i < m; i++) { + int sprite = *spl++; + + if (sprite) { // Sprite: Ja + int spritenum = SPRITENUM(sprite); + int spritepixel = sprite & 0x0f; + + if (sprite & 0x4000) { // Sprite: Hinter Text MDP = 1 + if (chr & 0x80) { + cpu.vic.fgcollision |= spritenum; + pixel = fgcol; + } else { + pixel = spritepixel; + } + } else { // Sprite: Vor Text //MDP = 0 + if (chr & 0x80) cpu.vic.fgcollision |= spritenum; + pixel = spritepixel; + } + } else { // Kein Sprite + pixel = (chr & 0x80) ? fgcol : cpu.vic.B0C; + } + + *p++ = cpu.vic.palette[pixel]; + chr = chr << 1; + + } + } while (p < pe); + PRINTOVERFLOWS + } else { //Keine Sprites + + while (p < pe - 8) { + + BADLINE(x) + + chr = cpu.vic.charsetPtr[cpu.vic.lineMemChr[x] * 8]; + fgcol = cpu.vic.palette[cpu.vic.lineMemCol[x]]; + bgcol = cpu.vic.colors[1]; + x++; + + *p++ = (chr & 0x80) ? fgcol : bgcol; + *p++ = (chr & 0x40) ? fgcol : bgcol; + *p++ = (chr & 0x20) ? fgcol : bgcol; + *p++ = (chr & 0x10) ? fgcol : bgcol; + *p++ = (chr & 0x08) ? fgcol : bgcol; + *p++ = (chr & 0x04) ? fgcol : bgcol; + *p++ = (chr & 0x02) ? fgcol : bgcol; + *p++ = (chr & 0x01) ? fgcol : bgcol; + + }; + + while (p < pe) { + + BADLINE(x) + + chr = cpu.vic.charsetPtr[cpu.vic.lineMemChr[x] * 8]; + fgcol = cpu.vic.palette[cpu.vic.lineMemCol[x]]; + bgcol = cpu.vic.colors[1]; + x++; + + *p++ = (chr & 0x80) ? fgcol : bgcol; if (p >= pe) break; + *p++ = (chr & 0x40) ? fgcol : bgcol; if (p >= pe) break; + *p++ = (chr & 0x20) ? fgcol : bgcol; if (p >= pe) break; + *p++ = (chr & 0x10) ? fgcol : bgcol; if (p >= pe) break; + *p++ = (chr & 0x08) ? fgcol : bgcol; if (p >= pe) break; + *p++ = (chr & 0x04) ? fgcol : bgcol; if (p >= pe) break; + *p++ = (chr & 0x02) ? fgcol : bgcol; if (p >= pe) break; + *p++ = (chr & 0x01) ? fgcol : bgcol; + }; + PRINTOVERFLOW + } + while (x<40) {BADLINE(x); x++;} +}; + +/*****************************************************************************************************/ +void mode1 (tpixel *p, const tpixel *pe, uint16_t *spl, const uint16_t vc) { + /* + Multicolor-Textmodus (ECM/BMM/MCM=0/0/1) + Dieser Modus ermöglicht es, auf Kosten der horizontalen Auflösung vierfarbige Zeichen darzustellen. + Ist Bit 11 der c-Daten Null, wird das Zeichen wie im Standard-Textmodus dargestellt, wobei aber nur die + Farben 0-7 für den Vordergrund zur Verfügung stehen. Ist Bit 11 gesetzt, bilden jeweils zwei horizontal + benachbarte Bits der Punktmatrix ein Pixel. Dadurch ist die Auflösung des Zeichens auf 4×8 reduziert + (die Pixel sind doppelt so breit, die Gesamtbreite der Zeichen ändert sich also nicht). + Interessant ist, daß nicht nur die Bitkombination „00”, sondern auch „01” für die Spritepriorität + und -kollisionserkennung zum "Hintergrund" gezählt wird. + + +----+----+----+----+----+----+----+----+ + | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + +----+----+----+----+----+----+----+----+ + | 8 Pixel (1 Bit/Pixel) | + | | MC-Flag = 0 + | "0": Hintergrundfarbe 0 ($d021) | + | "1": Farbe aus Bits 8-10 der c-Daten | + +---------------------------------------+ + | 4 Pixel (2 Bit/Pixel) | + | | + | "00": Hintergrundfarbe 0 ($d021) | MC-Flag = 1 + | "01": Hintergrundfarbe 1 ($d022) | + | "10": Hintergrundfarbe 2 ($d023) | + | "11": Farbe aus Bits 8-10 der c-Daten | + +---------------------------------------+ + + */ + + // POKE 53270,PEEK(53270) OR 16 + // poke 53270,peek(53270) or 16 + + uint16_t bgcol, fgcol, pixel; + uint16_t colors[4]; + uint8_t chr; + uint8_t x = 0; + + CHARSETPTR(); + + if (cpu.vic.lineHasSprites) { + + colors[0] = cpu.vic.B0C; + + do { + + if (cpu.vic.idle) { + cpu_clock(1); + fgcol = colors[1] = colors[2] = colors[3] = 0; + chr = cpu.RAM[cpu.vic.bank + 0x3fff]; + } else { + BADLINE(x); + fgcol = cpu.vic.lineMemCol[x]; + colors[1] = cpu.vic.R[0x22]; + colors[2] = cpu.vic.R[0x23]; + colors[3] = fgcol & 0x07; + chr = cpu.vic.charsetPtr[cpu.vic.lineMemChr[x] * 8]; + } + + x++; + + if ((fgcol & 0x08) == 0) { //Zeichen ist HIRES + + unsigned m = min(8, pe - p); + for (unsigned i = 0; i < m; i++) { + + int sprite = *spl++; + + if (sprite) { // Sprite: Ja + + /* + Sprite-Prioritäten (Anzeige) + MDP = 1: Grafikhintergrund, Sprite, Vordergrund + MDP = 0: Grafikhintergrund, Vordergrund, Sprite + + Kollision: + Eine Kollision zwischen Sprites und anderen Grafikdaten wird erkannt, + sobald beim Bildaufbau ein nicht transparentes Spritepixel und ein Vordergrundpixel ausgegeben wird. + + */ + int spritenum = SPRITENUM(sprite); + pixel = sprite & 0x0f; //Hintergrundgrafik + if (sprite & 0x4000) { // MDP = 1 + if (chr & 0x80) { //Vordergrundpixel ist gesetzt + cpu.vic.fgcollision |= spritenum; + pixel = colors[3]; + } + } else { // MDP = 0 + if (chr & 0x80) cpu.vic.fgcollision |= spritenum; //Vordergrundpixel ist gesetzt + } + + } else { // Kein Sprite + pixel = (chr >> 7) ? colors[3] : colors[0]; + } + + *p++ = cpu.vic.palette[pixel]; + + chr = chr << 1; + } + + } else {//Zeichen ist MULTICOLOR + + for (unsigned i = 0; i < 4; i++) { + if (p >= pe) break; + int c = (chr >> 6) & 0x03; + chr = chr << 2; + + int sprite = *spl++; + + if (sprite) { // Sprite: Ja + int spritenum = SPRITENUM(sprite); + pixel = sprite & 0x0f; //Hintergrundgrafik + if (sprite & 0x4000) { // MDP = 1 + + if (chr & 0x80) { //Vordergrundpixel ist gesetzt + cpu.vic.fgcollision |= spritenum; + pixel = colors[c]; + } + + } else { // MDP = 0 + if (chr & 0x80) cpu.vic.fgcollision |= spritenum; //Vordergrundpixel ist gesetzt + } + + } else { // Kein Sprite + pixel = colors[c]; + + } + *p++ = cpu.vic.palette[pixel]; + if (p >= pe) break; + + sprite = *spl++; + + //Das gleiche nochmal für das nächste Pixel + if (sprite) { // Sprite: Ja + int spritenum = SPRITENUM(sprite); + pixel = sprite & 0x0f; //Hintergrundgrafik + if (sprite & 0x4000) { // MDP = 1 + + if (chr & 0x80) { //Vordergrundpixel ist gesetzt + cpu.vic.fgcollision |= spritenum; + pixel = colors[c]; + } + } else { // MDP = 0 + if (chr & 0x80) cpu.vic.fgcollision |= spritenum; //Vordergrundpixel ist gesetzt + } + } else { // Kein Sprite + pixel = colors[c]; + } + *p++ = cpu.vic.palette[pixel]; + + } + + } + + } while (p < pe); + PRINTOVERFLOWS + } else { //Keine Sprites + + while (p < pe - 8) { + + int c; + + bgcol = cpu.vic.colors[1]; + colors[0] = bgcol; + + if (cpu.vic.idle) { + cpu_clock(1); + c = colors[1] = colors[2] = colors[3] = 0; + chr = cpu.RAM[cpu.vic.bank + 0x3fff]; + } else { + BADLINE(x); + + colors[1] = cpu.vic.colors[2]; + colors[2] = cpu.vic.colors[3]; + + chr = cpu.vic.charsetPtr[cpu.vic.lineMemChr[x] * 8]; + c = cpu.vic.lineMemCol[x]; + } + + x++; + + if ((c & 0x08) == 0) { //Zeichen ist HIRES + fgcol = cpu.vic.palette[c & 0x07]; + *p++ = (chr & 0x80) ? fgcol : bgcol; + *p++ = (chr & 0x40) ? fgcol : bgcol; + *p++ = (chr & 0x20) ? fgcol : bgcol; + *p++ = (chr & 0x10) ? fgcol : bgcol; + *p++ = (chr & 0x08) ? fgcol : bgcol; + *p++ = (chr & 0x04) ? fgcol : bgcol; + *p++ = (chr & 0x02) ? fgcol : bgcol; + *p++ = (chr & 0x01) ? fgcol : bgcol; + } else {//Zeichen ist MULTICOLOR + + colors[3] = cpu.vic.palette[c & 0x07]; + pixel = colors[(chr >> 6) & 0x03]; *p++ = pixel; *p++ = pixel; + pixel = colors[(chr >> 4) & 0x03]; *p++ = pixel; *p++ = pixel; + pixel = colors[(chr >> 2) & 0x03]; *p++ = pixel; *p++ = pixel; + pixel = colors[(chr ) & 0x03]; *p++ = pixel; *p++ = pixel; + } + + }; + + while (p < pe) { + + int c; + + bgcol = cpu.vic.colors[1]; + colors[0] = bgcol; + + if (cpu.vic.idle) { + cpu_clock(1); + c = colors[1] = colors[2] = colors[3] = 0; + chr = cpu.RAM[cpu.vic.bank + 0x3fff]; + } else { + BADLINE(x); + + colors[1] = cpu.vic.colors[2]; + colors[2] = cpu.vic.colors[3]; + + chr = cpu.vic.charsetPtr[cpu.vic.lineMemChr[x] * 8]; + c = cpu.vic.lineMemCol[x]; + } + + x++; + + if ((c & 0x08) == 0) { //Zeichen ist HIRES + fgcol = cpu.vic.palette[c & 0x07]; + *p++ = (chr & 0x80) ? fgcol : bgcol; if (p >= pe) break; + *p++ = (chr & 0x40) ? fgcol : bgcol; if (p >= pe) break; + *p++ = (chr & 0x20) ? fgcol : bgcol; if (p >= pe) break; + *p++ = (chr & 0x10) ? fgcol : bgcol; if (p >= pe) break; + *p++ = (chr & 0x08) ? fgcol : bgcol; if (p >= pe) break; + *p++ = (chr & 0x04) ? fgcol : bgcol; if (p >= pe) break; + *p++ = (chr & 0x02) ? fgcol : bgcol; if (p >= pe) break; + *p++ = (chr & 0x01) ? fgcol : bgcol; + } else {//Zeichen ist MULTICOLOR + + colors[3] = cpu.vic.palette[c & 0x07]; + pixel = colors[(chr >> 6) & 0x03]; *p++ = pixel; if (p >= pe) break; *p++ = pixel; if (p >= pe) break; + pixel = colors[(chr >> 4) & 0x03]; *p++ = pixel; if (p >= pe) break; *p++ = pixel; if (p >= pe) break; + pixel = colors[(chr >> 2) & 0x03]; *p++ = pixel; if (p >= pe) break; *p++ = pixel; if (p >= pe) break; + pixel = colors[(chr ) & 0x03]; *p++ = pixel; if (p >= pe) break; *p++ = pixel; + } + + }; + PRINTOVERFLOW + } + while (x<40) {BADLINE(x); x++;} +} + +/*****************************************************************************************************/ +void mode2 (tpixel *p, const tpixel *pe, uint16_t *spl, const uint16_t vc) { + /* + Standard-Bitmap-Modus (ECM / BMM / MCM = 0/1/0) ("HIRES") + In diesem Modus (wie in allen Bitmap-Modi) liest der VIC die Grafikdaten aus einer 320×200-Bitmap, + in der jedes Bit direkt einem Punkt auf dem Bildschirm entspricht. Die Daten aus der videomatrix + werden für die Farbinformation benutzt. Da die videomatrix weiterhin nur eine 40×25-Matrix ist, + können die Farben nur für Blöcke von 8×8 Pixeln individuell bestimmt werden (also eine Art YC-8:1-Format). + Da die Entwickler des VIC-II den Bitmap-Modus mit sowenig zusätzlichem Schaltungsaufwand wie möglich realisieren wollten + (der VIC-I hatte noch keinen Bitmap-Modus), ist die Bitmap etwas ungewöhnlich im Speicher abgelegt: + Im Gegensatz zu modernen Videochips, die die Bitmap linear aus dem Speicher lesen, bilden beim VIC jeweils 8 aufeinanderfolgende Bytes einen 8×8-Pixelblock + auf dem Bildschirm. Mit den Bits VM10-13 und CB13 aus Register $d018 lassen sich videomatrix und Bitmap im Speicher verschieben. + Im Standard-Bitmap-Modus entspricht jedes Bit in der Bitmap direkt einem Pixel auf dem Bildschirm. + Für jeden 8×8-Block können Vorder- und Hintergrundfarbe beliebig eingestellt werden. + + +----+----+----+----+----+----+----+----+ + | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + +----+----+----+----+----+----+----+----+ + | 8 Pixel (1 Bit/Pixel) | + | | + | "0": Farbe aus Bits 0-3 der c-Daten | + | "1": Farbe aus Bits 4-7 der c-Daten | + +---------------------------------------+ + + + http://www.devili.iki.fi/Computers/Commodore/C64/Programmers_Reference/Chapter_3/page_127.html + */ + + uint8_t chr; + uint16_t fgcol, pixel; + uint16_t bgcol; + uint8_t x = 0; + uint8_t * bP = cpu.vic.bitmapPtr + vc * 8 + cpu.vic.rc; + + if (cpu.vic.lineHasSprites) { + do { + + BADLINE(x); + + uint8_t t = cpu.vic.lineMemChr[x]; + fgcol = t >> 4; + bgcol = t & 0x0f; + chr = bP[x * 8]; + + x++; + + unsigned m = min(8, pe - p); + for (unsigned i = 0; i < m; i++) { + + int sprite = *spl++; + + chr = chr << 1; + if (sprite) { // Sprite: Ja + /* + Sprite-Prioritäten (Anzeige) + MDP = 1: Grafikhintergrund, Sprite, Vordergrund + MDP = 0: Grafikhintergung, Vordergrund, Sprite + + Kollision: + Eine Kollision zwischen Sprites und anderen Grafikdaten wird erkannt, + sobald beim Bildaufbau ein nicht transparentes Spritepixel und ein Vordergrundpixel ausgegeben wird. + + */ + int spritenum = SPRITENUM(sprite); + pixel = sprite & 0x0f; //Hintergrundgrafik + if (sprite & 0x4000) { // MDP = 1 + if (chr & 0x80) { //Vordergrundpixel ist gesetzt + cpu.vic.fgcollision |= spritenum; + pixel = fgcol; + } + } else { // MDP = 0 + if (chr & 0x80) cpu.vic.fgcollision |= spritenum; //Vordergrundpixel ist gesetzt + } + + } else { // Kein Sprite + pixel = (chr & 0x80) ? fgcol :cpu.vic.B0C; + } + + *p++ = cpu.vic.palette[pixel]; + + } + } while (p < pe); + PRINTOVERFLOWS + } else { //Keine Sprites + + while (p < pe - 8) { + //color-ram not used! + BADLINE(x); + + uint8_t t = cpu.vic.lineMemChr[x]; + fgcol = cpu.vic.palette[t >> 4]; + bgcol = cpu.vic.palette[t & 0x0f]; + chr = bP[x * 8]; + x++; + + *p++ = (chr & 0x80) ? fgcol : bgcol; + *p++ = (chr & 0x40) ? fgcol : bgcol; + *p++ = (chr & 0x20) ? fgcol : bgcol; + *p++ = (chr & 0x10) ? fgcol : bgcol; + *p++ = (chr & 0x08) ? fgcol : bgcol; + *p++ = (chr & 0x04) ? fgcol : bgcol; + *p++ = (chr & 0x02) ? fgcol : bgcol; + *p++ = (chr & 0x01) ? fgcol : bgcol; + }; + while (p < pe) { + //color-ram not used! + BADLINE(x); + + uint8_t t = cpu.vic.lineMemChr[x]; + fgcol = cpu.vic.palette[t >> 4]; + bgcol = cpu.vic.palette[t & 0x0f]; + chr = bP[x * 8]; + + x++; + + *p++ = (chr & 0x80) ? fgcol : bgcol; if (p >= pe) break; + *p++ = (chr & 0x40) ? fgcol : bgcol; if (p >= pe) break; + *p++ = (chr & 0x20) ? fgcol : bgcol; if (p >= pe) break; + *p++ = (chr & 0x10) ? fgcol : bgcol; if (p >= pe) break; + *p++ = (chr & 0x08) ? fgcol : bgcol; if (p >= pe) break; + *p++ = (chr & 0x04) ? fgcol : bgcol; if (p >= pe) break; + *p++ = (chr & 0x02) ? fgcol : bgcol; if (p >= pe) break; + *p++ = (chr & 0x01) ? fgcol : bgcol; + + }; + PRINTOVERFLOW + } + while (x<40) {BADLINE(x); x++;} +} +/*****************************************************************************************************/ +void mode3 (tpixel *p, const tpixel *pe, uint16_t *spl, const uint16_t vc) { + /* + Multicolor-Bitmap-Modus (ECM/BMM/MCM=0/1/1) + + Ähnlich wie beim Multicolor-Textmodus bilden auch in diesem Modus jeweils + zwei benachbarte Bits ein (doppelt so breites) Pixel. Die Auflösung + reduziert sich damit auf 160×200 Pixel. + + Genau wie beim Multicolor-Textmodus wird die Bitkombination "01" für die + Spritepriorität und -kollisionserkennung zum "Hintergrund" gezählt. + + +----+----+----+----+----+----+----+----+ + | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + +----+----+----+----+----+----+----+----+ + | 4 Pixel (2 Bit/Pixel) | + | | + | "00": Hintergrundfarbe 0 ($d021) | + | "01": Farbe aus Bits 4-7 der c-Daten | + | "10": Farbe aus Bits 0-3 der c-Daten | + | "11": Farbe aus Bits 8-11 der c-Daten | + +---------------------------------------+ + + POKE 53265,PEEK(53625)OR 32: POKE 53270,PEEK(53270)OR 16 + */ + uint8_t * bP = cpu.vic.bitmapPtr + vc * 8 + cpu.vic.rc; + uint16_t colors[4]; + uint16_t pixel; + uint8_t chr, x; + + x = 0; + + if (cpu.vic.lineHasSprites) { + colors[0] = cpu.vic.B0C; + do { + + if (cpu.vic.idle) { + cpu_clock(1); + colors[1] = colors[2] = colors[3] = 0; + chr = cpu.RAM[cpu.vic.bank + 0x3fff]; + } else { + BADLINE(x); + uint8_t t = cpu.vic.lineMemChr[x]; + colors[1] = t >> 4;//10 + colors[2] = t & 0x0f; //01 + colors[3] = cpu.vic.lineMemCol[x]; + chr = bP[x * 8]; + }; + + x++; + + for (unsigned i = 0; i < 4; i++) { + if (p >= pe) break; + uint32_t c = (chr >> 6) & 0x03; + chr = chr << 2; + + int sprite = *spl++; + + if (sprite) { // Sprite: Ja + int spritenum = SPRITENUM(sprite); + pixel = sprite & 0x0f; //Hintergrundgrafik + if (sprite & 0x4000) { // MDP = 1 + if (c & 0x02) { //Vordergrundpixel ist gesetzt + cpu.vic.fgcollision |= spritenum; + pixel = colors[c]; + } + } else { // MDP = 0 + if (c & 0x02) cpu.vic.fgcollision |= spritenum; //Vordergundpixel ist gesetzt + } + } else { // Kein Sprite + pixel = colors[c]; + } + + *p++ = cpu.vic.palette[pixel]; + if (p >= pe) break; + + sprite = *spl++; + + if (sprite) { // Sprite: Ja + int spritenum = SPRITENUM(sprite); + pixel = sprite & 0x0f; //Hintergrundgrafik + if (sprite & 0x4000) { // MDP = 1 + + if (c & 0x02) { //Vordergrundpixel ist gesetzt + cpu.vic.fgcollision |= spritenum; + pixel = colors[c]; + } + } else { // MDP = 0 + if (c & 0x02) cpu.vic.fgcollision |= spritenum; //Vordergundpixel ist gesetzt + } + } else { // Kein Sprite + pixel = colors[c]; + } + + *p++ = cpu.vic.palette[pixel]; + + } + + } while (p < pe); + PRINTOVERFLOWS + + } else { //Keine Sprites + + while (p < pe - 8) { + + colors[0] = cpu.vic.colors[1]; + + if (cpu.vic.idle) { + cpu_clock(1); + colors[1] = colors[2] = colors[3] = 0; + chr = cpu.RAM[cpu.vic.bank + 0x3fff]; + } else { + BADLINE(x); + + uint8_t t = cpu.vic.lineMemChr[x]; + colors[1] = cpu.vic.palette[t >> 4];//10 + colors[2] = cpu.vic.palette[t & 0x0f]; //01 + colors[3] = cpu.vic.palette[cpu.vic.lineMemCol[x]]; + chr = bP[x * 8]; + } + + x++; + pixel = colors[(chr >> 6) & 0x03]; *p++ = pixel; *p++ = pixel; + pixel = colors[(chr >> 4) & 0x03]; *p++ = pixel; *p++ = pixel; + pixel = colors[(chr >> 2) & 0x03]; *p++ = pixel; *p++ = pixel; + pixel = colors[chr & 0x03]; *p++ = pixel; *p++ = pixel; + + }; + while (p < pe) { + + colors[0] = cpu.vic.colors[1]; + + if (cpu.vic.idle) { + cpu_clock(1); + colors[1] = colors[2] = colors[3] = 0; + chr = cpu.RAM[cpu.vic.bank + 0x3fff]; + } else { + BADLINE(x); + + uint8_t t = cpu.vic.lineMemChr[x]; + colors[1] = cpu.vic.palette[t >> 4];//10 + colors[2] = cpu.vic.palette[t & 0x0f]; //01 + colors[3] = cpu.vic.palette[cpu.vic.lineMemCol[x]]; + chr = bP[x * 8]; + } + + x++; + pixel = colors[(chr >> 6) & 0x03]; *p++ = pixel; if (p >= pe) break; *p++ = pixel; if (p >= pe) break; + pixel = colors[(chr >> 4) & 0x03]; *p++ = pixel; if (p >= pe) break; *p++ = pixel; if (p >= pe) break; + pixel = colors[(chr >> 2) & 0x03]; *p++ = pixel; if (p >= pe) break; *p++ = pixel; if (p >= pe) break; + pixel = colors[chr & 0x03]; *p++ = pixel; if (p >= pe) break; *p++ = pixel; + + }; + PRINTOVERFLOW + } + while (x<40) {BADLINE(x); x++;} +} +/*****************************************************************************************************/ +void mode4 (tpixel *p, const tpixel *pe, uint16_t *spl, const uint16_t vc) { + //ECM-Textmodus (ECM/BMM/MCM=1/0/0) + /* + Dieser Textmodus entspricht dem Standard-Textmodus, erlaubt es aber, für + jedes einzelne Zeichen eine von vier Hintergrundfarben auszuwählen. Die + Auswahl geschieht über die oberen beiden Bits des Zeichenzeigers. Dadurch + reduziert sich der Zeichenvorrat allerdings von 256 auf 64 Zeichen. + + +----+----+----+----+----+----+----+----+ + | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + +----+----+----+----+----+----+----+----+ + | 8 Pixel (1 Bit/Pixel) | + | | + | "0": Je nach Bits 6/7 der c-Daten | + | 00: Hintergrundfarbe 0 ($d021) | + | 01: Hintergrundfarbe 1 ($d022) | + | 10: Hintergrundfarbe 2 ($d023) | + | 11: Hintergrundfarbe 3 ($d024) | + | "1": Farbe aus Bits 8-11 der c-Daten | + +---------------------------------------+ + */ + // https://www.c64-wiki.de/wiki/Hintergrundfarbe + // POKE 53265, PEEK(53265) OR 64:REM CURSOR BLINKT ROT abc + + uint8_t chr, pixel; + uint16_t fgcol; + uint16_t bgcol; + uint8_t x = 0; + + CHARSETPTR(); + if (cpu.vic.lineHasSprites) { + do { + + BADLINE(x); + + uint32_t td = cpu.vic.lineMemChr[x]; + bgcol = cpu.vic.R[0x21 + ((td >> 6) & 0x03)]; + chr = cpu.vic.charsetPtr[(td & 0x3f) * 8]; + fgcol = cpu.vic.lineMemCol[x]; + + x++; + + unsigned m = min(8, pe - p); + for (unsigned i = 0; i < m; i++) { + + int sprite = *spl++; + + if (sprite) { // Sprite: Ja + int spritenum = SPRITENUM(sprite); + if (sprite & 0x4000) { // Sprite: Hinter Text + if (chr & 0x80) { + cpu.vic.fgcollision |= spritenum; + pixel = fgcol; + } else pixel = bgcol; + } else { // Sprite: Vor Text + if (chr & 0x80) cpu.vic.fgcollision |= spritenum; + pixel = sprite & 0x0f; + } + } else { // Kein Sprite + pixel = (chr & 0x80) ? fgcol : bgcol; + } + + chr = chr << 1; + *p++ = cpu.vic.palette[pixel]; + } + } while (p < pe); + PRINTOVERFLOWS + } + else //Keine Sprites + while (p < pe - 8) { + + BADLINE(x); + + uint32_t td = cpu.vic.lineMemChr[x]; + bgcol = cpu.vic.palette[cpu.vic.R[0x21 + ((td >> 6) & 0x03)]]; + chr = cpu.vic.charsetPtr[(td & 0x3f) * 8]; + fgcol = cpu.vic.palette[cpu.vic.lineMemCol[x]]; + x++; + + *p++ = (chr & 0x80) ? fgcol : bgcol; + *p++ = (chr & 0x40) ? fgcol : bgcol; + *p++ = (chr & 0x20) ? fgcol : bgcol; + *p++ = (chr & 0x10) ? fgcol : bgcol; + *p++ = (chr & 0x08) ? fgcol : bgcol; + *p++ = (chr & 0x04) ? fgcol : bgcol; + *p++ = (chr & 0x02) ? fgcol : bgcol; + *p++ = (chr & 0x01) ? fgcol : bgcol; + + }; + while (p < pe) { + + BADLINE(x); + + uint32_t td = cpu.vic.lineMemChr[x]; + bgcol = cpu.vic.palette[cpu.vic.R[0x21 + ((td >> 6) & 0x03)]]; + chr = cpu.vic.charsetPtr[(td & 0x3f) * 8]; + fgcol = cpu.vic.palette[cpu.vic.lineMemCol[x]]; + + x++; + + *p++ = (chr & 0x80) ? fgcol : bgcol; if (p >= pe) break; + *p++ = (chr & 0x40) ? fgcol : bgcol; if (p >= pe) break; + *p++ = (chr & 0x20) ? fgcol : bgcol; if (p >= pe) break; + *p++ = (chr & 0x10) ? fgcol : bgcol; if (p >= pe) break; + *p++ = (chr & 0x08) ? fgcol : bgcol; if (p >= pe) break; + *p++ = (chr & 0x04) ? fgcol : bgcol; if (p >= pe) break; + *p++ = (chr & 0x02) ? fgcol : bgcol; if (p >= pe) break; + *p++ = (chr & 0x01) ? fgcol : bgcol; + + }; + PRINTOVERFLOW + while (x<40) {BADLINE(x); x++;} +} + +/*****************************************************************************************************/ +/* Ungültige Modi ************************************************************************************/ +/*****************************************************************************************************/ + +void mode5 (tpixel *p, const tpixel *pe, uint16_t *spl, const uint16_t vc) { + /* + Ungültiger Textmodus (ECM/BMM/MCM=1/0/1) + + Das gleichzeitige Setzen der ECM- und MCM-Bits wählt keinen der + "offiziellen" Grafikmodi des VIC, sondern erzeugt nur schwarze Pixel. + Nichtsdestotrotz erzeugt der Grafikdatensequenzer auch in diesem Modus + intern gültige Grafikdaten, die die Spritekollisionserkennung triggern + können. Über den Umweg der Spritekollisionen kann man die erzeugten Daten + auch auslesen (sehen kann man nichts, das Bild ist schwarz). Man kann so + allerdings nur Vordergrund- und Hintergrundpixel unterscheiden, die + Farbinformation läßt sich aus den Spritekollisionen nicht gewinnen. + + Die erzeugte Grafik entspricht der des Multicolor-Textmodus, allerdings ist + der Zeichenvorrat genau wie im ECM-Modus auf 64 Zeichen eingeschränkt. + */ + CHARSETPTR(); + + uint8_t chr, pixel; + uint16_t fgcol; + uint8_t x = 0; + + if (cpu.vic.lineHasSprites) { + + do { + + BADLINE(x); + + chr = cpu.vic.charsetPtr[(cpu.vic.lineMemChr[x] & 0x3F) * 8]; + fgcol = cpu.vic.lineMemCol[x]; + + x++; + + if ((fgcol & 0x08) == 0) { //Zeichen ist HIRES + + unsigned m = min(8, pe - p); + for (unsigned i = 0; i < m; i++) { + + int sprite = *spl; + *spl++ = 0; + + if (sprite) { // Sprite: Ja + + /* + Sprite-Prioritäten (Anzeige) + MDP = 1: Grafikhintergrund, Sprite, Vordergrund + MDP = 0: Grafikhintergrund, Vordergrund, Sprite + + Kollision: + Eine Kollision zwischen Sprites und anderen Grafikdaten wird erkannt, + sobald beim Bildaufbau ein nicht transparentes Spritepixel und ein Vordergrundpixel ausgegeben wird. + + */ + int spritenum = SPRITENUM(sprite); + pixel = sprite & 0x0f; //Hintergrundgrafik + + if (sprite & 0x4000) { // MDP = 1 + if (chr & 0x80) { //Vordergrundpixel ist gesetzt + cpu.vic.fgcollision |= spritenum; + pixel = 0; + } + } else { // MDP = 0 + if (chr & 0x80) cpu.vic.fgcollision |= spritenum; //Vordergrundpixel ist gesetzt + } + + } else { // Kein Sprite + pixel = 0; + } + + *p++ = cpu.vic.palette[pixel]; + + chr = chr << 1; + } + + } else {//Zeichen ist MULTICOLOR + + for (unsigned i = 0; i < 4; i++) { + if (p >= pe) break; + + chr = chr << 2; + + int sprite = *spl; + *spl++ = 0; + + if (sprite) { // Sprite: Ja + int spritenum = SPRITENUM(sprite); + pixel = sprite & 0x0f; //Hintergrundgrafik + if (sprite & 0x4000) { // MDP = 1 + + if (chr & 0x80) { //Vordergrundpixel ist gesetzt + cpu.vic.fgcollision |= spritenum; + pixel = 0; + } + } else { // MDP = 0 + if (chr & 0x80) cpu.vic.fgcollision |= spritenum; //Vordergrundpixel ist gesetzt + } + + } else { // Kein Sprite + pixel = 0; + + } + *p++ = cpu.vic.palette[pixel]; + if (p >= pe) break; + + sprite = *spl; + *spl++ = 0; + //Das gleiche nochmal für das nächste Pixel + if (sprite) { // Sprite: Ja + int spritenum = SPRITENUM(sprite); + pixel = sprite & 0x0f; //Hintergrundgrafik + if (sprite & 0x4000) { // MDP = 1 + if (chr & 0x80) { //Vordergrundpixel ist gesetzt + cpu.vic.fgcollision |= spritenum; + pixel = 0; + } + } else { // MDP = 0 + if (chr & 0x80) cpu.vic.fgcollision |= spritenum; //Vordergrundpixel ist gesetzt + } + } else { // Kein Sprite + pixel = 0; + } + *p++ = cpu.vic.palette[pixel]; + + } + + } + + } while (p < pe); + PRINTOVERFLOWS + + } else { //Keine Sprites + //Farbe immer schwarz + const uint16_t bgcol = palette[0]; + while (p < pe - 8) { + + BADLINE(x); + x++; + *p++ = bgcol; *p++ = bgcol; + *p++ = bgcol; *p++ = bgcol; + *p++ = bgcol; *p++ = bgcol; + *p++ = bgcol; *p++ = bgcol; + + }; + while (p < pe) { + + BADLINE(x); + x++; + *p++ = bgcol; if (p >= pe) break; *p++ = bgcol; if (p >= pe) break; + *p++ = bgcol; if (p >= pe) break; *p++ = bgcol; if (p >= pe) break; + *p++ = bgcol; if (p >= pe) break; *p++ = bgcol; if (p >= pe) break; + *p++ = bgcol; if (p >= pe) break; *p++ = bgcol; + + }; + PRINTOVERFLOW + } + while (x<40) {BADLINE(x); x++;} +} +/*****************************************************************************************************/ +void mode6 (tpixel *p, const tpixel *pe, uint16_t *spl, const uint16_t vc) { + /* + Ungültiger Bitmap-Modus 1 (ECM/BMM/MCM=1/1/0) + + Dieser Modus erzeugt nur ebenfalls nur ein schwarzes Bild, die Pixel lassen + sich allerdings auch hier mit dem Spritekollisionstrick auslesen. + + Der Aufbau der Grafik ist im Prinzip wie im Standard-Bitmap-Modus, aber die + Bits 9 und 10 der g-Adressen sind wegen dem gesetzten ECM-Bit immer Null, + entsprechend besteht auch die Grafik - grob gesagt - aus vier + "Abschnitten", die jeweils viermal wiederholt dargestellt werden. + + */ + + uint8_t chr, pixel; + uint8_t x = 0; + uint8_t * bP = cpu.vic.bitmapPtr + vc * 8 + cpu.vic.rc; + + if (cpu.vic.lineHasSprites) { + + do { + + BADLINE(x); + + chr = bP[x * 8]; + + x++; + + unsigned m = min(8, pe - p); + for (unsigned i = 0; i < m; i++) { + + int sprite = *spl; + *spl++ = 0; + + chr = chr << 1; + if (sprite) { // Sprite: Ja + /* + Sprite-Prioritäten (Anzeige) + MDP = 1: Grafikhintergrund, Sprite, Vordergrund + MDP = 0: Grafikhintergung, Vordergrund, Sprite + + Kollision: + Eine Kollision zwischen Sprites und anderen Grafikdaten wird erkannt, + sobald beim Bildaufbau ein nicht transparentes Spritepixel und ein Vordergrundpixel ausgegeben wird. + + */ + int spritenum = SPRITENUM(sprite); + pixel = sprite & 0x0f; //Hintergrundgrafik + if (sprite & 0x4000) { // MDP = 1 + if (chr & 0x80) { //Vordergrundpixel ist gesetzt + cpu.vic.fgcollision |= spritenum; + pixel = 0; + } + } else { // MDP = 0 + if (chr & 0x80) cpu.vic.fgcollision |= spritenum; //Vordergrundpixel ist gesetzt + } + + } else { // Kein Sprite + pixel = 0; + } + + *p++ = cpu.vic.palette[pixel]; + + } + + } while (p < pe); + PRINTOVERFLOWS + + } else { //Keine Sprites + //Farbe immer schwarz + const uint16_t bgcol = palette[0]; + while (p < pe - 8) { + + BADLINE(x); + x++; + *p++ = bgcol; *p++ = bgcol; + *p++ = bgcol; *p++ = bgcol; + *p++ = bgcol; *p++ = bgcol; + *p++ = bgcol; *p++ = bgcol; + + }; + while (p < pe) { + + BADLINE(x); + x++; + *p++ = bgcol; if (p >= pe) break; *p++ = bgcol; if (p >= pe) break; + *p++ = bgcol; if (p >= pe) break; *p++ = bgcol; if (p >= pe) break; + *p++ = bgcol; if (p >= pe) break; *p++ = bgcol; if (p >= pe) break; + *p++ = bgcol; if (p >= pe) break; *p++ = bgcol; + + }; + PRINTOVERFLOW + } + while (x<40) {BADLINE(x); x++;} +} +/*****************************************************************************************************/ +void mode7 (tpixel *p, const tpixel *pe, uint16_t *spl, const uint16_t vc) { + /* + Ungültiger Bitmap-Modus 2 (ECM/BMM/MCM=1/1/1) + + Der letzte ungültige Modus liefert auch ein schwarzes Bild, das sich jedoch + genauso mit Hilfe der Sprite-Grafik-Kollisionen "abtasten" läßt. + + Der Aufbau der Grafik ist im Prinzip wie im Multicolor-Bitmap-Modus, aber + die Bits 9 und 10 der g-Adressen sind wegen dem gesetzten ECM-Bit immer + Null, was sich in der Darstellung genauso wie beim ersten ungültigen + Bitmap-Modus wiederspiegelt. Die Bitkombination "01" wird wie gewohnt zum + Hintergrund gezählt. + + */ + + uint8_t chr; + uint8_t x = 0; + uint16_t pixel; + uint8_t * bP = cpu.vic.bitmapPtr + vc * 8 + cpu.vic.rc; + + if (cpu.vic.lineHasSprites) { + + do { + + BADLINE(x); + + chr = bP[x * 8]; + x++; + + for (unsigned i = 0; i < 4; i++) { + if (p >= pe) break; + + chr = chr << 2; + + int sprite = *spl; + *spl++ = 0; + + if (sprite) { // Sprite: Ja + int spritenum = SPRITENUM(sprite); + pixel = sprite & 0x0f;//Hintergrundgrafik + if (sprite & 0x4000) { // MDP = 1 + + if (chr & 0x80) { //Vordergrundpixel ist gesetzt + cpu.vic.fgcollision |= spritenum; + pixel = 0; + } + } else { // MDP = 0 + if (chr & 0x80) cpu.vic.fgcollision |= spritenum; //Vordergundpixel ist gesetzt + } + } else { // Kein Sprite + pixel = 0; + } + + *p++ = cpu.vic.palette[pixel]; + if (p >= pe) break; + + sprite = *spl; + *spl++ = 0; + + if (sprite) { // Sprite: Ja + int spritenum = SPRITENUM(sprite); + pixel = sprite & 0x0f;//Hintergrundgrafik + if (sprite & 0x4000) { // MDP = 1 + + if (chr & 0x80) { //Vordergrundpixel ist gesetzt + cpu.vic.fgcollision |= spritenum; + pixel = 0; + } + } else { // MDP = 0 + if (chr & 0x80) cpu.vic.fgcollision |= spritenum; //Vordergundpixel ist gesetzt + } + } else { // Kein Sprite + pixel = 0; + } + + *p++ = cpu.vic.palette[pixel]; + + } + + } while (p < pe); + PRINTOVERFLOWS + + } else { //Keine Sprites + + const uint16_t bgcol = palette[0]; + while (p < pe - 8) { + + BADLINE(x); + x++; + *p++ = bgcol; *p++ = bgcol; + *p++ = bgcol; *p++ = bgcol; + *p++ = bgcol; *p++ = bgcol; + *p++ = bgcol; *p++ = bgcol; + + }; + while (p < pe) { + + BADLINE(x); + x++; + *p++ = bgcol; if (p >= pe) break; *p++ = bgcol; if (p >= pe) break; + *p++ = bgcol; if (p >= pe) break; *p++ = bgcol; if (p >= pe) break; + *p++ = bgcol; if (p >= pe) break; *p++ = bgcol; if (p >= pe) break; + *p++ = bgcol; if (p >= pe) break; *p++ = bgcol; + + }; + PRINTOVERFLOW + + } + while (x<40) {BADLINE(x); x++;} +} +/*****************************************************************************************************/ +/*****************************************************************************************************/ +/*****************************************************************************************************/ + +typedef void (*modes_t)( tpixel *p, const tpixel *pe, uint16_t *spl, const uint16_t vc ); //Funktionspointer +const modes_t modes[8] = {mode0, mode1, mode2, mode3, mode4, mode5, mode6, mode7}; + + +//static tpixel linebuffer[SCREEN_WIDTH]; + +void vic_do(void) { + + uint16_t vc; + uint16_t xscroll; + tpixel *pe; + tpixel *p; + uint16_t *spl; + uint8_t mode; + + /*****************************************************************************************************/ + /* Linecounter ***************************************************************************************/ + /*****************************************************************************************************/ + /* + ?PEEK(678) NTSC =0 + ?PEEK(678) PAL = 1 + */ + + if ( cpu.vic.rasterLine >= LINECNT ) { + //reSID sound needs much time - too much to keep everything in sync and with stable refreshrate + //but it is not called very often, so most of the time, we have more time than needed. + //We can measure the time needed for a frame and calc a correction factor to speed things up. + unsigned long m = fbmicros(); + cpu.vic.neededTime = (m - cpu.vic.timeStart); + cpu.vic.timeStart = m; + cpu.vic.lineClock.setIntervalFast(LINETIMER_DEFAULT_FREQ - ((float)cpu.vic.neededTime / (float)LINECNT - LINETIMER_DEFAULT_FREQ )); + + cpu.vic.rasterLine = 0; + cpu.vic.vcbase = 0; + cpu.vic.denLatch = 0; + //if (cpu.vic.rasterLine == LINECNT) { + emu_DrawVsync(); + //} + + } else cpu.vic.rasterLine++; + + int r = cpu.vic.rasterLine; + + if (r == cpu.vic.intRasterLine )//Set Rasterline-Interrupt + cpu.vic.R[0x19] |= 1 | ((cpu.vic.R[0x1a] & 1) << 7); + + /*****************************************************************************************************/ + /* Badlines ******************************************************************************************/ + /*****************************************************************************************************/ + /* + Ein Bad-Line-Zustand liegt in einem beliebigen Taktzyklus vor, wenn an der + negativen Flanke von ø0 zu Beginn des Zyklus RASTER >= $30 und RASTER <= + $f7 und die unteren drei Bits von RASTER mit YSCROLL übereinstimmen und in + einem beliebigen Zyklus von Rasterzeile $30 das DEN-Bit gesetzt war. + + (default 3) + yscroll : POKE 53265, PEEK(53265) AND 248 OR 1:POKE 1024,0 + yscroll : poke 53265, peek(53265) and 248 or 1 + + DEN : POKE 53265, PEEK(53265) AND 224 Bildschirm aus + + Die einzige Verwendung von YSCROLL ist der Vergleich mit r in der Badline + + */ + + if (r == 0x30 ) cpu.vic.denLatch |= cpu.vic.DEN; + + /* 3.7.2 + 2. In der ersten Phase von Zyklus 14 jeder Zeile wird VC mit VCBASE geladen + (VCBASE->VC) und VMLI gelöscht. Wenn zu diesem Zeitpunkt ein + Bad-Line-Zustand vorliegt, wird zusätzlich RC auf Null gesetzt. + */ + + vc = cpu.vic.vcbase; + + cpu.vic.badline = (cpu.vic.denLatch && (r >= 0x30) && (r <= 0xf7) && ( (r & 0x07) == cpu.vic.YSCROLL)); + + if (cpu.vic.badline) { + cpu.vic.idle = 0; + cpu.vic.rc = 0; + } + + /*****************************************************************************************************/ + /*****************************************************************************************************/ +#if 1 + { + int t = MAXCYCLESSPRITES3_7 - cpu.vic.spriteCycles3_7; + if (t > 0) cpu_clock(t); + if (cpu.vic.spriteCycles3_7 > 0) cia_clockt(cpu.vic.spriteCycles3_7); + } +#endif + + //HBlank: + cpu_clock(10); + +#ifdef ADDITIONALCYCLES + cpu_clock(ADDITIONALCYCLES); +#endif + + //cpu.vic.videomatrix = cpu.vic.bank + (unsigned)(cpu.vic.R[0x18] & 0xf0) * 64; + + /* Rand oben /unten **********************************************************************************/ + /* + RSEL Höhe des Anzeigefensters Erste Zeile Letzte Zeile + 0 24 Textzeilen/192 Pixel 55 ($37) 246 ($f6) = 192 sichtbare Zeilen, der Rest ist Rand oder unsichtbar + 1 25 Textzeilen/200 Pixel 51 ($33) 250 ($fa) = 200 sichtbare Zeilen, der Rest ist Rand oder unsichtbar + */ + + if (cpu.vic.borderFlag) { + int firstLine = (cpu.vic.RSEL) ? 0x33 : 0x37; + if ((cpu.vic.DEN) && (r == firstLine)) cpu.vic.borderFlag = false; + } else { + int lastLine = (cpu.vic.RSEL) ? 0xfb : 0xf7; + if (r == lastLine) cpu.vic.borderFlag = true; + } + + if (r < FIRSTDISPLAYLINE || r > LASTDISPLAYLINE ) { + if (r == 0) + cpu_clock(CYCLESPERRASTERLINE - 10 - 2 - MAXCYCLESSPRITES - 1); // (minus hblank l + r) + else + cpu_clock(CYCLESPERRASTERLINE - 10 - 2 - MAXCYCLESSPRITES ); + goto noDisplayIncRC; + } + + //max_x = (!cpu.vic.CSEL) ? 40:38; + //p = SCREENMEM + (r - FIRSTDISPLAYLINE) * LINE_MEM_WIDTH; + //p = &linebuffer[0]; //tft.getLineBuffer((r - FIRSTDISPLAYLINE)); + p = (tpixel*)emu_LineBuffer((r - FIRSTDISPLAYLINE)); + pe = p + SCREEN_WIDTH; + //Left Screenborder: Cycle 10 + spl = &cpu.vic.spriteLine[24]; + cpu_clock(6); + + + if (cpu.vic.borderFlag) { + cpu_clock(5); + fastFillLineNoSprites(p, pe + BORDER_RIGHT, cpu.vic.colors[0]); + goto noDisplayIncRC ; + } + + + /*****************************************************************************************************/ + /* DISPLAY *******************************************************************************************/ + /*****************************************************************************************************/ + + + //max_x = (!cpu.vic.CSEL) ? 40:38; + //X-Scrolling: + + xscroll = cpu.vic.XSCROLL; + + if (xscroll > 0) { + uint16_t col = cpu.vic.colors[0]; + + if (!cpu.vic.CSEL) { + cpu_clock(1); + uint16_t sprite; + for (int i = 0; i < xscroll; i++) { + SPRITEORFIXEDCOLOR(); + } + } else { + spl += xscroll; + for (unsigned i = 0; i < xscroll; i++) { + *p++ = col; + } + + } + } + + /*****************************************************************************************************/ + /*****************************************************************************************************/ + /*****************************************************************************************************/ + + + cpu.vic.fgcollision = 0; + mode = (cpu.vic.ECM << 2) | (cpu.vic.BMM << 1) | cpu.vic.MCM; + + if ( !cpu.vic.idle) { + +#if 0 + static uint8_t omode = 99; + if (mode != omode) { + Serial.print("Graphicsmode:"); + Serial.println(mode); + omode = mode; + } +#endif + + modes[mode](p, pe, spl, vc); + vc = (vc + 40) & 0x3ff; + + } else { + /* +3.7.3.9. Idle-Zustand +--------------------- + +Im Idle-Zustand liest der VIC die Grafikdaten von Adresse $3fff (bzw. $39ff +bei gesetztem ECM-Bit) und stellt sie im ausgewählten Grafikmodus dar, +wobei aber die Videomatrix-Daten (normalerweise in den c-Zugriffen gelesen) +nur aus "0"-Bits bestehen. Es wird also immer wiederholt das Byte an +Adresse $3fff/$39ff ausgegeben. + +c-Zugriff + + Es werden keine c-Zugriffe ausgeführt. + + Daten + + +----+----+----+----+----+----+----+----+----+----+----+----+ + | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + +----+----+----+----+----+----+----+----+----+----+----+----+ + | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | + +----+----+----+----+----+----+----+----+----+----+----+----+ + +g-Zugriff + + Adressen (ECM=0) + + +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | + +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + + Adressen (ECM=1) + + +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + | 1 | 1 | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | + +----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + + Daten + + +----+----+----+----+----+----+----+----+ + | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + +----+----+----+----+----+----+----+----+ + | 8 Pixel (1 Bit/Pixel) | Standard-Textmodus/ + | | Multicolor-Textmodus/ + | "0": Hintergrundfarbe 0 ($d021) | ECM-Textmodus + | "1": Schwarz | + +---------------------------------------+ + | 8 Pixel (1 Bit/Pixel) | Standard-Bitmap-Modus/ + | | Ungültiger Textmodus/ + | "0": Schwarz (Hintergrund) | Ungültiger Bitmap-Modus 1 + | "1": Schwarz (Vordergrund) | + +---------------------------------------+ + | 4 Pixel (2 Bit/Pixel) | Multicolor-Bitmap-Modus + | | + | "00": Hintergrundfarbe 0 ($d021) | + | "01": Schwarz (Hintergrund) | + | "10": Schwarz (Vordergrund) | + | "11": Schwarz (Vordergrund) | + +---------------------------------------+ + | 4 Pixel (2 Bit/Pixel) | Ungültiger Bitmap-Modus 2 + | | + | "00": Schwarz (Hintergrund) | + | "01": Schwarz (Hintergrund) | + | "10": Schwarz (Vordergrund) | + | "11": Schwarz (Vordergrund) | + +---------------------------------------+ +*/ + //Modes 1 & 3 + if (mode == 1 || mode == 3) { + modes[mode](p, pe, spl, vc); + } else {//TODO: all other modes + fastFillLine(p, pe, cpu.vic.palette[0], spl); + } + } + + /* + Bei den MBC- und MMC-Interrupts löst jeweils nur die erste Kollision einen + Interrupt aus (d.h. wenn die Kollisionsregister $d01e bzw. $d01f vor der + Kollision den Inhalt Null hatten). Um nach einer Kollision weitere + Interrupts auszulösen, muß das betreffende Register erst durch Auslesen + gelöscht werden. + */ + + if (cpu.vic.fgcollision) { + if (cpu.vic.MD == 0) { + cpu.vic.R[0x19] |= 2 | ( (cpu.vic.R[0x1a] & 2) << 6); + } + cpu.vic.MD |= cpu.vic.fgcollision; + } + + /*****************************************************************************************************/ + + if (!cpu.vic.CSEL) { + cpu_clock(1); + uint16_t col = cpu.vic.colors[0]; + //p = &screen[r - FIRSTDISPLAYLINE][0]; + //p = SCREENMEM + (r - FIRSTDISPLAYLINE) * LINE_MEM_WIDTH + BORDER_LEFT; + //p = &linebuffer[0]; // tft.getLineBuffer((r - FIRSTDISPLAYLINE)); + p = (tpixel*)emu_LineBuffer((r - FIRSTDISPLAYLINE)) + BORDER_LEFT; +#if 0 + // Sprites im Rand + uint16_t sprite; + uint16_t * spl; + spl = &cpu.vic.spriteLine[24 + xscroll]; + + SPRITEORFIXEDCOLOR() + SPRITEORFIXEDCOLOR() + SPRITEORFIXEDCOLOR() + SPRITEORFIXEDCOLOR() + SPRITEORFIXEDCOLOR() + SPRITEORFIXEDCOLOR() + SPRITEORFIXEDCOLOR() //7 +#else + //keine Sprites im Rand + *p++ = col; *p++ = col; *p++ = col; *p++ = col; + *p++ = col; *p++ = col; *p = col; + +#endif + + //Rand rechts: + //p = &screen[r - FIRSTDISPLAYLINE][SCREEN_WIDTH - 9]; + //p = SCREENMEM + (r - FIRSTDISPLAYLINE) * LINE_MEM_WIDTH + SCREEN_WIDTH - 9 + BORDER_LEFT; + //p = &linebuffer[SCREEN_WIDTH - 9 + BORDER_LEFT]; //tft.getLineBuffer((r - FIRSTDISPLAYLINE)) + SCREEN_WIDTH - 9 + BORDER_LEFT; + p = (tpixel*)emu_LineBuffer((r - FIRSTDISPLAYLINE)) + SCREEN_WIDTH - 9 + BORDER_LEFT; + pe = p + 9; + +#if 0 + // Sprites im Rand + spl = &cpu.vic.spriteLine[24 + SCREEN_WIDTH - 9 + xscroll]; + while (p < pe) { + SPRITEORFIXEDCOLOR(); + } +#else + //keine Sprites im Rand + //while (p < pe) { + // *p++ = col; + //} +#endif + + + + } + +// emu_DrawLine8(&linebuffer[0], SCREEN_WIDTH, SCREEN_HEIGHT, (r - FIRSTDISPLAYLINE)); + + +//Rechter Rand nach CSEL, im Textbereich +cpu_clock(5); + + +noDisplayIncRC: + /* 3.7.2 + 5. In der ersten Phase von Zyklus 58 wird geprüft, ob RC=7 ist. Wenn ja, + geht die Videologik in den Idle-Zustand und VCBASE wird mit VC geladen + (VC->VCBASE). Ist die Videologik danach im Display-Zustand (liegt ein + Bad-Line-Zustand vor, ist dies immer der Fall), wird RC erhöht. + */ + + if (cpu.vic.rc == 7) { + cpu.vic.idle = 1; + cpu.vic.vcbase = vc; + } + //Ist dies richtig ?? + if ((!cpu.vic.idle) || (cpu.vic.denLatch && (r >= 0x30) && (r <= 0xf7) && ( (r & 0x07) == cpu.vic.YSCROLL))) { + cpu.vic.rc = (cpu.vic.rc + 1) & 0x07; + } + + + /*****************************************************************************************************/ + /* Sprites *******************************************************************************************/ + /*****************************************************************************************************/ + + cpu.vic.spriteCycles0_2 = 0; + cpu.vic.spriteCycles3_7 = 0; + + if (cpu.vic.lineHasSprites) { + cpu.vic.lineHasSprites = 0; + memset(cpu.vic.spriteLine, 0, sizeof(cpu.vic.spriteLine) ); + } + + uint32_t spriteYCheck = cpu.vic.R[0x15]; //Sprite enabled Register + + if (spriteYCheck) { + + unsigned short R17 = cpu.vic.R[0x17]; //Sprite-y-expansion + unsigned char collision = 0; + short lastSpriteNum = 0; + + for (unsigned short i = 0; i < 8; i++) { + if (!spriteYCheck) break; + + unsigned b = 1 << i; + + if (spriteYCheck & b ) { + spriteYCheck &= ~b; + short y = cpu.vic.R[i * 2 + 1]; + + if ( (r >= y ) && //y-Position > Sprite-y ? + (((r < y + 21) && (~R17 & b )) || // ohne y-expansion + ((r < y + 2 * 21 ) && (R17 & b ))) ) //mit y-expansion + { + + //Sprite Cycles + if (i < 3) { + if (!lastSpriteNum) cpu.vic.spriteCycles0_2 += 1; + cpu.vic.spriteCycles0_2 += 2; + } else { + if (!lastSpriteNum) cpu.vic.spriteCycles3_7 += 1; + cpu.vic.spriteCycles3_7 += 2; + } + lastSpriteNum = i; + //Sprite Cycles END + + + if (r < FIRSTDISPLAYLINE || r > LASTDISPLAYLINE ) continue; + + uint16_t x = (((cpu.vic.R[0x10] >> i) & 1) << 8) | cpu.vic.R[i * 2]; + if (x >= SPRITE_MAX_X) continue; + + unsigned short lineOfSprite = r - y; + if (R17 & b) lineOfSprite = lineOfSprite / 2; // Y-Expansion + unsigned short spriteadr = cpu.vic.bank | cpu.RAM[cpu.vic.videomatrix + (1024 - 8) + i] << 6 | (lineOfSprite * 3); + unsigned spriteData = ((unsigned)cpu.RAM[ spriteadr ] << 16) | ((unsigned)cpu.RAM[ spriteadr + 1 ] << 8) | ((unsigned)cpu.RAM[ spriteadr + 2 ]); + + if (!spriteData) continue; + cpu.vic.lineHasSprites = 1; + + uint16_t * slp = &cpu.vic.spriteLine[x]; //Sprite-Line-Pointer + unsigned short upperByte = ( 0x80 | ( (cpu.vic.MDP & b) ? 0x40 : 0 ) | i ) << 8; //Bit7 = Sprite "da", Bit 6 = Sprite-Priorität vor Grafik/Text, Bits 3..0 = Spritenummer + + //Sprite in Spritezeile schreiben: + if ((cpu.vic.MMC & b) == 0) { // NO MULTICOLOR + + uint16_t color = upperByte | cpu.vic.R[0x27 + i]; + + if ((cpu.vic.MXE & b) == 0) { // NO MULTICOLOR, NO SPRITE-EXPANSION + + for (unsigned cnt = 0; (spriteData > 0) && (cnt < 24); cnt++) { + int c = (spriteData >> 23) & 0x01; + spriteData = (spriteData << 1); + + if (c) { + if (*slp == 0) *slp = color; + else collision |= b | (1 << ((*slp >> 8) & 0x07)); + } + slp++; + + } + + } else { // NO MULTICOLOR, SPRITE-EXPANSION + + for (unsigned cnt = 0; (spriteData > 0) && (cnt < 24); cnt++) { + int c = (spriteData >> 23) & 0x01; + spriteData = (spriteData << 1); + //So wie oben, aber zwei gleiche Pixel + + if (c) { + if (*slp == 0) *slp = color; + else collision |= b | (1 << ((*slp >> 8) & 0x07)); + slp++; + if (*slp == 0) *slp = color; + else collision |= b | (1 << ((*slp >> 8) & 0x07)); + slp++; + } else { + slp += 2; + } + + } + } + + + + } else { // MULTICOLOR + /* Im Mehrfarbenmodus (Multicolor-Modus) bekommen alle Sprites zwei zusätzliche gemeinsame Farben. + Die horizontale Auflösung wird von 24 auf 12 halbiert, da bei der Sprite-Definition jeweils zwei Bits zusammengefasst werden. + */ + uint16_t colors[4]; + //colors[0] = 1; //dummy, color 0 is transparent + colors[1] = upperByte | cpu.vic.R[0x25]; + colors[2] = upperByte | cpu.vic.R[0x27 + i]; + colors[3] = upperByte | cpu.vic.R[0x26]; + + if ((cpu.vic.MXE & b) == 0) { // MULTICOLOR, NO SPRITE-EXPANSION + for (unsigned cnt = 0; (spriteData > 0) && (cnt < 24); cnt++) { + int c = (spriteData >> 22) & 0x03; + spriteData = (spriteData << 2); + + if (c) { + if (*slp == 0) *slp = colors[c]; + else collision |= b | (1 << ((*slp >> 8) & 0x07)); + slp++; + if (*slp == 0) *slp = colors[c]; + else collision |= b | (1 << ((*slp >> 8) & 0x07)); + slp++; + } else { + slp += 2; + } + + } + + } else { // MULTICOLOR, SPRITE-EXPANSION + for (unsigned cnt = 0; (spriteData > 0) && (cnt < 24); cnt++) { + int c = (spriteData >> 22) & 0x03; + spriteData = (spriteData << 2); + + //So wie oben, aber vier gleiche Pixel + if (c) { + if (*slp == 0) *slp = colors[c]; + else collision |= b | (1 << ((*slp >> 8) & 0x07)); + slp++; + if (*slp == 0) *slp = colors[c]; + else collision |= b | (1 << ((*slp >> 8) & 0x07)); + slp++; + if (*slp == 0) *slp = colors[c]; + else collision |= b | (1 << ((*slp >> 8) & 0x07)); + slp++; + if (*slp == 0) *slp = colors[c]; + else collision |= b | (1 << ((*slp >> 8) & 0x07)); + slp++; + } else { + slp += 4; + } + + } + + } + } + + } + else lastSpriteNum = 0; + } + + } + + if (collision) { + if (cpu.vic.MM == 0) { + cpu.vic.R[0x19] |= 4 | ((cpu.vic.R[0x1a] & 4) << 5 ); + } + cpu.vic.MM |= collision; + } + + } + /*****************************************************************************************************/ +#if 0 + { + int t = MAXCYCLESSPRITES0_2 - cpu.vic.spriteCycles0_2; + if (t > 0) cpu_clock(t); + if (cpu.vic.spriteCycles0_2 > 0) cia_clockt(cpu.vic.spriteCycles0_2); + } +#endif + + //HBlank: +#if PAL + cpu_clock(2); +#else + cpu_clock(3); +#endif + + +#if 0 + if (cpu.vic.idle) { + Serial.print("Cycles line "); + Serial.print(r); + Serial.print(": "); + Serial.println(cpu.lineCyclesAbs); + } +#endif + + + return; +} + +/*****************************************************************************************************/ +/*****************************************************************************************************/ +/*****************************************************************************************************/ +void fastFillLineNoSprites(tpixel * p, const tpixel * pe, const uint16_t col) { + int i = 0; + + while (p < pe) { + *p++ = col; + i = (i + 1) & 0x07; + if (!i) CYCLES(1); + } + + +} + +void fastFillLine(tpixel * p, const tpixel * pe, const uint16_t col, uint16_t * spl) { + if (spl != NULL && cpu.vic.lineHasSprites) { + int i = 0; + uint16_t sprite; + while ( p < pe ) { + SPRITEORFIXEDCOLOR(); + i = (i + 1) & 0x07; + if (!i) CYCLES(1); + }; + + } else { + + fastFillLineNoSprites(p, pe, col); + + } +} + +/*****************************************************************************************************/ +/*****************************************************************************************************/ +/*****************************************************************************************************/ + +void vic_displaySimpleModeScreen(void) { +} + + +void vic_do_simple(void) { + uint16_t vc; + int cycles = 0; + +if ( cpu.vic.rasterLine >= LINECNT ) { + + //reSID sound needs much time - too much to keep everything in sync and with stable refreshrate + //but it is not called very often, so most of the time, we have more time than needed. + //We can measure the time needed for a frame and calc a correction factor to speed things up. + unsigned long m = fbmicros(); + cpu.vic.neededTime = (m - cpu.vic.timeStart); + cpu.vic.timeStart = m; + cpu.vic.lineClock.setIntervalFast(LINETIMER_DEFAULT_FREQ - ((float)cpu.vic.neededTime / (float)LINECNT - LINETIMER_DEFAULT_FREQ )); + + cpu.vic.rasterLine = 0; + cpu.vic.vcbase = 0; + cpu.vic.denLatch = 0; + + } else { + cpu.vic.rasterLine++; + cpu_clock(1); + cycles += 1; + } + + int r = cpu.vic.rasterLine; + + if (r == cpu.vic.intRasterLine )//Set Rasterline-Interrupt + cpu.vic.R[0x19] |= 1 | ((cpu.vic.R[0x1a] & 1) << 7); + + cpu_clock(9); + cycles += 9; + + if (r == 0x30 ) cpu.vic.denLatch |= cpu.vic.DEN; + + vc = cpu.vic.vcbase; + + cpu.vic.badline = (cpu.vic.denLatch && (r >= 0x30) && (r <= 0xf7) && ( (r & 0x07) == cpu.vic.YSCROLL)); + + if (cpu.vic.badline) { + cpu.vic.idle = 0; + cpu.vic.rc = 0; + } + + + /* Rand oben /unten **********************************************************************************/ + /* + RSEL Höhe des Anzeigefensters Erste Zeile Letzte Zeile + 0 24 Textzeilen/192 Pixel 55 ($37) 246 ($f6) = 192 sichtbare Zeilen, der Rest ist Rand oder unsichtbar + 1 25 Textzeilen/200 Pixel 51 ($33) 250 ($fa) = 200 sichtbare Zeilen, der Rest ist Rand oder unsichtbar + */ + + if (cpu.vic.borderFlag) { + int firstLine = (cpu.vic.RSEL) ? 0x33 : 0x37; + if ((cpu.vic.DEN) && (r == firstLine)) cpu.vic.borderFlag = false; + } else { + int lastLine = (cpu.vic.RSEL) ? 0xfb : 0xf7; + if (r == lastLine) cpu.vic.borderFlag = true; + } + + + //left screenborder + cpu_clock(6); + cycles += 6; + + CYCLES(40); + cycles += 40; + vc += 40; + + //right screenborder + cpu_clock(4); //1 + cycles += 4; + + + if (cpu.vic.rc == 7) { + cpu.vic.idle = 1; + cpu.vic.vcbase = vc; + } + //Ist dies richtig ?? + if ((!cpu.vic.idle) || (cpu.vic.denLatch && (r >= 0x30) && (r <= 0xf7) && ( (r & 0x07) == cpu.vic.YSCROLL))) { + cpu.vic.rc = (cpu.vic.rc + 1) & 0x07; + } + + cpu_clock(3); //1 + cycles += 3; + + int cyclesleft = CYCLESPERRASTERLINE - cycles; + if (cyclesleft) cpu_clock(cyclesleft); + +} + + +/*****************************************************************************************************/ +/*****************************************************************************************************/ +/*****************************************************************************************************/ + +void installPalette(void) { + memcpy(cpu.vic.palette, (void*)palette, sizeof(cpu.vic.palette)); +} + + +/*****************************************************************************************************/ +/*****************************************************************************************************/ +/*****************************************************************************************************/ + +void vic_adrchange(void) { + uint8_t r18 = cpu.vic.R[0x18]; + cpu.vic.videomatrix = cpu.vic.bank + (unsigned)(r18 & 0xf0) * 64; + + unsigned charsetAddr = r18 & 0x0e; + if ((cpu.vic.bank & 0x4000) == 0) { + if (charsetAddr == 0x04) cpu.vic.charsetPtrBase = ((uint8_t *)&rom_characters); + else if (charsetAddr == 0x06) cpu.vic.charsetPtrBase = ((uint8_t *)&rom_characters) + 0x800; + else + cpu.vic.charsetPtrBase = &cpu.RAM[charsetAddr * 0x400 + cpu.vic.bank] ; + } else + cpu.vic.charsetPtrBase = &cpu.RAM[charsetAddr * 0x400 + cpu.vic.bank]; + + cpu.vic.bitmapPtr = (uint8_t*) &cpu.RAM[cpu.vic.bank | ((r18 & 0x08) * 0x400)]; + if ((cpu.vic.R[0x11] & 0x60) == 0x60) cpu.vic.bitmapPtr = (uint8_t*)((uintptr_t)cpu.vic.bitmapPtr & 0xf9ff); + +} +/*****************************************************************************************************/ +void vic_write(uint32_t address, uint8_t value) { + + address &= 0x3F; + + switch (address) { + case 0x11 : + cpu.vic.R[address] = value; + cpu.vic.intRasterLine = (cpu.vic.intRasterLine & 0xff) | ((((uint16_t) value) << 1) & 0x100); + if (cpu.vic.rasterLine == 0x30 ) cpu.vic.denLatch |= value & 0x10; + + cpu.vic.badline = (cpu.vic.denLatch && (cpu.vic.rasterLine >= 0x30) && (cpu.vic.rasterLine <= 0xf7) && ( (cpu.vic.rasterLine & 0x07) == (value & 0x07))); + + if (cpu.vic.badline) { + cpu.vic.idle = 0; + } + + vic_adrchange(); + + break; + case 0x12 : + cpu.vic.intRasterLine = (cpu.vic.intRasterLine & 0x100) | value; + cpu.vic.R[address] = value; + break; + case 0x18 : + cpu.vic.R[address] = value; + vic_adrchange(); + break; + case 0x19 : //IRQs + cpu.vic.R[0x19] &= (~value & 0x0f); + break; + case 0x1A : //IRQ Mask + cpu.vic.R[address] = value & 0x0f; + break; + case 0x1e: + case 0x1f: + cpu.vic.R[address] = 0; + break; + case 0x20 ... 0x2E: + cpu.vic.R[address] = value & 0x0f; + cpu.vic.colors[address - 0x20] = cpu.vic.palette[value & 0x0f]; + break; + case 0x2F ... 0x3F: + break; + default : + cpu.vic.R[address] = value; + break; + } + + //#if DEBUGVIC +#if 0 + Serial.print("VIC "); + Serial.print(address, HEX); + Serial.print("="); + Serial.println(value, HEX); + //logAddr(address, value, 1); +#endif +} + +/*****************************************************************************************************/ +/*****************************************************************************************************/ +/*****************************************************************************************************/ + +uint8_t vic_read(uint32_t address) { + uint8_t ret; + + address &= 0x3F; + switch (address) { + + case 0x11: + ret = (cpu.vic.R[address] & 0x7F) | ((cpu.vic.rasterLine & 0x100) >> 1); + break; + case 0x12: + ret = cpu.vic.rasterLine; + break; + case 0x16: + ret = cpu.vic.R[address] | 0xC0; + break; + case 0x18: + ret = cpu.vic.R[address] | 0x01; + break; + case 0x19: + ret = cpu.vic.R[address] | 0x70; + break; + case 0x1a: + ret = cpu.vic.R[address] | 0xF0; + break; + case 0x1e: + case 0x1f: + ret = cpu.vic.R[address]; + cpu.vic.R[address] = 0; + break; + case 0x20 ... 0x2E: + ret = cpu.vic.R[address] | 0xF0; + break; + case 0x2F ... 0x3F: + ret = 0xFF; + break; + default: + ret = cpu.vic.R[address]; + break; + } + +#if DEBUGVIC + Serial.print("VIC "); + logAddr(address, ret, 0); +#endif + return ret; +} + +/*****************************************************************************************************/ +/*****************************************************************************************************/ +/*****************************************************************************************************/ + +void resetVic(void) { + enableCycleCounter(); + + cpu.vic.intRasterLine = 0; + cpu.vic.rasterLine = 0; + cpu.vic.lineHasSprites = 0; + memset(&cpu.RAM[0x400], 0, 1000); + memset(&cpu.vic, 0, sizeof(cpu.vic)); + + + + installPalette(); + + //http://dustlayer.com/vic-ii/2013/4/22/when-visibility-matters + cpu.vic.R[0x11] = 0x9B; + cpu.vic.R[0x16] = 0x08; + cpu.vic.R[0x18] = 0x14; + cpu.vic.R[0x19] = 0x0f; + + for (unsigned i = 0; i < sizeof(cpu.vic.COLORRAM); i++) + cpu.vic.COLORRAM[i] = (rand() & 0x0F); + + cpu.RAM[0x39FF] = 0x0; + cpu.RAM[0x3FFF] = 0x0; + cpu.RAM[0x39FF + 16384] = 0x0; + cpu.RAM[0x3FFF + 16384] = 0x0; + cpu.RAM[0x39FF + 32768] = 0x0; + cpu.RAM[0x3FFF + 32768] = 0x0; + cpu.RAM[0x39FF + 49152] = 0x0; + cpu.RAM[0x3FFF + 49152] = 0x0; + + vic_adrchange(); +} + + +/* + ?PEEK(678) NTSC =0 + ?PEEK(678) PAL = 1 + PRINT TIME$ +*/ +/* + Raster- Takt- sichtb. sichtbare + VIC-II System zeilen zyklen Zeilen Pixel/Zeile + ------------------------------------------------------- + 6569 PAL 312 63 284 403 + 6567R8 NTSC 263 65 235 418 + 6567R56A NTSC 262 64 234 411 +*/ diff --git a/MCUME_pico/vga_t4/VGA_t4.cpp b/MCUME_pico/vga_t4/VGA_t4.cpp index 70ddd42..f7bd217 100755 --- a/MCUME_pico/vga_t4/VGA_t4.cpp +++ b/MCUME_pico/vga_t4/VGA_t4.cpp @@ -34,7 +34,7 @@ static vga_pixel * visible_framebuffer = NULL; static vga_pixel * framebuffer = NULL; static vga_pixel * fb0 = NULL; -uint8_t * pio_fb = NULL; +uint32_t * pio_fb = NULL; int pio_fbwidth=0; @@ -46,14 +46,13 @@ static int fb_stride; static int left_border; static int top_border; +static scanvideo_mode_t vga_mode; -static semaphore_t video_initted; +static semaphore_t core1_initted; static void core1_func(); PolyDef PolySet; // will contain a polygon data -#define vga_mode vga_mode_320x240_60 - #define RGBVAL16(r,g,b) ( (((b>>3)&0x1f)<<11) | (((g>>2)&0x3f)<<5) | (((r>>3)&0x1f)<<0) ) #define PICO_SCANVIDEO_PIXEL_FROM_RGBVAL8(rgb) (((rgb&0x3)<<(PICO_SCANVIDEO_PIXEL_BSHIFT))|(((rgb&0x1C)>>2)<<(PICO_SCANVIDEO_PIXEL_GSHIFT))|(((rgb&0xE0)>>5)<<(PICO_SCANVIDEO_PIXEL_RSHIFT))) @@ -63,15 +62,16 @@ static void core1_sio_irq(); static void core1_func() { - // initialize video and interrupts on core 1 - scanvideo_setup(&vga_mode); - scanvideo_timing_enable(true); - sem_release(&video_initted); - + // initialize video + scanvideo_setup(&vga_mode); + scanvideo_timing_enable(true); + multicore_fifo_clear_irq(); irq_set_exclusive_handler(SIO_IRQ_PROC1,core1_sio_irq); //irq_set_priority (SIO_IRQ_PROC1, 129); irq_set_enabled(SIO_IRQ_PROC1,true); + + sem_release(&core1_initted); while (true) { tight_loop_contents(); @@ -116,26 +116,23 @@ vga_error_t VGA_T4::begin(vga_mode_t mode) { switch(mode) { case VGA_MODE_320x240: + vga_mode = vga_mode_320x240_60; fb_width = 320; - fb_height = 240; - left_border = (320-fb_width)/2; - top_border = (240-fb_height)/2; + fb_height = 240; + left_border = 0; + top_border = 0; fb_stride = fb_width+16; break; - case VGA_MODE_320x480: - break; - case VGA_MODE_640x240: - break; - case VGA_MODE_640x480: - break; - case VGA_MODE_512x240: - break; - case VGA_MODE_512x480: - break; case VGA_MODE_352x240: + break; + case VGA_MODE_400x240: + vga_mode = vga_mode_tft_400x240_50; + fb_width = 400; + fb_height = 240; + left_border = 0; + top_border = 0; + fb_stride = fb_width+16; break; - case VGA_MODE_352x480: - break; } @@ -147,19 +144,19 @@ vga_error_t VGA_T4::begin(vga_mode_t mode) init_pio_framebuffer(fb0); - pio_fb = fb0; - pio_fbwidth = (320 + 2*6)/4; + pio_fb = (uint32_t*)fb0; + pio_fbwidth = (fb_width + 2*6)/4; visible_framebuffer = fb0+PIO_FB_OFFSET; framebuffer = fb0+PIO_FB_OFFSET; - // create a semaphore to be posted when video init is complete - sem_init(&video_initted, 0, 1); - // launch all the video on core 1, so it isn't affected by USB handling on core 0 + // create a semaphore to be posted when audio init is complete + sem_init(&core1_initted, 0, 1); + multicore_launch_core1(core1_func); - // wait for initialization of video to be complete - sem_acquire_blocking(&video_initted); + // wait for initialization of audio to be complete + sem_acquire_blocking(&core1_initted); return(VGA_OK); } @@ -173,10 +170,11 @@ void VGA_T4::debug() } // retrieve size of the frame buffer -void VGA_T4::get_frame_buffer_size(int *width, int *height) +int VGA_T4::get_frame_buffer_size(int *width, int *height) { - *width = fb_width; - *height = fb_height; + if (width != nullptr) *width = fb_width; + if (height != nullptr) *height = fb_height; + return fb_stride; } void VGA_T4::waitSync() @@ -1450,13 +1448,13 @@ void VGA_T4::run_gfxengine() waitSync(); if (fb1 != NULL) { - if (pio_fb == fb0) { - pio_fb = fb1; + if (pio_fb == (uint32_t*)fb0) { + pio_fb = (uint32_t*)fb1; visible_framebuffer = fb1+PIO_FB_OFFSET; framebuffer = fb0+PIO_FB_OFFSET; } else { - pio_fb = fb0; + pio_fb = (uint32_t*)fb0; visible_framebuffer = fb0+PIO_FB_OFFSET; framebuffer = fb1+PIO_FB_OFFSET; } diff --git a/MCUME_pico/vga_t4/VGA_t4.h b/MCUME_pico/vga_t4/VGA_t4.h index b5af672..0d21ef3 100755 --- a/MCUME_pico/vga_t4/VGA_t4.h +++ b/MCUME_pico/vga_t4/VGA_t4.h @@ -32,13 +32,8 @@ typedef uint8_t vga_pixel; typedef enum vga_mode_t { VGA_MODE_320x240 = 0, - VGA_MODE_320x480 = 1, - VGA_MODE_352x240 = 2, - VGA_MODE_352x480 = 3, - VGA_MODE_512x240 = 4, - VGA_MODE_512x480 = 5, - VGA_MODE_640x240 = 6, - VGA_MODE_640x480 = 7 + VGA_MODE_352x240 = 1, + VGA_MODE_400x240 = 2, } vga_mode_t; @@ -170,7 +165,7 @@ public: void tweak_video(int shiftdelta, int numdelta, int denomdelta); // retrieve real size of the frame buffer - void get_frame_buffer_size(int *width, int *height); + int get_frame_buffer_size(int *width, int *height); // wait next Vsync void waitSync(); diff --git a/MCUME_pico/vga_t4/scanvideo.c b/MCUME_pico/vga_t4/scanvideo.c index e81699f..f8b6a06 100644 --- a/MCUME_pico/vga_t4/scanvideo.c +++ b/MCUME_pico/vga_t4/scanvideo.c @@ -48,7 +48,7 @@ #define PICO_SCANVIDEO_SCANLINE_DMA_CHANNELS_MASK (1u << PICO_SCANVIDEO_SCANLINE_DMA_CHANNEL) -extern uint8_t * pio_fb; +extern uint32_t * pio_fb; extern int pio_fbwidth; @@ -75,6 +75,28 @@ const scanvideo_timing_t vga_timing_640x480_60_default = .enable_den = 0 }; +const scanvideo_timing_t vga_timing_wide_480_50 = + { + .clock_freq = 24000000, //24000000, + + .h_active = 800, + .v_active = 480, + + .h_front_porch = 32, //12, + .h_pulse = 48, + .h_total = 960, + .h_sync_polarity = 0, + + .v_front_porch = 1, + .v_pulse = 2, + .v_total = 500, + .v_sync_polarity = 0, + + .enable_clock = 0, + .clock_polarity = 0, + + .enable_den = 0 + }; const scanvideo_mode_t vga_mode_320x240_60 = { @@ -87,6 +109,15 @@ const scanvideo_mode_t vga_mode_320x240_60 = }; +const scanvideo_mode_t vga_mode_tft_400x240_50 = + { + .default_timing = &vga_timing_wide_480_50, + .pio_program = &video_24mhz_composable, + .width = 400, + .height = 240, + .xscale = 2, + .yscale = 2, + }; #define scanline_assert(x) (void)0 @@ -120,7 +151,6 @@ const scanvideo_pio_program_t video_24mhz_composable = { .configure_pio = video_24mhz_composable_configure_pio }; -#define PIO_WAIT_IRQ4 pio_encode_wait_irq(1, false, 4) static uint8_t video_htiming_load_offset; static uint8_t video_program_load_offset; @@ -231,9 +261,9 @@ void __video_time_critical_func(prepare_for_active_scanline_irqs_enabled)() { full_scanline_buffer_t *fsb = &scanline_buffer; uint16_t scanline = shared_state.scanline.next_scanline_id & 0xffffu; if (scanline < 240) - fsb->core.data = (uint32_t *)&pio_fb[(320+16)*scanline]; + fsb->core.data = &pio_fb[(pio_fbwidth+1)*scanline]; else - fsb->core.data = (uint32_t *)&pio_fb[(320+16)*(0)]; + fsb->core.data = &pio_fb[0]; dma_channel_transfer_from_buffer_now(PICO_SCANVIDEO_SCANLINE_DMA_CHANNEL, fsb->core.data, (uint32_t) fsb->core.data_used); shared_state.scanline.in_vblank = false; @@ -352,19 +382,9 @@ void setup_sm(int sm, uint offset) { sm_config_set_out_shift(&config, true, true, 32); const uint BASE = PICO_SCANVIDEO_SYNC_PIN_BASE; // hsync and vsync are +0 and +1, clock is +2 uint pin_count; -#if PICO_SCANVIDEO_ENABLE_DEN_PIN - pin_count = 3; - // 3 OUT pins and maybe 1 sideset pin following them -#else // 2 OUT pins and 1 sideset pin following them pin_count = 2; -#endif sm_config_set_out_pins(&config, BASE, pin_count); -#if PICO_SCANVIDEO_ENABLE_DEN_PIN - // side set pin as well - sm_config_set_sideset_pins(&config, BASE + pin_count); - pin_count++; -#endif pio_sm_set_consecutive_pindirs(video_pio, sm, BASE, pin_count, true); } @@ -387,9 +407,6 @@ void scanvideo_set_scanline_repeat_fn(scanvideo_scanline_repeat_count_fn fn) { _scanline_repeat_count_fn = fn ? fn : default_scanvideo_scanline_repeat_count_fn; } -bool scanvideo_setup(const scanvideo_mode_t *mode) { - return scanvideo_setup_with_timing(mode, mode->default_timing); -} static pio_program_t copy_program(const pio_program_t *program, uint16_t *instructions, uint32_t max_instructions) { @@ -400,8 +417,7 @@ static pio_program_t copy_program(const pio_program_t *program, uint16_t *instru return copy; } - -bool scanvideo_setup_with_timing(const scanvideo_mode_t *mode, const scanvideo_timing_t *timing) { +static bool scanvideo_setup_with_timing(const scanvideo_mode_t *mode, const scanvideo_timing_t *timing) { __builtin_memset(&shared_state, 0, sizeof(shared_state)); // init non zero members @@ -409,9 +425,6 @@ bool scanvideo_setup_with_timing(const scanvideo_mode_t *mode, const scanvideo_t video_mode = *mode; video_mode.default_timing = timing; - static_assert(BPP == 16, ""); // can't do 8 bit now because of pixel count - // this is no longer necessary - //assert(!(mode->width & 1)); if (!video_mode.yscale_denominator) video_mode.yscale_denominator = 1; // todo is this still necessary? //invalid_params_if(SCANVIDEO_DPI, (timing->v_active % mode->yscale)); @@ -424,15 +437,6 @@ bool scanvideo_setup_with_timing(const scanvideo_mode_t *mode, const scanvideo_t uint pin_mask = 3u << PICO_SCANVIDEO_SYNC_PIN_BASE; bi_decl_if_func_used(bi_2pins_with_names(PICO_SCANVIDEO_SYNC_PIN_BASE, "HSync", PICO_SCANVIDEO_SYNC_PIN_BASE + 1, "VSync")); - -#if PICO_SCANVIDEO_ENABLE_DEN_PIN - bi_decl_if_func_used(bi_1pin_with_name(PICO_SCANVIDEO_SYNC_PIN_BASE + 2, "Display Enable")); - pin_mask |= 4u << PICO_SCANVIDEO_SYNC_PIN_BASE; -#endif -#if PICO_SCANVIDEO_ENABLE_CLOCK_PIN - bi_decl_if_func_used(bi_1pin_with_name(PICO_SCANVIDEO_SYNC_PIN_BASE + 3, "Pixel Clock")); - pin_mask |= 8u << PICO_SCANVIDEO_SYNC_PIN_BASE; -#endif static_assert(PICO_SCANVIDEO_PIXEL_RSHIFT + PICO_SCANVIDEO_PIXEL_RCOUNT <= PICO_SCANVIDEO_COLOR_PIN_COUNT, "red bits do not fit in color pins"); static_assert(PICO_SCANVIDEO_PIXEL_GSHIFT + PICO_SCANVIDEO_PIXEL_GCOUNT <= PICO_SCANVIDEO_COLOR_PIN_COUNT, "green bits do not fit in color pins"); static_assert(PICO_SCANVIDEO_PIXEL_BSHIFT + PICO_SCANVIDEO_PIXEL_BCOUNT <= PICO_SCANVIDEO_COLOR_PIN_COUNT, "blue bits do not fit in color pins"); @@ -455,15 +459,9 @@ bool scanvideo_setup_with_timing(const scanvideo_mode_t *mode, const scanvideo_t #else uint sys_clk = clock_get_hz(clk_sys); video_clock_down_times_2 = sys_clk / timing->clock_freq; -#if PICO_SCANVIDEO_ENABLE_CLOCK_PIN - if (video_clock_down_times_2 * timing->clock_freq != sys_clk) { - panic("System clock (%d) must be an integer multiple of 2 times the requested pixel clock (%d).", sys_clk, timing->clock_freq); - } -#else - if (video_clock_down_times_2 * timing->clock_freq != sys_clk) { - panic("System clock (%d) must be an integer multiple of the requested pixel clock (%d).", sys_clk, timing->clock_freq); - } -#endif + //if (video_clock_down_times_2 * timing->clock_freq != sys_clk) { + // panic("System clock (%d) must be an integer multiple of the requested pixel clock (%d).", sys_clk, timing->clock_freq); + //} #endif valid_params_if(SCANVIDEO_DPI, mode->width * mode->xscale <= timing->h_active); @@ -583,6 +581,11 @@ bool scanvideo_setup_with_timing(const scanvideo_mode_t *mode, const scanvideo_t return true; } +bool scanvideo_setup(const scanvideo_mode_t *mode) { + return scanvideo_setup_with_timing(mode, mode->default_timing); +} + + bool video_24mhz_composable_adapt_for_mode(const scanvideo_pio_program_t *program, const scanvideo_mode_t *mode, scanvideo_scanline_buffer_t *missing_scanline_buffer, uint16_t *modifiable_instructions) { @@ -602,22 +605,13 @@ bool video_24mhz_composable_adapt_for_mode(const scanvideo_pio_program_t *progra } - -void scanvideo_default_configure_pio(pio_hw_t *pio, uint sm, uint offset, pio_sm_config *config, bool overlay) { - pio_sm_set_consecutive_pindirs(pio, sm, PICO_SCANVIDEO_COLOR_PIN_BASE, PICO_SCANVIDEO_COLOR_PIN_COUNT, true); - sm_config_set_out_pins(config, PICO_SCANVIDEO_COLOR_PIN_BASE, PICO_SCANVIDEO_COLOR_PIN_COUNT); - sm_config_set_out_shift(config, true, true, 32); // autopull - sm_config_set_fifo_join(config, PIO_FIFO_JOIN_TX); - if (overlay) { - sm_config_set_out_special(config, 1, 1, PICO_SCANVIDEO_ALPHA_PIN); - } else { - sm_config_set_out_special(config, 1, 0, 0); - } -} - pio_sm_config video_24mhz_composable_configure_pio(pio_hw_t *pio, uint sm, uint offset) { pio_sm_config config = video_24mhz_composable_default_program_get_default_config(offset); - scanvideo_default_configure_pio(pio, sm, offset, &config, sm != PICO_SCANVIDEO_SCANLINE_SM); + pio_sm_set_consecutive_pindirs(pio, sm, PICO_SCANVIDEO_COLOR_PIN_BASE, PICO_SCANVIDEO_COLOR_PIN_COUNT, true); + sm_config_set_out_pins(&config, PICO_SCANVIDEO_COLOR_PIN_BASE, PICO_SCANVIDEO_COLOR_PIN_COUNT); + sm_config_set_out_shift(&config, true, true, 32); // autopull + sm_config_set_fifo_join(&config, PIO_FIFO_JOIN_TX); + sm_config_set_out_special(&config, 1, 0, 0); return config; } diff --git a/MCUME_pico/vga_t4/scanvideo.h b/MCUME_pico/vga_t4/scanvideo.h index d520fec..a31109d 100644 --- a/MCUME_pico/vga_t4/scanvideo.h +++ b/MCUME_pico/vga_t4/scanvideo.h @@ -9,7 +9,7 @@ // note that defining to false will force non-inclusion also #if !defined(PICO_SCANVIDEO_DPI) -#define PICO_SCANVIDEO_DPI 1 +//#define PICO_SCANVIDEO_DPI 1 #ifndef PARAM_ASSERTIONS_ENABLED_SCANVIDEO_DPI #define PARAM_ASSERTIONS_ENABLED_SCANVIDEO_DPI 0 @@ -17,10 +17,6 @@ #include "scanvideo_base.h" -#ifndef PICO_SCANVIDEO_DPI_ALPHA_PIN -#define PICO_SCANVIDEO_DPI_ALPHA_PIN 5u -#endif - #ifndef PICO_SCANVIDEO_DPI_PIXEL_RSHIFT #define PICO_SCANVIDEO_DPI_PIXEL_RSHIFT 0u #endif @@ -45,10 +41,6 @@ #define PICO_SCANVIDEO_DPI_PIXEL_BCOUNT 2 #endif -#ifndef PICO_SCANVIDEO_ALPHA_PIN -#define PICO_SCANVIDEO_ALPHA_PIN PICO_SCANVIDEO_DPI_ALPHA_PIN -#endif - #ifndef PICO_SCANVIDEO_PIXEL_RSHIFT #define PICO_SCANVIDEO_PIXEL_RSHIFT PICO_SCANVIDEO_DPI_PIXEL_RSHIFT #endif diff --git a/MCUME_pico/vga_t4/scanvideo_base.h b/MCUME_pico/vga_t4/scanvideo_base.h index 27cd7d5..ee5b2da 100644 --- a/MCUME_pico/vga_t4/scanvideo_base.h +++ b/MCUME_pico/vga_t4/scanvideo_base.h @@ -8,13 +8,8 @@ #define SCANVIDEO_scanvideo_H_ #include "pico/types.h" - -#if !PICO_NO_HARDWARE - #include "hardware/pio.h" -#endif - #ifdef __cplusplus extern "C" { #endif @@ -26,14 +21,6 @@ extern "C" { */ // == CONFIG ============ -#ifndef PICO_SCANVIDEO_ENABLE_CLOCK_PIN -#define PICO_SCANVIDEO_ENABLE_CLOCK_PIN 0 -#endif - -#ifndef PICO_SCANVIDEO_ENABLE_DEN_PIN -#define PICO_SCANVIDEO_ENABLE_DEN_PIN 0 -#endif - #ifndef PICO_SCANVIDEO_COLOR_PIN_BASE #define PICO_SCANVIDEO_COLOR_PIN_BASE 2 #endif @@ -46,16 +33,10 @@ extern "C" { #define PICO_SCANVIDEO_SYNC_PIN_BASE (PICO_SCANVIDEO_COLOR_PIN_BASE + PICO_SCANVIDEO_COLOR_PIN_COUNT) #endif -#ifndef PICO_SCANVIDEO_ENABLE_VIDEO_CLOCK_DOWN -#define PICO_SCANVIDEO_ENABLE_VIDEO_CLOCK_DOWN 0 -#endif - // ====================== -#define BPP 16 - // most likely 24000000 extern const uint32_t video_clock_freq; @@ -101,12 +82,7 @@ typedef struct scanvideo_mode { } scanvideo_mode_t; extern bool scanvideo_setup(const scanvideo_mode_t *mode); -extern bool scanvideo_setup_with_timing(const scanvideo_mode_t *mode, const scanvideo_timing_t *timing); extern void scanvideo_timing_enable(bool enable); -// these take effect after the next vsync -extern void scanvideo_display_enable(bool enable); -// doesn't exist yet! -// extern void video_set_display_mode(const struct scanvideo_mode *mode); // --- scanline management --- @@ -158,53 +134,24 @@ void scanvideo_set_scanline_repeat_fn(scanvideo_scanline_repeat_count_fn fn); extern const scanvideo_timing_t vga_timing_640x480_60_default; extern const scanvideo_mode_t vga_mode_320x240_60; +extern const scanvideo_mode_t vga_mode_tft_400x240_50; // mode implementation struct scanvideo_pio_program { -#if !PICO_NO_HARDWARE const pio_program_t *program; const uint8_t entry_point; // modifiable_instructions is of size program->length bool (*adapt_for_mode)(const scanvideo_pio_program_t *program, const scanvideo_mode_t *mode, scanvideo_scanline_buffer_t *missing_scanline_buffer, uint16_t *modifiable_instructions); pio_sm_config (*configure_pio)(pio_hw_t *pio, uint sm, uint offset); -#else - const char *id; -#endif }; extern const scanvideo_pio_program_t video_24mhz_composable; -#if !PICO_NO_HARDWARE -extern void scanvideo_default_configure_pio(pio_hw_t *pio, uint sm, uint offset, pio_sm_config *config, bool overlay); -#endif -// note this is not necessarily an absolute gpio pin mask, it is still shifted by PICO_SCANVIDEO_COLOR_PIN_BASE -#define PICO_SCANVIDEO_ALPHA_MASK (1u << PICO_SCANVIDEO_ALPHA_PIN) - -#ifndef PICO_SCANVIDEO_PIXEL_FROM_RGB8 -#define PICO_SCANVIDEO_PIXEL_FROM_RGB8(r, g, b) ((((b)>>3u)<>3u)<>3u)<>PICO_SCANVIDEO_PIXEL_RSHIFT)&0x1f) -#endif - -#ifndef PICO_SCANVIDEO_G5_FROM_PIXEL -#define PICO_SCANVIDEO_G5_FROM_PIXEL(p) (((p)>>PICO_SCANVIDEO_PIXEL_GSHIFT)&0x1f) -#endif - -#ifndef PICO_SCANVIDEO_B5_FROM_PIXEL -#define PICO_SCANVIDEO_B5_FROM_PIXEL(p) (((p)>>PICO_SCANVIDEO_PIXEL_BSHIFT)&0x1f) -#endif - #ifdef __cplusplus } #endif