/****************************************************************************** * Copyright 2013-2015 Espressif Systems * 2015 <>< Charles Lohr * 2017 Hrvoje Cavrak * 2018 Jan Ostman * 2021 WS ---> modified for the purpose of ESP8266 PC-XT emulator * */ #include #include "slc_register.h" #include #include "user_interface.h" #include "pin_mux_register.h" #include "dmastuff.h" #include #define WS_I2S_BCK 1 #define WS_I2S_DIV 2 #define LINE_BUFFER_LENGTH 159 #define NTSC_LINES 525 #define LINETYPES 6 #define YOFFS 0 #define SYNC_LEVEL 0x00000000 #define WHITE_LEVEL 0xffffffff #define BLACK_LEVEL 0x88888888 extern uint8_t lowmemRAM[0x600]; extern uint8_t screenmem[16384]; extern uint8_t videomode; int currow; int curcol; int curmem; extern bool curset; extern bool curshow; extern uint8_t CURSOR_ASCII; uint16_t offset; uint32_t i2s_dma_buffer[LINE_BUFFER_LENGTH*LINETYPES]; static struct sdio_queue i2sBufDesc[NTSC_LINES]; int current_pixel_line; uint8_t testdummy; /* C64 Colors & Luminocity 0 $00 black 0 0 1 $01 white 100 15 2 $02 red 54 5 3 $03 cyan 83 11 4 $04 purple 60 7 5 $05 green 88 12 6 $06 blue 30 2 7 $07 yellow 97 14 8 $08 orange 69 9 9 $09 brown 33 3 10 $0A pink 67 8 11 $0B dark grey 31 4 12 $0C grey 50 6 13 $0D light green 91 13 14 $0E light blue 67 8 15 $0F light grey 75 10 */ // Luminocity table for B/W rendering #define even0 0x00000000 #define odd0 0x00000000 #define even1 0x01010101 #define odd1 0x00000000 #define even2 0x11111111 #define odd2 0x00000000 #define even3 0x55555555 #define odd3 0x00000000 #define even4 0x00000000 #define odd4 0x44444444 #define even5 0x10101010 #define odd5 0x44444444 #define even6 0x11111111 #define odd6 0x44444444 #define even7 0x55555555 #define odd7 0x44444444 #define even8 0x00000000 #define odd8 0xaaaaaaaa #define even9 0x10101010 #define odd9 0xaaaaaaaa #define evenA 0x11111111 #define oddA 0xaaaaaaaa #define evenB 0x55555555 #define oddB 0xaaaaaaaa #define evenC 0x00000000 #define oddC 0xffffffff #define evenD 0x10101010 #define oddD 0xffffffff #define evenE 0x55555555 #define oddE 0xffffffff #define evenF 0xffffffff #define oddF 0xffffffff uint32_t BWcodes[32]={even0,evenF,even5,evenB,even7,evenC,even2,evenE,even9,even3,even8,even4,even6,evenD,oddE,evenA, odd0,oddF,odd5,oddB,odd7,oddC,odd2,oddE,odd9,odd3,odd8,odd4,odd6,oddD,evenE,oddA}; uint32_t gfxA[64] = { 0x00000000, 0x00000003, 0x000000fc, 0x000000ff, 0x00003f00, 0x00003f03, 0x00003ffc, 0x00003fff, 0x000fc000, 0x000fc003, 0x000fc0fc, 0x000fc0ff, 0x000fff00, 0x000fff03, 0x000ffffc, 0x000fffff, 0x03f00000, 0x03f00003, 0x03f000fc, 0x03f000ff, 0x03f03f00, 0x03f03f03, 0x03f03ffc, 0x03f03fff, 0x03ffc000, 0x03ffc003, 0x03ffc0fc, 0x03ffc0ff, 0x03ffff00, 0x03ffff03, 0x03fffffc, 0x03ffffff, 0xfc000000, 0xfc000003, 0xfc0000fc, 0xfc0000ff, 0xfc003f00, 0xfc003f03, 0xfc003ffc, 0xfc003fff, 0xfc0fc000, 0xfc0fc003, 0xfc0fc0fc, 0xfc0fc0ff, 0xfc0fff00, 0xfc0fff03, 0xfc0ffffc, 0xfc0fffff, 0xfff00000, 0xfff00003, 0xfff000fc, 0xfff000ff, 0xfff03f00, 0xfff03f03, 0xfff03ffc, 0xfff03fff, 0xffffc000, 0xffffc003, 0xffffc0fc, 0xffffc0ff, 0xffffff00, 0xffffff03, 0xfffffffc, 0xffffffff}; uint32_t gfxB[8] = { 0x00000000, 0x003f0000, 0x0fc00000, 0x0fff0000, 0xf0000000, 0xf03f0000, 0xffc00000, 0xffff0000}; uint32_t gfxC[8] = { 0x00000000, 0x0000000f, 0x000003f0, 0x000003ff, 0x0000fc00, 0x0000fc0f, 0x0000fff0, 0x0000ffff}; uint32_t gfxD[64] = { 0x00000000, 0x0000003f, 0x00000fc0, 0x00000fff, 0x0003f000, 0x0003f03f, 0x0003ffc0, 0x0003ffff, 0x00fc0000, 0x00fc003f, 0x00fc0fc0, 0x00fc0fff, 0x00fff000, 0x00fff03f, 0x00ffffc0, 0x00ffffff, 0x3f000000, 0x3f00003f, 0x3f000fc0, 0x3f000fff, 0x3f03f000, 0x3f03f03f, 0x3f03ffc0, 0x3f03ffff, 0x3ffc0000, 0x3ffc003f, 0x3ffc0fc0, 0x3ffc0fff, 0x3ffff000, 0x3ffff03f, 0x3fffffc0, 0x3fffffff, 0xc0000000, 0xc000003f, 0xc0000fc0, 0xc0000fff, 0xc003f000, 0xc003f03f, 0xc003ffc0, 0xc003ffff, 0xc0fc0000, 0xc0fc003f, 0xc0fc0fc0, 0xc0fc0fff, 0xc0fff000, 0xc0fff03f, 0xc0ffffc0, 0xc0ffffff, 0xff000000, 0xff00003f, 0xff000fc0, 0xff000fff, 0xff03f000, 0xff03f03f, 0xff03ffc0, 0xff03ffff, 0xfffc0000, 0xfffc003f, 0xfffc0fc0, 0xfffc0fff, 0xfffff000, 0xfffff03f, 0xffffffc0, 0xffffffff}; uint32_t gfx1[64] = { 0x88888888, 0x888888aa, 0x888888dd, 0x888888ff, 0x888aaa88, 0x888aaaaa, 0x888aaadd, 0x888aaaff, 0x888ddd88, 0x888dddaa, 0x888ddddd, 0x888dddff, 0x888fff88, 0x888fffaa, 0x888fffdd, 0x888fffff, 0xaaa88888, 0xaaa888aa, 0xaaa888dd, 0xaaa888ff, 0xaaaaaa88, 0xaaaaaaaa, 0xaaaaaadd, 0xaaaaaaff, 0xaaaddd88, 0xaaadddaa, 0xaaaddddd, 0xaaadddff, 0xaaafff88, 0xaaafffaa, 0xaaafffdd, 0xaaafffff, 0xddd88888, 0xddd888aa, 0xddd888dd, 0xddd888ff, 0xdddaaa88, 0xdddaaaaa, 0xdddaaadd, 0xdddaaaff, 0xdddddd88, 0xddddddaa, 0xdddddddd, 0xddddddff, 0xdddfff88, 0xdddfffaa, 0xdddfffdd, 0xdddfffff, 0xfff88888, 0xfff888aa, 0xfff888dd, 0xfff888ff, 0xfffaaa88, 0xfffaaaaa, 0xfffaaadd, 0xfffaaaff, 0xfffddd88, 0xfffdddaa, 0xfffddddd, 0xfffdddff, 0xffffff88, 0xffffffaa, 0xffffffdd, 0xffffffff}; uint32_t gfx2[16] = { 0x88888888, 0x8aaa8888, 0x8ddd8888, 0x8fff8888, 0xa8888888, 0xaaaa8888, 0xaddd8888, 0xafff8888, 0xd8888888, 0xdaaa8888, 0xdddd8888, 0xdfff8888, 0xf8888888, 0xfaaa8888, 0xfddd8888, 0xffff8888}; uint32_t gfx3[16] = { 0x88888888, 0x8888888a, 0x8888888d, 0x8888888f, 0x8888aaa8, 0x8888aaaa, 0x8888aaad, 0x8888aaaf, 0x8888ddd8, 0x8888ddda, 0x8888dddd, 0x8888dddf, 0x8888fff8, 0x8888fffa, 0x8888fffd, 0x8888ffff}; uint32_t gfx4[64] = { 0x88888888, 0x88888aaa, 0x88888ddd, 0x88888fff, 0x88aaa888, 0x88aaaaaa, 0x88aaaddd, 0x88aaafff, 0x88ddd888, 0x88dddaaa, 0x88dddddd, 0x88dddfff, 0x88fff888, 0x88fffaaa, 0x88fffddd, 0x88ffffff, 0xaa888888, 0xaa888aaa, 0xaa888ddd, 0xaa888fff, 0xaaaaa888, 0xaaaaaaaa, 0xaaaaaddd, 0xaaaaafff, 0xaaddd888, 0xaadddaaa, 0xaadddddd, 0xaadddfff, 0xaafff888, 0xaafffaaa, 0xaafffddd, 0xaaffffff, 0xdd888888, 0xdd888aaa, 0xdd888ddd, 0xdd888fff, 0xddaaa888, 0xddaaaaaa, 0xddaaaddd, 0xddaaafff, 0xddddd888, 0xdddddaaa, 0xdddddddd, 0xdddddfff, 0xddfff888, 0xddfffaaa, 0xddfffddd, 0xddffffff, 0xff888888, 0xff888aaa, 0xff888ddd, 0xff888fff, 0xffaaa888, 0xffaaaaaa, 0xffaaaddd, 0xffaaafff, 0xffddd888, 0xffdddaaa, 0xffdddddd, 0xffdddfff, 0xfffff888, 0xfffffaaa, 0xfffffddd, 0xffffffff}; LOCAL void ICACHE_RAM_ATTR __attribute__((optimize("O2"))) slc_isr(void) { struct sdio_queue *finishedDesc; uint32 slc_intr_status; uint8_t x; //Grab int status slc_intr_status = READ_PERI_REG(SLC_INT_STATUS); //clear all intr flags WRITE_PERI_REG(SLC_INT_CLR, 0xffffffff); if (slc_intr_status & SLC_RX_EOF_INT_ST) { //The DMA subsystem is done with this block: Push it on the queue so it can be re-used. finishedDesc=(struct sdio_queue*)READ_PERI_REG(SLC_RX_EOF_DES_ADDR); struct sdio_queue * next = (struct sdio_queue *)finishedDesc->next_link_ptr; uint32_t * buffer_pointer = (uint32_t*)next->buf_ptr; if( next->unused > 1) { current_pixel_line = 0; offset=0; } else if( next->unused ) { uint8_t pixel_column; uint8_t *video_line; uint8_t charbyte; uint8_t colorbyte; uint8_t gfxbyte; uint32_t character; int tempvalue; int tempvalue2; int tempvalue3; switch (videomode) { case 0: case 1: // CGA 40x25 BW text mode curmem = ((lowmemRAM[0x451] * 40) + lowmemRAM[0x450] ) * 2; pixel_column = 29; tempvalue2 = (current_pixel_line>>3)*80; video_line = &screenmem[tempvalue2]; /* For each byte in the line */ for( x = 0; x < 40; x++ ) { // encode each 32bit word for the video bytes charbyte = video_line[x<<1]; colorbyte = video_line[(x<<1)+1]; tempvalue = current_pixel_line&0x07; gfxbyte = screenmem[14336+ tempvalue +(charbyte<<3)]; character = 0x88888888; if (colorbyte >15) character = 0x99999999; if (((tempvalue2 + (x<<1)) == curmem) && (curset)) { // set the cursor if the current position is the cursor position if (tempvalue == 0x07) { buffer_pointer[pixel_column++] = 0xffffffff; buffer_pointer[pixel_column++] = 0xffffffff; buffer_pointer[pixel_column++] = 0xffffffff; } else{ buffer_pointer[pixel_column++] = character; buffer_pointer[pixel_column++] = character; buffer_pointer[pixel_column++] = character; } } else { if (gfxbyte == 0) { // skip if it is blank ==> cut down time buffer_pointer[pixel_column++] = character; buffer_pointer[pixel_column++] = character; buffer_pointer[pixel_column++] = character; } else { if (gfxbyte&128) character |= 0xfff00000; if (gfxbyte&64) character |= 0x000fff00; if (gfxbyte&32) character |= 0x000000ff; buffer_pointer[pixel_column++] = character; character = 0x88888888; if (colorbyte >15) character = 0x99999999; if (gfxbyte&32) character |=0xf0000000; if (gfxbyte&16) character |=0x0fff0000; if (gfxbyte&8) character |= 0x0000fff0; if (gfxbyte&4) character |= 0x0000000f; buffer_pointer[pixel_column++] = character; character = 0x88888888; if (colorbyte >15) character = 0x99999999; if (gfxbyte&4) character |= 0xff000000; if (gfxbyte&2) character |= 0x00fff000; if (gfxbyte&1) character |= 0x00000fff; buffer_pointer[pixel_column++] = character; } } } current_pixel_line++; break; case 2: case 3: // CGA 80x25 text mode curmem = ((lowmemRAM[0x451] *80) + lowmemRAM[0x450] ) * 2; pixel_column = 29; tempvalue2 = (current_pixel_line>>3)*160; video_line = &screenmem[tempvalue2]; for( x = 0; x < 40; x++ ) { /* encode each 32bit word for the video bytes */ charbyte = video_line[(x<<2)]; colorbyte = video_line[(x<<2)+1]; tempvalue = current_pixel_line&0x07; gfxbyte = screenmem[14336 + tempvalue + (charbyte<<3)]; character = 0x88888888; if (colorbyte >15) character = 0x99999999; if (((tempvalue2 + (x<<2)) == curmem) && (curset)) { // set the cursor if the current position is the cursor position if (tempvalue == 0x07) { buffer_pointer[pixel_column++] = 0xffffffff; character = 0xffff8888; if (colorbyte >15) character = 0xffff9999; } else { buffer_pointer[pixel_column++] = 0x88888888; character = 0x88888888; if (colorbyte >15) character = 0x99999999; } } else { if (gfxbyte == 0) { // skip if it is blank ==> cut down time buffer_pointer[pixel_column++] = character; character = 0x88888888; if (colorbyte >15) character = 0x99998888; } else { character |= gfxA[gfxbyte>>2]; buffer_pointer[pixel_column++] = character; character = 0x88888888; if (colorbyte >15) character = 0x99998888; character |= gfxB[gfxbyte&0x07]; } } charbyte = video_line[(x<<2) + 2]; colorbyte = video_line[(x<<2) + 3]; gfxbyte = screenmem[14336 + tempvalue + (charbyte<<3)]; if (colorbyte >15) character |= 0x00009999; if (((tempvalue2 + (x<<2) + 2) == curmem) && (curset)) { // set the cursor if the current position is the cursor position if (tempvalue == 0x07) { buffer_pointer[pixel_column++] = character | 0x0000ffff; buffer_pointer[pixel_column++] = 0xffffffff; } else { if (colorbyte >15) { buffer_pointer[pixel_column++] = character | 0x00009999; buffer_pointer[pixel_column++] = 0x99999999; } else { buffer_pointer[pixel_column++] = character | 0x00008888; buffer_pointer[pixel_column++] = 0x88888888; } } } else { if (gfxbyte == 0) { // skip if it is blank ==> cut down time buffer_pointer[pixel_column++] = character; character = 0x88888888; if (colorbyte >15) character = 0x99999999; buffer_pointer[pixel_column++] = character; } else { character |= gfxC[gfxbyte >> 5]; buffer_pointer[pixel_column++] = character; character = 0x88888888; if (colorbyte >15) character = 0x99999999; character |= gfxD[gfxbyte&0x3f]; buffer_pointer[pixel_column++] = character; } } } current_pixel_line++; break; case 4: case 5: // CGA 320x200 grayscale pixel_column = 29; video_line = &screenmem[((current_pixel_line>>1) * 80) + ((current_pixel_line % 2)*8192)]; for( x = 0; x < 40; x++ ) // 40 x 2 bytes = 80 bytes, for each video line { gfxbyte = video_line[x<<1]; // get 1st byte buffer_pointer[pixel_column++] = gfx1[gfxbyte>>2]; character = gfx2[gfxbyte&0x0f]; gfxbyte = video_line[(x<<1) +1]; // get 2nd byte! buffer_pointer[pixel_column++] = character | gfx3[gfxbyte>>4]; buffer_pointer[pixel_column++] = gfx4[gfxbyte&0x3f]; } current_pixel_line++; break; case 6: pixel_column = 29; video_line = &screenmem[((current_pixel_line>>1) * 80) + ((current_pixel_line % 2)*8192)]; for( x = 0; x < 40; x++ ) { // 40 x 2 bytes = 80 bytes, for each video line gfxbyte = video_line[x<<1]; // get 1st byte character = 0x88888888; // still use it for image 0x88888888 = black if (gfxbyte == 0) { // skip is it is blank ==> save time buffer_pointer[pixel_column++] = character; character = 0x88888888; } else { buffer_pointer[pixel_column++] = character | gfxA[gfxbyte>>2];; character = 0x88888888| gfxB[gfxbyte&0x07]; } gfxbyte = video_line[(x<<1) +1]; // get 2nd byte! if (gfxbyte == 0) { // skip is it is blank ==> save time buffer_pointer[pixel_column++] = character; character = 0x88888888; buffer_pointer[pixel_column++] = character; } else { buffer_pointer[pixel_column++] = character | gfxC[gfxbyte >> 5]; buffer_pointer[pixel_column++] = 0x88888888 | gfxD[gfxbyte&0x3f]; } } current_pixel_line++; break; default: break; } } } } /* NTSC signals */ #define SHORT_SYNC_INTERVAL 6 #define LONG_SYNC_INTERVAL 73 #define SERRATION_PULSE_INT 67 #define BACK_PORCH 20 #define NORMAL_SYNC_INTERVAL 12 #define LINE_SIGNAL_INTERVAL 147 #define PIXEL_LINE_RESET_EVEN 38-12 #define PIXEL_LINE_RESET_ODD 299-12 #define SHORT_SYNC 0 #define LONG_SYNC 1 #define BLACK_SIGNAL 2 #define SHORT_TO_LONG 3 #define LONG_TO_SHORT 4 #define LINE_SIGNAL 5 //Initialize I2S subsystem for DMA circular buffer use void ICACHE_FLASH_ATTR videoinit() { int x, y; uint32_t * line = i2s_dma_buffer; uint8_t single_line_timings[20] = { SHORT_SYNC_INTERVAL, LONG_SYNC_INTERVAL, SHORT_SYNC_INTERVAL, LONG_SYNC_INTERVAL + 1, SERRATION_PULSE_INT, NORMAL_SYNC_INTERVAL, SERRATION_PULSE_INT, NORMAL_SYNC_INTERVAL + 1, NORMAL_SYNC_INTERVAL, LINE_SIGNAL_INTERVAL, SHORT_SYNC_INTERVAL, LONG_SYNC_INTERVAL, SERRATION_PULSE_INT, NORMAL_SYNC_INTERVAL + 1, SERRATION_PULSE_INT, NORMAL_SYNC_INTERVAL, SHORT_SYNC_INTERVAL, LONG_SYNC_INTERVAL + 1, NORMAL_SYNC_INTERVAL, LINE_SIGNAL_INTERVAL, }; uint32_t single_line_levels[20] = { SYNC_LEVEL, BLACK_LEVEL, SYNC_LEVEL, BLACK_LEVEL, SYNC_LEVEL, BLACK_LEVEL, SYNC_LEVEL, BLACK_LEVEL, SYNC_LEVEL, BLACK_LEVEL, SYNC_LEVEL, BLACK_LEVEL, SYNC_LEVEL, BLACK_LEVEL, SYNC_LEVEL, BLACK_LEVEL, SYNC_LEVEL, BLACK_LEVEL, SYNC_LEVEL, BLACK_LEVEL, }; uint8_t i, signal; for (signal = 0; signal < 20; signal++) { for (i=0; i < single_line_timings[signal]; i++, *line++ = single_line_levels[signal]); } uint16_t ntsc_lines[48] = { /* Even Field */ 3, SHORT_SYNC, 0, 6, LONG_SYNC, 0, 9, SHORT_SYNC, 0, 40-YOFFS+1, BLACK_SIGNAL, 0, 240+YOFFS+1, LINE_SIGNAL, 1, 263, BLACK_SIGNAL, 0, /* Odd Field */ 265, SHORT_SYNC, 0, 266, SHORT_TO_LONG, 0, 268, LONG_SYNC, 0, 269, LONG_TO_SHORT, 0, 271, SHORT_SYNC, 0, 302-YOFFS+1, BLACK_SIGNAL, 0, 502+YOFFS+1, LINE_SIGNAL, 1, 525, BLACK_SIGNAL, 0, }; uint16_t *ntsc_line = ntsc_lines; //Initialize DMA buffer descriptors in such a way that they will form a circular //buffer. for (x=0; x