video_out.h atualizada , para rodar esp32 true color

/*Dentro da biblioteca ESP_Composite_Color_Video, src, subsitua o conteúdo de video_out.h , por este à seguir, para funcionar nas versões 2x, 3x do esp32*/ /* Copyright (c) 2020, Peter Barrett ** ** Permission to use, copy, modify, and/or distribute this software for ** any purpose with or without fee is hereby granted, provided that the ** above copyright notice and this permission notice appear in all copies. ** ** THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL ** WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED ** WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR ** BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES ** OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, ** WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ** ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS ** SOFTWARE. */ /** * Change log: * * Oct 2021 - Original code from https://github.com/rossumur/esp_8_bit * Modified by Marcio Teixeira to extract Atari framebuffer * and make it compatible with Bitluni's code. I don't * really understand this code very well, but there are * some implementation details in the esp_8_bit repo. * * Out 2026 - Added an optional NTSC TRUE COLOR path (see "TRUECOLOR BEGIN" below). The palette * path is unchanged; true color is only used while _tc_on is set. * * Out 2026 - blanking() (NTSC) rewritten: 32-bit fills + skip when the DMA buffer already holds the same * blanking/vsync line (see "BLANKING RAPIDO"). Added per-line ISR timing (isr_stats_lines). * Added tc_set_xmargin(): black side margins in the true color path (TV overscan). */ #define VIDEO_PIN 26 #define AUDIO_PIN 18 // can be any pin //#define IR_PIN 0 // TSOP4838 or equivalent on any pin if desired #include "esp_types.h" #include "esp_heap_caps.h" #include "esp_attr.h" #include "esp_intr_alloc.h" #include "esp_err.h" #include "soc/gpio_reg.h" #include "soc/rtc.h" // --- compatibilidade ESP32 Arduino core 3.x (IDF 5.x): rtc_clk_apll_enable() so liga/desliga --- #if defined(ESP_ARDUINO_VERSION_MAJOR) && ESP_ARDUINO_VERSION_MAJOR >= 3 static inline void apll_set_enable(int en, uint32_t sdm0, uint32_t sdm1, uint32_t sdm2, uint32_t o_div) { rtc_clk_apll_enable(en != 0); // 1o: liga (power-up) o APLL if (en) rtc_clk_apll_coeff_set(o_div, sdm0, sdm1, sdm2); // 2o: coeficientes + calibracao com o APLL ligado } #else static inline void apll_set_enable(int en, uint32_t sdm0, uint32_t sdm1, uint32_t sdm2, uint32_t o_div) { rtc_clk_apll_enable(en, sdm0, sdm1, sdm2, o_div); } #endif #include "soc/soc.h" #include "soc/i2s_struct.h" #include "soc/i2s_reg.h" #include "soc/ledc_struct.h" #include "soc/rtc_io_reg.h" #include "soc/io_mux_reg.h" #include "rom/gpio.h" #include "rom/lldesc.h" #include "driver/periph_ctrl.h" #include "driver/dac.h" #include "driver/gpio.h" #include "driver/i2s.h" #if !defined(SUPPORT_NTSC) && !defined(SUPPORT_PAL) #define SUPPORT_NTSC 1 #define SUPPORT_PAL 1 #endif #ifdef IR_PIN #include "ir_input.h" // ir peripherals #endif #define COLORBURST 1 // Prioridade da interrupcao de video: 1 (padrao, como na biblioteca original), 2 ou 3. Se as estatisticas // (comando "i" do sketch) mostrarem atrasos de interrupcao, experimente 3. #ifndef VIDEO_ISR_LEVEL #define VIDEO_ISR_LEVEL 1 #endif #if VIDEO_ISR_LEVEL == 3 #define VIDEO_ISR_FLAG_LEVEL ESP_INTR_FLAG_LEVEL3 #elif VIDEO_ISR_LEVEL == 2 #define VIDEO_ISR_FLAG_LEVEL ESP_INTR_FLAG_LEVEL2 #else #define VIDEO_ISR_FLAG_LEVEL ESP_INTR_FLAG_LEVEL1 #endif namespace RawCompositeVideoBlitter { enum VideoStandard {NTSC, PAL}; int _pal_ = 0; //==================================================================================================== //==================================================================================================== // Color palettes // const static uint32_t atari_palette_rgb[256] = { 0x00000000,0x000F0F0F,0x001B1B1B,0x00272727,0x00333333,0x00414141,0x004F4F4F,0x005E5E5E, 0x00686868,0x00787878,0x00898989,0x009A9A9A,0x00ABABAB,0x00BFBFBF,0x00D3D3D3,0x00EAEAEA, 0x00001600,0x000F2100,0x001A2D00,0x00273900,0x00334500,0x00405300,0x004F6100,0x005D7000, 0x00687A00,0x00778A17,0x00899B29,0x009AAC3B,0x00ABBD4C,0x00BED160,0x00D2E574,0x00E9FC8B, 0x001C0000,0x00271300,0x00331F00,0x003F2B00,0x004B3700,0x00594500,0x00675300,0x00756100, 0x00806C12,0x008F7C22,0x00A18D34,0x00B29E45,0x00C3AF56,0x00D6C36A,0x00EAD77E,0x00FFEE96, 0x002F0000,0x003A0000,0x00460F00,0x00521C00,0x005E2800,0x006C3600,0x007A4416,0x00885224, 0x00925D2F,0x00A26D3F,0x00B37E50,0x00C48F62,0x00D6A073,0x00E9B487,0x00FDC89B,0x00FFDFB2, 0x00390000,0x00440000,0x0050000A,0x005C0F17,0x00681B23,0x00752931,0x0084373F,0x0092464E, 0x009C5058,0x00AC6068,0x00BD7179,0x00CE838A,0x00DF949C,0x00F2A7AF,0x00FFBBC3,0x00FFD2DA, 0x00370020,0x0043002C,0x004E0037,0x005A0044,0x00661350,0x0074215D,0x0082306C,0x00903E7A, 0x009B4984,0x00AA5994,0x00BC6AA5,0x00CD7BB6,0x00DE8CC7,0x00F1A0DB,0x00FFB4EF,0x00FFCBFF, 0x002B0047,0x00360052,0x0042005E,0x004E006A,0x005A1276,0x00672083,0x00762F92,0x00843DA0, 0x008E48AA,0x009E58BA,0x00AF69CB,0x00C07ADC,0x00D18CED,0x00E59FFF,0x00F9B3FF,0x00FFCAFF, 0x0016005F,0x0021006A,0x002D0076,0x00390C82,0x0045198D,0x0053279B,0x006135A9,0x006F44B7, 0x007A4EC2,0x008A5ED1,0x009B6FE2,0x00AC81F3,0x00BD92FF,0x00D0A5FF,0x00E4B9FF,0x00FBD0FF, 0x00000063,0x0000006F,0x00140C7A,0x00201886,0x002C2592,0x003A329F,0x004841AE,0x00574FBC, 0x00615AC6,0x00716AD6,0x00827BE7,0x00948CF8,0x00A59DFF,0x00B8B1FF,0x00CCC5FF,0x00E3DCFF, 0x00000054,0x00000F5F,0x00001B6A,0x00002776,0x00153382,0x00234190,0x0031509E,0x00405EAC, 0x004A68B6,0x005A78C6,0x006B89D7,0x007D9BE8,0x008EACF9,0x00A1BFFF,0x00B5D3FF,0x00CCEAFF, 0x00001332,0x00001E3E,0x00002A49,0x00003655,0x00004261,0x0012506F,0x00205E7D,0x002F6D8B, 0x00397796,0x004987A6,0x005B98B7,0x006CA9C8,0x007DBAD9,0x0091CEEC,0x00A5E2FF,0x00BCF9FF, 0x00001F00,0x00002A12,0x0000351E,0x0000422A,0x00004E36,0x000B5B44,0x00196A53,0x00287861, 0x0033826B,0x0043927B,0x0054A38C,0x0065B49E,0x0077C6AF,0x008AD9C2,0x009EEDD6,0x00B5FFED, 0x00002400,0x00003000,0x00003B00,0x00004700,0x0000530A,0x00106118,0x001E6F27,0x002D7E35, 0x00378840,0x00479850,0x0059A961,0x006ABA72,0x007BCB84,0x008FDE97,0x00A3F2AB,0x00BAFFC2, 0x00002300,0x00002F00,0x00003A00,0x00004600,0x00115200,0x001F6000,0x002E6E00,0x003C7C12, 0x0047871C,0x0057972D,0x0068A83E,0x0079B94F,0x008ACA61,0x009EDD74,0x00B2F189,0x00C9FFA0, 0x00001B00,0x00002700,0x000F3200,0x001C3E00,0x00284A00,0x00365800,0x00446600,0x00527500, 0x005D7F00,0x006D8F19,0x007EA02B,0x008FB13D,0x00A0C24E,0x00B4D662,0x00C8EA76,0x00DFFF8D, 0x00110E00,0x001D1A00,0x00292500,0x00353100,0x00413D00,0x004F4B00,0x005D5A00,0x006B6800, 0x0076720B,0x0085821B,0x0097932D,0x00A8A43E,0x00B9B650,0x00CCC963,0x00E0DD77,0x00F7F48F, }; // swizzed ntsc palette in RAM const static DRAM_ATTR uint32_t atari_4_phase_ntsc[256] = { 0x18181818,0x1A1A1A1A,0x1C1C1C1C,0x1F1F1F1F,0x21212121,0x24242424,0x27272727,0x2A2A2A2A, 0x2D2D2D2D,0x30303030,0x34343434,0x38383838,0x3B3B3B3B,0x40404040,0x44444444,0x49494949, 0x1A15210E,0x1C182410,0x1E1A2612,0x211D2915,0x231F2B18,0x26222E1A,0x2925311D,0x2C283420, 0x2F2B3723,0x322E3A27,0x36323E2A,0x3A36412E,0x3D394532,0x423D4936,0x46424E3A,0x4B46523F, 0x151A210E,0x171D2310,0x191F2613,0x1C222815,0x1E242B18,0x21272E1B,0x242A311D,0x272D3420, 0x2A303724,0x2E333A27,0x31373D2A,0x353A412E,0x393E4532,0x3D424936,0x41474D3A,0x464B523F, 0x101F1F10,0x13212113,0x15232315,0x18262618,0x1A28281A,0x1D2B2B1D,0x202E2E20,0x23313123, 0x26343426,0x29383729,0x2D3B3B2D,0x303F3F31,0x34434234,0x38474738,0x3D4B4B3D,0x41505041, 0x0E211A15,0x10231D17,0x13261F19,0x1528221C,0x182B241F,0x1B2E2721,0x1D312A24,0x20342D27, 0x2337302A,0x273A332E,0x2A3E3731,0x2E413A35,0x32453E39,0x3649423D,0x3A4D4741,0x3F524B46, 0x0E21151A,0x1024181C,0x12261A1E,0x15291D21,0x182B1F24,0x1A2E2226,0x1D312529,0x2034282C, 0x23372B2F,0x273A2E33,0x2A3E3236,0x2E41353A,0x3245393E,0x36493D42,0x3A4E4246,0x3F52464B, 0x101F111E,0x12211320,0x15241623,0x17261825,0x1A291B28,0x1D2C1E2B,0x202F202E,0x23322331, 0x26352634,0x29382A37,0x2C3B2D3B,0x303F313E,0x34433542,0x38473946,0x3C4B3D4A,0x4150424F, 0x141B0E21,0x161D1023,0x19201326,0x1B221528,0x1E25182B,0x21281B2E,0x242A1E30,0x272E2133, 0x2A312436,0x2D34273A,0x30372B3D,0x343B2E41,0x383F3245,0x3C433649,0x40473A4D,0x454C3F52, 0x19160E21,0x1B181024,0x1E1B1226,0x201D1529,0x2320172B,0x26231A2E,0x29261D31,0x2C292034, 0x2F2C2337,0x322F273A,0x35322A3E,0x39362E41,0x3D3A3245,0x413E3649,0x45423A4E,0x4A473F52, 0x1E11101F,0x20141222,0x22161424,0x25191727,0x271B1929,0x2A1E1C2C,0x2D211F2F,0x30242232, 0x33272535,0x362A2838,0x3A2E2C3C,0x3E323040,0x41353343,0x46393847,0x4A3E3C4C,0x4F424150, 0x210E131C,0x2311161E,0x25131820,0x28161B23,0x2A181D26,0x2D1B2028,0x301E232B,0x3321262E, 0x36242931,0x3A272C35,0x3D2B3038,0x412E333C,0x45323740,0x49363B44,0x4D3B4048,0x523F444D, 0x210E1817,0x24101B19,0x26121D1B,0x29151F1E,0x2B172221,0x2E1A2523,0x311D2826,0x34202B29, 0x37232E2C,0x3A263130,0x3E2A3533,0x422E3837,0x45313C3B,0x4936403F,0x4E3A4543,0x523F4948, 0x200F1D12,0x22111F14,0x25142217,0x27162419,0x2A19271C,0x2D1C2A1F,0x2F1F2C22,0x32222F25, 0x35253328,0x3928362B,0x3C2C392F,0x402F3D32,0x44334136,0x4837453A,0x4C3B493E,0x51404E43, 0x1C13200F,0x1F152311,0x21172513,0x241A2816,0x261D2A19,0x291F2D1B,0x2C22301E,0x2F253321, 0x32283624,0x352C3928,0x392F3D2B,0x3C33402F,0x40374433,0x443B4837,0x493F4D3B,0x4D445140, 0x1818220E,0x1A1A2410,0x1C1C2612,0x1F1F2915,0x21212B17,0x24242E1A,0x2727311D,0x2A2A3420, 0x2D2D3723,0x30303A26,0x34343E2A,0x3838422E,0x3B3B4531,0x40404A36,0x44444E3A,0x4949533F, 0x131C200F,0x151F2311,0x17212513,0x1A242816,0x1D262A19,0x1F292D1B,0x222C301E,0x252F3321, 0x28323624,0x2C353928,0x2F393D2B,0x333C402F,0x37404433,0x3B444837,0x3F494D3B,0x444D5140, }; const static DRAM_ATTR uint32_t atari_4_phase_pal[] = { 0x18181818,0x1B1B1B1B,0x1E1E1E1E,0x21212121,0x25252525,0x28282828,0x2B2B2B2B,0x2E2E2E2E, 0x32323232,0x35353535,0x38383838,0x3B3B3B3B,0x3F3F3F3F,0x42424242,0x45454545,0x49494949, 0x16271A09,0x192A1D0C,0x1C2D200F,0x1F302312,0x23342716,0x26372A19,0x293A2D1C,0x2C3D301F, 0x30413423,0x33443726,0x36473A29,0x394A3D2C,0x3D4E4130,0x40514433,0x43544736,0x47584B3A, 0x0F24210C,0x1227240F,0x152A2712,0x182D2A15,0x1C312E19,0x1F34311C,0x2237341F,0x253A3722, 0x293E3B26,0x2C413E29,0x2F44412C,0x3247442F,0x364B4833,0x394E4B36,0x3C514E39,0x4055523D, 0x0B1F2511,0x0E222814,0x11252B17,0x14282E1A,0x182C321E,0x1B2F3521,0x1E323824,0x21353B27, 0x25393F2B,0x283C422E,0x2B3F4531,0x2E424834,0x32464C38,0x35494F3B,0x384C523E,0x3C505642, 0x09182718,0x0C1B2A1B,0x0F1E2D1E,0x12213021,0x16253425,0x19283728,0x1C2B3A2B,0x1F2E3D2E, 0x23324132,0x26354435,0x29384738,0x2C3B4A3B,0x303F4E3F,0x33425142,0x36455445,0x3A495849, 0x0B11251F,0x0E142822,0x11172B25,0x141A2E28,0x181E322C,0x1B21352F,0x1E243832,0x21273B35, 0x252B3F39,0x282E423C,0x2B31453F,0x2E344842,0x32384C46,0x353B4F49,0x383E524C,0x3C425650, 0x0F0C2124,0x120F2427,0x1512272A,0x18152A2D,0x1C192E31,0x1F1C3134,0x221F3437,0x2522373A, 0x29263B3E,0x2C293E41,0x2F2C4144,0x322F4447,0x3633484B,0x39364B4E,0x3C394E51,0x403D5255, 0x15091B27,0x180C1E2A,0x1B0F212D,0x1E122430,0x22162834,0x25192B37,0x281C2E3A,0x2B1F313D, 0x2F233541,0x32263844,0x35293B47,0x382C3E4A,0x3C30424E,0x3F334551,0x42364854,0x463A4C58, 0x1C0A1426,0x1F0D1729,0x22101A2C,0x25131D2F,0x29172133,0x2C1A2436,0x2F1D2739,0x32202A3C, 0x36242E40,0x39273143,0x3C2A3446,0x3F2D3749,0x43313B4D,0x46343E50,0x49374153,0x4D3B4557, 0x220D0E23,0x25101126,0x28131429,0x2B16172C,0x2F1A1B30,0x321D1E33,0x35202136,0x38232439, 0x3C27283D,0x3F2A2B40,0x422D2E43,0x45303146,0x4934354A,0x4C37384D,0x4F3A3B50,0x533E3F54, 0x26130A1D,0x29160D20,0x2C191023,0x2F1C1326,0x3320172A,0x36231A2D,0x39261D30,0x3C292033, 0x402D2437,0x4330273A,0x46332A3D,0x49362D40,0x4D3A3144,0x503D3447,0x5340374A,0x57443B4E, 0x271A0916,0x2A1D0C19,0x2D200F1C,0x3023121F,0x34271623,0x372A1926,0x3A2D1C29,0x3D301F2C, 0x41342330,0x44372633,0x473A2936,0x4A3D2C39,0x4E41303D,0x51443340,0x54473643,0x584B3A47, 0x24200C10,0x27230F13,0x2A261216,0x2D291519,0x312D191D,0x34301C20,0x37331F23,0x3A362226, 0x3E3A262A,0x413D292D,0x44402C30,0x47432F33,0x4B473337,0x4E4A363A,0x514D393D,0x55513D41, 0x1F25110B,0x2228140E,0x252B1711,0x282E1A14,0x2C321E18,0x2F35211B,0x3238241E,0x353B2721, 0x393F2B25,0x3C422E28,0x3F45312B,0x4248342E,0x464C3832,0x494F3B35,0x4C523E38,0x5056423C, 0x19271709,0x1C2A1A0C,0x1F2D1D0F,0x22302012,0x26342416,0x29372719,0x2C3A2A1C,0x2F3D2D1F, 0x33413123,0x36443426,0x39473729,0x3C4A3A2C,0x404E3E30,0x43514133,0x46544436,0x4A58483A, 0x12261E0A,0x1529210D,0x182C2410,0x1B2F2713,0x1F332B17,0x22362E1A,0x2539311D,0x283C3420, 0x2C403824,0x2F433B27,0x32463E2A,0x3549412D,0x394D4531,0x3C504834,0x3F534B37,0x43574F3B, //odd 0x18181818,0x1B1B1B1B,0x1E1E1E1E,0x21212121,0x25252525,0x28282828,0x2B2B2B2B,0x2E2E2E2E, 0x32323232,0x35353535,0x38383838,0x3B3B3B3B,0x3F3F3F3F,0x42424242,0x45454545,0x49494949, 0x1A271609,0x1D2A190C,0x202D1C0F,0x23301F12,0x27342316,0x2A372619,0x2D3A291C,0x303D2C1F, 0x34413023,0x37443326,0x3A473629,0x3D4A392C,0x414E3D30,0x44514033,0x47544336,0x4B58473A, 0x21240F0C,0x2427120F,0x272A1512,0x2A2D1815,0x2E311C19,0x31341F1C,0x3437221F,0x373A2522, 0x3B3E2926,0x3E412C29,0x41442F2C,0x4447322F,0x484B3633,0x4B4E3936,0x4E513C39,0x5255403D, 0x251F0B11,0x28220E14,0x2B251117,0x2E28141A,0x322C181E,0x352F1B21,0x38321E24,0x3B352127, 0x3F39252B,0x423C282E,0x453F2B31,0x48422E34,0x4C463238,0x4F49353B,0x524C383E,0x56503C42, 0x27180918,0x2A1B0C1B,0x2D1E0F1E,0x30211221,0x34251625,0x37281928,0x3A2B1C2B,0x3D2E1F2E, 0x41322332,0x44352635,0x47382938,0x4A3B2C3B,0x4E3F303F,0x51423342,0x54453645,0x58493A49, 0x25110B1F,0x28140E22,0x2B171125,0x2E1A1428,0x321E182C,0x35211B2F,0x38241E32,0x3B272135, 0x3F2B2539,0x422E283C,0x45312B3F,0x48342E42,0x4C383246,0x4F3B3549,0x523E384C,0x56423C50, 0x210C0F24,0x240F1227,0x2712152A,0x2A15182D,0x2E191C31,0x311C1F34,0x341F2237,0x3722253A, 0x3B26293E,0x3E292C41,0x412C2F44,0x442F3247,0x4833364B,0x4B36394E,0x4E393C51,0x523D4055, 0x1B091527,0x1E0C182A,0x210F1B2D,0x24121E30,0x28162234,0x2B192537,0x2E1C283A,0x311F2B3D, 0x35232F41,0x38263244,0x3B293547,0x3E2C384A,0x42303C4E,0x45333F51,0x48364254,0x4C3A4658, 0x140A1C26,0x170D1F29,0x1A10222C,0x1D13252F,0x21172933,0x241A2C36,0x271D2F39,0x2A20323C, 0x2E243640,0x31273943,0x342A3C46,0x372D3F49,0x3B31434D,0x3E344650,0x41374953,0x453B4D57, 0x0E0D2223,0x11102526,0x14132829,0x17162B2C,0x1B1A2F30,0x1E1D3233,0x21203536,0x24233839, 0x28273C3D,0x2B2A3F40,0x2E2D4243,0x31304546,0x3534494A,0x38374C4D,0x3B3A4F50,0x3F3E5354, 0x0A13261D,0x0D162920,0x10192C23,0x131C2F26,0x1720332A,0x1A23362D,0x1D263930,0x20293C33, 0x242D4037,0x2730433A,0x2A33463D,0x2D364940,0x313A4D44,0x343D5047,0x3740534A,0x3B44574E, 0x091A2716,0x0C1D2A19,0x0F202D1C,0x1223301F,0x16273423,0x192A3726,0x1C2D3A29,0x1F303D2C, 0x23344130,0x26374433,0x293A4736,0x2C3D4A39,0x30414E3D,0x33445140,0x36475443,0x3A4B5847, 0x0C202410,0x0F232713,0x12262A16,0x15292D19,0x192D311D,0x1C303420,0x1F333723,0x22363A26, 0x263A3E2A,0x293D412D,0x2C404430,0x2F434733,0x33474B37,0x364A4E3A,0x394D513D,0x3D515541, 0x11251F0B,0x1428220E,0x172B2511,0x1A2E2814,0x1E322C18,0x21352F1B,0x2438321E,0x273B3521, 0x2B3F3925,0x2E423C28,0x31453F2B,0x3448422E,0x384C4632,0x3B4F4935,0x3E524C38,0x4256503C, 0x17271909,0x1A2A1C0C,0x1D2D1F0F,0x20302212,0x24342616,0x27372919,0x2A3A2C1C,0x2D3D2F1F, 0x31413323,0x34443626,0x37473929,0x3A4A3C2C,0x3E4E4030,0x41514333,0x44544636,0x48584A3A, 0x1E26120A,0x2129150D,0x242C1810,0x272F1B13,0x2B331F17,0x2E36221A,0x3139251D,0x343C2820, 0x38402C24,0x3B432F27,0x3E46322A,0x4149352D,0x454D3931,0x48503C34,0x4B533F37,0x4F57433B, }; const uint32_t* ntsc_palette() {return atari_4_phase_ntsc;}; const uint32_t* pal_palette() {return atari_4_phase_pal;} //==================================================================================================== //==================================================================================================== // Frame buffer // // (The following code isn't actually used when combined with bitluni's CompositeGraphics code) void* fb_malloc(size_t size, const char* label, uint32_t caps) { printf("fb_malloc %d free, %d biggest, allocating %s:%d\n", heap_caps_get_free_size(caps), heap_caps_get_largest_free_block(caps), label, size); void *r = heap_caps_malloc(size, caps); if (!r) { printf("fb_malloc failed allocation of %s:%d!\n", label, size); esp_restart(); } else printf("fb_malloc allocation of %s:%d %08X\n", label, size, r); return r; } constexpr unsigned int Screen_WIDTH = 336; constexpr unsigned int Screen_HEIGHT = 240; uint32_t * _fb; uint8_t ** _lines; void frame_clear(uint8_t color) { uint32_t color_x4 = (color << 24) | (color << 16) | (color << 8) | (color << 0); int i = Screen_WIDTH*Screen_HEIGHT/4; while (i--) _fb[i] = color_x4; } /** * Creates a framebuffer. The caps argument can be one of the following: * * MALLOC_CAP_8BIT - Accessible in single bytes * MALLOC_CAP_32BIT - All access must be 32-bit aligned * * The latter allows the ESP32 to store the data in IRAM (instruction RAM), * but makes access to individual bytes more difficult. */ void frame_init(uint32_t caps = MALLOC_CAP_8BIT) { _fb = (uint32_t*) fb_malloc(Screen_WIDTH * Screen_HEIGHT, "_fb", caps); _lines = (uint8_t**) fb_malloc(Screen_HEIGHT * sizeof(uint8_t*), "_lines", MALLOC_CAP_32BIT); uint8_t* s = (uint8_t*)_fb; for (int y = 0; y < Screen_HEIGHT; y++) { _lines[y] = s; s += Screen_WIDTH; } frame_clear(0x00); } //==================================================================================================== //==================================================================================================== // // low level HW setup of DAC/DMA/APLL/PWM // lldesc_t _dma_desc[4] = {0}; intr_handle_t _isr_handle; extern "C" void IRAM_ATTR video_isr(volatile void* buf); // simple isr static void IRAM_ATTR i2s_intr_handler_video(void *arg) { if (I2S0.int_st.out_eof) video_isr((volatile void*)((lldesc_t*)I2S0.out_eof_des_addr)->buf); // get the next line of video I2S0.int_clr.val = I2S0.int_st.val; // reset the interrupt } static esp_err_t start_dma(int line_width,int samples_per_cc, int ch = 1) { periph_module_enable(PERIPH_I2S0_MODULE); // setup interrupt if (esp_intr_alloc(ETS_I2S0_INTR_SOURCE, VIDEO_ISR_FLAG_LEVEL | ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_INTRDISABLED, i2s_intr_handler_video, 0, &_isr_handle) != ESP_OK) return -1; // reset conf I2S0.conf.val = 1; I2S0.conf.val = 0; I2S0.conf.tx_right_first = 1; I2S0.conf.tx_mono = (ch == 2 ? 0 : 1); I2S0.conf2.lcd_en = 1; I2S0.fifo_conf.tx_fifo_mod_force_en = 1; I2S0.sample_rate_conf.tx_bits_mod = 16; I2S0.conf_chan.tx_chan_mod = (ch == 2) ? 0 : 1; // Create TX DMA buffers for (int i = 0; i < 2; i++) { int n = line_width*2*ch; if (n >= 4092) { printf("DMA chunk too big:%s\n",n); return -1; } _dma_desc[i].buf = (uint8_t*)heap_caps_calloc(1, n, MALLOC_CAP_DMA); if (!_dma_desc[i].buf) return -1; _dma_desc[i].owner = 1; _dma_desc[i].eof = 1; _dma_desc[i].length = n; _dma_desc[i].size = n; _dma_desc[i].empty = (uint32_t)(i == 1 ? _dma_desc : _dma_desc+1); } I2S0.out_link.addr = (uint32_t)_dma_desc; // Setup up the apll: See ref 3.2.7 Audio PLL // f_xtal = (int)rtc_clk_xtal_freq_get() * 1000000; // f_out = xtal_freq * (4 + sdm2 + sdm1/256 + sdm0/65536); // 250 < f_out < 500 // apll_freq = f_out/((o_div + 2) * 2) // operating range of the f_out is 250 MHz ~ 500 MHz // operating range of the apll_freq is 16 ~ 128 MHz. // select sdm0,sdm1,sdm2 to produce nice multiples of colorburst frequencies // see calc_freq() for math: (4+a)*10/((2 + b)*2) mhz // up to 20mhz seems to work ok: // rtc_clk_apll_enable(1,0x00,0x00,0x4,0); // 20mhz for fancy DDS #if SUPPORT_NTSC if (!_pal_) { switch (samples_per_cc) { case 3: apll_set_enable(1,0x46,0x97,0x4,2); break; // 10.7386363636 3x NTSC (10.7386398315mhz) case 4: apll_set_enable(1,0x46,0x97,0x4,1); break; // 14.3181818182 4x NTSC (14.3181864421mhz) } } #endif #if SUPPORT_PAL if (_pal_) { apll_set_enable(1,0x04,0xA4,0x6,1); // 17.734476mhz ~4x PAL } #endif I2S0.clkm_conf.clkm_div_num = 1; // I2S clock divider’s integral value. I2S0.clkm_conf.clkm_div_b = 0; // Fractional clock divider’s numerator value. I2S0.clkm_conf.clkm_div_a = 1; // Fractional clock divider’s denominator value I2S0.sample_rate_conf.tx_bck_div_num = 1; I2S0.clkm_conf.clka_en = 1; // Set this bit to enable clk_apll. I2S0.fifo_conf.tx_fifo_mod = (ch == 2) ? 0 : 1; // 32-bit dual or 16-bit single channel data dac_output_enable(DAC_CHANNEL_1); // DAC, video on GPIO25 dac_i2s_enable(); // start DAC! I2S0.conf.tx_start = 1; // start DMA! I2S0.int_clr.val = 0xFFFFFFFF; I2S0.int_ena.out_eof = 1; I2S0.out_link.start = 1; return esp_intr_enable(_isr_handle); // start interruprs! } void video_init_hw(int line_width, int samples_per_cc) { // setup apll 4x NTSC or PAL colorburst rate start_dma(line_width,samples_per_cc,1); // Now ideally we would like to use the decoupled left DAC channel to produce audio // But when using the APLL there appears to be some clock domain conflict that causes // nasty digitial spikes and dropouts. You are also limited to a single audio channel. // So it is back to PWM/PDM and a 1 bit DAC for us. Good news is that we can do stereo // if we want to and have lots of different ways of doing nice noise shaping etc. // PWM audio out of pin 18 -> can be anything // lots of other ways, PDM by hand over I2S1, spi circular buffer etc // but if you would like stereo the led pwm seems like a fine choice // needs a simple rc filter (1k->1.2k resistor & 10nf->15nf cap work fine) // 18 ----/\/\/\/----|------- a out // 1k | // --- // --- 10nf // | // v gnd #if !(defined(ESP_ARDUINO_VERSION_MAJOR) && ESP_ARDUINO_VERSION_MAJOR >= 3) ledcSetup(0,2000000,7); // 625000 khz is as fast as we go w 7 bits ledcAttachPin(AUDIO_PIN, 0); ledcWrite(0,0); #endif // core 3.x: PWM de audio desativado (nao usado) // IR input if used #ifdef IR_PIN pinMode(IR_PIN,INPUT); #endif } #if SUPPORT_AUDIO // send an audio sample every scanline (15720hz for ntsc, 15600hz for PAL) inline void IRAM_ATTR audio_sample(uint8_t s) { auto& reg = LEDC.channel_group[0].channel[0]; reg.duty.duty = s << 4; // 25 bit (21.4) reg.conf0.sig_out_en = 1; // This is the output enable control bit for channel reg.conf1.duty_start = 1; // When duty_num duty_cycle and duty_scale has been configured. these register won't take effect until set duty_start. this bit is automatically cleared by hardware reg.conf0.clk_en = 1; } #endif // Appendix /* static void calc_freq(double f) { f /= 1000000; printf("looking for sample rate of %fmhz\n",(float)f); int xtal_freq = 40; for (int o_div = 0; o_div < 3; o_div++) { float f_out = 4*f*((o_div + 2)*2); // 250 < f_out < 500 if (f_out < 250 || f_out > 500) continue; int sdm = round((f_out/xtal_freq - 4)*65536); float apll_freq = 40 * (4 + (float)sdm/65536)/((o_div + 2)*2); // 16 < apll_freq < 128 MHz if (apll_freq < 16 || apll_freq > 128) continue; printf("f_out:%f %d:0x%06X %fmhz %f\n",f_out,o_div,sdm,apll_freq/4,f/(apll_freq/4)); } printf("\n"); } static void freqs() { calc_freq(PAL_FREQUENCY*3); calc_freq(PAL_FREQUENCY*4); calc_freq(NTSC_FREQUENCY*3); calc_freq(NTSC_FREQUENCY*4); calc_freq(20000000); } */ //==================================================================================================== //==================================================================================================== uint32_t cpu_ticks() { return xthal_get_ccount(); } uint32_t us() { return cpu_ticks()/240; } // Color clock frequency is 315/88 (3.57954545455) // DAC_MHZ is 315/11 or 8x color clock // 455/2 color clocks per line, round up to maintain phase // HSYNCH period is 44/315*455 or 63.55555..us // Field period is 262*44/315*455 or 16651.5555us #define IRE(_x) ((uint32_t)(((_x)+40)*255/3.3/147.5) << 8) // 3.3V DAC #define SYNC_LEVEL IRE(-40) #define BLANKING_LEVEL IRE(0) #define BLACK_LEVEL IRE(7.5) #define GRAY_LEVEL IRE(50) #define WHITE_LEVEL IRE(100) #define P0 (color >> 16) #define P1 (color >> 8) #define P2 (color) #define P3 (color << 8) volatile int _line_counter = 0; volatile int _frame_counter = 0; int _active_lines; int _line_count; int _line_width; int _samples_per_cc = 4; // 3 or 4 const uint32_t* _palette; float _sample_rate; int _hsync; int _hsync_long; int _hsync_short; int _burst_start; int _burst_width; int _active_start; int16_t* _burst0 = 0; // pal bursts int16_t* _burst1 = 0; static int usec(float us) { uint32_t r = (uint32_t)(us*_sample_rate); return ((r + _samples_per_cc)/(_samples_per_cc << 1))*(_samples_per_cc << 1); // multiple of color clock, word align } #define NTSC_COLOR_CLOCKS_PER_SCANLINE 228 // really 227.5 for NTSC but want to avoid half phase fiddling for now #define NTSC_FREQUENCY (315000000.0/88) #define NTSC_LINES 262 #define PAL_COLOR_CLOCKS_PER_SCANLINE 284 // really 283.75 ? #define PAL_FREQUENCY 4433618.75 #define PAL_LINES 312 void ntsc_init(); void pal_init(); void video_init(VideoStandard standard) { _pal_ = standard == PAL; #if SUPPORT_PAL if (_pal_) pal_init(); #endif #if SUPPORT_NTSC if (!_pal_) ntsc_init(); #endif _active_lines = 240; video_init_hw(_line_width,_samples_per_cc); // init the hardware } //=================================================================================================== //=================================================================================================== // Performance Testing #ifdef PERF #define BEGIN_TIMING() uint32_t t = cpu_ticks() #define END_TIMING() t = cpu_ticks() - t; _blit_ticks_min = min(_blit_ticks_min,t); _blit_ticks_max = max(_blit_ticks_max,t); #define ISR_BEGIN() uint32_t t = cpu_ticks() #define ISR_END() t = cpu_ticks() - t;_isr_us += (t+120)/240; uint32_t _blit_ticks_min = 0; uint32_t _blit_ticks_max = 0; uint32_t _isr_us = 0; void perf() { static int _next = 0; if (_drawn >= _next) { float elapsed_us = 120*1000000/(_emu->standard ? 60 : 50); _next = _drawn + 120; printf("frame_time:%d drawn:%d displayed:%d blit_ticks:%d->%d, isr time:%2.2f%%\n", _frame_time/240,_drawn,_frame_counter,_blit_ticks_min,_blit_ticks_max,(_isr_us*100)/elapsed_us); _blit_ticks_min = 0xFFFFFFFF; _blit_ticks_max = 0; _isr_us = 0; } } #else #define BEGIN_TIMING() #define END_TIMING() #define ISR_BEGIN() #define ISR_END() void perf(){}; #endif //=================================================================================================== //=================================================================================================== // PAL #if SUPPORT_PAL void pal_init() { _palette = pal_palette(); _sample_rate = PAL_FREQUENCY*_samples_per_cc/1000000.0; // DAC rate in mhz _line_width = PAL_COLOR_CLOCKS_PER_SCANLINE*_samples_per_cc; _line_count = PAL_LINES; _hsync_short = usec(2); _hsync_long = usec(30); _hsync = usec(4.7); _burst_start = usec(5.6); _burst_width = (int)(10*_samples_per_cc + 4) & 0xFFFE; _active_start = usec(10.4); // make colorburst tables for even and odd lines _burst0 = new int16_t[_burst_width]; _burst1 = new int16_t[_burst_width]; float phase = 2*M_PI/2; for (int i = 0; i < _burst_width; i++) { _burst0[i] = BLANKING_LEVEL + sin(phase + 3*M_PI/4) * BLANKING_LEVEL/1.5; _burst1[i] = BLANKING_LEVEL + sin(phase - 3*M_PI/4) * BLANKING_LEVEL/1.5; phase += 2*M_PI/_samples_per_cc; } } void IRAM_ATTR blit_pal(uint8_t* src, uint16_t* dst) { const bool even = _line_counter & 1; const uint32_t* p = even ? _palette : _palette + 256; uint32_t c,color; uint8_t c0,c1,c2,c3,c4; uint8_t y1,y2,y3; // pal is 5/4 wider than ntsc to account for pal 288 color clocks per line vs 228 in ntsc // so do an ugly stretch on pixels (actually luma) to accomodate -> 384 pixels are now 240 pal color clocks wide const int left = 0; const int right = 336; dst += 40; for (int i = left; i < right; i += 4) { c = *((uint32_t*)(src+i)); // make 5 colors out of 4 by interpolating y: 0000 0111 1122 2223 3333 c0 = c; c1 = c >> 8; c3 = c >> 16; c4 = c >> 24; y1 = (((c1 & 0xF) << 1) + ((c0 + c1) & 0x1F) + 2) >> 2; // (c0 & 0xF)*0.25 + (c1 & 0xF)*0.75; y2 = ((c1 + c3 + 1) >> 1) & 0xF; // (c1 & 0xF)*0.50 + (c2 & 0xF)*0.50; y3 = (((c3 & 0xF) << 1) + ((c3 + c4) & 0x1F) + 2) >> 2; // (c2 & 0xF)*0.75 + (c3 & 0xF)*0.25; c1 = (c1 & 0xF0) + y1; c2 = (c1 & 0xF0) + y2; c3 = (c3 & 0xF0) + y3; color = p[c0]; dst[0^1] = P0; dst[1^1] = P1; color = p[c1]; dst[2^1] = P2; dst[3^1] = P3; color = p[c2]; dst[4^1] = P0; dst[5^1] = P1; color = p[c3]; dst[6^1] = P2; dst[7^1] = P3; color = p[c4]; dst[8^1] = P0; dst[9^1] = P1; i += 4; c = *((uint32_t*)(src+i)); // make 5 colors out of 4 by interpolating y: 0000 0111 1122 2223 3333 c0 = c; c1 = c >> 8; c3 = c >> 16; c4 = c >> 24; y1 = (((c1 & 0xF) << 1) + ((c0 + c1) & 0x1F) + 2) >> 2; // (c0 & 0xF)*0.25 + (c1 & 0xF)*0.75; y2 = ((c1 + c3 + 1) >> 1) & 0xF; // (c1 & 0xF)*0.50 + (c2 & 0xF)*0.50; y3 = (((c3 & 0xF) << 1) + ((c3 + c4) & 0x1F) + 2) >> 2; // (c2 & 0xF)*0.75 + (c3 & 0xF)*0.25; c1 = (c1 & 0xF0) + y1; c2 = (c1 & 0xF0) + y2; c3 = (c3 & 0xF0) + y3; color = p[c0]; dst[10^1] = P2; dst[11^1] = P3; color = p[c1]; dst[12^1] = P0; dst[13^1] = P1; color = p[c2]; dst[14^1] = P2; dst[15^1] = P3; color = p[c3]; dst[16^1] = P0; dst[17^1] = P1; color = p[c4]; dst[18^1] = P2; dst[19^1] = P3; dst += 20; } } void IRAM_ATTR burst_pal(uint16_t* line) { line += _burst_start; int16_t* b = (_line_counter & 1) ? _burst0 : _burst1; for (int i = 0; i < _burst_width; i += 2) { line[i^1] = b[i]; line[(i+1)^1] = b[i+1]; } } // Fancy pal non-interlace // http://martin.hinner.info/vga/pal.html void IRAM_ATTR pal_sync2(uint16_t* line, int width, int swidth) { swidth = swidth ? _hsync_long : _hsync_short; int i; for (i = 0; i < swidth; i++) line[i] = SYNC_LEVEL; for (; i < width; i++) line[i] = BLANKING_LEVEL; } uint8_t DRAM_ATTR _sync_type[8] = {0,0,0,3,3,2,0,0}; void IRAM_ATTR pal_sync(uint16_t* line, int i) { uint8_t t = _sync_type[i-304]; pal_sync2(line,_line_width/2, t & 2); pal_sync2(line+_line_width/2,_line_width/2, t & 1); } #endif //=================================================================================================== //=================================================================================================== // ntsc tables // AA AA // 2 pixels, 1 color clock - atari // AA AB BB // 3 pixels, 2 color clocks - nes // AAA ABB BBC CCC // 4 pixels, 3 color clocks - sms // cc == 3 gives 684 samples per line, 3 samples per cc, 3 pixels for 2 cc // cc == 4 gives 912 samples per line, 4 samples per cc, 2 pixels per cc #if SUPPORT_NTSC void ntsc_init() { _palette = ntsc_palette(); _sample_rate = 315.0/88 * _samples_per_cc; // DAC rate _line_width = NTSC_COLOR_CLOCKS_PER_SCANLINE*_samples_per_cc; _line_count = NTSC_LINES; _hsync_long = usec(63.555-4.7); _active_start = usec(_samples_per_cc == 4 ? 10 : 10.5); _hsync = usec(4.7); } // draw a line of game in NTSC void IRAM_ATTR blit_ntsc(uint8_t* src, uint16_t* dst) { uint32_t* d = (uint32_t*)dst; const uint32_t* p = _palette; uint32_t c; int i; // 2 pixels per color clock, 4 samples per cc, used by atari // AA AA // 192 color clocks wide // only show 336 pixels d += 16; for (i = 0; i < 336; i += 4) { uint32_t c = *((uint32_t*)src); // screen may be in 32 bit mem d[0] = p[(uint8_t)c]; d[1] = p[(uint8_t)(c>>8)] << 8; d[2] = p[(uint8_t)(c>>16)]; d[3] = p[(uint8_t)(c>>24)] << 8; d += 4; src += 4; } END_TIMING(); } void IRAM_ATTR burst_ntsc(uint16_t* line) { int i,phase; switch (_samples_per_cc) { case 4: // 4 samples per color clock for (i = _hsync; i < _hsync + (4*10); i += 4) { #if COLORBURST line[i+1] = BLANKING_LEVEL; line[i+0] = BLANKING_LEVEL + BLANKING_LEVEL/2; line[i+3] = BLANKING_LEVEL; line[i+2] = BLANKING_LEVEL - BLANKING_LEVEL/2; #else line[i+1] = BLANKING_LEVEL; line[i+0] = BLANKING_LEVEL; line[i+3] = BLANKING_LEVEL; line[i+2] = BLANKING_LEVEL; #endif } break; case 3: // 3 samples per color clock phase = 0.866025*BLANKING_LEVEL/2; for (i = _hsync; i < _hsync + (3*10); i += 6) { line[i+1] = BLANKING_LEVEL; line[i+0] = BLANKING_LEVEL + phase; line[i+3] = BLANKING_LEVEL - phase; line[i+2] = BLANKING_LEVEL; line[i+5] = BLANKING_LEVEL + phase; line[i+4] = BLANKING_LEVEL - phase; } break; } } // ---- TRUECOLOR BEGIN ------------------------------------------------------------------------------ // MODO "TRUE COLOR" (so NTSC). Em vez de usar o pixel de 8 bits como indice da paleta de 256 cores, // cada pixel de uma imagem propria (_tc_img, largura _tc_w) guarda Y, a, b e o ISR gera as 4 amostras // do DAC de UM ciclo da subportadora (1 ciclo = 2 pixels da tela = 4 amostras) direto: // T0 = Y + b T1 = Y + a T2 = Y - b T3 = Y - a (mesma estrutura da paleta atari_4_phase_ntsc) // Formato do pixel (16 bits): Y(6) | a(5) | b(5) // Y = valor_DAC - 24 (0..49 = preto..branco), a e b = croma em niveis do DAC, -16..15 (sem sinal: +16) // A palavra de 32 bits montada e identica a uma entrada da paleta (b3=T0, b2=T2, b1=T1, b0=T3) e e escrita // exatamente como blit_ntsc escreve (d[0] = p; d[1] = p << 8), entao a posicao das amostras e a mesma. #define TC_PIX(y, a, b) ((uint16_t)((((y) & 63) << 10) | ((((a) + 16) & 31) << 5) | (((b) + 16) & 31))) #define TC_BLACK TC_PIX(0, 0, 0) #define TC_CC 168 // ciclos de subportadora visiveis por linha (336 pixels / 2) volatile bool _tc_on = false; // true: o ISR desenha a imagem true color no lugar do quadro de 8 bits const uint16_t* volatile _tc_img = 0; // imagem _tc_w x (linhas) de pixels TC_PIX int _tc_w = 160; volatile int _tc_mx = 8; // margem preta lateral, em ciclos de cor por lado (1 ciclo = 2 pixels da tela): compensa o overscan da TV uint8_t _tc_xmap[TC_CC]; // ciclo de cor -> coluna da imagem (so vale entre as margens) uint8_t _tc_rowmap[240]; // linha da tela -> linha da imagem (0xFF = linha preta) // Tabela (a,b) -> 4 bytes [T0 T2 T1 T3] = [b+16, 16-b, a+16, 16-a]; soma-se (Y+8)*0x01010101 e sai a palavra // final sem "vai um" entre bytes (cada byte <= 49+8+31). Troca ~12 instrucoes por ciclo de cor por 1 tabela de 4 KB. uint32_t _tc_tab[1024]; // Monta o mapa ciclo -> coluna para a janela util [mx, TC_CC-mx); fora dela a linha sai preta (ver blit_ntsc_tc). static void tc_build_xmap(int mx) { const int width = TC_CC - 2 * mx; for (int c = 0; c < TC_CC; c++) { int k = ((c - mx) * _tc_w + width / 2) / width; if (c < mx) k = 0; if (k < 0) k = 0; if (k > _tc_w - 1) k = _tc_w - 1; _tc_xmap[c] = (uint8_t)k; } } // Margem lateral preta (0..60 ciclos por lado). Pode ser chamada a qualquer momento. void tc_set_xmargin(int mx) { if (mx < 0) mx = 0; if (mx > 60) mx = 60; tc_build_xmap(mx); _tc_mx = mx; } void tc_setup(const uint16_t* img, int w) { // chamar uma vez (w <= 255) _tc_w = w; tc_build_xmap(_tc_mx); for (int y = 0; y < 240; y++) _tc_rowmap[y] = 0xFF; for (int a = -16; a < 16; a++) for (int b = -16; b < 16; b++) _tc_tab[((a + 16) << 5) | (b + 16)] = ((uint32_t)(b + 16) << 24) | ((uint32_t)(16 - b) << 16) | ((uint32_t)(a + 16) << 8) | (uint32_t)(16 - a); _tc_img = img; } void tc_enable(bool on) { _tc_on = on && !_pal_; } // so existe em NTSC void IRAM_ATTR blit_ntsc_tc(int line, uint16_t* dst) { uint32_t* d = (uint32_t*)dst; d += 16; // mesmo inicio de linha do blit_ntsc const uint8_t r = _tc_rowmap[line]; if (r == 0xFF) { // linha preta for (int c = 0; c < TC_CC; c++) { d[0] = 0x18181818; d[1] = 0x18181800; d += 2; } return; } const uint16_t* src = _tc_img + (int)r * _tc_w; const uint8_t* xm = _tc_xmap; const uint32_t* tab = _tc_tab; const int mx = _tc_mx; int c = 0; for (; c < mx; c++) { d[0] = 0x18181818; d[1] = 0x18181800; d += 2; } // margem esquerda (preto) for (; c < TC_CC - mx; c++) { const uint32_t v = src[xm[c]]; const uint32_t p = ((v >> 10) + 8) * 0x01010101u + tab[v & 1023]; d[0] = p; d[1] = p << 8; d += 2; } for (; c < TC_CC; c++) { d[0] = 0x18181818; d[1] = 0x18181800; d += 2; } // margem direita (preto) } // ---- TRUECOLOR END -------------------------------------------------------------------------------- #endif void IRAM_ATTR blit(uint8_t* src, uint16_t* dst) { BEGIN_TIMING(); #if SUPPORT_PAL if (_pal_) blit_pal(src,dst); #endif #if SUPPORT_NTSC if (!_pal_) blit_ntsc(src,dst); #endif END_TIMING(); } void IRAM_ATTR burst(uint16_t* line) { #if SUPPORT_PAL if (_pal_) burst_pal(line); #endif #if SUPPORT_NTSC if(!_pal_) burst_ntsc(line); #endif } void IRAM_ATTR sync(uint16_t* line, int syncwidth) { for (int i = 0; i < syncwidth; i++) line[i] = SYNC_LEVEL; } // ---- BLANKING RAPIDO (NTSC) ------------------------------------------------------------------------- // As linhas fora da imagem (22 por quadro) eram regeneradas inteiras a cada vez: 912 escritas de 16 bits, // o que levava ~70 us (> 63,6 us de uma linha) e fazia o ISR perder linhas. Agora: // - o preenchimento e feito em palavras de 32 bits (2 amostras por escrita, mesmo valor nas duas metades); // - cada um dos 2 buffers de DMA lembra o que ja tem (_buf_kind): 0 = sujo (linha ativa), 1 = blanking // com burst, 2 = vsync. Se o buffer ja contem exatamente a linha pedida, nada e reescrito. // O conteudo final de cada linha e identico ao do codigo antigo; so muda a forma de escrever. PAL nao muda. uint8_t DRAM_ATTR _buf_kind[2] = {0, 0}; static inline __attribute__((always_inline)) int _buf_idx(const uint16_t* p) { return ((const void*)p == (const void*)_dma_desc[0].buf) ? 0 : 1; } static inline __attribute__((always_inline)) void fill32(uint32_t* w, int n, uint32_t v) { while (n >= 4) { w[0] = v; w[1] = v; w[2] = v; w[3] = v; w += 4; n -= 4; } while (n-- > 0) *w++ = v; } void IRAM_ATTR blanking(uint16_t* line, bool vbl) { if (!_pal_) { const int k = _buf_idx(line); const uint8_t want = vbl ? 2 : 1; if (_buf_kind[k] == want) return; // o buffer ja tem esta linha const uint32_t S32 = (uint32_t)(uint16_t)SYNC_LEVEL * 0x00010001u; const uint32_t B32 = (uint32_t)(uint16_t)BLANKING_LEVEL * 0x00010001u; uint32_t* w = (uint32_t*)line; const int sw = (vbl ? _hsync_long : _hsync) >> 1; // largura do sincronismo, em palavras const int n = _line_width >> 1; // linha inteira, em palavras fill32(w, sw, S32); fill32(w + sw, n - sw, B32); if (!vbl) burst(line); // sem burst durante o vsync _buf_kind[k] = want; return; } int syncwidth = vbl ? _hsync_long : _hsync; sync(line,syncwidth); for (int i = syncwidth; i < _line_width; i++) line[i] = BLANKING_LEVEL; if (!vbl) burst(line); // no burst during vbl } // Wait for blanking before starting drawing // avoids tearing in our unsynchonized world #ifdef ESP_PLATFORM void video_sync() { if (!_lines) return; int n = 0; if (_pal_) { if (_line_counter < _active_lines) n = (_active_lines - _line_counter)*1000/15600; } else { if (_line_counter < _active_lines) n = (_active_lines - _line_counter)*1000/15720; } vTaskDelay(n+1); } #endif // ---- ESTATISTICAS DO ISR DE VIDEO (diagnostico de "pontos esticados": ISR atrasado ou lento demais) ---- // Duas copias do buffer de DMA se alternam; o ISR tem UMA linha (63,5 us = ~15250 ciclos a 240 MHz) para // preencher a linha seguinte. Se a interrupcao chega tarde ou o preenchimento demora, o fim da linha sai com // dados velhos. Medido com o contador de ciclos da CPU (instrucao rsr, segura dentro do ISR). #ifndef VIDEO_ISR_STATS #define VIDEO_ISR_STATS 1 #endif #if VIDEO_ISR_STATS volatile uint32_t _st_calls = 0, _st_max_run = 0, _st_max_gap = 0, _st_late = 0, _st_long = 0; volatile uint32_t _st_line_max[312]; // maior duracao (ciclos) do ISR por indice de linha (0..261 NTSC, 0..311 PAL) uint32_t _st_last = 0; static inline uint32_t IRAM_ATTR _ccount() { uint32_t c; __asm__ __volatile__("rsr %0, ccount" : "=a"(c)); return c; } // Le e zera. Ciclos -> microssegundos pelo clock de 240 MHz. void isr_stats_take(uint32_t* calls, float* max_run_us, float* max_gap_us, uint32_t* late, uint32_t* longrun) { *calls = _st_calls; *max_run_us = _st_max_run / 240.0f; *max_gap_us = _st_max_gap / 240.0f; *late = _st_late; *longrun = _st_long; _st_calls = 0; _st_max_run = 0; _st_max_gap = 0; _st_late = 0; _st_long = 0; } // Diz QUAIS linhas passaram de limit_us (em faixas, ex. "240-261") e o pior tempo nas linhas visiveis // (0-239) e nas demais. Zera o vetor. Serve para confirmar se o ISR so demora nas linhas fora da imagem. void isr_stats_lines(char* out, size_t n, float limit_us, float* max_vis_us, float* max_blank_us) { const uint32_t lim = (uint32_t)(limit_us * 240.0f); uint32_t mv = 0, mb = 0; size_t pos = 0; int start = -1; out[0] = 0; for (int k = 0; k <= 312; k++) { const uint32_t v = (k < 312) ? _st_line_max[k] : 0; if (k < 240) { if (v > mv) mv = v; } else if (k < 312) { if (v > mb) mb = v; } const bool over = v > lim; if (over && start < 0) start = k; if (!over && start >= 0) { const int e = k - 1; int w = (e == start) ? snprintf(out + pos, n - pos, "%s%d", pos ? "," : "", start) : snprintf(out + pos, n - pos, "%s%d-%d", pos ? "," : "", start, e); start = -1; if (w < 0 || (size_t)w >= n - pos) break; // sem espaco: para de listar pos += (size_t)w; } } if (!out[0]) snprintf(out, n, "nenhuma"); for (int k = 0; k < 312; k++) _st_line_max[k] = 0; *max_vis_us = mv / 240.0f; *max_blank_us = mb / 240.0f; } #endif // Workhorse ISR handles audio and video updates extern "C" void IRAM_ATTR video_isr(volatile void* vbuf) { if (!_lines) return; ISR_BEGIN(); #if VIDEO_ISR_STATS const uint32_t _t0 = _ccount(); if (_st_last) { const uint32_t gap = _t0 - _st_last; if (gap > _st_max_gap && gap < 2000000u) _st_max_gap = gap; if (gap > 17000u && gap < 2000000u) _st_late++; // > 70 us entre duas linhas (nominal 63,6 us) } _st_last = _t0; #endif #if SUPPORT_AUDIO uint8_t s = _audio_r < _audio_w ? _audio_buffer[_audio_r++ & (sizeof(_audio_buffer)-1)] : 0x20; audio_sample(s); //audio_sample(_sin64[_x++ & 0x3F]); #endif #ifdef IR_PIN ir_sample(); #endif int i = _line_counter++; uint16_t* buf = (uint16_t*)vbuf; #if SUPPORT_PAL if (_pal_) { // pal if (i < 32) { blanking(buf,false); // pre render/black 0-32 } else if (i < _active_lines + 32) { // active video 32-272 sync(buf,_hsync); burst(buf); blit(_lines[i-32],buf + _active_start); } else if (i < 304) { // post render/black 272-304 if (i < 274) // slight optimization here, once you have 2 blanking buffers blanking(buf,false); } else { pal_sync(buf,i); // 8 lines of sync 304-312 } } #endif #if SUPPORT_NTSC if(!_pal_) { // ntsc if (i < _active_lines) { // active video _buf_kind[_buf_idx(buf)] = 0; // este buffer deixa de ser uma linha de blanking pura sync(buf,_hsync); burst(buf); if (_tc_on && _tc_img) blit_ntsc_tc(i, buf + _active_start); // true color else blit(_lines[i],buf + _active_start); // paleta de 256 cores } else if (i < (_active_lines + 5)) { // post render/black blanking(buf,false); } else if (i < (_active_lines + 8)) { // vsync blanking(buf,true); } else { // pre render/black blanking(buf,false); } } #endif if (_line_counter == _line_count) { _line_counter = 0; // frame is done _frame_counter++; } ISR_END(); #if VIDEO_ISR_STATS { const uint32_t run = _ccount() - _t0; _st_calls++; if (run > _st_max_run) _st_max_run = run; if (run > 9600u) _st_long++; // ISR levou mais de 40 us if (i >= 0 && i < 312 && run > _st_line_max[i]) _st_line_max[i] = run; } #endif } //=================================================================================================== //=================================================================================================== // sound routines from "src/gui.cpp" and "src/emu.cpp" #if SUPPORT_AUDIO // audio is buffered as 6 bit unsigned samples uint8_t _audio_buffer[1024]; uint32_t _audio_r = 0; uint32_t _audio_w = 0; void audio_write_16(const int16_t* s, int len, int channels) { int b; while (len--) { if (_audio_w == (_audio_r + sizeof(_audio_buffer))) break; if (channels == 2) { b = (s[0] + s[1]) >> 9; s += 2; } else b = *s++ >> 8; if (b < -32) b = -32; if (b > 31) b = 31; _audio_buffer[_audio_w++ & (sizeof(_audio_buffer)-1)] = b + 32; } } #endif #if 0 Emu::Emu(const char* n,int w,int h, int st, int aformat, int cc, int f) : name(n),width(w),height(h),standard(st),audio_format(aformat),cc_width(cc),flavor(f) { //audio_frequency = 15625; // requires fixed point sampler audio_frequency = standard == 1 ? 15720 : 15600; audio_frame_samples = standard ? (audio_frequency << 16)/60 : (audio_frequency << 16)/50; // fixed point sampler audio_fraction = 0; } int audio_format = (16 | (1 << 8)); // soft click wave soundy thing const uint16_t _wav[16] = { 0x0000,0x187D,0x2D41,0x3B20,0x3FFF,0x3B20,0x2D41,0x187D, 0x0000,0xE783,0xD2BF,0xC4E0,0xC001,0xC4E0,0xD2BF,0xE783 }; void update_audio() { int16_t abuffer[313*2]; int format = _emu->audio_format >> 8; int sample_count = _emu->frame_sample_count(); if (_visible) { format = 1; if (_click) { _click = 0; for (int i = 0; i < sample_count; i++) abuffer[i] = _wav[i&0xF]; // just a signed sine click } else memset(abuffer,0,sizeof(abuffer)); } else { sample_count = _emu->audio_buffer(abuffer,sizeof(abuffer)); } audio_write_16(abuffer,sample_count,format); } #endif } // Namespace RawCompositeVideoBlitter

Comentários

Postagens mais visitadas deste blog

JVC Compulink, COMPU LINK, JVC, Tape Deck, Arduino, DCS Codes

JVC Compu Link Final

RTTY, PSK31, FT8, JS8CALL, NBTV, RDS FM, OLIVIA, SSTV, SSTV AUTO,CW, ASCII Encoder / Decoder