// **************************************************************************** // // VGA videomodes // // **************************************************************************** #include "include.h" sVmode Vmode; // videomode setup sVgaCfg Cfg; // required configuration sCanvas Canvas; // canvas of draw box // 16-color palette translation table u16 Pal16Trans[256]; /* http://martin.hinner.info/vga/pal.html VGA system (525 lines total): time 0: - line 1, 2: (2) vertical sync - line 3..35: (33) dark - line 36..515: (480) image lines 0..479 - line 516..525: (10) dark PAL system (625 lines total): time 0: - line 1, 2: (2) vertical sync + vertical sync - line 3: (1) vertical sync + half sync - line 4, 5: (2) half sync + half sync - line 6..23: (18) dark - line 24..46: (23) dark image time 46: - line 47..286: (240) image lines odd 1, 3, 5 ... 479 - line 287..310: (24) dark image - line 311..312: (2) half sync + half sync - line 313: (1) half sync + vertical sync vsync time 313 (vsync time 312.5): - line 314..315: (2) vertical sync + vertical sync - line 316..317: (2) half sync + half sync - line 318..335: (18) dark - line 336..358: (23) dark image time 358 (45.5 from last vsync) - line 359..598: (240) image lines even 0, 2, ... 478 - line 599..622: (24) dark image - line 623..625: (3) half sync + half sync time 625: NTSC system (525 lines total): time 0, even field: - line 1..3: (3) vertical sync + vertical sync (6 serration pulses: 27.3 us low, 4.5 us high) - line 4..6: (3) half sync + half sync (6 equalizing pulses: 2.3 us low, 29.5 us high) - line 7..16: (10) dark (blanked video: 4.7 us low, 58.9 us high) - line 17,18: (2) dark image time 18: - line 19..258: (240) image lines even 0, 2, ... 478 - line 259: (1) dark image - line 260..262: (3) half sync + half sync (7 equalizing pulses) - line 263: (1) half sync + vertical sync (6 serration pulses) time 263 (vsync time 262.5): - line 264,265: (2) vertical sync + vertical sync - line 266: (1) vertical sync + half sync (5 equalizing pulses) - line 267..268: (2) half sync + half sync - line 269..279: (11) dark - line 280..281: (2) dark image time 281 (18.5 from last vsync) - line 282..521: (240) image lines odd 1, 3, 5 ... 479 - line 522: (1) dark image - line 523..525: (3) half sync + half sync time 525: */ // === TV videomodes // TV PAL interlaced 5:4 720x576 (4:3 768x576, 16:9 1024x576) const sVideo VideoPAL = { // horizontal (horizontal frequency 15625 Hz, effective sync pulses 16000 Hz) .htot= 64.00000f, // total scanline in [us] .hfront= 1.65000f, // H front porch (after image, before HSYNC) in [us] .hsync= 4.70000f, // H sync pulse in [us] .hback= 5.70000f, // H back porch (after HSYNC, before image) in [us] .hfull= 47.36000f, // H full visible in [us] (formally should be 51.95 us) // vertical (vertical frequency 50 Hz) .vtot=625, // total scanlines (both subframes) .vmax=576, // maximal height // subframe 1 .vsync1=5, // V sync (half-)pulses on subframe 1 .vpost1=5, // V sync post half-pulses on subframe 1 .vback1=18+23, // V back porch (after VSYNC, before image) on subframe 1 .vact1=240, // active visible scanlines, subframe 1 (formally should be 288, 576 total) .vfront1=24, // V front porch (after image, before VSYNC) on subframe 1 .vpre1=5, // V sync pre half-pulses on subframe 1 // subframe 2 (ignored if not interlaced) .vsync2=5, // V sync half-pulses on subframe 2 .vpost2=4, // V sync post half-pulses on subframe 2 .vback2=18+23, // V back porch (after VSYNC, before image) on subframe 2 .vact2=240, // active visible scanlines, subframe 2 (formally should be 288, 576 total) .vfront2=24, // V front porch (after image, before VSYNC) on subframe 2 .vpre2=6, // V sync pre half-pulses on subframe 2 // name .name = "PAL ", // video timing name (VIDEO_NAME_LEN characters + terminating 0) // flags .inter=True, // interlaced (use subframes) .psync=False, // positive synchronization .odd=True, // first sub-frame is odd lines 1, 3, 5,... (PAL) }; // TV PAL progressive 5:4 360x288 (4:3 384x288, 16:9 512x288) const sVideo VideoPALp = { // horizontal (horizontal frequency 15625 Hz) .htot= 64.00000f, // total scanline in [us] .hfront= 1.65000f, // H front porch (after image, before HSYNC) in [us] .hsync= 4.70000f, // H sync pulse in [us] .hback= 5.70000f, // H back porch (after HSYNC, before image) in [us] .hfull= 47.36000f, // H full visible in [us] (formally should be 51.95 us) // vertical (vertical frequency 50 Hz) .vtot=312, // total scanlines (both subframes) .vmax=288, // maximal height // subframe 1 .vsync1=2, // V sync (half-)pulses on subframe 1 .vpost1=0, // V sync post half-pulses on subframe 1 .vback1=18+23+2, // V back porch (after VSYNC, before image) on subframe 1 .vact1=240, // active visible scanlines, subframe 1 (formally should be 288, 576 total) .vfront1=24+3, // V front porch (after image, before VSYNC) on subframe 1 .vpre1=0, // V sync pre half-pulses on subframe 1 // subframe 2 (ignored if not interlaced) .vsync2=0, // V sync half-pulses on subframe 2 .vpost2=0, // V sync post half-pulses on subframe 2 .vback2=0, // V back porch (after VSYNC, before image) on subframe 2 .vact2=0, // active visible scanlines, subframe 2 (formally should be 288, 576 total) .vfront2=0, // V front porch (after image, before VSYNC) on subframe 2 .vpre2=0, // V sync pre half-pulses on subframe 2 // name .name = "PALp ", // video timing name (VIDEO_NAME_LEN characters + terminating 0) // flags .inter=False, // interlaced (use subframes) .psync=False, // positive synchronization .odd=True, // first sub-frame is odd lines 1, 3, 5,... (PAL) }; // TV NTSC interlaced 4:3 640x480 (5:4 600x480, 16:9 848x480) // serration pulses (half vsync): 27.3 us low, 4.5 us high // equalizing pulses (half hsync): 2.3 us low, 29.5 us high // blanked video (hsync pulses): 4.7 us low, 58.9 us high const sVideo VideoNTSC = { // horizontal (horizontal frequency 15734 Hz, effective sync pulses 16274 Hz) .htot= 63.55582f, // total scanline in [us] .hfront= 1.50000f, // H front porch (after image, before HSYNC) in [us] .hsync= 4.70000f, // H sync pulse in [us] .hback= 4.50000f, // H back porch (after HSYNC, before image) in [us] .hfull= 47.03130f, // H full visible in [us] // vertical .vtot=525, // total scanlines (both subframes) .vmax=480, // maximal height // subframe 1 .vsync1=6, // V sync (half-)pulses on subframe 1 .vpost1=6, // V sync post half-pulses on subframe 1 .vback1=10+2, // V back porch (after VSYNC, before image) on subframe 1 .vact1=240, // active visible scanlines, subframe 1 .vfront1=1, // V front porch (after image, before VSYNC) on subframe 1 .vpre1=7, // V sync pre half-pulses on subframe 1 // subframe 2 (ignored if not interlaced) .vsync2=6, // V sync half-pulses on subframe 2 .vpost2=5, // V sync post half-pulses on subframe 2 .vback2=11+2, // V back porch (after VSYNC, before image) on subframe 2 .vact2=240, // active visible scanlines, subframe 2 .vfront2=1, // V front porch (after image, before VSYNC) on subframe 2 .vpre2=6, // V sync pre half-pulses on subframe 2 // name .name = "NTSC ", // video timing name (VIDEO_NAME_LEN characters + terminating 0) // flags .inter=True, // interlaced (use subframes) .psync=False, // positive synchronization .odd=False, // first sub-frame is odd lines 1, 3, 5,... (PAL) }; // TV NTSC progressive 4:3 320x240 (5:4 300x240, 16:9 424x240) const sVideo VideoNTSCp = { // horizontal (horizontal frequency 15734 Hz) .htot= 63.55582f, // total scanline in [us] .hfront= 1.50000f, // H front porch (after image, before HSYNC) in [us] .hsync= 4.70000f, // H sync pulse in [us] .hback= 4.50000f, // H back porch (after HSYNC, before image) in [us] .hfull= 47.03130f, // H full visible in [us] // vertical .vtot=262, // total scanlines (both subframes) .vmax=240, // maximal height // subframe 1 .vsync1=3, // V sync (half-)pulses on subframe 1 .vpost1=0, // V sync post half-pulses on subframe 1 .vback1=10+2+3, // V back porch (after VSYNC, before image) on subframe 1 .vact1=240, // active visible scanlines, subframe 1 .vfront1=1+3, // V front porch (after image, before VSYNC) on subframe 1 .vpre1=0, // V sync pre half-pulses on subframe 1 // subframe 2 (ignored if not interlaced) .vsync2=0, // V sync half-pulses on subframe 2 .vpost2=0, // V sync post half-pulses on subframe 2 .vback2=0, // V back porch (after VSYNC, before image) on subframe 2 .vact2=0, // active visible scanlines, subframe 2 .vfront2=0, // V front porch (after image, before VSYNC) on subframe 2 .vpre2=6, // V sync pre half-pulses on subframe 2 // name .name = "NTSCp", // video timing name (VIDEO_NAME_LEN characters + terminating 0) // flags .inter=False, // interlaced (use subframes) .psync=False, // positive synchronization .odd=False, // first sub-frame is odd lines 1, 3, 5,... (PAL) }; // === Monitor videomodes // EGA 8:5 640x400 (5:4 500x400, 4:3 528x400, 16:9 704x400), vert. 70 Hz, hor. 31.4685 kHz, pixel clock 25.175 MHz const sVideo VideoEGA = { // horizontal .htot= 31.77781f, // total scanline in [us] .hfront= 0.63556f, // H front porch (after image, before HSYNC) in [us] .hsync= 3.81334f, // H sync pulse in [us] .hback= 1.90667f, // H back porch (after HSYNC, before image) in [us] .hfull= 25.42224f, // H full visible in [us] // vertical .vtot=449, // total scanlines (both subframes) .vmax=400, // maximal height // subframe 1 .vsync1=2, // V sync (half-)pulses on subframe 1 .vpost1=0, // V sync post half-pulses on subframe 1 .vback1=35, // V back porch (after VSYNC, before image) on subframe 1 .vact1=400, // active visible scanlines, subframe 1 .vfront1=12, // V front porch (after image, before VSYNC) on subframe 1 .vpre1=0, // V sync pre half-pulses on subframe 1 // subframe 2 (ignored if not interlaced) .vsync2=0, // V sync half-pulses on subframe 2 .vpost2=0, // V sync post half-pulses on subframe 2 .vback2=0, // V back porch (after VSYNC, before image) on subframe 2 .vact2=0, // active visible scanlines, subframe 2 .vfront2=0, // V front porch (after image, before VSYNC) on subframe 2 .vpre2=0, // V sync pre half-pulses on subframe 2 // name .name = "EGA ", // video timing name (VIDEO_NAME_LEN characters + terminating 0) // flags .inter=False, // interlaced (use subframes) .psync=False, // positive synchronization .odd=False, // first sub-frame is odd lines 1, 3, 5,... (PAL) }; // VGA 4:3 640x480 (16:9 848x480), vert. 60 Hz, hor. 31.4685 kHz, pixel clock 25.175 MHz const sVideo VideoVGA = { // horizontal .htot= 31.77781f, // total scanline in [us] (800 pixels) .hfront= 0.63556f, // H front porch (after image, before HSYNC) in [us] (16 pixels) .hsync= 3.81334f, // H sync pulse in [us] (96 pixels) .hback= 1.90667f, // H back porch (after HSYNC, before image) in [us] (48 pixels) .hfull= 25.42224f, // H full visible in [us] (640 pixels) // vertical .vtot=525, // total scanlines (both subframes) .vmax=480, // maximal height // subframe 1 .vsync1=2, // V sync (half-)pulses on subframe 1 .vpost1=0, // V sync post half-pulses on subframe 1 .vback1=33, // V back porch (after VSYNC, before image) on subframe 1 .vact1=480, // active visible scanlines, subframe 1 .vfront1=10, // V front porch (after image, before VSYNC) on subframe 1 .vpre1=0, // V sync pre half-pulses on subframe 1 // subframe 2 (ignored if not interlaced) .vsync2=0, // V sync half-pulses on subframe 2 .vpost2=0, // V sync post half-pulses on subframe 2 .vback2=0, // V back porch (after VSYNC, before image) on subframe 2 .vact2=0, // active visible scanlines, subframe 2 .vfront2=0, // V front porch (after image, before VSYNC) on subframe 2 .vpre2=0, // V sync pre half-pulses on subframe 2 // name .name = "VGA ", // video timing name (VIDEO_NAME_LEN characters + terminating 0) // flags .inter=False, // interlaced (use subframes) .psync=False, // positive synchronization .odd=False, // first sub-frame is odd lines 1, 3, 5,... (PAL) }; // SVGA 4:3 800x600 (16:9 1064x600), vert. 60 Hz, hor. 37.897 kHz, pixel clock 40 MHz const sVideo VideoSVGA = { // horizontal .htot= 26.40000f, // total scanline in [us] (1056 pixels) .hfront= 1.00000f, // H front porch (after image, before HSYNC) in [us] (40 pixels) .hsync= 3.20000f, // H sync pulse in [us] (128 pixels) .hback= 2.20000f, // H back porch (after HSYNC, before image) in [us] (88 pixels) .hfull= 20.00000f, // H full visible in [us] (800 pixels) // vertical .vtot=628, // total scanlines (both subframes) .vmax=600, // maximal height // subframe 1 .vsync1=4, // V sync (half-)pulses on subframe 1 .vpost1=0, // V sync post half-pulses on subframe 1 .vback1=23, // V back porch (after VSYNC, before image) on subframe 1 .vact1=600, // active visible scanlines, subframe 1 .vfront1=1, // V front porch (after image, before VSYNC) on subframe 1 .vpre1=0, // V sync pre half-pulses on subframe 1 // subframe 2 (ignored if not interlaced) .vsync2=0, // V sync half-pulses on subframe 2 .vpost2=0, // V sync post half-pulses on subframe 2 .vback2=0, // V back porch (after VSYNC, before image) on subframe 2 .vact2=0, // active visible scanlines, subframe 2 .vfront2=0, // V front porch (after image, before VSYNC) on subframe 2 .vpre2=0, // V sync pre half-pulses on subframe 2 // name .name = "SVGA ", // video timing name (VIDEO_NAME_LEN characters + terminating 0) // flags .inter=False, // interlaced (use subframes) .psync=True, // positive synchronization .odd=False, // first sub-frame is odd lines 1, 3, 5,... (PAL) }; // XGA 4:3 1024x768 (16:9 1360x768), vert. 60 Hz, hor. 48.36310 kHz, pixel clock 65 MHz const sVideo VideoXGA = { // horizontal .htot= 20.67692f, // total scanline in [us] (1344 pixels) .hfront= 0.36923f, // H front porch (after image, before HSYNC) in [us] (24 pixels) .hsync= 2.09231f, // H sync pulse in [us] (136 pixels) .hback= 2.46154f, // H back porch (after HSYNC, before image) in [us] (160 pixels) .hfull= 15.75385f, // H full visible in [us] (1024 pixels) // vertical .vtot=806, // total scanlines (both subframes) .vmax=768, // maximal height // subframe 1 .vsync1=6, // V sync (half-)pulses on subframe 1 .vpost1=0, // V sync post half-pulses on subframe 1 .vback1=29, // V back porch (after VSYNC, before image) on subframe 1 .vact1=768, // active visible scanlines, subframe 1 .vfront1=3, // V front porch (after image, before VSYNC) on subframe 1 .vpre1=0, // V sync pre half-pulses on subframe 1 // subframe 2 (ignored if not interlaced) .vsync2=0, // V sync half-pulses on subframe 2 .vpost2=0, // V sync post half-pulses on subframe 2 .vback2=0, // V back porch (after VSYNC, before image) on subframe 2 .vact2=0, // active visible scanlines, subframe 2 .vfront2=0, // V front porch (after image, before VSYNC) on subframe 2 .vpre2=0, // V sync pre half-pulses on subframe 2 // name .name = "XGA ", // video timing name (VIDEO_NAME_LEN characters + terminating 0) // flags .inter=False, // interlaced (use subframes) .psync=False, // positive synchronization .odd=False, // first sub-frame is odd lines 1, 3, 5,... (PAL) }; // VESA 4:3 1152x864, vert. 60 Hz, hor. 53.697 kHz, pixel clock 81.62 MHz const sVideo VideoVESA = { // horizontal .htot= 18.62289f, // total scanline in [us] (1520 pixels) .hfront= 0.78412f, // H front porch (after image, before HSYNC) in [us] (64 pixels) .hsync= 1.47023f, // H sync pulse in [us] (120 pixels) .hback= 2.25435f, // H back porch (after HSYNC, before image) in [us] (184 pixels) .hfull= 14.11419f, // H full visible in [us] (1152 pixels) // vertical .vtot=895, // total scanlines (both subframes) .vmax=864, // maximal height // subframe 1 .vsync1=3, // V sync (half-)pulses on subframe 1 .vpost1=0, // V sync post half-pulses on subframe 1 .vback1=27, // V back porch (after VSYNC, before image) on subframe 1 .vact1=864, // active visible scanlines, subframe 1 .vfront1=1, // V front porch (after image, before VSYNC) on subframe 1 .vpre1=0, // V sync pre half-pulses on subframe 1 // subframe 2 (ignored if not interlaced) .vsync2=0, // V sync half-pulses on subframe 2 .vpost2=0, // V sync post half-pulses on subframe 2 .vback2=0, // V back porch (after VSYNC, before image) on subframe 2 .vact2=0, // active visible scanlines, subframe 2 .vfront2=0, // V front porch (after image, before VSYNC) on subframe 2 .vpre2=0, // V sync pre half-pulses on subframe 2 // name .name = "VESA ", // video timing name (VIDEO_NAME_LEN characters + terminating 0) // flags .inter=False, // interlaced (use subframes) .psync=True, // positive synchronization .odd=False, // first sub-frame is odd lines 1, 3, 5,... (PAL) }; // HD 4:3 1280x960, vert. 53 Hz, hor. 51.858 kHz, pixel clock 102.1 MHz #define HD_SLOW 1.15f const sVideo VideoHD = { // horizontal .htot= 16.76787f*HD_SLOW, // total scanline in [us] (1712 pixels) .hfront= 0.78355f*HD_SLOW, // H front porch (after image, before HSYNC) in [us] (80 pixels) .hsync= 1.33203f*HD_SLOW, // H sync pulse in [us] (136 pixels) .hback= 2.11557f*HD_SLOW, // H back porch (after HSYNC, before image) in [us] (216 pixels) .hfull= 12.53673f*HD_SLOW, // H full visible in [us] (1280 pixels) // vertical .vtot=994-10, // total scanlines (both subframes) .vmax=960, // maximal height // subframe 1 .vsync1=3, // V sync (half-)pulses on subframe 1 .vpost1=0, // V sync post half-pulses on subframe 1 .vback1=30-10, // V back porch (after VSYNC, before image) on subframe 1 .vact1=960, // active visible scanlines, subframe 1 .vfront1=1, // V front porch (after image, before VSYNC) on subframe 1 .vpre1=0, // V sync pre half-pulses on subframe 1 // subframe 2 (ignored if not interlaced) .vsync2=0, // V sync half-pulses on subframe 2 .vpost2=0, // V sync post half-pulses on subframe 2 .vback2=0, // V back porch (after VSYNC, before image) on subframe 2 .vact2=0, // active visible scanlines, subframe 2 .vfront2=0, // V front porch (after image, before VSYNC) on subframe 2 .vpre2=0, // V sync pre half-pulses on subframe 2 // name .name = "HD ", // video timing name (VIDEO_NAME_LEN characters + terminating 0) // flags .inter=False, // interlaced (use subframes) .psync=False, // positive synchronization .odd=False, // first sub-frame is odd lines 1, 3, 5,... (PAL) }; // initialize default VGA configuration void VgaCfgDef(sVgaCfg* cfg) { cfg->width = 640; // width in pixels cfg->height = 480; // height in lines cfg->wfull = 0; // width of full screen, corresponding to 'hfull' time (0=use 'width' parameter) cfg->video = &VideoVGA; // used video timings cfg->freq = 120000; // required minimal system frequency in kHz (real frequency can be higher) cfg->fmax = 270000; // maximal system frequency in kHz (limit resolution if needed) cfg->mode[0] = LAYERMODE_BASE; // modes of overlapped layers 0..3 LAYERMODE_* (LAYERMODE_BASE = layer is off) cfg->mode[1] = LAYERMODE_BASE; // - mode of layer 0 is ignored (always use LAYERMODE_BASE) cfg->mode[2] = LAYERMODE_BASE; // - all overlapped layers must use same layer program cfg->mode[3] = LAYERMODE_BASE; cfg->dbly = False; // double in Y direction cfg->lockfreq = False; // lock required frequency, do not change it } // debug print videomode setup void VgaPrintCfg(const sVmode* vmode) { printf("width=%u height=%u wfull=%u wmax=%u\n", vmode->width, vmode->height, vmode->wfull, vmode->wmax); printf("freq=%u vco=%u fbdiv=%u pd1=%u pd2=%u\n", vmode->freq, vmode->vco, vmode->fbdiv, vmode->pd1, vmode->pd2); printf("div=%u cpp=%u prog=%u mode=%u %u %u %u\n", vmode->div, vmode->cpp, vmode->prog, vmode->mode[0], vmode->mode[1], vmode->mode[2], vmode->mode[3]); printf("htot=%u hfront=%u hsync=%u hback=%u\n", vmode->htot, vmode->hfront, vmode->hsync, vmode->hback); printf("vtot=%u vmax=%u\n", vmode->vtot, vmode->vmax); printf("vsync1=%u vpost1=%u vback1=%u vact1=%u vfront1=%u vpre1=%u vfirst1=%u\n", vmode->vsync1, vmode->vpost1, vmode->vback1, vmode->vact1, vmode->vfront1, vmode->vpre1, vmode->vfirst1); printf("vsync2=%u vpost2=%u vback2=%u vact2=%u vfront2=%u vpre2=%u vfirst2=%u\n", vmode->vsync2, vmode->vpost2, vmode->vback2, vmode->vact2, vmode->vfront2, vmode->vpre2, vmode->vfirst2); printf("lockfreq=%u dbly=%u inter=%u psync=%u odd=%u\n", vmode->lockfreq, vmode->dbly, vmode->inter, vmode->psync, vmode->odd); } // calculate videomode setup // cfg ... required configuration // vmode ... destination videomode setup for driver void VgaCfg(const sVgaCfg* cfg, sVmode* vmode) { int i; // prepare layer program, copy layer modes u8 prog = LAYERMODE_BASE; vmode->mode[0] = prog; for (i = 1; i < LAYERS; i++) { if (cfg->mode[i] != LAYERMODE_BASE) prog = LayerMode[cfg->mode[i]].prog; vmode->mode[i] = cfg->mode[i]; } vmode->prog = prog; // prepare minimal and maximal clocks per pixel int mincpp = LayerMode[LAYERMODE_BASE].mincpp; int maxcpp = LayerMode[LAYERMODE_BASE].maxcpp; int cpp; for (i = 1; i < LAYERS; i++) { cpp = LayerMode[cfg->mode[i]].mincpp; if (cpp > mincpp) mincpp = cpp; cpp = LayerMode[cfg->mode[i]].maxcpp; if (cpp < maxcpp) maxcpp = cpp; } // prepare full width int w = cfg->width; // required width int wfull = cfg->wfull; // full width if (wfull == 0) wfull = w; // use required width as 100% width // prepare maximal active time and maximal pixels const sVideo* v = cfg->video; float hmax = v->htot - v->hfront - v->hsync - v->hback; float hfull = v->hfull; int wmax = (int)(wfull*hmax/hfull + 0.001f); // calculate cpp from required frequency (rounded down), limit minimal cpp u32 freq = cfg->freq; cpp = (int)(freq*hfull/1000/wfull + 0.1f); if (cpp < mincpp) cpp = mincpp; // recalculate frequency if not locked if (!cfg->lockfreq) { int freq2 = (int)(cpp*wfull*1000/hfull + 0.5f) + 200; if (freq2 < freq) { cpp++; freq2 = (int)(cpp*wfull*1000/hfull + 0.5f) + 200; } if (freq2 >= freq) freq = freq2; if (freq > cfg->fmax) freq = cfg->fmax; } // find sysclock setup (use set_sys_clock_pll to set sysclock) u32 vco; u16 fbdiv; u8 pd1, pd2; FindSysClock(freq, &freq, &vco, &fbdiv, &pd1, &pd2); vmode->freq = freq; vmode->vco = vco; vmode->fbdiv = fbdiv; vmode->pd1 = pd1; vmode->pd2 = pd2; // calculate divisor cpp = (int)(freq*hfull/1000/wfull + 0.2f); int div = 1; while (cpp > maxcpp) { div++; cpp = (int)(freq*hfull/1000/wfull/div + 0.2f); } vmode->div = div; vmode->cpp = cpp; // calculate new full resolution and max resolution wfull = (int)(freq*hfull/1000/cpp/div + 0.4f); wmax = (int)(freq*hmax/1000/cpp/div + 0.4f); // limit resolution if (w > wmax) w = wmax; w = ALIGN4(w); vmode->width = w; // active width vmode->wfull = wfull; // width of full screen (image should be full visible) vmode->wmax = wmax; // maximal width (can be > wfull) // horizontal timings int hwidth = w*cpp; // active width in state machine clocks int htot = (int)(freq*v->htot/1000/div + 0.5f); // total state machine clocks per line int hsync = (int)(freq*v->hsync/1000/div + 0.5f); // H sync pulse in state machine clocks (min. 4) if (hsync < 4) { htot -= 4 - hsync; hsync = 4; } int hfront = (int)(freq*v->hfront/1000/div + 0.5f); // H front porch in state machine clocks (min. 2) int hback = (int)(freq*v->hback/1000/div + 0.5f); // H back porch in state machine clocks (min. 13) int d = htot - hfront - hsync - hback - hwidth; // difference hfront += d/2; hback += (d < 0) ? (d-1)/2 : (d+1)/2; if (hfront < 4) { hback -= 4 - hfront; hfront = 4; } if (hback < 13) { hfront -= 13 - hback; hback = 13; if (hfront < 2) hfront = 2; } htot = hfront + hsync + hback + hwidth; // total state machine clocks per line // interliced htot must be even (to enable split to half-sync) if (v->inter && ((htot & 1) != 0)) { htot--; hfront++; } vmode->htot = (u16)htot; // total state machine clocks per line vmode->hfront = (u16)hfront; // H front porch in state machine clocks (min. 2) vmode->hsync = (u16)hsync; // H sync pulse in state machine clocks (min. 4) vmode->hback = (u16)hback; // H back porch in state machine clocks (min. 13) // vertical timings int h = cfg->height; // required height if (cfg->dbly) h *= 2; // use double lines vmode->vmax = v->vmax; // maximal height if (h > v->vmax) h = v->vmax; // limit height if (cfg->dbly) h &= ~1; // must be even number if double lines int vact1 = h; // active lines in progress mode int vact2 = 0; if (v->inter) // interlaced { if (v->odd) // first frame is odd lines { vact1 = h/2; vact2 = (h+1)/2; // if even lines, even frame will have more lines } else { vact1 = (h+1)/2; // if even lines, even frame will have more lines vact2 = h/2; } } if (cfg->dbly) h /= 2; // return double lines to single lines vmode->height = h; // vertical timings vmode->vtot = v->vtot; // total scanlines vmode->vact1 = vact1; // active scanlines of 1st subframe int dh = vact1 - v->vact1; // difference vmode->vsync1 = v->vsync1; // V sync (half-)pulses on subframe 1 vmode->vpost1 = v->vpost1; // V sync post (half-)pulses on subframe 1 vmode->vback1 = v->vback1 - dh/2; // V back porch (after VSYNC, before image) on subframe 1 vmode->vfront1 = v->vfront1 - ((dh < 0) ? (dh-1)/2 : (dh+1)/2); // V front porch (after image, before VSYNC) on subframe 1 vmode->vpre1 = v->vpre1; // V sync pre (half-)pulses on subframe 1 vmode->vact2 = vact2; // active scanlines of 2nd subframe dh = vact2 - v->vact2; // difference vmode->vsync2 = v->vsync2; // V sync half-pulses on subframe 2 vmode->vpost2 = v->vpost2; // V sync post half-pulses on subframe 2 vmode->vback2 = v->vback2 - dh/2; // V back porch (after VSYNC, before image) on subframe 2 vmode->vfront2 = v->vfront2 - ((dh < 0) ? (dh-1)/2 : (dh+1)/2); // V front porch (after image, before VSYNC) on subframe 2 vmode->vpre2 = v->vpre2; // V sync pre half-pulses on subframe 2 // frequency vmode->hfreq = vmode->freq * 1000.0f / vmode->div / vmode->htot; vmode->vfreq = vmode->hfreq / vmode->vtot; // name vmode->name = v->name; // video timing name // flags vmode->lockfreq = cfg->lockfreq; // lock current frequency, do not change it vmode->dbly = cfg->dbly; // double scanlines vmode->inter = v->inter; // interlaced (use sub-frames) vmode->psync = v->psync; // positive synchronization vmode->odd = v->odd; // first sub-frame is odd lines 1, 3, 5,... (PAL) // first active scanline if (v->inter) { // interlaced vmode->vfirst1 = (vmode->vsync1 + vmode->vpost1)/2 + vmode->vback1 + 1; vmode->vfirst2 = vmode->vfirst1 + vmode->vact1 + vmode->vfront1 + (vmode->vpre1 + vmode->vsync2 + vmode->vpost2)/2 + vmode->vback2; } else { // progressive vmode->vfirst1 = vmode->vsync1 + vmode->vback1 + 1; vmode->vfirst2 = 0; } } // timings const sVideo* VideoResTab[DEV_MAX*RES_MAX] = { // DEV_PAL &VideoPALp, // RES_ZX = 0, // 256x192 &VideoPALp, // RES_CGA, // 320x200 &VideoPALp, // RES_QVGA, // 320x240 &VideoPAL, // RES_EGA, // 528x400 &VideoPAL, // RES_VGA, // 640x480 &VideoPAL, // RES_SVGA, // 800x600 (not for TV device) &VideoPAL, // RES_XGA, // 1024x768 (not for TV device) &VideoPAL, // RES_HD, // 1280x960 (not for TV device) // DEV_NTSC &VideoNTSCp, // RES_ZX = 0, // 256x192 &VideoNTSCp, // RES_CGA, // 320x200 &VideoNTSCp, // RES_QVGA, // 320x240 &VideoNTSC, // RES_EGA, // 528x400 &VideoNTSC, // RES_VGA, // 640x480 &VideoNTSC, // RES_SVGA, // 800x600 (not for TV device) &VideoNTSC, // RES_XGA, // 1024x768 (not for TV device) &VideoNTSC, // RES_HD, // 1280x960 (not for TV device) // DEV_VGA &VideoEGA, // RES_ZX = 0, // 256x192 &VideoVGA, // RES_CGA, // 320x200 &VideoVGA, // RES_QVGA, // 320x240 &VideoEGA, // RES_EGA, // 528x400 &VideoVGA, // RES_VGA, // 640x480 &VideoSVGA, // RES_SVGA, // 800x600 (not for TV device) &VideoXGA, // RES_XGA, // 1024x768 (not for TV device) &VideoHD, // RES_HD, // 1280x960 (not for TV device) }; // required resolution width x height const u16 VideoResReq[RES_MAX*2] = { 256, 192, // RES_ZX = 0, // 256x192 320, 200, // RES_CGA, // 320x200 320, 240, // RES_QVGA, // 320x240 512, 400, // RES_EGA, // 512x400 640, 480, // RES_VGA, // 640x480 800, 600, // RES_SVGA, // 800x600 (not for TV device) 1024, 768, // RES_XGA, // 1024x768 (not for TV device) 1280, 960, // RES_HD, // 1280x960 (not for TV device) }; // initialize videomode // dev ... device DEV_* // res ... resolution RES_* // form ... format FORM_* // buf ... pointer to frame buffer (must be aligned to 4-bites, use ALIGNED attribute) // buf2 ...pointer to additional buffer: // FORM_TILE: pointer to column of tiles 32x32 in 8-bit graphics // FORM_TEXT: pointer to font 8x16 or 8x8 (size 4 KB or 2 KB, ALIGNED attribute, should be in RAM) // - copy font to 4KB or 2 KB RAM buffer with ALIGNED attribute // - text uses color attributes PC_* // FORM_RLE: pointer to image rows (ALIGNED attribute, should be in RAM) void Video(u8 dev, u8 res, u8 form, u8* buf, const void* buf2 /* = FontBoldB8x16 */) { // stop VGA core multicore_reset_core1(); // run VGA core multicore_launch_core1(VgaCore); // prepare timings structure if (dev >= DEV_MAX) dev = DEV_VGA; if (res >= RES_MAX) res = RES_MAX-1; if (form >= FORM_MAX) form = FORM_MAX-1; const sVideo* v = VideoResTab[dev*RES_MAX + res]; // required resolution u16 w = VideoResReq[res*2]; u16 h = VideoResReq[res*2+1]; if (h > v->vmax) h = v->vmax; if ((form == FORM_TEXT8) || (form == FORM_MTEXT8)) { w = w/8*8; h = h/8*8; } if ((form == FORM_TEXT16) || (form == FORM_MTEXT16)) { w = w/8*8; h = h/16*16; } // setup videomode VgaCfgDef(&Cfg); // get default configuration Cfg.video = v; // video timings Cfg.width = w; // screen width Cfg.height = h; // screen height if (form == FORM_RLE) Cfg.mode[1] = LAYERMODE_RLE; Cfg.dbly = h <= v->vmax/2; // double scanlines VgaCfg(&Cfg, &Vmode); // calculate videomode setup // initialize base layer 0 ScreenClear(pScreen); sStrip* t = ScreenAddStrip(pScreen, h); sSegm* g = ScreenAddSegm(t, w); switch (form) { case FORM_8BIT: // 8-bit pixel graphics (up to EGA resolution) ScreenSegmGraph8(g, buf, w); Canvas.img = buf; Canvas.w = w; Canvas.h = h; Canvas.wb = w; Canvas.format = CANVAS_8; break; case FORM_4BIT: // 4-bit pixel graphics (up to SVGA graphics) GenPal16Trans(Pal16Trans, DefPal16); // generate palette translation table ScreenSegmGraph4(g, buf, Pal16Trans, w/2); Canvas.img = buf; Canvas.w = w; Canvas.h = h; Canvas.wb = w/2; Canvas.format = CANVAS_4; break; case FORM_MONO: // 1-bit pixel graphics ScreenSegmGraph1(g, buf, COL_BLACK, COL_WHITE, w/8); Canvas.img = buf; Canvas.w = w; Canvas.h = h; Canvas.wb = w/8; Canvas.format = CANVAS_1; break; case FORM_TILE8: // 8x8 tiles ScreenSegmTile(g, buf, buf2, 8, 8, (w+7)/8); break; case FORM_TILE12: // 12x12 tiles ScreenSegmTile(g, buf, buf2, 12, 12, (w+11)/12); break; case FORM_TILE16: // 16x16 tiles ScreenSegmTile(g, buf, buf2, 16, 16, (w+15)/16); break; case FORM_TILE24: // 24x24 tiles ScreenSegmTile(g, buf, buf2, 24, 24, (w+23)/24); break; case FORM_TILE32: // 32x32 tiles ScreenSegmTile(g, buf, buf2, 32, 32, (w+31)/32); break; case FORM_TILE48: // 48x48 tiles ScreenSegmTile(g, buf, buf2, 48, 48, (w+47)/48); break; case FORM_TILE64: // 64x64 tiles ScreenSegmTile(g, buf, buf2, 64, 64, (w+63)/64); break; case FORM_MTEXT8: // mono text with font 8x8 ScreenSegmMText(g, buf, buf2, 8, COL_BLACK, COL_WHITE, w/8); PrintSetup(buf, w/8, h/8, w/8); PrintClear(); break; case FORM_MTEXT16: // mono text with font 8x16 ScreenSegmMText(g, buf, buf2, 16, COL_BLACK, COL_WHITE, w/8); PrintSetup(buf, w/8, h/8, w/8); PrintClear(); break; case FORM_TEXT8: // attribute text with font 8x8 ScreenSegmAText(g, buf, buf2, 8, DefPal16, w/8*2); PrintSetup(buf, w/8, h/8, w/8*2); PrintClear(); break; case FORM_TEXT16: // attribute text with font 8x16 ScreenSegmAText(g, buf, buf2, 16, DefPal16, w/8*2); PrintSetup(buf, w/8, h/16, w/8*2); PrintClear(); break; case FORM_RLE: // images with RLE compression (on overlapped layer 1) ScreenSegmColor(g, 0, 0); LayerSetup(1, buf, &Vmode, w, h, 0, buf2); LayerOn(1); break; } // initialize system clock set_sys_clock_pll(Vmode.vco*1000, Vmode.pd1, Vmode.pd2); // initialize videomode VgaInitReq(&Vmode); }