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

  1.  
  2. /*
  3.   VDPSPRITES.C                --    handle sprite-change functions and drawing.
  4.     
  5.     
  6.   TODO:
  7.  
  8.     1)  fix magnified problems with messies on screen
  9.     2)  when screen changes, change sprites
  10.     
  11.     -- set vdpstatus bit for sprite coinc (0x40):
  12.     checked every 1/60 second when any 'on' bits in
  13.     sprite patterns match other sprites' 'on' bits
  14.     -- set five sprites on a line bit (0x20)
  15.     (only four are displayed)
  16.     -- set fifth sprite number (0x1f)
  17. */
  18.  
  19. #define __VDP_INTERNAL__
  20. #include "v9t9_common.h"
  21. #include "video.h"
  22. #include "vdp.h"
  23. #include "vdpsprites.h"
  24. #include "memory.h"
  25.  
  26. #define _L     LOG_SPRITES | LOG_INFO
  27.  
  28. #define SPRITE_PTR(x)    (struct sprite *)(FLAT_MEMORY_PTR(md_video, vdp_mode.sprite.base) + (x)*4)
  29. #define SPRPAT_PTR(x)    (FLAT_MEMORY_PTR(md_video, vdp_mode.sprpat.base) + ((x) << 3))
  30.  
  31. /*    r==0xd0     ==> all sprites here and beyond are 'deleted'
  32.     color&0x80  ==> early clock on, move sprite 32 pixels to the left
  33. */
  34. struct sprite {
  35.      u8          y, x;
  36.      u8          ch, color;
  37. };
  38.  
  39. #define GETX(sp) (((sp)->color&0x80) ? ((sp)->x)-32 : (sp)->x)
  40. #define GETY(sp) (((sp)->y+1)&255)
  41.  
  42. struct sprite oldsprites[32];
  43. int         olddelptr;
  44. struct sprite *newsprites;
  45. int         newdelptr;
  46.  
  47. u32         toredraw;
  48.  
  49. u32         sprbitmap[32 * 32];    /* where are sprites? */
  50.  
  51. static u32  sprlines[256];    /* sprites on each line */
  52. static u8   sprlinecnt[256];    /* counts of # bits set */
  53.  
  54. /*
  55.     This function does the mammoth tasks of:
  56.     
  57.     0)  Change sprites under whom chars changed
  58.     1)  See if sprite pattern change forces a sprite update.
  59.     2)  See if position change forces a sprite update.
  60.     3)  See if deleted-ptr changes, forcing sprite updates.
  61.     4)  Update vdp_changes.screen where sprites dance.
  62.     5)  Force updates to sprites under changed ones.
  63.     6)  Weed out blank sprites.
  64.     7)  Set VDP status flags for coincidence and five sprites on a line (hackish)
  65. */
  66. void        
  67. vdp_update_sprites(void)
  68. {
  69.     int         i;
  70.     struct sprite *sp, *osp;
  71.     int         deleted;
  72.     u32         moved;
  73.     u32         olddelbitmap, newdelbitmap;
  74.     u32         nearchanges;
  75.  
  76.     int            five_sprites = -1;
  77.  
  78.     if (!draw_sprites) 
  79.         return;
  80.  
  81.     logger(_L | L_2, "---------");
  82.  
  83.     toredraw = 0;
  84.     newsprites = SPRITE_PTR(0);
  85.     sp = newsprites;
  86.     osp = oldsprites;
  87.     deleted = 0;
  88.     newdelptr = 32;
  89.  
  90.     logger(_L | L_2, "spritechanges: %08X\n\n", vdp_changes.sprite);
  91.     if (log_level(LOG_SPRITES) >= 2)
  92.         for (i = 0; i < 32; i++) {
  93.             if (vdp_changes.sprite & SPRBIT(i)) {
  94.                 logger(_L | L_3, "Sprite %02d changed: %s%s%s\n", i,
  95.                      (sp->x != osp->x || sp->y != osp->y) ? "pos, " : "",
  96.                      (sp->color != osp->color) ? "color, " : "",
  97.                      (sp->ch != osp->ch) ? "patt" : "");
  98.             }
  99.             sp++;
  100.             osp++;
  101.         }
  102.  
  103.  
  104.     /*  Check deleted ptr and force redraws  */
  105.     sp = newsprites;
  106.     osp = oldsprites;
  107.  
  108.     newdelbitmap = olddelbitmap = 0;
  109.     for (i = 0; i < 32; i++) {
  110.         /*  Check deleted state  */
  111.         if (!deleted) {
  112.             deleted = (sp->y == 0xd0);
  113.             if (deleted) {
  114.                 logger(_L | L_2, "Deleted sprites past %d\n", i);
  115.                 newdelptr = i;
  116.             }
  117.         }
  118.  
  119.         if (i >= olddelptr && i < newdelptr) {    /* need to draw undeleted sprite */
  120.             vdp_changes.sprite |= SPRBIT(i);
  121.             logger(_L | L_1, "Updating sprite %d because undeleted\n", i);
  122.         } else if (i >= newdelptr && i < olddelptr) {    /* need to delete sprite */
  123.             newdelbitmap |= SPRBIT(i);    /* newly deleted */
  124.             logger(_L | L_1, "Updating sprite %d because deleted\n", i);
  125.         } else
  126.             /*  Check for pattern changes  */
  127.         if (i < newdelptr) {    /* definitely not deleted */
  128.             if (vdp_changes.sprpat[sp->ch]) {
  129.                 logger(_L | L_1, "Sprite %d changed pattern\n", i);
  130.                 vdp_changes.sprite |= SPRBIT(i);
  131.             }
  132.         } else
  133.             /*  still deleted  */
  134.         {
  135.             /*vdp_changes.sprite&=~SPRBIT(i); */
  136.             olddelbitmap |= SPRBIT(i);    /* still deleted */
  137.             /* deleted in either case, can't redraw */
  138.         }
  139.  
  140.         sp++;
  141.     }
  142.  
  143. //  vdp_changes.sprite&=~(olddelbitmap|newdelbitmap); /* deleted, don't bother */
  144.  
  145.     logger(_L | L_1, "VDP_CHANGES.SPRITE: %04X  DELBITMAP: %04X/%04X\n", vdp_changes.sprite,
  146.          olddelbitmap, newdelbitmap);
  147.  
  148.     if (newdelptr == 32)
  149.         logger(_L | L_2, "No deleted sprites\n");
  150.  
  151.  
  152.     /*  Now, check sprite moves, and redraw.  
  153.  
  154.        This looks at the old bitmap.
  155.      */
  156.  
  157.     /*  1)  Which sprites moved?  
  158.        We consider newly deleted sprites to have moved.
  159.      */
  160.     osp = oldsprites;
  161.     sp = newsprites;
  162.     moved = newdelbitmap;
  163.     for (i = 0; i < 32; i++) {
  164. //      if (i>=newdelptr && i<olddelptr)
  165. //          moved|=SPRBIT(i);
  166. //      else
  167.         if ((osp->x != sp->x || osp->y != sp->y))
  168.             moved |= SPRBIT(i);
  169.         osp++;
  170.         sp++;
  171.     }
  172.  
  173.     /*  2)  Where WERE they?  */
  174.     for (i = 0; i < 768; i++) {
  175.         if (sprbitmap[i] & (vdp_changes.sprite | newdelbitmap))    /* old bitmap */
  176.             vdp_changes.screen[i] = SC_SPRITE_DELETED;
  177.     }
  178.  
  179.  
  180.     /*  Now, create new sprite bitmap.  */
  181.  
  182.     memset((void *) sprbitmap, 0, sizeof(sprbitmap));
  183.     memset((void *) sprlines, five_sprites_on_a_line ? 0 : -1, sizeof(sprlines));
  184.     memset((void *) sprlinecnt, 0, sizeof(sprlinecnt));
  185.     five_sprites = -1;
  186.     sp = newsprites;
  187.  
  188.     for (i = 0; i < 32; i++) {
  189.         u8 px, py;        // pixel coordinate
  190.         u8 bx, by;        // block coordinate
  191.         int dx, dy;        // delta for sprite
  192.         int pyl;
  193.  
  194.         py = GETY(sp);
  195.         by = (py >> 3);
  196.         dy = (py & 7) + sprwidth;
  197.  
  198.  
  199.         /*  count sprites on each line and keep track
  200.             of which of the first four we can draw */
  201.  
  202.         pyl = (py + sprwidth) & 255;
  203.         while (py != pyl) {
  204.             sprlinecnt[py]++;
  205.             if (sprlinecnt[py] > 4) {
  206.                 /* record fifth sprite */
  207.                 if (five_sprites == -1) five_sprites = i;
  208.             } else {
  209.                 /* set bit so this sprite will be drawn */
  210.                 sprlines[py] |= SPRBIT(i);
  211.             }
  212.             py = (py+1) & 255;
  213.         }
  214.     
  215.         while (dy > 0) {
  216.  
  217.             px = GETX(sp);
  218.             bx = (px >> 3);
  219.             dx = (px & 7) + sprwidth;
  220.  
  221.             while (dx > 0) {
  222.                 /* very rough check for sprite coincidence:  FIXME !!! */
  223.                 if (sprbitmap[(by << 5) + bx])
  224.                     vdp_mmio_set_status(vdp_mmio_get_status() | VDP_COINC);
  225.  
  226.                 sprbitmap[(by << 5) + bx] |= SPRBIT(i);
  227.  
  228.                 bx = (bx + 1) & 31;
  229.                 dx -= 8;
  230.             }
  231.  
  232.             by = (by + 1) & 31;
  233.             dy -= 8;
  234.         }
  235.         sp++;
  236.     }
  237.  
  238.     if (five_sprites != -1) {
  239.         vdp_mmio_set_status( (vdp_mmio_get_status() 
  240.                               & ~(VDP_FIVE_SPRITES | VDP_FIFTH_SPRITE))
  241.                              | VDP_FIVE_SPRITES | i );
  242.  
  243.         vdp_changes.sprite |= -1;
  244.         if (log_level(LOG_SPRITES) >= 3) {
  245.             logger(_L|L_3, "Five sprites on lines:\n");
  246.             for (i=0; i<256; i++) {
  247.                 if (sprlinecnt[i] > 4) {
  248.                     u32 b, n;
  249.                     logger(_L|L_3, "%d: ", i);
  250.                     b = 1;
  251.                     n = 0;
  252.                     while (b) {
  253.                         if (sprlines[i] & b) {
  254.                             logger(_L|L_3, "%d,", n);
  255.                         }
  256.                         n++;
  257.                         b <<= 1;
  258.                     }
  259.                     logger(_L|L_3, "\n");
  260.                 }
  261.             }
  262.         }
  263.     } else {
  264.         vdp_mmio_set_status( (vdp_mmio_get_status() 
  265.                               & ~(VDP_FIVE_SPRITES | VDP_FIFTH_SPRITE)) );
  266.     }
  267.  
  268.     /*  Where chars change, force sprite update  */
  269.     for (i = 0; i < 768; i++)
  270.         if (vdp_changes.screen[i])
  271.             vdp_changes.sprite |= sprbitmap[i];
  272.  
  273.  
  274.     /*
  275.        Now, set all the sprites below/above a changed one 
  276.  
  277.        Do this by walking sprbitmap.  If sprbitmap&vdp_changes.sprite,
  278.        this means this char is affected by a change, so we need to
  279.        redraw all other sprites on that char, so vdp_changes.sprite|sprbitmap.
  280.      */
  281.  
  282. /*    for (i=0; i<768; i++)
  283.  *    {
  284.  *    
  285.  *        if (sprbitmap[i]&vdp_changes.sprite)
  286.  *            vdp_changes.sprite|=sprbitmap[i];
  287.  *  }
  288.  *
  289.  *    This is not the correct solution. Imagine a diagonal line
  290.  *    of sprites from the top left to the screen to the bottom right,
  291.  *  in increasing sprite number.  When the bottommost one changes,
  292.  *    we'll get the next one above it with this algorithm, but not
  293.  *    the one on top of *that* one, etc.  The "solution" to recursively
  294.  *    redraw every sprite on up the line would lead to terrible 
  295.  *    performance problems in these cases.
  296.  *
  297.  *    What we need to do is keep track of sprites that are redrawn
  298.  *    in the vicinity of a real changed sprite, and avoid setting
  299.  *    their bits in vdp_changes.screen[].  That's what nearchanges is.
  300.  */
  301.  
  302.     nearchanges = 0;
  303.     if (!five_sprites_on_a_line) {
  304.         for (i = 0; i < 768; i++) {
  305.             if (sprbitmap[i] & vdp_changes.sprite) 
  306.                 nearchanges |= sprbitmap[i];
  307.         }
  308.     } else {
  309.         for (i = 0; i < 32; i++) {
  310.             if (sprlines[i] & vdp_changes.sprite) {
  311.                 nearchanges |= sprlines[i];
  312.             }
  313.         }
  314.     }
  315.  
  316.     /*  Don't *draw* deleted sprites */
  317.     vdp_changes.sprite &= ~(newdelbitmap);
  318.  
  319.     /*  Now, figure which sprites should be drawn  */
  320.     sp = newsprites;
  321.     toredraw = 0;
  322.     for (i = 0; i < 32; i++) {
  323.         if ((vdp_changes.sprite | nearchanges) & SPRBIT(i)) {
  324.             /*  CHECK BLANK SPRITE PATTERNS  */
  325.  
  326.             if (i < newdelptr && (sp->color & 15)) {
  327.                 toredraw |= SPRBIT(i);
  328.                 logger(_L | L_3, "going to redraw sprite %d at %d,%d\n", i, GETX(sp),
  329.                      GETY(sp));
  330.             }
  331.         }
  332.         sp++;
  333.     }
  334.  
  335.  
  336.     /*
  337.        Mark all the screen positions that need to be updated  
  338.  
  339.        If sprbitmap&vdp_changes.sprite, then that space needs update.
  340.  
  341.        NOT toredraw here -- clear sprites aren't drawn but the spaces
  342.        they occupy may have changed (if they turn clear -- as in carwars)
  343.      */
  344.  
  345.     logger(_L | L_1, "Toredraw: %08X  spritechanges: %08X\n", toredraw, vdp_changes.sprite);
  346.  
  347.     for (i = 0; i < 768; i++) {
  348.         if (sprbitmap[i] & (vdp_changes.sprite | newdelbitmap))
  349.             vdp_changes.screen[i] = SC_SPRITE_COVERING;
  350.     }
  351.  
  352.     if (log_level(LOG_VIDEO) > 3)
  353.     {
  354.         logger(_L, "screenchanges:\n\t");
  355.         for (i = 0; i < 768; i++) {
  356.             logger(_L, "%1d ", vdp_changes.screen[i]);
  357.             if ((i & 31) == 31)
  358.                 logger(_L, "\n\t");
  359.         }
  360.     }
  361.  
  362.     vdp_changes.sprite = 0;
  363.     memset(vdp_changes.sprpat, 0, sizeof(vdp_changes.sprpat));
  364.     memcpy((void *) oldsprites, (void *) newsprites, 128);
  365.     olddelptr = newdelptr;
  366. }
  367.  
  368. static int
  369. spritecoinc(struct sprite *a, struct sprite *b)
  370. {
  371.     s32         ax0, ay0;
  372.     s32         bx0, by0;
  373.  
  374.     ax0 = GETX(a);
  375.     ay0 = GETY(a);
  376.  
  377.     bx0 = GETX(b);
  378.     by0 = GETY(b);
  379.  
  380.     /*  See if 'b' intersects 'a' */
  381.     if (((bx0 >= ax0 && bx0 < ax0 + sprwidth)
  382.          || (bx0 + sprwidth > ax0 && bx0 <= ax0)) 
  383.         && 
  384.         ((by0 >= ay0 && by0 < ay0 + sprwidth)
  385.          || (by0 + sprwidth > ay0 && by0 <= ay0))) 
  386.     {
  387.         logger(_L | L_2, "Sprite at (%d,%d) intersects (%d,%d)\n", ax0, ay0, bx0, by0);
  388.         return 1;
  389.     } else
  390.         return 0;
  391.  
  392. }
  393.  
  394. /****************************************************/
  395.  
  396. /*
  397.     Draw the sprites we have set to update.
  398.     This is called after graphics chars have been changed into
  399.         updatelist.
  400.     updblock is a copy of the screen.  just draw on it.
  401. */
  402.  
  403. static int vdp_sprite_current;
  404.  
  405. /*    'x' is an int, which is necessary for proper positioning
  406.     of the sprite once the early clock has been calculated.
  407.     'y' is a u8 so we can avoid clipping it to range. */
  408. INLINE void
  409. drawspritechar(int x, u8 y, u8 * patt, u8 color)
  410. {
  411.     u8         *block;
  412.     u8          mask;
  413.     int         xx, yy;
  414.     s8          shift;
  415.  
  416.     shift = ((color & 0x80) ? -32 : 0);
  417. //  y&=255;
  418.     block = UPDPTR(y, x + shift);
  419.     for (yy = 0; yy < 8; yy++) {
  420.         if (sprlines[y & 255] & (1 << vdp_sprite_current)) {
  421.             mask = 0x80;
  422.             for (xx = 0; xx < 8; xx++) {
  423.                 if (*patt & mask)
  424.                     if (x + xx < 256)
  425.                         *(block + xx) = color & 0xf;
  426.                 mask >>= 1;
  427.             }
  428.         }
  429.         patt++;
  430.         block += UPDATEBLOCK_ROW_STRIDE;
  431. //      y=(y+1)&255;
  432.         y++;
  433.         if (y == 0)
  434.             block = UPDPTR(0, x + shift);
  435.     }
  436. }
  437.  
  438.  
  439. /*    'x' is an int, which is necessary for proper positioning
  440.     of the sprite once the early clock has been calculated.
  441.     'y' is a u8 so we can avoid clipping it to range. */
  442. INLINE void
  443. drawspritecharmag(int x, u8 y, u8 * patt, u8 color)
  444. {
  445.     u8         *block;
  446.     u8          mask;
  447.     int         xx, yy;
  448.     s8          shift;
  449.  
  450.     shift = ((color & 0x80) ? -32 : 0);
  451. //  y&=255;
  452.     block = UPDPTR(y, x + shift);
  453.     for (yy = 0; yy < 16; yy++) {
  454.         mask = 0x80;
  455.         if (sprlines[y & 255] & (1 << vdp_sprite_current)) {
  456.             for (xx = 0; xx < 16; xx++) {
  457.                 if (*patt & mask)
  458.                     if (x + xx < 256)
  459.                         *(block + xx) = color & 0xf;
  460.                 if (xx & 1)
  461.                     mask >>= 1;
  462.             }
  463.         }
  464.         if (yy & 1)
  465.             patt++;
  466.         block += UPDATEBLOCK_ROW_STRIDE;
  467.         //y=(y+1)&255;
  468.         y++;
  469.         if (y == 0)
  470.             block = UPDPTR(0, x + shift);
  471.     }
  472. }
  473.  
  474. static void
  475. drawsprite8x8(struct sprite *sp)
  476. {
  477.     int         x, y;
  478.  
  479.     x = sp->x;
  480.     y = GETY(sp);
  481.     drawspritechar(x, y, SPRPAT_PTR(sp->ch), sp->color);
  482. }
  483.  
  484. static void
  485. drawsprite16x16(struct sprite *sp)
  486. {
  487.     int         x, y;
  488.     u8            *ptr = SPRPAT_PTR(sp->ch);
  489.  
  490.     x = sp->x;
  491.     y = GETY(sp);
  492.     drawspritechar(x, y, ptr, sp->color);
  493.     drawspritechar(x + 8, y, ptr + 16, sp->color);
  494.     drawspritechar(x, y + 8, ptr + 8,  sp->color);
  495.     drawspritechar(x + 8, y + 8, ptr + 24, sp->color);
  496. }
  497.  
  498. static void
  499. drawspritemag8x8(struct sprite *sp)
  500. {
  501.     int         x, y;
  502.  
  503.     x = sp->x;
  504.     y = GETY(sp);
  505.     drawspritecharmag(x, y, SPRPAT_PTR(sp->ch), sp->color);
  506. }
  507.  
  508. static void
  509. drawspritemag16x16(struct sprite *sp)
  510. {
  511.     int         x, y;
  512.     u8            *ptr = SPRPAT_PTR(sp->ch);
  513.  
  514.     x = sp->x;
  515.     y = GETY(sp);
  516.     drawspritecharmag(x, y, ptr, sp->color);
  517.     drawspritecharmag(x + 16, y, ptr + 16, sp->color);
  518.     drawspritecharmag(x, y + 16, ptr + 8,  sp->color);
  519.     drawspritecharmag(x + 16, y + 16, ptr + 24, sp->color);
  520. }
  521.  
  522.  
  523. void
  524. vdp_redraw_sprites(void)
  525. {
  526.     int         i;
  527.     void        (*func) (struct sprite *);
  528.  
  529.     if (!draw_sprites)
  530.         return;
  531.  
  532.     switch (vdpregs[1] & (R1_SPRMAG + R1_SPR4)) {
  533.     case 0:
  534.         func = drawsprite8x8;
  535.         break;
  536.     case R1_SPRMAG:
  537.         func = drawspritemag8x8;
  538.         break;
  539.     case R1_SPR4:
  540.         func = drawsprite16x16;
  541.         break;
  542.     case R1_SPR4 + R1_SPRMAG:
  543.         func = drawspritemag16x16;
  544.     }
  545.  
  546.     for (i = 31; i >= 0; i--) {
  547.         vdp_sprite_current = i;
  548.         if (toredraw & SPRBIT(i)) {
  549.             logger(_L | L_2, "Redrawing sprite #%d [%d,%d,%02x,%02x]\n", i,
  550.                    newsprites[i].x,newsprites[i].y,newsprites[i].ch,
  551.                    newsprites[i].color);
  552.             func(&newsprites[i]);
  553.         }
  554.     }
  555. }
  556.