Faster Mode-X Sprite Rendering

Back in June, I made a sprite rendering system for DOS/VGA Mode-X.  Running it on a pcem 386 showed that the sprite clipping code was hella slow.

So I decided to rip out the clipping code.  To allow sprites to overlap the screen boundary, I set up the VGA display registers to have a 32px off-screen border around the display.

The system still splits the sprite into separate planes, and it still works by skipping some pixels then drawing some.

It works across a scanline (within a single plane) so I can use rep movsb.  It also hard-codes the scanline width into the number of pixel bytes to skip - that's so I don't have to multiply the display width by the number of scan-lines to skip.  That means my game can only run in a single screen resolution, but it's only going to run in Mode-X so that's not a problem.

Another tweak is that as much as possible, it loads 16-bits of data at a time.  This should make better use of the bus, which can be an issue with the 386SX.

The command buffer format:

  • number of skip/draw pairs (word) - used as a loop counter
  • for each skip/draw pair:
    • numToSkip/numToDraw (word)
      • top 3 bits are numToDraw-1 (min 1, max 8)
      • bottom 13 bits are numToSkip (can include multiple scan-lines)
    • numToDraw * pixel bytes
    • 0 or 1 bytes to pad to 2-byte boundary

The asm code to render a plane is this:

DrawSkipDrawPlane PROC near WATCOM_C public   
      push ds  
      push si  
      push es  
      push di  
      push cx  
   
      mov ds, ax     ; ax holds command segment  
      mov si, dx     ; dx holds command offset  
      mov di, bx     ; bx holds destination offset  
   
      mov cx, 0xA000  
      mov es, cx     ; set up vga segment  
   
      lodsw          ; load number of skip/draw pairs  
      mov dx, ax     ; ...into dx  
        
      skipDrawLoop:  
           lodsw     ; load numDraw/numSkip  
             
           mov cx, ax  
           shr cx, 13     ; split out (numDraw-1) into cx  
           inc cx  
             
           and ax, 0x1fff     ; mask to get numSkip  
           add di, ax          ; skip them  
             
           rep movsb     ; mov cx bytes of pixel data from command list to screen  
             
           inc si  
           and si, 0xfffe     ; round si up to 2-byte boundary  
             
           dec dx                    ; another skip/draw pair?  
           jnz     skipDrawLoop     ; ...then go do it  
        
      pop cx  
      pop di  
      pop es  
      pop si  
      pop ds  
        
      ret  
 DrawSkipDrawPlane ENDP  



Comments

Popular posts from this blog

Micro:Bit and SPI display

DCS World with TrackIR under Ubuntu

Cardboard Mock-up of USB Joystick