home *** CD-ROM | disk | FTP | other *** search
/ ftp.whtech.com / ftp.whtech.com.7z / ftp.whtech.com / emulators / v9t9 / linux / sources / V9t9 / source / vdp.c < prev    next >
Encoding:
C/C++ Source or Header  |  2006-10-19  |  37.2 KB  |  1,689 lines

  1.  
  2. /*
  3.     =====
  4.     VDP.C
  5.     =====
  6.     
  7.     Handle changes to VDP memory, namely graphics changes.
  8. */
  9.  
  10. #define __VDP_INTERNAL__
  11.  
  12. #include "v9t9_common.h"
  13. #include "timer.h"
  14. #include "video.h"
  15. #include "memory.h"
  16. #include "vdp.h"
  17. #include "vdpsprites.h"
  18. #include "command.h"
  19. #include "v9t9.h"
  20. #include "demo.h"
  21. #include "9901.h"
  22.  
  23. static void
  24. vdpwritereg(u16 addr);
  25.  
  26. static vdp_redrawfunc     vdp_redraw;
  27. static vdp_modify_info     vdp_modify;
  28.  
  29. u32              screenxsize, screenysize;
  30.  
  31. u8          vdpregs[8];
  32. u8          vdpbg, vdpfg;
  33. static u32         vdpmode;
  34.  
  35. static u8       vdpram[16384];
  36. static u16             vdpaddr;
  37. static char            vdpaddrflag;
  38. static u8            vdpreadahead;
  39. static u8          vdpstatus;
  40.  
  41. //     We switched into text mode or changed bg color; on next update,
  42. //     clear sides of screen and tell video module to update.
  43. static bool vdp_redraw_text_sides;
  44.  
  45. /*
  46.  *    Configuration variables
  47.  */
  48.  
  49. int         videoupdatespeed = 30;
  50. int         vdp_interrupt_rate = 60;
  51. bool        draw_sprites = true;
  52. bool        five_sprites_on_a_line = true;        // five-sprite limit active?
  53.  
  54. /*
  55.  *    Shared between vdp.c and vdpsprites.c 
  56.  */
  57.  
  58. #ifdef __VDP_INTERNAL__
  59.  
  60. u8               vdp_updarea[UPDATEBLOCK_ROW_STRIDE * 256];
  61. vdp_mode_info    vdp_mode;
  62.  
  63. vdp_changes_info vdp_changes;
  64.  
  65. static u16      bitpattmask, bitcolormask;
  66.  
  67. u32      sprwidth = 8;        /* in pixels */
  68.  
  69. #endif
  70.  
  71.  
  72. #if 1
  73.  
  74. u8 vdp_palette[17][3] = 
  75. {
  76.     { 0x00, 0x00, 0x00 }, // clear
  77.     { 0x00, 0x00, 0x00 }, // black
  78.     { 0x40, 0xb0, 0x40 }, // medium green
  79.     { 0x60, 0xc0, 0x60 }, // light green
  80.     { 0x40, 0x40, 0xc0 }, // dark blue
  81.     { 0x60, 0x60, 0xf0 }, // light blue
  82.     { 0xc0, 0x40, 0x40 }, // dark red
  83.     { 0x40, 0xf0, 0xf0 }, // cyan
  84.     { 0xf0, 0x40, 0x40 }, // medium red
  85.     { 0xff, 0x80, 0x60 }, // light red
  86.     { 0xf0, 0xc0, 0x40 }, // dark yellow
  87.     { 0xff, 0xe0, 0x60 }, // light yellow
  88.     { 0x40, 0x80, 0x40 }, // dark green
  89.     { 0xc0, 0x40, 0xc0 }, // magenta
  90.     { 0xd0, 0xd0, 0xd0 }, // grey
  91.     { 0xff, 0xff, 0xff }, // white
  92.     { 0x00, 0x00, 0x00 }, // text fg
  93. };
  94.  
  95. #else
  96.  
  97. // colors from Sean Young (sean@msxnet.org)
  98. u8 vdp_palette[17][3] = 
  99. {
  100.     { 0x00, 0x00, 0x00 }, // clear
  101.     { 0x00, 0x00, 0x00 }, // black
  102.     { 0x24, 0xDA, 0x24 }, // medium green
  103.     { 0x6D, 0xFF, 0x48 }, // light green
  104.     { 0x24, 0x24, 0xFF }, // dark blue
  105.     { 0x48, 0x6D, 0xFF }, // light blue
  106.     { 0xB6, 0x24, 0x24 }, // dark red
  107.     { 0x48, 0xDA, 0xFF }, // cyan
  108.     { 0xFF, 0x24, 0x24 }, // medium red
  109.     { 0xFF, 0x6D, 0x6D }, // light red
  110.     { 0xDA, 0xDA, 0x24 }, // dark yellow
  111.     { 0xDA, 0xDA, 0x91 }, // light yellow
  112.     { 0x24, 0x91, 0x24 }, // dark green
  113.     { 0xDA, 0x48, 0xB6 }, // magenta
  114.     { 0xB6, 0xB6, 0xB6 }, // grey
  115.     { 0xFF, 0xFF, 0xFF }, // white
  116.     { 0x00, 0x00, 0x00 }, // text fg
  117. };
  118.  
  119. #endif
  120.  
  121. #define _L     LOG_VIDEO | LOG_INFO
  122.  
  123. /*    Interface to v9t9.c.  This installs a timer event that
  124.     periodically refreshes the video display by using the
  125.     currently selected video module. */
  126. static int
  127. video_event_tag, vdp_interrupt_tag;
  128.  
  129. static void
  130. vdp_interrupt(int tag)
  131. {
  132.     if (vdpregs[1] & R1_INT) {
  133.         trigger9901int(M_INT_VDP);
  134. //      currentints |= M_INT_VDP;
  135. //      handle9901();
  136. //      stateflag |= ST_INTERRUPT;
  137.     }
  138. }
  139.  
  140. static void
  141. video_update(int tag)
  142. {
  143.     if (vdpregs[1] & R1_INT) {
  144.         vdpstatus |= VDP_INTERRUPT;
  145.     }
  146.  
  147.     if (features & FE_SHOWVIDEO) {
  148.         vdp_update();
  149.     }
  150. }
  151.  
  152. static void
  153. video_changing_rates(void)
  154. {
  155.     if (!video_event_tag)
  156.         video_event_tag = TM_UniqueTag();
  157.     else
  158.         TM_ResetEvent(video_event_tag);
  159.  
  160.     if (videoupdatespeed <= 0)
  161.         videoupdatespeed = 1;
  162.     else if (videoupdatespeed > TM_HZ)
  163.         videoupdatespeed = TM_HZ;
  164.  
  165.     TM_SetEvent(video_event_tag, TM_HZ * 100 / videoupdatespeed, 0,
  166.                 TM_REPEAT | TM_FUNC, video_update);
  167.  
  168.     if (!vdp_interrupt_tag)
  169.         vdp_interrupt_tag = TM_UniqueTag();
  170.     else
  171.         TM_ResetEvent(vdp_interrupt_tag);
  172.  
  173.     if (vdp_interrupt_rate <= 0)
  174.         vdp_interrupt_rate = 1;
  175.     else if (vdp_interrupt_rate > TM_HZ)
  176.         vdp_interrupt_rate = TM_HZ;
  177.     TM_SetEvent(vdp_interrupt_tag, TM_HZ * 100 / vdp_interrupt_rate, 0,
  178.                 TM_REPEAT | TM_FUNC, vdp_interrupt);
  179.  
  180. }
  181.  
  182. int
  183. video_restart(void)
  184. {
  185.     video_changing_rates();
  186.  
  187.     return 1;
  188. }
  189.  
  190. void
  191. video_restop(void)
  192. {
  193.     TM_ResetEvent(video_event_tag);
  194.     TM_ResetEvent(vdp_interrupt_tag);
  195. }
  196.  
  197.  
  198. /*********************************/
  199.  
  200. /*    Write to memory-mapped port for VDP */
  201.  
  202. static s8 vdp_read_byte(const mrstruct *mr, u32 addr)
  203. {
  204.     return vdpram[addr & 0x3fff];
  205. }
  206.  
  207. static void vdp_write_byte(const mrstruct *mr, u32 addr, s8 val)
  208. {
  209.     addr &= 0x3fff;
  210.     if (vdpram[addr] != val) {
  211.         vdpram[addr] = val;
  212.         if (stateflag & ST_DEMOING) {
  213.             demo_record_event(demo_type_video, addr, val);
  214.         }
  215.         vdp_touch(addr);
  216.     }
  217. }
  218.  
  219. mrstruct vdp_memory_handler =
  220. {
  221.     vdpram, 0L, 0L,
  222.     NULL, vdp_read_byte,
  223.     NULL, vdp_write_byte
  224. };
  225.  
  226. void
  227. vdp_memory_init(void)
  228. {
  229.     memory_insert_new_entry(MEMENT_VIDEO, 0x0000, 0x4000, 
  230.                            "VDP memory", 
  231.                             0L /*filename*/, 0L /*fileoffs*/, 
  232.                             &vdp_memory_handler);
  233. }
  234.  
  235. /*    addr == 0 or addr != 0 based on
  236.     write to >8C00 or >8C02 */
  237. void
  238. vdp_mmio_write(u16 addr, u8 val)
  239. {
  240.     if (addr) {                    /* >8C02, address write */
  241.         vdpaddr = (vdpaddr >> 8) | (val << 8);
  242.         if (!(vdpaddrflag ^= 1)) {
  243.             if (vdpaddr & 0x8000) {
  244.                 vdpaddr &= 0x3fff;
  245.                 vdpwritereg(vdpaddr);
  246.                 if (stateflag & ST_DEMOING) {
  247.                     demo_record_event(demo_type_video, vdpaddr | 0x8000);
  248.                 }
  249.             } else if (vdpaddr & 0x4000) {
  250.                 vdpaddr &= 0x3fff;
  251.             } else {
  252.                 // read ahead one byte
  253.                 vdpreadahead = vdpram[vdpaddr];
  254.                 vdpaddr = (vdpaddr+1) & 0x3fff;
  255.             }
  256.         }
  257.     } else {                    /* >8C00, data write */
  258.         /* this flag is used to verify that the VDP
  259.            address was written as >4000 + vdpaddr.
  260.            If not, then writing to it functions as
  261.            a read-before-write. */
  262.  
  263.         vdpaddrflag = 0;
  264.         domain_write_byte(md_video, vdpaddr, val);
  265.         vdpaddr = (vdpaddr + 1) & 0x3fff;
  266.         vdpreadahead = val;
  267.     }
  268. }
  269.  
  270. /*    Read a byte from the VDP. */
  271.  
  272. s8 vdp_mmio_read(u16 addr)
  273. {
  274.     s8          ret;
  275.  
  276.     vdpaddrflag = 0;
  277.     if (addr & 2) {                /* >8802, status read */
  278.         ret = vdpstatus;
  279.         vdpstatus &= ~0xe0;        // thierry:  reset bits when read
  280.         reset9901int(M_INT_VDP);
  281.     } else {                    /* >8800, memory read */
  282.         ret = vdpreadahead;
  283.         vdpreadahead = domain_read_byte(md_video, vdpaddr);
  284.         vdpaddr = (vdpaddr + 1) & 0x3fff;
  285.     }
  286.     return ret;
  287. }
  288.  
  289. extern     u16     vdp_mmio_get_addr(void)
  290. {
  291.     return vdpaddr;
  292. }
  293.  
  294. extern    void vdp_mmio_set_addr(u16 addr)
  295. {
  296.     vdpaddr = addr & 0x3fff;
  297. }
  298.  
  299. extern     bool vdp_mmio_addr_is_complete(void)
  300. {
  301.     return !vdpaddrflag;
  302. }
  303.  
  304. extern void vdp_mmio_set_status(u8 status)
  305. {
  306.     vdpstatus = status;
  307. }
  308.  
  309. extern u8 vdp_mmio_get_status(void)
  310. {
  311.     return vdpstatus;
  312. }
  313.  
  314. /*********************************************************/
  315.  
  316. /*    This section of code handles the mapping from a VDP address
  317.     to the various areas in VDP memory that affect the generation
  318.     of the display. */
  319.  
  320. int         vdpchanged;
  321.  
  322.  
  323. void        redraw_graphics(void);
  324. void        redraw_bitmap(void);
  325. void        redraw_text(void);
  326. void        redraw_multi(void);
  327. void        redraw_blank(void);
  328.  
  329.  
  330. /************************************************************************/
  331.  
  332. /*    
  333.  * Notify that an address in VDP has been modified externally.
  334.  */
  335. void
  336. vdp_touch(u32 addr)
  337. {
  338.     if (vdp_mode.screen.base <= addr && addr < vdp_mode.screen.base + vdp_mode.screen.size)
  339.         vdp_modify.screen(addr - vdp_mode.screen.base);
  340.  
  341.     if (vdp_mode.patt.base <= addr && addr < vdp_mode.patt.base + vdp_mode.patt.size)
  342.         vdp_modify.patt(addr - vdp_mode.patt.base);
  343.  
  344.     if (vdp_mode.color.base <= addr && addr < vdp_mode.color.base + vdp_mode.color.size)
  345.         vdp_modify.color(addr - vdp_mode.color.base);
  346.  
  347.     if (vdp_mode.sprite.base <= addr && addr < vdp_mode.sprite.base + vdp_mode.sprite.size)
  348.         vdp_modify.sprite(addr - vdp_mode.sprite.base);
  349.  
  350.     if (vdp_mode.sprpat.base <= addr && addr < vdp_mode.sprpat.base + vdp_mode.sprpat.size)
  351.         vdp_modify.sprpat(addr - vdp_mode.sprpat.base);
  352. }
  353.  
  354. void
  355. vdp_dirty_screen(u32 x, u32 y, s32 sx, s32 sy)
  356. {
  357.     u8  *ptr;
  358.     int width;
  359.  
  360.     if (sx < 0 || sy < 0) return;
  361.  
  362.     if (vdpregs[1] & R1_TEXT) {
  363.         // left blank column?
  364.         if (x < 8) {
  365.             vdp_redraw_text_sides = true;
  366.             if (sx <= x)
  367.                 return;
  368.             sx -= x;
  369.             x = 0;
  370.         // right blank column?
  371.         } else if (x >= 240 + 8) {
  372.             vdp_redraw_text_sides = true;
  373.             return;
  374.         } else {
  375.             x -= 8;
  376.         }
  377.         width = 40;
  378.     } else {
  379.         width = 32;
  380.     }
  381.  
  382.     if (x >= screenxsize || y >= screenysize) return;
  383.  
  384.     if (width == 40) {
  385.         sx = (sx + (x % 6) + 5) / 6; 
  386.         x /= 6;
  387.     }
  388.     else {
  389.         sx = (sx + (x & 7) + 7) >> 3;
  390.         x >>= 3;
  391.     }
  392.     sy = (sy + (y & 7) + 7) >> 3;
  393.     y >>= 3;
  394.  
  395.     if (x + sx > width) sx = width - x;
  396.     if (y + sy > 24) sy = 24 - y;
  397.  
  398.     ptr = vdp_changes.screen + (y * width) + x;
  399.     while (sy--) {
  400.         memset(ptr, 1, sx);
  401.         ptr += width;
  402.     }
  403.     vdpchanged = 1;
  404. }
  405.  
  406.  
  407. /*************************************************************/
  408.  
  409. static void
  410. vdp_dirty_sprites(void)
  411. {
  412.     if (!draw_sprites)
  413.         return;
  414.     vdp_changes.sprite = -1;
  415.     memset(vdp_changes.sprpat, 1, vdp_mode.sprpat.size >> 3);
  416.     vdpchanged = 1;
  417. }
  418.  
  419.  
  420. static void
  421. vdp_dirty_all(void)
  422. {
  423.     memset(vdp_changes.screen, SC_BACKGROUND, vdp_mode.screen.size);
  424.     memset(vdp_changes.patt, 1, vdp_mode.patt.size >> 3);
  425.     vdp_dirty_sprites();
  426.     vdpchanged = 1;
  427.     vdp_redraw_text_sides = true;
  428. }
  429.  
  430. /*    Force a complete redraw of the display
  431.     by making it look like the whole VDP context
  432.     has changed. */
  433. void
  434. vdpcompleteredraw(void)
  435. {
  436.     u8          i;
  437.  
  438.     vdp_dirty_all();
  439.     for (i = 0; i < 8; i++) {
  440.         vdpwritereg(((i | 0x80) << 8) + vdpregs[i]);
  441.     }
  442.     vdp_update();
  443. }
  444.  
  445. /*************************************************************/
  446.  
  447. /*  VDP update routines  
  448.  
  449.     All of these are type 'updatefunc' and are assigned to
  450.     function pointers when the VDP mode is changed.  
  451.     These routines are called through 'vdp_touch'.
  452. */
  453.  
  454. static void
  455. modify_sprite_default(u32 offs)
  456. {
  457.     vdp_changes.sprite |= SPRBIT(offs >> 2);
  458.     vdpchanged = 1;
  459.     /*  debug("modify_sprite_default (%d)\n",offs>>2); */
  460. }
  461.  
  462.  
  463. static void
  464. modify_sprpat_default(u32 offs)
  465. {
  466.     u32         patt;
  467.  
  468.     if (vdpregs[1] & R1_SPR4) {
  469.         patt = (offs >> 3) & 0xfc;
  470.         memset(&vdp_changes.sprpat[patt], 1, 4);
  471.     } else {
  472.         patt = offs >> 3;
  473.         vdp_changes.sprpat[patt] = 1;
  474.     }
  475.  
  476.     vdpchanged = 1;
  477. }
  478.  
  479. /*************************************************************/
  480.  
  481. /*
  482.     Possible optimization -- if the overall change from the
  483.     previous refresh to the next is none, don't bother to
  484.     redraw.  This has the deficiency that it would require
  485.     a copy of the VDP RAM to be saved at each refresh.
  486. */
  487.  
  488. static void
  489. modify_screen_graphics(u32 offs)
  490. {
  491.     vdp_changes.screen[offs] = vdpchanged = SC_BACKGROUND;
  492. }
  493.  
  494. static void
  495. modify_patt_graphics(u32 offs)
  496. {
  497.     vdp_changes.patt[offs >> 3] = vdpchanged = 1;
  498. }
  499.  
  500. static void
  501. modify_color_graphics(u32 offs)
  502. {
  503.     memset(&vdp_changes.patt[offs << 3], 1, 8);
  504.     vdpchanged = 1;
  505. }
  506.  
  507. /************************************************************/
  508.  
  509. static void
  510. modify_patt_bitmap(u32 offs)
  511. {
  512.     vdp_changes.patt[offs >> 3] = vdpchanged = 1;
  513. }
  514.  
  515. static void
  516. modify_color_bitmap(u32 offs)
  517. {
  518.     vdp_changes.color[offs >> 3] = vdpchanged = 1;
  519. }
  520.  
  521. /************************************************************/
  522.  
  523. /*    This routine updates all the updatefuncs when some
  524.     register controlling the VDP context has changed. */
  525. static void
  526. vdp_update_params(void)
  527. {
  528.     u16         ramsize = (vdpregs[1] & R1_RAMSIZE) ? 0x3fff : 0xfff;
  529.  
  530.     /* Is the screen really blank?  
  531.        If so, respond to nothing but calls to vdp_dirty_screen */
  532.     if (!(vdpregs[1] & R1_NOBLANK)) {
  533.         logger(_L | L_1, "vdp_update_params:  blank screen\n");
  534.         vdp_mode.screen.base = 0;
  535.         vdp_mode.screen.size = 0;
  536.         vdp_mode.color.base = 0;
  537.         vdp_mode.color.size = 0;
  538.         vdp_mode.patt.base = 0;
  539.         vdp_mode.patt.size = 0;
  540.         vdp_mode.sprite.base = 0;
  541.         vdp_mode.sprite.size = 0;
  542.         vdp_mode.sprpat.base = 0;
  543.         vdp_mode.sprpat.size = 0;
  544.         screenxsize = 256;
  545.         screenysize = 192;
  546.         vdp_modify.patt = NULL;
  547.         vdp_modify.sprite = vdp_modify.sprpat = NULL;
  548.         vdp_modify.screen = NULL;
  549.         vdp_modify.color = NULL;
  550.  
  551.         vdp_redraw = redraw_blank;
  552.         return;
  553.     }
  554.  
  555.     switch (vdpmode) {
  556.     case MODE_GRAPHICS:
  557.         logger(_L | L_1, "vdp_update_params:  graphics mode\n");
  558.         vdp_mode.screen.base = (vdpregs[2] * 0x400) & ramsize;
  559.         vdp_mode.screen.size = 768;
  560.         vdp_mode.color.base = (vdpregs[3] * 0x40) & ramsize;
  561.         vdp_mode.color.size = 32;
  562.         vdp_mode.patt.base = (vdpregs[4] * 0x800) & ramsize;
  563.         vdp_mode.patt.size = 2048;
  564.         vdp_mode.sprite.base = (vdpregs[5] * 0x80) & ramsize;
  565.         vdp_mode.sprite.size = 128;
  566.         vdp_mode.sprpat.base = (vdpregs[6] * 0x800) & ramsize;
  567.         vdp_mode.sprpat.size = 2048;
  568.         screenxsize = 256;
  569.         screenysize = 192;
  570.         vdp_modify.screen = modify_screen_graphics;
  571.         vdp_modify.color = modify_color_graphics;
  572.         vdp_modify.patt = modify_patt_graphics;
  573.         vdp_modify.sprite = modify_sprite_default;
  574.         vdp_modify.sprpat = modify_sprpat_default;
  575.         vdp_redraw = redraw_graphics;
  576.  
  577.         break;
  578.  
  579.     case MODE_TEXT:
  580.         logger(_L | L_1, "vdp_update_params:  text mode\n");
  581.         vdp_mode.screen.base = (vdpregs[2] * 0x400) & ramsize;
  582.         vdp_mode.screen.size = 960;
  583.         vdp_mode.color.base = 0;
  584.         vdp_mode.color.size = 0;
  585.         vdp_mode.patt.base = (vdpregs[4] * 0x800) & ramsize;
  586.         vdp_mode.patt.size = 2048;
  587.         vdp_mode.sprite.base = 0;
  588.         vdp_mode.sprite.size = 0;
  589.         vdp_mode.sprpat.base = 0;
  590.         vdp_mode.sprpat.size = 0;
  591. //        screenxsize = 240;
  592.         screenxsize = 256;
  593.         screenysize = 192;
  594.         vdp_modify.patt = modify_patt_graphics;
  595.         vdp_modify.sprite = vdp_modify.sprpat = NULL;
  596.         vdp_modify.screen = modify_screen_graphics;
  597.         vdp_modify.color = NULL;
  598.  
  599.         vdp_redraw_text_sides = true;
  600.         vdp_redraw = redraw_text;
  601.         break;
  602.  
  603.     case MODE_BITMAP:
  604.         logger(_L | L_1, "vdp_update_params:  bitmap mode\n");
  605.         vdp_mode.screen.base = (vdpregs[2] * 0x400) & ramsize;
  606.         vdp_mode.screen.size = 768;
  607.         vdp_mode.sprite.base = (vdpregs[5] * 0x80) & ramsize;
  608.         vdp_mode.sprite.size = 128;
  609.         vdp_mode.sprpat.base = (vdpregs[6] * 0x800) & ramsize;
  610.         vdp_mode.sprpat.size = 2048;
  611.         screenxsize = 256;
  612.         screenysize = 192;
  613.         vdp_modify.sprite = modify_sprite_default;
  614.         vdp_modify.sprpat = modify_sprpat_default;
  615.         vdp_modify.screen = modify_screen_graphics;
  616.         vdp_modify.color = modify_color_bitmap;
  617.         vdp_modify.patt = modify_patt_bitmap;
  618.         vdp_redraw = redraw_bitmap;
  619.  
  620.         vdp_mode.color.base = (vdpregs[3] & 0x80) ? 0x2000 : 0;
  621.         vdp_mode.color.size = 6144;
  622.         bitcolormask = (((u16) (vdpregs[3] & 0x7f)) << 6) | 0x3f;
  623.  
  624.         vdp_mode.patt.base = (vdpregs[4] & 0x4) ? 0x2000 : 0;
  625.         vdp_mode.patt.size = 6144;
  626.  
  627.         // thanks, Thierry!
  628.         if (vdpregs[1] & 0x10)
  629.             bitpattmask = (((u16) (vdpregs[4] & 0x03) << 11)) | 0x7ff;
  630.         else
  631.             bitpattmask =
  632.                 (((u16) (vdpregs[4] & 0x03) << 11)) | (bitcolormask & 0x7ff);
  633.  
  634.         break;
  635.  
  636.     case MODE_MULTI:
  637.         logger(_L | L_1, "vdp_update_params:  multi mode\n");
  638.         vdp_mode.screen.base = (vdpregs[2] * 0x400) & ramsize;
  639.         vdp_mode.screen.size = 768;
  640.         vdp_mode.color.base = 0;
  641.         vdp_mode.color.size = 0;
  642.         vdp_mode.patt.base = (vdpregs[4] * 0x800) & ramsize;
  643.         vdp_mode.patt.size = 1536;
  644.         vdp_mode.sprite.base = (vdpregs[5] * 0x80) & ramsize;
  645.         vdp_mode.sprite.size = 128;
  646.         vdp_mode.sprpat.base = (vdpregs[6] * 0x800) & ramsize;
  647.         vdp_mode.sprpat.size = 2048;
  648.         screenxsize = 256;
  649.         screenysize = 192;
  650.         vdp_modify.sprite = modify_sprite_default;
  651.         vdp_modify.sprpat = modify_sprpat_default;
  652.         vdp_modify.screen = modify_screen_graphics;
  653.         vdp_modify.color = NULL;
  654.         vdp_modify.patt = modify_patt_graphics;
  655.         vdp_redraw = redraw_multi;
  656.         break;
  657.  
  658.     default:
  659.         logger(_L | LOG_FATAL, "Unknown graphics mode %d set\n\n", vdpmode);
  660.         break;
  661.     }
  662. }
  663.  
  664. /*    Figure out the VDP mode from the VDP mode registers */
  665. static void
  666. vdp_update_mode(void)
  667. {
  668.     if (vdpregs[0] & R0_BITMAP)
  669.         vdpmode = MODE_BITMAP;
  670.     else if (vdpregs[1] & R1_TEXT)
  671.         vdpmode = MODE_TEXT;
  672.     else if (vdpregs[1] & R1_MULTI)
  673.         vdpmode = MODE_MULTI;
  674.     else
  675.         vdpmode = MODE_GRAPHICS;
  676. }
  677.  
  678. /**************************/
  679.  
  680. #define REDRAW_NOW 1            /* same-mode change */
  681. #define REDRAW_SPRITES 2        /* sprites change */
  682. #define REDRAW_MODE 4            /* mode change */
  683. #define REDRAW_BLANK 8            /* make blank */
  684. #define REDRAW_PALETTE 16        /* palette update */
  685.  
  686. #define CHANGED(r,v) ((vdpregs[r]&(v))!=(val&(v)))
  687.  
  688. static void
  689. vdpwritereg(u16 addr)
  690. {
  691.     u8          reg = (addr >> 8) & 0xf;
  692.     u8          val = addr & 0xff;
  693.     int         redraw = 0;
  694.  
  695.     u8          old = vdpregs[reg];
  696.  
  697.     logger(_L | L_1, "vdpwritereg %1X=%2X\n", reg, val);
  698.     switch (reg) {
  699.     case 0:                    /* bitmap/video-in */
  700.         if (CHANGED(0, R0_BITMAP)) {
  701.             redraw |= REDRAW_MODE;
  702.         }
  703.         vdpregs[0] = val;
  704.         break;
  705.  
  706.     case 1:                    /* various modes, sprite stuff */
  707.         if (CHANGED(1, R1_NOBLANK)) {
  708.             redraw |= REDRAW_BLANK | REDRAW_MODE;
  709.         }
  710.  
  711.         if (CHANGED(1, R1_SPRMAG + R1_SPR4)) {
  712.             redraw |= REDRAW_SPRITES;
  713.             sprwidth = (val & (R1_SPRMAG + R1_SPR4)) == (R1_SPRMAG | R1_SPR4) ? 32 :
  714.                 (val & (R1_SPRMAG + R1_SPR4)) ? 16 : 
  715.                 8;
  716.             logger(_L | L_1, "SprWidth=%d\n", sprwidth);
  717.         }
  718.  
  719.         if (CHANGED(1, R1_TEXT | R1_MULTI)) {
  720.             redraw |= REDRAW_MODE;
  721.         }
  722.  
  723.         vdpregs[1] = val;
  724.         break;
  725.  
  726.     case 2:                    /* screen image table */
  727.         if (vdpregs[2] != val) {
  728.             redraw |= REDRAW_MODE;
  729.             vdpregs[2] = val;
  730.         }
  731.         break;
  732.  
  733.     case 3:                    /* color table */
  734.         if (vdpregs[3] != val) {
  735.             redraw |= REDRAW_MODE;
  736.             vdpregs[3] = val;
  737.         }
  738.         break;
  739.  
  740.     case 4:                    /* pattern table */
  741.         if (vdpregs[4] != val) {
  742.             redraw |= REDRAW_MODE;
  743.             vdpregs[4] = val;
  744.         }
  745.         break;
  746.  
  747.     case 5:                    /* sprite table */
  748.         if (vdpregs[5] != val) {
  749.             redraw |= REDRAW_MODE;
  750.             vdpregs[5] = val;
  751.         }
  752.         break;
  753.  
  754.     case 6:                    /* sprite pattern table */
  755.         if (vdpregs[6] != val) {
  756.             redraw |= REDRAW_MODE;
  757.             vdpregs[6] = val;
  758.         }
  759.         break;
  760.  
  761.     case 7:                    /* foreground/background color */
  762.         if (vdpregs[7] != val) {
  763.             vdpfg = val >> 4;
  764.             vdpbg = val & 0xf;
  765.             redraw |= REDRAW_PALETTE;
  766.             vdpregs[7] = val;
  767.         }
  768.         break;
  769.  
  770.     default:
  771.         logger(_L | L_1, "Undefined VDP register %d\n", reg);
  772.  
  773.     }
  774.  
  775.     /*  This flag must be checked first because
  776.        it affects the meaning of the following 
  777.        calls and checks. */
  778.     if (redraw & REDRAW_MODE) {
  779.         vdp_update_mode();
  780.         vdp_update_params();
  781.         if (features & FE_SHOWVIDEO)
  782.             VIDEO(resize, (screenxsize, screenysize));    /* clear edges? */
  783.         vdp_dirty_all();
  784.     }
  785.  
  786.     if (redraw & (REDRAW_SPRITES))
  787.         vdp_dirty_sprites();
  788.  
  789.     if (redraw & REDRAW_PALETTE)
  790.         if (features & FE_SHOWVIDEO) {
  791.             VIDEO(setfgbg, (vdpfg, vdpbg));
  792.             // if screen is blank, force something to change
  793.             if (!(vdpregs[1] & R1_NOBLANK))
  794.                 redraw |= REDRAW_BLANK;
  795.             vdp_redraw_text_sides = true;
  796.             vdp_update();
  797.         }
  798.  
  799.     if (redraw & REDRAW_BLANK)
  800.         if (!(vdpregs[1] & R1_NOBLANK)) {
  801.             if (features & FE_SHOWVIDEO) {
  802.                 VIDEO(setblank, (vdpbg));
  803.                 vdp_update();
  804.             }
  805.         } else {
  806.             vdp_update_params();
  807.             if (features & FE_SHOWVIDEO) {
  808.                 VIDEO(resetfromblank, ());
  809.                 vdp_update();
  810.             }
  811.         }
  812. }
  813.  
  814.  
  815.  
  816. /************************************************************/
  817.  
  818. /*
  819.     Redraw strategy:
  820.     
  821.     Using a 256-color mode, we will present to the video module a list
  822.     of coordinates and 8x8 blocks of update information.  There will
  823.     only be one visible page, upon which all changes will be made.
  824.     
  825.     The point will be that, even with sprites, we won't draw to video
  826.     memory at one place more than once.  Instead, a bitmap is maintained
  827.     in RAM that is only written to video memory after all applicable
  828.     changes have been made.
  829. */
  830.  
  831. /*
  832.  *    Tell if an updateblock can be drawn with one color
  833.  *
  834.  *    If collapse is true, treat color 0 as vdpfg and 16 and vdpbg;
  835.  *    this may cause problems on palettized displays when these values
  836.  *    change.
  837.  */
  838. bool video_block_is_solid(updateblock *ptr, bool collapse, u8 *color)
  839. {
  840.     u8 *mem;
  841.     u8 byt;
  842.     int row;
  843.     unsigned long long run;
  844.  
  845.     // get representative byte (i.e., one color)
  846.     byt = ptr->data[0];
  847.  
  848.     // duplicate it eight times
  849.     run = byt | (byt<<8) | (byt<<16) | (byt<<24);
  850.     run = (run << 32) | run;
  851.  
  852.     // compare the 8 rows in memory with the byte
  853.     mem = ptr->data;
  854.     for (row = 0; row < 8; row++) {
  855.         if (*(unsigned long long *)mem != run)
  856.             return false;
  857.         mem += UPDATEBLOCK_ROW_STRIDE;
  858.     }
  859.  
  860.     // matched!  
  861.     *color = byt;
  862.     return true;
  863.  
  864. #if 0
  865.     static u8 zeroes[8] = {0,0,0,0,0,0,0,0};
  866.     static u8 ones[8] = {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};
  867.     u8 fg, bg;
  868.     bool colors_same;
  869.     
  870.     // if pattern is not set, we've got sprites
  871.     if (!ptr->pattern)
  872.         return false;
  873.  
  874.     // check for a run of the same colors...
  875.     colors_same = false;
  876.  
  877.     // if colors set, we have bitmap mode, and
  878.     // must make sure all of the colors are the same as each other.
  879.     if (ptr->colors) {
  880.         u8 colorrun[8];
  881.         memset(colorrun, ptr->colors[0], 8);
  882.         if (memcmp(colorrun, ptr->colors, 8) == 0) {
  883.             colors_same = true;
  884.  
  885.             // get those colors
  886.             fg = (ptr->colors[0] & 0xf0) >> 4;
  887.             bg = (ptr->colors[0] & 0xf);
  888.  
  889.             if (collapse) {
  890.                 if (!fg) fg = vdpfg;
  891.                 if (!bg) bg = vdpbg;
  892.             }
  893.         }
  894.     } else {
  895.         // all one colors are the same...
  896.         colors_same = true;
  897.  
  898.         fg = (!collapse || ptr->fg) ? ptr->fg : vdpfg;
  899.         bg = (!collapse || ptr->bg) ? ptr->bg : vdpbg;
  900.     }
  901.  
  902.     // it could be solid if the colors are the same...
  903.     if (!colors_same)
  904.         return false;
  905.  
  906.     if (fg == bg) {
  907.         *color = fg;
  908.         return true;
  909.     } 
  910.     // or if the pattern is solid...
  911.     else if (memcmp(ptr->pattern, zeroes, 8) == 0) {
  912.         *color = bg;
  913.         return true;
  914.     } 
  915.     else if (memcmp(ptr->pattern, ones, 8) == 0) {
  916.         *color = fg;
  917.         return true;
  918.     } 
  919.  
  920.     return false;
  921. #endif
  922. }
  923.  
  924. /*
  925.     These drawing functions use the highly optimized 256-color
  926.     routines for mapping eight pixels to eight bytes in
  927.     vdpdrawrow.c and vdpdrawrowtext.c.
  928. */
  929.  
  930.  
  931. static void
  932. redraw_graphics_block(u8 * pattern, u8 color, updateblock *ull)
  933. {
  934.     int         i;
  935.     u8          fg, bg;
  936.     u8            *block;
  937.  
  938.     block = ull->data = UPDPTR(ull->r, ull->c);
  939.     
  940.     bg = color & 0xf;
  941.     fg = color >> 4;
  942.     
  943.     ull->bg = bg;            
  944.     ull->fg = fg;
  945.     ull->colors = NULL;        // simple block
  946.     ull->pattern = pattern;
  947.  
  948.     for (i = 0; i < 8; i++) {
  949.         vdpdrawrow[*pattern++] (block, fg, bg);
  950.         block += UPDATEBLOCK_ROW_STRIDE;
  951.     }
  952. }
  953.  
  954. /*    The blank screen must be redrawn, i.e., when a minimized
  955.     window is expanded again.  */
  956. void
  957. redraw_blank(void)
  958. {
  959.     struct updateblock updatelist[768];
  960.     struct updateblock *ull = updatelist;
  961.  
  962.     u32         i;
  963.     u8         *scptr;
  964.  
  965.     if (!vdpchanged)
  966.         return;
  967.  
  968.     /*  Redraw changed chars  */
  969.     for (i = 0, scptr = vdp_changes.screen; i < 768; i++, scptr++) {
  970.         if (*scptr) {            /* this screen pos updated? */
  971.             ull->r = (i >> 5) << 3;
  972.             ull->c = (i & 31) << 3;
  973.  
  974.             redraw_graphics_block(vdpram /*ignored*/,
  975.                                   ((vdpregs[7] & 0xf) << 4) | (vdpregs[7] & 0xf),
  976.                                   ull);
  977.                 
  978.             ull++;                /* next slot */
  979.         }
  980.     }
  981.  
  982.     memset(vdp_changes.screen, 0, sizeof(vdp_changes.screen));
  983.  
  984.     if (ull)                    /* any changes? (most likely) */
  985.         VIDEO(updatelist, (updatelist, ull - updatelist));
  986. }
  987.  
  988. void
  989. redraw_graphics(void)
  990. {
  991.     struct updateblock updatelist[768];
  992.     struct updateblock *ull = updatelist;
  993.  
  994.     u32         i;
  995.     u8         *scptr;
  996.     u32         currchar;
  997.  
  998.     if (!vdpchanged)
  999.         return;
  1000.  
  1001.     /*  Set pattern changes in chars, for sprites  */
  1002.  
  1003.     for (i = 0, scptr = vdp_changes.screen; i < 768; i++, scptr++) {
  1004.         currchar = vdpram[vdp_mode.screen.base + i];    /* char # to update */
  1005.         if (vdp_changes.patt[currchar])    /* this pattern changed? */
  1006.             *scptr = 1;            /* then this char changed */
  1007.     }
  1008.  
  1009.     /*  vdp_changes.sprite makes changes in vdp_changes.screen */
  1010.     vdp_update_sprites();
  1011.  
  1012.     /*  Redraw changed chars  */
  1013.     for (i = 0, scptr = vdp_changes.screen; i < 768; i++, scptr++) {
  1014.         if (*scptr) {            /* this screen pos updated? */
  1015.             currchar = vdpram[vdp_mode.screen.base + i];    /* char # to update */
  1016.  
  1017.             ull->r = (i >> 5) << 3;    /* for graphics mode */
  1018.             ull->c = (i & 31) << 3;
  1019.  
  1020.             redraw_graphics_block(&vdpram[vdp_mode.patt.base + (currchar << 3)],
  1021.                                   vdpram[vdp_mode.color.base + (currchar >> 3)], 
  1022.                                   ull);
  1023.  
  1024.                 /* can't redraw easily */
  1025.             if (*scptr == SC_SPRITE_COVERING)
  1026.                 ull->pattern = ull->colors = NULL;
  1027.                 
  1028.             ull++;                /* next slot */
  1029.         }
  1030.     }
  1031.  
  1032.     /* draw sprites */
  1033.     vdp_redraw_sprites();
  1034.  
  1035.     memset(vdp_changes.screen, 0, sizeof(vdp_changes.screen));
  1036.     memset(vdp_changes.patt, 0, 256);
  1037.     vdp_changes.sprite = 0;
  1038.     memset(vdp_changes.sprpat, 0, sizeof(vdp_changes.sprpat));
  1039.  
  1040.     if (ull)                    /* any changes? (most likely) */
  1041.         VIDEO(updatelist, (updatelist, ull - updatelist));
  1042. }
  1043.  
  1044.  
  1045.  
  1046. static void
  1047. redraw_bitmap_block(u8 * pattern, u8 * color, updateblock * ull)
  1048. {
  1049.     int         i;
  1050.     u8          fg, bg;
  1051.     u8            *block;
  1052.  
  1053.     block = ull->data = UPDPTR(ull->r, ull->c);
  1054.     
  1055.     ull->fg = vdpfg; ull->bg = vdpbg;
  1056.     ull->colors = color;
  1057.     ull->pattern = pattern;
  1058.     
  1059.     for (i = 0; i < 8; i++) {
  1060.         bg = *color & 0xf;
  1061.         fg = *color >> 4;
  1062.         color++;
  1063.  
  1064.         vdpdrawrow[*pattern++] (block, fg, bg);
  1065.         block += UPDATEBLOCK_ROW_STRIDE;
  1066.     }
  1067. }
  1068.  
  1069.  
  1070. void
  1071. redraw_bitmap(void)
  1072. {
  1073.     struct updateblock updatelist[768];
  1074.     struct updateblock *ull = updatelist;
  1075.  
  1076.     u32         i;
  1077.     u8         *scptr;
  1078.     u32         currchar;
  1079.     u16         pp, cp;
  1080.  
  1081.     if (!vdpchanged)
  1082.         return;
  1083.  
  1084.     /*  Set pattern or color changes in chars, for sprites  */
  1085.  
  1086.     for (i = 0, scptr = vdp_changes.screen; i < 768; i++, scptr++) {
  1087.         currchar = vdpram[vdp_mode.screen.base + i];    /* char # to update */
  1088.         if (vdp_changes.patt[currchar + (i & 0x300)] ||
  1089.             vdp_changes.color[currchar + (i & 0x300)])
  1090.             *scptr = 1;
  1091.     }
  1092.  
  1093.  
  1094.     /*  vdp_changes.sprite makes changes in vdp_changes.screen */
  1095.     vdp_update_sprites();
  1096.  
  1097.     /*  Redraw changed chars  */
  1098.     for (i = 0, scptr = vdp_changes.screen; i < 768; i++, scptr++) {
  1099.         if (*scptr) {            /* this screen pos updated? */
  1100.             currchar = vdpram[vdp_mode.screen.base + i];    /* char # to update */
  1101.  
  1102.             ull->r = (i >> 5) << 3;    /* for graphics mode */
  1103.             ull->c = (i & 31) << 3;
  1104.  
  1105.             pp = cp = (currchar + (i & 0x300)) << 3;
  1106.             pp &= bitpattmask;
  1107.             cp &= bitcolormask;
  1108.             redraw_bitmap_block(&vdpram[pp + vdp_mode.patt.base], 
  1109.                                 &vdpram[cp + vdp_mode.color.base],
  1110.                                 ull);
  1111.  
  1112.                 /* can't redraw easily */
  1113.             if (*scptr == SC_SPRITE_COVERING)
  1114.                 ull->pattern = ull->colors = NULL;
  1115.  
  1116.             ull++;                /* next slot */
  1117.         }
  1118.     }
  1119.  
  1120.     /* draw sprites */
  1121.     vdp_redraw_sprites();
  1122.  
  1123.     memset(vdp_changes.screen, 0, sizeof(vdp_changes.screen));
  1124.     memset(vdp_changes.patt, 0, sizeof(vdp_changes.patt));
  1125.     memset(vdp_changes.color, 0, sizeof(vdp_changes.color));
  1126.     vdp_changes.sprite = 0;
  1127.     memset(vdp_changes.sprpat, 0, sizeof(vdp_changes.sprpat));
  1128.  
  1129.     if (ull)                    /* any changes? (most likely) */
  1130.         VIDEO(updatelist, (updatelist, ull - updatelist));
  1131. }
  1132.  
  1133.  
  1134.  
  1135. static void
  1136. redraw_text_block(u8 * pattern, updateblock *ull)
  1137. {
  1138.     int         i;
  1139.     u8            *block;
  1140.  
  1141.     block = ull->data = UPDPTR(ull->r, ull->c);
  1142.     
  1143.     ull->fg = vdpfg;
  1144.     ull->bg = vdpbg;
  1145.     ull->colors = NULL;
  1146.     //ull->pattern = pattern;
  1147.     ull->pattern = NULL;        // we redraw 8x8 blocks; this is only 6x8
  1148.     
  1149.     for (i = 0; i < 8; i++) {
  1150.         vdpdrawrowtext[*pattern++] (block);
  1151.         block += UPDATEBLOCK_ROW_STRIDE;
  1152.     }
  1153. }
  1154.  
  1155. static void
  1156. redraw_text_side_block(updateblock *ull)
  1157. {
  1158.     int         i;
  1159.     u8            *block;
  1160.     static u8    ones[8] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
  1161.  
  1162.     block = ull->data = UPDPTR(ull->r, ull->c);
  1163.     ull->fg = vdpbg;
  1164.     ull->bg = vdpbg;
  1165.     ull->colors = NULL;
  1166.     ull->pattern = ones;
  1167.     
  1168.     for (i = 0; i < 8; i++) {
  1169.         vdpdrawrow[0xff] (block, vdpbg, vdpbg);
  1170.         block += UPDATEBLOCK_ROW_STRIDE;
  1171.     }
  1172. }
  1173.  
  1174.  
  1175. void
  1176. redraw_text(void)
  1177. {
  1178.     struct updateblock updatelist[960 + 24*2];
  1179.     struct updateblock *ull = updatelist;
  1180.  
  1181.     u32         i;
  1182.     u8         *scptr;
  1183.     u32         currchar;
  1184.  
  1185.     /*  Set update blocks for text sides */
  1186.     if (vdp_redraw_text_sides)
  1187.     {
  1188.         for (i = 0; i < 24; i++) {
  1189.             ull->r = i << 3;
  1190.             ull->c = 0;
  1191.             
  1192.             redraw_text_side_block(ull);
  1193.             ull++;
  1194.  
  1195.             ull->r = i << 3;
  1196.             ull->c = 256 - (256 - 240) / 2;
  1197.  
  1198.             redraw_text_side_block(ull);
  1199.             ull++;
  1200.         }            
  1201.         vdp_redraw_text_sides = false;
  1202.     }
  1203.  
  1204.     if (vdpchanged)
  1205.     {
  1206.         /*  Set pattern changes in chars */
  1207.  
  1208.         for (i = 0, scptr = vdp_changes.screen; i < 960; i++, scptr++) {
  1209.             currchar = vdpram[vdp_mode.screen.base + i];    /* char # to update */
  1210.             if (vdp_changes.patt[currchar])    /* this pattern changed? */
  1211.                 *scptr = 1;            /* then this char changed */
  1212.         }
  1213.  
  1214.  
  1215.         /*  Redraw changed chars  */
  1216.  
  1217.         for (i = 0, scptr = vdp_changes.screen; i < 960; i++, scptr++) {
  1218.             if (*scptr) {            /* this screen pos updated? */
  1219.                 currchar = vdpram[vdp_mode.screen.base + i];    /* char # to update */
  1220.  
  1221.                 ull->r = (i / 40) << 3;    /* for graphics mode */
  1222.                 ull->c = (i % 40) * 6 + (256 - 240) / 2;
  1223.  
  1224.                 redraw_text_block(&vdpram[vdp_mode.patt.base + (currchar << 3)], 
  1225.                                   ull);
  1226.             
  1227.                 ull++;                /* next slot */
  1228.             }
  1229.         }
  1230.  
  1231.         memset(vdp_changes.screen, 0, sizeof(vdp_changes.screen));
  1232.         memset(vdp_changes.patt, 0, sizeof(vdp_changes.patt));
  1233.  
  1234.     }
  1235.  
  1236.     if (ull)                    /* any changes? (most likely) */
  1237.         VIDEO(updatelist, (updatelist, ull - updatelist));
  1238. }
  1239.  
  1240.  
  1241. static void
  1242. redraw_multi_block(u8 * color, updateblock *ull)
  1243. {
  1244.     int         i;
  1245.     u8          fg, bg;
  1246.     u8            *block;
  1247.  
  1248.     static u8    vdp_multi_block_pattern[] = { 
  1249.         0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0 };
  1250.     
  1251.     block = ull->data = UPDPTR(ull->r, ull->c);
  1252.     ull->fg = vdpfg; ull->bg = vdpbg;
  1253.     ull->colors = color;
  1254.     ull->pattern = vdp_multi_block_pattern;
  1255.  
  1256.     for (i = 0; i < 2; i++) {
  1257.         bg = *color & 0xf;
  1258.         fg = *color >> 4;
  1259.         color++;
  1260.  
  1261.         vdpdrawrow[0xf0] (block, fg, bg);
  1262.         vdpdrawrow[0xf0] (block + UPDATEBLOCK_ROW_STRIDE, fg, bg);
  1263.         vdpdrawrow[0xf0] (block + UPDATEBLOCK_ROW_STRIDE * 2, fg, bg);
  1264.         vdpdrawrow[0xf0] (block + UPDATEBLOCK_ROW_STRIDE * 3, fg, bg);
  1265.         block += UPDATEBLOCK_ROW_STRIDE * 4;
  1266.     }
  1267. }
  1268.  
  1269.  
  1270. void
  1271. redraw_multi(void)
  1272. {
  1273.     struct updateblock updatelist[768];
  1274.     struct updateblock *ull = updatelist;
  1275.  
  1276.     u32         i;
  1277.     u8         *scptr;
  1278.     u32         currchar;
  1279.     u32            patt;
  1280.  
  1281.     if (!vdpchanged)
  1282.         return;
  1283.  
  1284.     /*  Set pattern changes in chars, for sprites  */
  1285.  
  1286.     for (i = 0, scptr = vdp_changes.screen; i < 768; i++, scptr++) {
  1287.         currchar = vdpram[vdp_mode.screen.base + i];    /* char # to update */
  1288.         if (vdp_changes.patt[currchar])    /* this pattern changed? */
  1289.             *scptr = 1;            /* then this char changed */
  1290.     }
  1291.  
  1292.     /*  vdp_changes.sprite makes changes in vdp_changes.screen */
  1293.     vdp_update_sprites();
  1294.  
  1295.     /*  Redraw changed chars  */
  1296.     for (i = 0, scptr = vdp_changes.screen; i < 768; i++, scptr++) {
  1297.         if (*scptr) {            /* this screen pos updated? */
  1298.             currchar = vdpram[vdp_mode.screen.base + i];    /* char # to update */
  1299.  
  1300.             ull->r = (i >> 5) << 3;    /* for graphics mode */
  1301.             ull->c = (i & 31) << 3;
  1302.  
  1303.             patt = vdp_mode.patt.base + (currchar << 3) + ((i >> 5) & 3) * 2;
  1304.  
  1305.             redraw_multi_block(&vdpram[patt],
  1306.                                ull);
  1307.  
  1308.                 /* can't redraw easily */
  1309.             if (*scptr == SC_SPRITE_COVERING)
  1310.                 ull->pattern = ull->colors = NULL;
  1311.  
  1312.             ull++;                /* next slot */
  1313.         }
  1314.     }
  1315.  
  1316.     /* draw sprites */
  1317.     vdp_redraw_sprites();
  1318.  
  1319.     memset(vdp_changes.screen, 0, sizeof(vdp_changes.screen));
  1320.     memset(vdp_changes.patt, 0, 256);
  1321.     vdp_changes.sprite = 0;
  1322.     memset(vdp_changes.sprpat, 0, sizeof(vdp_changes.sprpat));
  1323.  
  1324.     if (ull)                    /* any changes? (most likely) */
  1325.         VIDEO(updatelist, (updatelist, ull - updatelist));
  1326. }
  1327.  
  1328. /***************************************/
  1329.  
  1330. void
  1331. vdp_update(void)
  1332. {
  1333.     if (features & FE_VIDEO) {
  1334.         if (vdp_redraw)
  1335.             vdp_redraw();
  1336.     }
  1337. }
  1338.  
  1339. static
  1340. DECL_SYMBOL_ACTION(video_showvideo_toggle)
  1341. {
  1342.     if (task == csa_WRITE) {
  1343.         if (MODULE_ITERATE(vmVideo,vmRestopModule) == vmOk)
  1344.             MODULE_ITERATE(vmVideo,vmRestartModule);
  1345.     }
  1346.     return 1;
  1347. }
  1348.  
  1349. static
  1350. DECL_SYMBOL_ACTION(video_draw_sprites_toggle)
  1351. {
  1352.     if (task == csa_WRITE) {
  1353.         vdp_dirty_all();
  1354.     }
  1355.     return 1;
  1356. }
  1357.  
  1358. static
  1359. DECL_SYMBOL_ACTION(video_change_rates)
  1360. {
  1361.     video_changing_rates();
  1362.     return 1;
  1363. }
  1364.  
  1365. void
  1366. vdpinit(void)
  1367. {
  1368.     command_symbol_table *videocommands =
  1369.         command_symbol_table_new("Video Options",
  1370.                                  "These are generic commands for controlling video emulation",
  1371.  
  1372.          command_symbol_new("ShowVideo",
  1373.                             "Control whether the screen is displayed",
  1374.                             c_STATIC,
  1375.                             video_showvideo_toggle,
  1376.                             RET_FIRST_ARG,
  1377.                             command_arg_new_toggle
  1378.                             ("on|off",
  1379.                              "toggle video on or off",
  1380.                              NULL /* action */ ,
  1381.                              ARG_NUM(features),
  1382.                              FE_SHOWVIDEO,
  1383.                              NULL /* next */ )
  1384.                             ,
  1385.  
  1386.         command_symbol_new("VideoUpdateSpeed",
  1387.                            "Control how often the screen is updated",
  1388.                            c_STATIC,
  1389.                            video_change_rates,
  1390.                            RET_FIRST_ARG,
  1391.                            command_arg_new_num
  1392.                            ("hertz",
  1393.                             "number of times per second",
  1394.                             NULL /* action */ ,
  1395.                             ARG_NUM
  1396.                             (videoupdatespeed),
  1397.                             NULL /* next */ )
  1398.                            ,
  1399.  
  1400.         command_symbol_new("VDPInterruptRate",
  1401.                            "Control how often the VDP interrupts the CPU",
  1402.                            c_STATIC,
  1403.                            video_change_rates,
  1404.                            RET_FIRST_ARG,
  1405.                            command_arg_new_num
  1406.                            ("hertz",
  1407.                             "number of times per second",
  1408.                             NULL /* action */ ,
  1409.                             ARG_NUM
  1410.                             (vdp_interrupt_rate),
  1411.                             NULL /* next */ )
  1412.                            ,
  1413.  
  1414.          command_symbol_new("DrawSprites",
  1415.                             "Control whether sprites are displayed",
  1416.                             c_STATIC,
  1417.                             video_draw_sprites_toggle,
  1418.                             RET_FIRST_ARG,
  1419.                             command_arg_new_num
  1420.                             ("on|off",
  1421.                              "toggle sprites on or off",
  1422.                              NULL /* action */ ,
  1423.                              ARG_NUM(draw_sprites),
  1424.                              NULL /* next */ )
  1425.                             ,
  1426.  
  1427.          command_symbol_new("FiveSpritesOnLine",
  1428.                             "Obey five-sprites-on-a-line limit of TMS9918A",
  1429.                             c_STATIC,
  1430.                             NULL /*action*/,
  1431.                             RET_FIRST_ARG,
  1432.                             command_arg_new_num
  1433.                             ("on|off",
  1434.                              "on: fifth sprite on a line not drawn (default); "
  1435.                              "off: all sprites always drawn",
  1436.                              NULL /* action */ ,
  1437.                              ARG_NUM(five_sprites_on_a_line),
  1438.                              NULL /* next */ )
  1439.                             ,
  1440.  
  1441.           NULL /* next */ ))))),
  1442.  
  1443.          NULL /* sub */ ,
  1444.  
  1445.          NULL    /* next */
  1446.         );
  1447.  
  1448.     command_symbol_table_add_subtable(universe, videocommands);
  1449.  
  1450.     features |= FE_VIDEO;
  1451.     memset(vdpregs, 0, 8);
  1452.     vdpregs[1] = 0xe0;
  1453.     vdp_update_mode();
  1454.     vdp_update_params();
  1455. }
  1456.  
  1457. /*
  1458.  *    Callbacks from emulate.c
  1459.  */
  1460. DECL_SYMBOL_ACTION(vdp_set_register)
  1461. {
  1462.     int reg, val;
  1463.     if (task == csa_READ) {
  1464. #if 0
  1465.         if (iter > 9)
  1466.             return 0;
  1467. #else
  1468.         if (iter >= 8)
  1469.             return 0;
  1470. #endif
  1471.         command_arg_set_num(sym->args, iter);
  1472.         if (iter < 8)
  1473.             command_arg_set_num(sym->args->next, vdpregs[iter]);
  1474.         else if (iter == 8)
  1475.             command_arg_set_num(sym->args->next, vdpreadahead);
  1476.         else if (iter == 9)
  1477.             command_arg_set_num(sym->args->next, vdpaddrflag);
  1478.         return 1;
  1479.     }
  1480.  
  1481.     command_arg_get_num(sym->args, ®);
  1482.     command_arg_get_num(sym->args->next, &val);
  1483.  
  1484.     if (reg < 8) {
  1485.         vdpwritereg(0x8000 | ((reg & 0x7) <<8) | (val & 0xff));
  1486.     } 
  1487.     // deprecated!  Use VDPAddrFlag and VDPReadAhead now
  1488.     else if (reg == 8) {
  1489.         vdpreadahead = val;
  1490.     } else if (reg == 9) {
  1491.         vdpaddrflag = val;
  1492.     }
  1493.     return 1;
  1494. }
  1495.  
  1496. DECL_SYMBOL_ACTION(vdp_set_addr_flag)
  1497. {
  1498.     int val;
  1499.     if (task == csa_READ) {
  1500.         if (iter > 0) return 0;
  1501.         command_arg_set_num(sym->args, vdpaddrflag);
  1502.         return 1;
  1503.     }
  1504.  
  1505.     command_arg_get_num(sym->args, &val);
  1506.     vdpaddrflag = val;
  1507.  
  1508.     return 1;
  1509. }
  1510.  
  1511. DECL_SYMBOL_ACTION(vdp_set_read_ahead)
  1512. {
  1513.     int val;
  1514.  
  1515.     if (task == csa_READ) {
  1516.         if (iter > 0) return 0;
  1517.         command_arg_set_num(sym->args, vdpreadahead);
  1518.         return 1;
  1519.     }
  1520.  
  1521.     command_arg_get_num(sym->args, &val);
  1522.     vdpreadahead = val;
  1523.  
  1524.     return 1;
  1525. }
  1526.  
  1527. #ifdef WITH_LIB_PNG
  1528. #include <png.h>
  1529.  
  1530. static int
  1531. vdp_save_screen_png(char *filename)
  1532. {
  1533.     FILE *fp;
  1534.     png_structp png_ptr;
  1535.     png_infop info_ptr;
  1536.     png_color png_palette[17];
  1537.     int i;
  1538.     png_time ptime;
  1539.     png_color_16 pbkgd;
  1540.     png_text ptext;
  1541.     png_byte *row_pointers[256];
  1542.  
  1543.     fp = fopen(filename, "wb");
  1544.     if (!fp)
  1545.     {
  1546.         logger(_L|LOG_USER|LOG_ERROR, "Could not open '%s' for writing\n", filename);
  1547.     }
  1548.  
  1549.     png_ptr = png_create_write_struct(
  1550.         PNG_LIBPNG_VER_STRING,
  1551.         (png_voidp) 0L /* error_ptr */,
  1552.         0L /* error_fn */,
  1553.         0L /* warning_fn */);
  1554.     if (!png_ptr)
  1555.     {
  1556.         logger(_L|LOG_USER|LOG_ERROR, "Could not initialize PNG subsystem\n");
  1557.         return 0;
  1558.     }
  1559.  
  1560.     info_ptr = png_create_info_struct(png_ptr);
  1561.     if (!info_ptr)
  1562.     {
  1563.         logger(_L|LOG_USER|LOG_ERROR, "Could not initialize PNG subsystem\n");
  1564.         png_destroy_write_struct(&png_ptr, (png_infopp)0L);
  1565.         return 0;
  1566.     }
  1567.  
  1568.     if (setjmp(png_ptr->jmpbuf))
  1569.     {
  1570.         logger(_L|LOG_USER|LOG_ERROR, "Failed to write PNG file '%s'\n", filename);
  1571.         png_destroy_write_struct(&png_ptr, &info_ptr);
  1572.         fclose(fp);
  1573.         return 0;
  1574.     }
  1575.  
  1576.     /* set up filters */
  1577.     png_set_filter(png_ptr, 0, 
  1578.                    PNG_FILTER_NONE | PNG_FILTER_SUB |
  1579.                    PNG_FILTER_PAETH);
  1580.  
  1581.     /* set up compression */
  1582.     png_set_compression_level(png_ptr, Z_BEST_COMPRESSION);
  1583.  
  1584.     /* set up header */
  1585.     png_set_IHDR(png_ptr, info_ptr, screenxsize, screenysize,
  1586.                  8, PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE,
  1587.                  PNG_COMPRESSION_TYPE_DEFAULT,
  1588.                  PNG_FILTER_TYPE_DEFAULT);
  1589.  
  1590.     /* set up palette */
  1591.     for (i = 0; i <= 16; i++)
  1592.     {
  1593.         int j = (i == 0 ? vdpbg : i == 16 ? vdpfg : i);
  1594.         png_palette[i].red = vdp_palette[j][0];
  1595.         png_palette[i].green = vdp_palette[j][1];
  1596.         png_palette[i].blue = vdp_palette[j][2];
  1597.     }
  1598.     png_set_PLTE(png_ptr, info_ptr, png_palette, 16);
  1599.  
  1600.     /* set up time */
  1601.     png_convert_from_time_t(&ptime, time(0L));
  1602.     png_set_tIME(png_ptr, info_ptr, &ptime);
  1603.  
  1604.     /* set up background and transparency */
  1605. //    pbkgd.index = 0; /* paletted */
  1606. //    png_set_tRNS(png_ptr, info_ptr, (png_bytep)&pbkgd.index, 1, 0L /*color16*/);
  1607. //    png_set_bKGD(png_ptr, info_ptr, &pbkgd);
  1608.  
  1609.     /* info text */
  1610.     ptext.compression = PNG_TEXT_COMPRESSION_NONE;
  1611.     ptext.key = "Software";
  1612.     ptext.text = "V9t9";
  1613.     ptext.text_length = strlen(ptext.text);
  1614.     png_set_text(png_ptr, info_ptr, &ptext, 1);
  1615.  
  1616.     /* point to file */
  1617.     png_init_io(png_ptr, fp);
  1618.  
  1619.     /* write info */
  1620.     png_write_info(png_ptr, info_ptr);
  1621.  
  1622.     /* write rows */
  1623.     for (i = 0; i < screenysize; i++)
  1624.     {
  1625.         row_pointers[i] = UPDPTR(i, 0);
  1626.     }
  1627.     png_write_image(png_ptr, row_pointers);
  1628.  
  1629.     /* done! */
  1630.     png_write_end(png_ptr, info_ptr);
  1631.  
  1632.     png_destroy_write_struct(&png_ptr, &info_ptr);
  1633.  
  1634.     fclose(fp);
  1635.     return 1;
  1636. }
  1637.  
  1638. #endif    // WITH_LIB_PNG
  1639.  
  1640. /*
  1641.  *    Callback from emulate.c
  1642.  */
  1643.  
  1644. static char *vdp_auto_name(void)
  1645. {
  1646.     int count = 0;
  1647.     static char *pattern = "scrn-%03d.png";
  1648.     static char buffer[OS_NAMESIZE];
  1649.     OSSpec spec;
  1650.     OSError err;
  1651.     while (count < 999) {
  1652.         sprintf(buffer, pattern, count);
  1653.         if ((err = OS_MakeFileSpec(buffer, &spec)) != OS_NOERR) {
  1654.             logger(_L|LOG_ERROR|LOG_USER, "Cannot make filename '%s'\n", buffer);
  1655.             return NULL;
  1656.         }
  1657.         if ((err = OS_Status(&spec)) == OS_FNFERR) {
  1658.             logger(_L|LOG_USER, "Writing to '%s'\n", buffer);
  1659.             return buffer;
  1660.         }
  1661.         count++;
  1662.     }
  1663.     return NULL;
  1664. }
  1665.  
  1666. DECL_SYMBOL_ACTION(vdp_take_screenshot)
  1667. {
  1668.     if (task == csa_WRITE) {
  1669.         char *filename;
  1670.         if (!command_arg_get_string(SYM_ARG_1st, &filename))
  1671.             return 0;
  1672.  
  1673.         if (!filename || !*filename)
  1674.             filename = vdp_auto_name();
  1675.  
  1676.         if (!filename) {
  1677.             logger(_L|LOG_ERROR|LOG_USER, "Could not make a filename for screen shot\n");
  1678.             return 0;
  1679.         }
  1680.  
  1681. #ifdef WITH_LIB_PNG
  1682.         return vdp_save_screen_png(filename);
  1683. #endif 
  1684.         logger(_L|LOG_USER|LOG_ERROR, "No graphics file formats supported!\n");
  1685.         return 0;
  1686.     }
  1687.     return 1;
  1688. }
  1689.