Program Rendering Chip Overview
Since the Pokemon Mini does not drive a conventional display, it does not operate like typical display driver. The PRC was created as a method for driving a serial LCD display. It's entire purpose is to blit images from various locations in memory to an internal frame buffer, and then force it out to GDRAM in on the LCD Controller. This is all done transparently.
The PRC operates by monitoring a display counter, which overflows approximately 66 times a second. When the counter reaches specific values, the PRC triggers various components. It can operates in 3 stages, Map render, Sprite Render and Frame Copy. The values which the counters trigger on have not been determined.
During these various stages, the CPU is halted, and yields the bus to the PRC. Depending on the enabled stages, the PRC will read various locations from memory (cartridge and ram) and build a frame buffer to $1000 ~ $12FF. Depending on the complexity of the scene, and the enabled modes, developers lose varying amounts of processor time.
Since all three display stages can be enabled or disabled independently, users can perform a number of tricks such as multiplexing sprites (by disabling frame copy on even frames, and map copy on odd frames, changing the sprites each time), running in a pure frame-buffered mode, or simply use the LCD controller manually by disabling the VPU all together.
The PRC shares ram with the CPU, so the more modes that are enabled, the less ram is usable by the developer.
Two IRQs are associated with the PRC, one associated with the Map and Sprite stage, and one with the Frame Copy stage. After Frame Copy is finished, a "PRC Render Done" IRQ fires. This event latches even if the PRC has both the map and sprite renderer disabled, so this should be taken into account. After the frame is copied to the LCD, the "PRC Frame Copy" IRQ fires. It is not yet known if this fires if the frame copy is disabled.
It is not uncomment for commercial games to disable the render done IRQ, and simply probe the strobe manually. Shock tetris uses this to double the number of on screen sprites.
Map Rendering Stage
Stage 1 of the PRC is a map renderer. This stage can render a variable size 8x8 tile based map to the frame buffer (located at $1000). This mode operates by using a 24bit address (PRC_MAP_*) as the base for the tile set, and a tile map located at $1360. This stage is enabled by using bit 2 of PRC_MODE.
|0||12 x 16|
|1||16 x 12|
|2||24 x 8|
|3||24 x 16|
Tile maps are arranged in left to right order, wrapping around to the next line.
The value in the tile map then points to an 8 byte tile. The location of the tile is specified by (Tile * 8 + PRC_MAP_*). The tiles are encoded in vertical strips, each 8 pixel strip = 1 byte. The strips are encoded with the least significant bit on the top, shifting by one bit right as it scans vertically. The PRC can also invert the graphics as it renders them by setting the appropriate bit in PRC_MODE.
Maps can be offset up to 127 pixels vertically or horizontally (PRC_SCROLL_X and PRC_SCROLL_Y), and is clamped to the size of the map itself, writes to the scroll registers when the value is out of bounds does not update the location in which rendering begins, but the values of the registers themselves changes. Interestingly, these values are only verified when the register is written, and changing the tile map size will result in maps rendering gibberish tiles.
Under no circumstances do the maps wrap. To create smooth scrolling, the entire map must be rewritten to simulate wrap around.
Sprite Rendering Stage
After the map rendering stage, the Sprite Rendering stage takes effect. The PRC allows for 24 16x16 on screen sprites per frame, which can be positioned anywhere on screen.
Sprites are controlled by 24, 4 byte attribute blocks located between $1300 ~ $135F.
|0||Sprite X Position|
|1||Sprite Y Position|
|3||Enable||Invert Color||Vertical Flip||Horizontal Flip|
The Position operates on a virtual (128x128) screen, with is positioned at (-16,-16) on the frame buffer. This allows for sprites to be positioned partially off screen on all sides. The MSB of both coordinate elements are ignored, so it can wrap around if care is not taken.
Sprite Tiles are loaded using PRC_SPR_* as the base address, with sprites tiles being 64 bytes in length (making a full tile set 16kB).
Sprites are rendered by using 8 tiles, 4 mask and 4 graphic. The mask tiles use a set pixel to signify which bits are to be left alone and which bits are to be overwritten with the graphic bits. A set bit leaves a pixel unchanged. The tiles are formatted in the same way as the Map tiles.
The 8 tiles (in order) are: Mask (0,0) Mask (0,8) Graphic (0,0) Graphic (0,8) Mask (8,0) Mask (8,8) Graphic (8,0) Graphic (8,8)
Sprites are rendered in reverse order, so Sprite #0 would always been on top. This means internally sprite #23 is drawn first, followed by #22 up to #0 (painter's order)
Frame Copy Stage
The third and final stage is the simplest of all the PRC functions. It simply sequentially moves the $300 bytes from $1000~$12FF to the GDRAM of the LCD Controller. This mode can be disabled independently of all the other modes allowing for advanced rendering techniques by sacrificing frame rate.
PRC Timing Information
The PRC timing is controlled by two registers, the PRC counter (PRC_CNT), and a frame divider. The frame counter appears to move between the values $01 ~ $41, and overflows approximately 72 times per second (4,000,000 / 55634). As previously mentioned, this counter controls when the PRC fires the various display stages. These stages do not fire every time, however. The PRC is configured to skip a certain number of display cycles, only activating once every Nth frames as configured by (PRC_SPEED).
There is no known way to use the PRC to use the full 72hz update frequency. This can be circumvented by directly accessing GDRAM through the LCD Controller
The amount of time each stage takes is unknown, likewise it is unknown when at what values the stages fire.
The time for each logical frame is 55634 cycles + a few (Determined by time between Frame Copy interrupts)
The time was determined by:
MOV BA, [0x2036]
MOV [NN+0x30], $8F
The extra cycles is the amount of time between the [0x2036] read and the [NN+0x30] write, which is either or 6 and 8 cycles. Since we are operating off a /2 divider, the error is also divided by two.