PC-6002 Sprites Library: SPRITESR
SPRITESR is a work-in-progress sprite library to automate drawing and animating sprites. SPRITESR is written to work specifically with N66 SR Mode 6 Screen 2 (Warka PC-6002, NEC PC6001 Mk2 SR, NEC PC-6601 SR, and NEC Mr PC).
SPRITESR Demo: disk for N66 SR emulator
To run the demo in the emulator, insert floppy disk image, go to mode 6 then type:
bload"picard",r
Features
SPRITESR provides 8 sprite slots that auto-redraw on VSYNC at half rate (30 fps).
Sprites coordinates can be dynamically updated and actual bitmap data can be changed to allow multi-frame animations.
SPRITESR works best for 8x8 pixels sprites but there is no hard limit, it can draw any size of sprite defined with two conditions: height must be divisible by 2, and width must be divisible by 4.
However for larger than 8x8 sprites flicker may occur.
SPRITESR allows specifying which sprites were changed this frame by calling the procedure SPRITESR_SETREDRAW. It will only fully redraw changed sprites and skip clearing unchanged sprites (useful for minimizing flicker slightly).
Modules
The library is currently made up of the following modules:
- n66sr_bios.asm: definitions for a number of N66 SR BIOS calls (addresses) and useful system constants.
- vsync.asm: vsync user event setup utility.
- fastclear.asm: fast screen 2 clearing procedures covering all 3 areas of VRAM.
- spritesr.asm: fast sprite drawing in direct VRAM accessible rectangle in screen 2 which starts at 0, 12 and has width/height of 256x188. PNG60 was updated with new functionality to support exporting p6bmp binaries directly usable by SPRITESR.
The demo is made up of the following modules:
- picard.asm: actual demo code setting up all sprites, reading input, and updating sprites accordingly.
- sprites.asm: definitions for a number of test sprites with different sizes.
Implementation Details
SPRITESR is currently written entirely in z80 assembly and doesn’t make use of COMPILE60 tool yet.
I went through multiple attempts to implement fast sprites, for the life of me I still can’t figure out what’s wrong with VRAM in SR screen 2.. for some reason writing directly to rectangular area 0,0 to 255,11 or 256,0 to 320,200 just produces garbage despite the memory map clearly being marked for those areas in all information I found.
The VRAM layout is also odd. Each 2 horizontal pixels are directly equal to 1 byte but these pairs of pixels are arranged in 8 pixel blocks (4 bytes of memory) representing 4x2 pixels on screen:
When drawing sprites, rather than doing it pixel by pixel it’s done by block where every 4x2 pixels make 1 block which means width and height of sprites must be divisible by 4x2.
This produces very fast drawing approaching hardware sprites performance although things get costly quickly the larger the sprites get. As a consequence of drawing blocks of pixels the animation must be done in steps of 2 pixels for both horizontal and vertical movement.
For the garbage areas at the top and right there are two ways to write pixels correctly:
- Output Y coordinate to 2 special ports: 0xce (low) and 0xcf (high) and read/write pixels from/to X coordinate 0 to 320 mapped directly to address 0x0000.
- View screen 1 (text screen) and set screen 2 as work area. When writing data directly to screen 2 vram in this case it works flawlessly but you can’t see screen 2 in the meantime so this isn’t useful.
First method always works perfectly but is noticeably slower as 2 bytes need to be output for every horizontal line.
Introducing VSYNC
The missing piece in the puzzle was how exactly to eliminate or at least minimize flicker?
In N66 mode you get multiple pages and the ability to set which page is viewed instantly and also set which page to work on and video memory will update accordingly.
This allowed a straight forward double buffering drawing technique, no need to interrupt on VSYNC and time drawing correctly.
In N66 SR you only get a single graphics page, so double buffering is no longer possible.
While searching for information about the platform online I ran across TinyYarou’s blog where he writes about his projects for the P6 platform, in one particular blog post he demonstrates a project he did in N66 SR BASIC and briefly mentions something about VSYNC interrupt usage.
I could not find any information I could use about what’s that interrupt’s address, nor how to set it up and use it. I sent him a message through twitter and he replied with a perfect assembly code snippet showing exactly what I’m looking for!
I immediately put together a demo and it worked! no more flicker or at least a lot less compared to just directly redrawing sprites.
This became the basis for SPRITESR’s current sprite drawing and animating method. vsync.asm module is mostly based on that code snippet.
- sprite movement without VRTC:
- sprite movement with VRTC:
Limitations:
- Due to how VRAM in SR screen 2 work, sprites are limited to rectangular area starting at 0, 12 with width/height = 256x188.
- Sprites are cleared using background color specified, meaning the background must be a solid color.
- Sprite overlap is problematic as background clearing will overwrite sprite pixels below.
- No transparency is supported.
- Sprite movement horizontally and vertically can only be 2 pixels at a time due to the VRAM layout.
Planned Features
- Allow supporting simple dithered background clear colors.
- Attempt implementing some sort of transparency with minimal performance impact.
- Implement simple overlap detection between user-marked sprites.
- Simple tile map support, each tile is 16x16 pixels and may be used for clearing sprite backgrounds if performance impact was acceptable.
- Tile map scrolling support using the ROLL hardware scroller available in SR, single direction only horizontal or vertical.
Procedures List
VSYNC Module (vsync.asm)
- SETVSYNCEVENT: start vsync event
- ENDVSYNCEVENT: end vsync event
VSYNC event automatically calls event: VSYNCEVENT (implemented in SPRITESR)
FASTCLEAR Module (fastclear.asm)
- CLEARSCREEN2: slow clears all screen 2.
- Set clear color: register C (0x0 to 0xf)
- FASTCLEARSCREEN2: fast clears sprite rectangle in screen 2 @ 0, 12 (256x188).
- Set clear color: register C (two 16 colors 0x00 to 0xff)
- CLEARSCREEN2TOP: slow clears screen 2 top area.
- Set clear color: register C (0x0 to 0xf).
- CLEARSCREEN2RIGHT: slow clears right area of screen 2.
- Set clear color: register C (0x0 to 0xf)
- FASTCLEAR: fast fill memory using given value.
- Start address: HL
- Size high byte: D
- Size low byte: B
- Clear value: C
SPRITESR Module (spritesr.asm)
- Sprites data init and update: SPRITESR_DATA0 to SPRITESR_DATA7
- SPRITESR_SETREDRAW: mark sprite updated and should be redrawn this frame.
- Use sprite index: register A (0 to 7)
- SPRITESR_REDRAWALL: mark all sprites updated (full redraw) this frame.
- FASTDRAWSPRITE: draw a sprite exported by PNG60 for mode 6 (-6 -f flags).
- Sprite data address: HL
- X coordinate: C
- Y coordinate: B
- FASTCLEARSPRITE: clear sprite are using given color.
- Sprite data address: HL
- X coordinate: C
- Y coordinate: B
- Clear color (two pixel colors 0x00 to 0xff): A
Next Objective
Compile SPRITESR as a usable z88dk library. Implement the same bouncy sprites demo in C.
More fun experiments
blog comments powered by Disqus