gameboy wormboy manboy gameworm gameman wormgame mangame manworm
Dependencies: mbed SDFileSystem2
video.cpp@17:c9afe1a7b423, 2019-01-13 (annotated)
- Committer:
- dicarloj
- Date:
- Sun Jan 13 19:00:10 2019 +0000
- Revision:
- 17:c9afe1a7b423
a
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
dicarloj | 17:c9afe1a7b423 | 1 | #include <stdlib.h> |
dicarloj | 17:c9afe1a7b423 | 2 | #include <mbed.h> |
dicarloj | 17:c9afe1a7b423 | 3 | #define NDEBUG |
dicarloj | 17:c9afe1a7b423 | 4 | #include <assert.h> |
dicarloj | 17:c9afe1a7b423 | 5 | #include "video.h" |
dicarloj | 17:c9afe1a7b423 | 6 | #include "mem.h" |
dicarloj | 17:c9afe1a7b423 | 7 | #include "graphics_display.h" |
dicarloj | 17:c9afe1a7b423 | 8 | #include "main.h" |
dicarloj | 17:c9afe1a7b423 | 9 | |
dicarloj | 17:c9afe1a7b423 | 10 | u32 frameCount = 0; |
dicarloj | 17:c9afe1a7b423 | 11 | |
dicarloj | 17:c9afe1a7b423 | 12 | // the implementation of sprites is poor. |
dicarloj | 17:c9afe1a7b423 | 13 | // pick a value for this that is between 0 and 256 and not equal to one of the colors |
dicarloj | 17:c9afe1a7b423 | 14 | #define TRANSPARENT_SPRITE 37 |
dicarloj | 17:c9afe1a7b423 | 15 | |
dicarloj | 17:c9afe1a7b423 | 16 | #define BRIGHTEST_COLOR 6 |
dicarloj | 17:c9afe1a7b423 | 17 | |
dicarloj | 17:c9afe1a7b423 | 18 | VideoState globalVideoState; |
dicarloj | 17:c9afe1a7b423 | 19 | |
dicarloj | 17:c9afe1a7b423 | 20 | // colors on screen (white, light gray, dark gray, black) |
dicarloj | 17:c9afe1a7b423 | 21 | // the first one must be "BRIGHTEST_COLOR" - other code depends on this! |
dicarloj | 17:c9afe1a7b423 | 22 | static const u8 colors[4] = {BRIGHTEST_COLOR, 4, 2, 0}; |
dicarloj | 17:c9afe1a7b423 | 23 | |
dicarloj | 17:c9afe1a7b423 | 24 | |
dicarloj | 17:c9afe1a7b423 | 25 | |
dicarloj | 17:c9afe1a7b423 | 26 | |
dicarloj | 17:c9afe1a7b423 | 27 | void initVideo(u8* frameBuffer) { |
dicarloj | 17:c9afe1a7b423 | 28 | globalVideoState.mode = 0; |
dicarloj | 17:c9afe1a7b423 | 29 | globalVideoState.modeClock = 0; |
dicarloj | 17:c9afe1a7b423 | 30 | globalVideoState.line = 0; |
dicarloj | 17:c9afe1a7b423 | 31 | globalVideoState.frameBuffer = frameBuffer; // <- todo change for STM32 |
dicarloj | 17:c9afe1a7b423 | 32 | |
dicarloj | 17:c9afe1a7b423 | 33 | // for(u32 i = 0; i < 160*144; i++) { |
dicarloj | 17:c9afe1a7b423 | 34 | // globalVideoState.frameBuffer[i] = 255; |
dicarloj | 17:c9afe1a7b423 | 35 | // } |
dicarloj | 17:c9afe1a7b423 | 36 | |
dicarloj | 17:c9afe1a7b423 | 37 | } |
dicarloj | 17:c9afe1a7b423 | 38 | |
dicarloj | 17:c9afe1a7b423 | 39 | inline u32 xy2px(u8 x, u8 y) { |
dicarloj | 17:c9afe1a7b423 | 40 | return H_RES*(y+Y0) + x + X0; |
dicarloj | 17:c9afe1a7b423 | 41 | } |
dicarloj | 17:c9afe1a7b423 | 42 | |
dicarloj | 17:c9afe1a7b423 | 43 | void dumpVideo() { |
dicarloj | 17:c9afe1a7b423 | 44 | for(u16 i = 0x8000; i < 0x87ff; i++) { |
dicarloj | 17:c9afe1a7b423 | 45 | if((i%8) == 1) { |
dicarloj | 17:c9afe1a7b423 | 46 | printf("@ 0x%04x: ", i); |
dicarloj | 17:c9afe1a7b423 | 47 | } |
dicarloj | 17:c9afe1a7b423 | 48 | printf("0x%02x ", readByte(i)); |
dicarloj | 17:c9afe1a7b423 | 49 | if(!(i%8)) printf("\n"); |
dicarloj | 17:c9afe1a7b423 | 50 | } |
dicarloj | 17:c9afe1a7b423 | 51 | printf("\n"); |
dicarloj | 17:c9afe1a7b423 | 52 | assert(false); |
dicarloj | 17:c9afe1a7b423 | 53 | } |
dicarloj | 17:c9afe1a7b423 | 54 | |
dicarloj | 17:c9afe1a7b423 | 55 | // read a pixel out of a tile and apply the given palette |
dicarloj | 17:c9afe1a7b423 | 56 | u8 readTile(u16 tileAddr, u8 x, u8 y, u8 palette) { |
dicarloj | 17:c9afe1a7b423 | 57 | assert(x <= 8); |
dicarloj | 17:c9afe1a7b423 | 58 | assert(y <= 8); |
dicarloj | 17:c9afe1a7b423 | 59 | x = (7 - x); |
dicarloj | 17:c9afe1a7b423 | 60 | u16 loAddr = tileAddr + (y*(u16)2); |
dicarloj | 17:c9afe1a7b423 | 61 | u16 hiAddr = loAddr + (u16)1; |
dicarloj | 17:c9afe1a7b423 | 62 | u8 lo = readByte(loAddr); |
dicarloj | 17:c9afe1a7b423 | 63 | u8 hi = readByte(hiAddr); |
dicarloj | 17:c9afe1a7b423 | 64 | u8 loV = (lo >> x) & (u8)1; |
dicarloj | 17:c9afe1a7b423 | 65 | u8 hiV = (hi >> x) & (u8)1; |
dicarloj | 17:c9afe1a7b423 | 66 | //u8 result = loV * 120 + hiV * 60; |
dicarloj | 17:c9afe1a7b423 | 67 | u8 colorIdx = loV + 2 * hiV; |
dicarloj | 17:c9afe1a7b423 | 68 | u8 colorID = (palette >> (2 * colorIdx)) & 3; |
dicarloj | 17:c9afe1a7b423 | 69 | return colors[colorID]; |
dicarloj | 17:c9afe1a7b423 | 70 | } |
dicarloj | 17:c9afe1a7b423 | 71 | |
dicarloj | 17:c9afe1a7b423 | 72 | u8 readTilePtr(u8* tileAddr, u8 x, u8 y, u8 palette) { |
dicarloj | 17:c9afe1a7b423 | 73 | assert(x <= 8); |
dicarloj | 17:c9afe1a7b423 | 74 | assert(y <= 8); |
dicarloj | 17:c9afe1a7b423 | 75 | x = (7 - x); |
dicarloj | 17:c9afe1a7b423 | 76 | u8* loAddr = tileAddr + (y*(u16)2); |
dicarloj | 17:c9afe1a7b423 | 77 | u8* hiAddr = loAddr + (u16)1; |
dicarloj | 17:c9afe1a7b423 | 78 | u8 lo = *(loAddr); |
dicarloj | 17:c9afe1a7b423 | 79 | u8 hi = *(hiAddr); |
dicarloj | 17:c9afe1a7b423 | 80 | u8 loV = (lo >> x) & (u8)1; |
dicarloj | 17:c9afe1a7b423 | 81 | u8 hiV = (hi >> x) & (u8)1; |
dicarloj | 17:c9afe1a7b423 | 82 | //u8 result = loV * 120 + hiV * 60; |
dicarloj | 17:c9afe1a7b423 | 83 | u8 colorIdx = loV + 2 * hiV; |
dicarloj | 17:c9afe1a7b423 | 84 | u8 colorID = (palette >> (2 * colorIdx)) & 3; |
dicarloj | 17:c9afe1a7b423 | 85 | return colors[colorID]; |
dicarloj | 17:c9afe1a7b423 | 86 | } |
dicarloj | 17:c9afe1a7b423 | 87 | |
dicarloj | 17:c9afe1a7b423 | 88 | // read a pixel out of a tile and apply the given palette |
dicarloj | 17:c9afe1a7b423 | 89 | // returns TRANSPARENT_SPRITE if the sprite should be transparent |
dicarloj | 17:c9afe1a7b423 | 90 | u8 readSpriteTile(u16 tileAddr, u8 x, u8 y, u8 palette) { |
dicarloj | 17:c9afe1a7b423 | 91 | //tileAddr = 0x8180; |
dicarloj | 17:c9afe1a7b423 | 92 | assert(x <= 8); |
dicarloj | 17:c9afe1a7b423 | 93 | assert(y <= 8); |
dicarloj | 17:c9afe1a7b423 | 94 | x = (7 - x); |
dicarloj | 17:c9afe1a7b423 | 95 | u16 loAddr = tileAddr + (y*(u16)2); |
dicarloj | 17:c9afe1a7b423 | 96 | u16 hiAddr = loAddr + (u16)1; |
dicarloj | 17:c9afe1a7b423 | 97 | u8 lo = readByte(loAddr); |
dicarloj | 17:c9afe1a7b423 | 98 | u8 hi = readByte(hiAddr); |
dicarloj | 17:c9afe1a7b423 | 99 | u8 loV = (lo >> x) & (u8)1; |
dicarloj | 17:c9afe1a7b423 | 100 | u8 hiV = (hi >> x) & (u8)1; |
dicarloj | 17:c9afe1a7b423 | 101 | u8 colorIdx = loV + 2 * hiV; |
dicarloj | 17:c9afe1a7b423 | 102 | if(colorIdx == 0) { |
dicarloj | 17:c9afe1a7b423 | 103 | return TRANSPARENT_SPRITE; |
dicarloj | 17:c9afe1a7b423 | 104 | } |
dicarloj | 17:c9afe1a7b423 | 105 | u8 colorID = (palette >> (2 * colorIdx)) & 3; |
dicarloj | 17:c9afe1a7b423 | 106 | return colors[colorID]; |
dicarloj | 17:c9afe1a7b423 | 107 | } |
dicarloj | 17:c9afe1a7b423 | 108 | |
dicarloj | 17:c9afe1a7b423 | 109 | u8 readSpriteTileAddr(u8* tileAddr, u8 x, u8 y, u8 palette) { |
dicarloj | 17:c9afe1a7b423 | 110 | //tileAddr = 0x8180; |
dicarloj | 17:c9afe1a7b423 | 111 | assert(x <= 8); |
dicarloj | 17:c9afe1a7b423 | 112 | assert(y <= 8); |
dicarloj | 17:c9afe1a7b423 | 113 | x = (7 - x); |
dicarloj | 17:c9afe1a7b423 | 114 | u8* loAddr = tileAddr + (y*(u16)2); |
dicarloj | 17:c9afe1a7b423 | 115 | u8* hiAddr = loAddr + (u16)1; |
dicarloj | 17:c9afe1a7b423 | 116 | u8 lo = *(loAddr); |
dicarloj | 17:c9afe1a7b423 | 117 | u8 hi = *(hiAddr); |
dicarloj | 17:c9afe1a7b423 | 118 | u8 loV = (lo >> x) & (u8)1; |
dicarloj | 17:c9afe1a7b423 | 119 | u8 hiV = (hi >> x) & (u8)1; |
dicarloj | 17:c9afe1a7b423 | 120 | u8 colorIdx = loV + 2 * hiV; |
dicarloj | 17:c9afe1a7b423 | 121 | if(colorIdx == 0) { |
dicarloj | 17:c9afe1a7b423 | 122 | return TRANSPARENT_SPRITE; |
dicarloj | 17:c9afe1a7b423 | 123 | } |
dicarloj | 17:c9afe1a7b423 | 124 | u8 colorID = (palette >> (2 * colorIdx)) & 3; |
dicarloj | 17:c9afe1a7b423 | 125 | return colors[colorID]; |
dicarloj | 17:c9afe1a7b423 | 126 | } |
dicarloj | 17:c9afe1a7b423 | 127 | |
dicarloj | 17:c9afe1a7b423 | 128 | // compute the address of the tile from the tile's index |
dicarloj | 17:c9afe1a7b423 | 129 | // this is confusing because depending on the tileData selected, |
dicarloj | 17:c9afe1a7b423 | 130 | // the tileIdx is either signed or unsigned |
dicarloj | 17:c9afe1a7b423 | 131 | u16 computeTileAddr(u8 tileIdx, bool tileData) { |
dicarloj | 17:c9afe1a7b423 | 132 | if(tileData) { |
dicarloj | 17:c9afe1a7b423 | 133 | return 0x8000 + 16 * tileIdx; |
dicarloj | 17:c9afe1a7b423 | 134 | } else { |
dicarloj | 17:c9afe1a7b423 | 135 | if(tileIdx <= 127) { |
dicarloj | 17:c9afe1a7b423 | 136 | return 0x9000 + 16 * tileIdx; |
dicarloj | 17:c9afe1a7b423 | 137 | } else { |
dicarloj | 17:c9afe1a7b423 | 138 | return 0x8000 + 16 * (tileIdx); |
dicarloj | 17:c9afe1a7b423 | 139 | } |
dicarloj | 17:c9afe1a7b423 | 140 | } |
dicarloj | 17:c9afe1a7b423 | 141 | } |
dicarloj | 17:c9afe1a7b423 | 142 | |
dicarloj | 17:c9afe1a7b423 | 143 | u8* computeTileAddrPtr(u8 tileIdx, bool tileData) { |
dicarloj | 17:c9afe1a7b423 | 144 | if(tileData) { |
dicarloj | 17:c9afe1a7b423 | 145 | return globalMemState.vram + 16 * tileIdx; |
dicarloj | 17:c9afe1a7b423 | 146 | } else { |
dicarloj | 17:c9afe1a7b423 | 147 | if(tileIdx <= 127) { |
dicarloj | 17:c9afe1a7b423 | 148 | return globalMemState.vram + 0x1000 + 16 * tileIdx; |
dicarloj | 17:c9afe1a7b423 | 149 | } else { |
dicarloj | 17:c9afe1a7b423 | 150 | return globalMemState.vram + 16 * (tileIdx); |
dicarloj | 17:c9afe1a7b423 | 151 | } |
dicarloj | 17:c9afe1a7b423 | 152 | } |
dicarloj | 17:c9afe1a7b423 | 153 | } |
dicarloj | 17:c9afe1a7b423 | 154 | |
dicarloj | 17:c9afe1a7b423 | 155 | // main function to render a line of the display. |
dicarloj | 17:c9afe1a7b423 | 156 | // this implementation is missing a number of things, including (but not limited to) |
dicarloj | 17:c9afe1a7b423 | 157 | // -- proper position of the WINDOW |
dicarloj | 17:c9afe1a7b423 | 158 | // -- 16x8 sprites |
dicarloj | 17:c9afe1a7b423 | 159 | // -- sprite sorting |
dicarloj | 17:c9afe1a7b423 | 160 | // -- 10 sprite limit |
dicarloj | 17:c9afe1a7b423 | 161 | void renderLine() { |
dicarloj | 17:c9afe1a7b423 | 162 | if(frameCount % 3) return; |
dicarloj | 17:c9afe1a7b423 | 163 | //return; |
dicarloj | 17:c9afe1a7b423 | 164 | //if(drawing) return; |
dicarloj | 17:c9afe1a7b423 | 165 | //printf("%x %x\n", im_line_va, im_back); |
dicarloj | 17:c9afe1a7b423 | 166 | globalVideoState.frameBuffer = im_line_vas[bufferSelect]; |
dicarloj | 17:c9afe1a7b423 | 167 | u8 lcdc = globalMemState.ioRegs[IO_LCDC]; // lcd control register |
dicarloj | 17:c9afe1a7b423 | 168 | bool lcdOn = (lcdc >> 7) & (u8)1; // lcd display on? |
dicarloj | 17:c9afe1a7b423 | 169 | bool windowTileMap = (lcdc >> 6) & (u8)1; // select tilemap source for window |
dicarloj | 17:c9afe1a7b423 | 170 | bool windowEnable = (lcdc >> 5) & (u8)1; // draw window? |
dicarloj | 17:c9afe1a7b423 | 171 | bool tileData = (lcdc >> 4) & (u8)1; // select tile data source |
dicarloj | 17:c9afe1a7b423 | 172 | bool bgTileMap = (lcdc >> 3) & (u8)1; // select tilemap source for background |
dicarloj | 17:c9afe1a7b423 | 173 | bool objSize = (lcdc >> 2) & (u8)1; // pick sprite size (nyi) |
dicarloj | 17:c9afe1a7b423 | 174 | bool objEnable = (lcdc >> 1) & (u8)1; // enable sprite renderer |
dicarloj | 17:c9afe1a7b423 | 175 | bool bgWinEnable = (lcdc >> 0) & (u8)1; // enable background and window renderer? |
dicarloj | 17:c9afe1a7b423 | 176 | |
dicarloj | 17:c9afe1a7b423 | 177 | u16 windowMapAddr = (u16)(windowTileMap ? 0x9c00 : 0x9800); |
dicarloj | 17:c9afe1a7b423 | 178 | u16 bgTileMapAddr = (u16)(bgTileMap ? 0x9c00 : 0x9800); |
dicarloj | 17:c9afe1a7b423 | 179 | |
dicarloj | 17:c9afe1a7b423 | 180 | // background renderer |
dicarloj | 17:c9afe1a7b423 | 181 | if(lcdOn && bgWinEnable) { |
dicarloj | 17:c9afe1a7b423 | 182 | // render background onto framebuffer |
dicarloj | 17:c9afe1a7b423 | 183 | u8 pal = globalMemState.ioRegs[IO_BGP]; // color palette |
dicarloj | 17:c9afe1a7b423 | 184 | u16 tileMapRowAddr = (u16)(bgTileMapAddr + 32*((((u16)globalVideoState.line + |
dicarloj | 17:c9afe1a7b423 | 185 | globalMemState.ioRegs[IO_SCROLLY]) & (u16)255) >> 3)); // address of the row of the tilemap |
dicarloj | 17:c9afe1a7b423 | 186 | u8 tileMapColIdx = globalMemState.ioRegs[IO_SCROLLX] >> 3; // column index of the tilemap |
dicarloj | 17:c9afe1a7b423 | 187 | u8 yPixOffset = ((u8)globalVideoState.line + globalMemState.ioRegs[IO_SCROLLY]) & (u8)7; // y-pixel of tile |
dicarloj | 17:c9afe1a7b423 | 188 | u8 xPixOffset = globalMemState.ioRegs[IO_SCROLLX] & (u8)7; // x-pixel of tile |
dicarloj | 17:c9afe1a7b423 | 189 | //u8* linePtr = globalVideoState.frameBuffer + 160 * globalVideoState.line; // frame buffer pointer |
dicarloj | 17:c9afe1a7b423 | 190 | u8 tileIdx = readByte(tileMapRowAddr + tileMapColIdx); // tile index |
dicarloj | 17:c9afe1a7b423 | 191 | |
dicarloj | 17:c9afe1a7b423 | 192 | // loop over pixels in the line |
dicarloj | 17:c9afe1a7b423 | 193 | for(u8 px = 0; px < 160; px++) { |
dicarloj | 17:c9afe1a7b423 | 194 | globalVideoState.frameBuffer = im_line_vas[bufferSelect]; |
dicarloj | 17:c9afe1a7b423 | 195 | globalVideoState.frameBuffer[xy2px(px,globalVideoState.line)] = |
dicarloj | 17:c9afe1a7b423 | 196 | readTilePtr(computeTileAddrPtr(tileIdx, tileData), xPixOffset, yPixOffset, pal); |
dicarloj | 17:c9afe1a7b423 | 197 | //readTile(computeTileAddr(tileIdx, tileData), xPixOffset, yPixOffset, pal); // set the frame buffer |
dicarloj | 17:c9afe1a7b423 | 198 | |
dicarloj | 17:c9afe1a7b423 | 199 | xPixOffset++; // increment tile pixel |
dicarloj | 17:c9afe1a7b423 | 200 | //linePtr++; // increment frame buffer pixel |
dicarloj | 17:c9afe1a7b423 | 201 | if(xPixOffset == 8) { // if we have overflowed the tile |
dicarloj | 17:c9afe1a7b423 | 202 | xPixOffset = 0; // go to the beginning |
dicarloj | 17:c9afe1a7b423 | 203 | tileMapColIdx = (tileMapColIdx + 1) & 31; // of the next tile (allow wraparound) |
dicarloj | 17:c9afe1a7b423 | 204 | tileIdx = readByte(tileMapRowAddr + tileMapColIdx); // and look up the tile index in the tile map |
dicarloj | 17:c9afe1a7b423 | 205 | } |
dicarloj | 17:c9afe1a7b423 | 206 | } |
dicarloj | 17:c9afe1a7b423 | 207 | } |
dicarloj | 17:c9afe1a7b423 | 208 | |
dicarloj | 17:c9afe1a7b423 | 209 | // // window renderer |
dicarloj | 17:c9afe1a7b423 | 210 | // if(windowEnable) { |
dicarloj | 17:c9afe1a7b423 | 211 | // u8 pal = globalMemState.ioRegs[IO_BGP]; // palette |
dicarloj | 17:c9afe1a7b423 | 212 | // u8 wx = globalMemState.ioRegs[IO_WINX]; // location of the window (nyi) |
dicarloj | 17:c9afe1a7b423 | 213 | // u8 wy = globalMemState.ioRegs[IO_WINY]; // location of the window (nyi) |
dicarloj | 17:c9afe1a7b423 | 214 | // if(wx > 166 || wy > 143) { |
dicarloj | 17:c9afe1a7b423 | 215 | // // if the window is out of this range, it is disabled too. |
dicarloj | 17:c9afe1a7b423 | 216 | // } else { |
dicarloj | 17:c9afe1a7b423 | 217 | // u16 tileMapRowAddr = windowMapAddr + 32*((((u16)globalVideoState.line)) >> 3); // address of the row of the tilemap |
dicarloj | 17:c9afe1a7b423 | 218 | // u8 tileMapColIdx = 0; // column index of the tilemap |
dicarloj | 17:c9afe1a7b423 | 219 | // u8 yPixOffset = ((u8)globalVideoState.line) & (u8)7; // y-pixel of tile |
dicarloj | 17:c9afe1a7b423 | 220 | // u8 xPixOffset = 0; // x-pixel of tile |
dicarloj | 17:c9afe1a7b423 | 221 | // u8* linePtr = globalVideoState.frameBuffer + 160 * globalVideoState.line; // frame buffer pointer |
dicarloj | 17:c9afe1a7b423 | 222 | // u8 tileIdx = readByte(tileMapRowAddr + tileMapColIdx); // tile index |
dicarloj | 17:c9afe1a7b423 | 223 | // |
dicarloj | 17:c9afe1a7b423 | 224 | // // loop over pixels in the line |
dicarloj | 17:c9afe1a7b423 | 225 | // for(u8 px = 0; px < 160; px++) { |
dicarloj | 17:c9afe1a7b423 | 226 | // *linePtr = readTile(computeTileAddr(tileIdx, tileData), xPixOffset, yPixOffset, pal); // set the frame buffer |
dicarloj | 17:c9afe1a7b423 | 227 | // |
dicarloj | 17:c9afe1a7b423 | 228 | // xPixOffset++; // increment tile pixel |
dicarloj | 17:c9afe1a7b423 | 229 | // linePtr++; // increment frame buffer pixel |
dicarloj | 17:c9afe1a7b423 | 230 | // if(xPixOffset == 8) { // if we have overflowed the tile |
dicarloj | 17:c9afe1a7b423 | 231 | // xPixOffset = 0; // go to the beginning |
dicarloj | 17:c9afe1a7b423 | 232 | // tileMapColIdx = (tileMapColIdx + 1) & 31; // of the next tile (allow wraparound, but it shouldn't happen?) |
dicarloj | 17:c9afe1a7b423 | 233 | // tileIdx = readByte(tileMapRowAddr + tileMapColIdx); // and look up the tile index in the tile map |
dicarloj | 17:c9afe1a7b423 | 234 | // } |
dicarloj | 17:c9afe1a7b423 | 235 | // } |
dicarloj | 17:c9afe1a7b423 | 236 | // } |
dicarloj | 17:c9afe1a7b423 | 237 | // } |
dicarloj | 17:c9afe1a7b423 | 238 | // |
dicarloj | 17:c9afe1a7b423 | 239 | // |
dicarloj | 17:c9afe1a7b423 | 240 | // sprite renderer |
dicarloj | 17:c9afe1a7b423 | 241 | if(objEnable) { |
dicarloj | 17:c9afe1a7b423 | 242 | for(u16 spriteID = 0; spriteID < 40; spriteID++) { |
dicarloj | 17:c9afe1a7b423 | 243 | u16 oamPtr = 0xfe00 + 4 * spriteID; // sprite information table |
dicarloj | 17:c9afe1a7b423 | 244 | u8 spriteY = readByte(oamPtr); // y-coordinate of sprite |
dicarloj | 17:c9afe1a7b423 | 245 | u8 spriteX = readByte(oamPtr + 1); // x-coordinate of sprite |
dicarloj | 17:c9afe1a7b423 | 246 | u8 patternIdx = readByte(oamPtr + 2); // sprite pattern |
dicarloj | 17:c9afe1a7b423 | 247 | u8 flags = readByte(oamPtr + 3); // flag bits |
dicarloj | 17:c9afe1a7b423 | 248 | |
dicarloj | 17:c9afe1a7b423 | 249 | bool pri = (flags >> 7) & (u8)1; // priority (transparency stuff) |
dicarloj | 17:c9afe1a7b423 | 250 | bool yFlip = (flags >> 6) & (u8)1; // flip around y? |
dicarloj | 17:c9afe1a7b423 | 251 | bool xFlip = (flags >> 5) & (u8)1; // flip around x? |
dicarloj | 17:c9afe1a7b423 | 252 | bool palID = (flags >> 4) & (u8)1; // palette ID (OBP0/OBP2) |
dicarloj | 17:c9afe1a7b423 | 253 | |
dicarloj | 17:c9afe1a7b423 | 254 | u8 pal = palID ? globalMemState.ioRegs[IO_OBP1] : globalMemState.ioRegs[IO_OBP0]; |
dicarloj | 17:c9afe1a7b423 | 255 | |
dicarloj | 17:c9afe1a7b423 | 256 | |
dicarloj | 17:c9afe1a7b423 | 257 | if(spriteX | spriteY) { |
dicarloj | 17:c9afe1a7b423 | 258 | // the sprite coordinates have an offset |
dicarloj | 17:c9afe1a7b423 | 259 | u8 spriteStartY = spriteY - 16; |
dicarloj | 17:c9afe1a7b423 | 260 | u8 spriteLastY = spriteStartY + 8; // todo 16 row sprites |
dicarloj | 17:c9afe1a7b423 | 261 | |
dicarloj | 17:c9afe1a7b423 | 262 | // reject based on y if the sprite won't be visible in the current line |
dicarloj | 17:c9afe1a7b423 | 263 | if(globalVideoState.line < spriteStartY || globalVideoState.line >= spriteLastY) { |
dicarloj | 17:c9afe1a7b423 | 264 | continue; |
dicarloj | 17:c9afe1a7b423 | 265 | } |
dicarloj | 17:c9afe1a7b423 | 266 | |
dicarloj | 17:c9afe1a7b423 | 267 | // get y px relative to the sprite pattern |
dicarloj | 17:c9afe1a7b423 | 268 | u8 tileY = globalVideoState.line - spriteStartY; |
dicarloj | 17:c9afe1a7b423 | 269 | if(yFlip) { |
dicarloj | 17:c9afe1a7b423 | 270 | tileY = 7 - tileY; |
dicarloj | 17:c9afe1a7b423 | 271 | } |
dicarloj | 17:c9afe1a7b423 | 272 | |
dicarloj | 17:c9afe1a7b423 | 273 | assert(tileY < 8); |
dicarloj | 17:c9afe1a7b423 | 274 | |
dicarloj | 17:c9afe1a7b423 | 275 | // loop over the 8 pixels that the sprite is on: |
dicarloj | 17:c9afe1a7b423 | 276 | for(u8 tileX = 0; tileX < 8; tileX++) { |
dicarloj | 17:c9afe1a7b423 | 277 | |
dicarloj | 17:c9afe1a7b423 | 278 | u8 xPos = spriteX - 8 + tileX; // position on the screen |
dicarloj | 17:c9afe1a7b423 | 279 | if(xPos >= 160) continue; // reject if we go off the end, don't wrap around |
dicarloj | 17:c9afe1a7b423 | 280 | |
dicarloj | 17:c9afe1a7b423 | 281 | u32 fbIdx = xy2px(xPos, globalVideoState.line); |
dicarloj | 17:c9afe1a7b423 | 282 | |
dicarloj | 17:c9afe1a7b423 | 283 | globalVideoState.frameBuffer = im_line_vas[bufferSelect]; |
dicarloj | 17:c9afe1a7b423 | 284 | |
dicarloj | 17:c9afe1a7b423 | 285 | |
dicarloj | 17:c9afe1a7b423 | 286 | // current color at the screen |
dicarloj | 17:c9afe1a7b423 | 287 | u8 old = globalVideoState.frameBuffer[fbIdx]; |
dicarloj | 17:c9afe1a7b423 | 288 | |
dicarloj | 17:c9afe1a7b423 | 289 | // get the pixel from the sprite pattern data |
dicarloj | 17:c9afe1a7b423 | 290 | u8 tileLookupX = tileX; |
dicarloj | 17:c9afe1a7b423 | 291 | if(xFlip) { |
dicarloj | 17:c9afe1a7b423 | 292 | tileLookupX = 7 - tileX; |
dicarloj | 17:c9afe1a7b423 | 293 | } |
dicarloj | 17:c9afe1a7b423 | 294 | //u8 tileValue = readSpriteTile(0x8000 + patternIdx * 16, tileLookupX, tileY, pal); |
dicarloj | 17:c9afe1a7b423 | 295 | u8 tileValue = readSpriteTileAddr(globalMemState.vram + patternIdx * 16, tileLookupX, tileY, pal); |
dicarloj | 17:c9afe1a7b423 | 296 | //u8 tileValue = readSpriteTileAddr(globalMemState.vram + patternIdx*16, tileLookupX, tileY, pal); |
dicarloj | 17:c9afe1a7b423 | 297 | //u8 tileValue = 4; |
dicarloj | 17:c9afe1a7b423 | 298 | // don't draw transparent |
dicarloj | 17:c9afe1a7b423 | 299 | if(tileValue == TRANSPARENT_SPRITE) continue; // (transparent sprites) |
dicarloj | 17:c9afe1a7b423 | 300 | |
dicarloj | 17:c9afe1a7b423 | 301 | // not sure this is 100% right... |
dicarloj | 17:c9afe1a7b423 | 302 | if(!pri) { |
dicarloj | 17:c9afe1a7b423 | 303 | globalVideoState.frameBuffer[fbIdx] = tileValue; |
dicarloj | 17:c9afe1a7b423 | 304 | } else { |
dicarloj | 17:c9afe1a7b423 | 305 | if(old == BRIGHTEST_COLOR) { |
dicarloj | 17:c9afe1a7b423 | 306 | globalVideoState.frameBuffer[fbIdx] = tileValue; |
dicarloj | 17:c9afe1a7b423 | 307 | } |
dicarloj | 17:c9afe1a7b423 | 308 | } |
dicarloj | 17:c9afe1a7b423 | 309 | } |
dicarloj | 17:c9afe1a7b423 | 310 | } |
dicarloj | 17:c9afe1a7b423 | 311 | } |
dicarloj | 17:c9afe1a7b423 | 312 | } |
dicarloj | 17:c9afe1a7b423 | 313 | } |
dicarloj | 17:c9afe1a7b423 | 314 | |
dicarloj | 17:c9afe1a7b423 | 315 | static u32 oldTics = 0; |
dicarloj | 17:c9afe1a7b423 | 316 | float filteredFrameRate = 0.f; |
dicarloj | 17:c9afe1a7b423 | 317 | |
dicarloj | 17:c9afe1a7b423 | 318 | // step the video by a number of clock cycles |
dicarloj | 17:c9afe1a7b423 | 319 | void stepVideo(u32 cycles) { |
dicarloj | 17:c9afe1a7b423 | 320 | globalVideoState.modeClock += cycles; |
dicarloj | 17:c9afe1a7b423 | 321 | switch(globalVideoState.mode) { |
dicarloj | 17:c9afe1a7b423 | 322 | case 2: // OAM read, scanning |
dicarloj | 17:c9afe1a7b423 | 323 | |
dicarloj | 17:c9afe1a7b423 | 324 | if(globalVideoState.modeClock >= 80) { |
dicarloj | 17:c9afe1a7b423 | 325 | globalVideoState.modeClock = 0; |
dicarloj | 17:c9afe1a7b423 | 326 | globalVideoState.mode = 3; // VRAM read, scanning |
dicarloj | 17:c9afe1a7b423 | 327 | } |
dicarloj | 17:c9afe1a7b423 | 328 | break; |
dicarloj | 17:c9afe1a7b423 | 329 | case 3: // VRAM read, scanning |
dicarloj | 17:c9afe1a7b423 | 330 | if(globalVideoState.modeClock >= 172) { |
dicarloj | 17:c9afe1a7b423 | 331 | globalVideoState.modeClock = 0; |
dicarloj | 17:c9afe1a7b423 | 332 | globalVideoState.mode = 0; // hblank |
dicarloj | 17:c9afe1a7b423 | 333 | renderLine(); // draw line into framebuffer |
dicarloj | 17:c9afe1a7b423 | 334 | } |
dicarloj | 17:c9afe1a7b423 | 335 | break; |
dicarloj | 17:c9afe1a7b423 | 336 | case 0: // hblank |
dicarloj | 17:c9afe1a7b423 | 337 | if(globalVideoState.modeClock >= 204) { |
dicarloj | 17:c9afe1a7b423 | 338 | globalVideoState.modeClock = 0; |
dicarloj | 17:c9afe1a7b423 | 339 | globalVideoState.line++; |
dicarloj | 17:c9afe1a7b423 | 340 | |
dicarloj | 17:c9afe1a7b423 | 341 | if(globalVideoState.line == 143) { |
dicarloj | 17:c9afe1a7b423 | 342 | |
dicarloj | 17:c9afe1a7b423 | 343 | globalVideoState.mode = 1; // vblank |
dicarloj | 17:c9afe1a7b423 | 344 | globalMemState.ioRegs[IO_IF] |= 0x1; // set interrupt for vblank |
dicarloj | 17:c9afe1a7b423 | 345 | if(!keyboard.turbo) // if we are in "turbo" mode, don't update graphics |
dicarloj | 17:c9afe1a7b423 | 346 | updateGraphics(); // display framebuffer on screen |
dicarloj | 17:c9afe1a7b423 | 347 | //bufferSelect = !bufferSelect; |
dicarloj | 17:c9afe1a7b423 | 348 | |
dicarloj | 17:c9afe1a7b423 | 349 | |
dicarloj | 17:c9afe1a7b423 | 350 | u32 dTics = tics - oldTics; |
dicarloj | 17:c9afe1a7b423 | 351 | float frameTime = 64.0e-6 * (float)dTics; |
dicarloj | 17:c9afe1a7b423 | 352 | filteredFrameRate = (0.94 * filteredFrameRate) + (0.06 * frameTime); |
dicarloj | 17:c9afe1a7b423 | 353 | printf("%d %f %f\n", dTics, 1.f/frameTime, 1.f/filteredFrameRate); |
dicarloj | 17:c9afe1a7b423 | 354 | frameCount++; |
dicarloj | 17:c9afe1a7b423 | 355 | oldTics = tics; |
dicarloj | 17:c9afe1a7b423 | 356 | |
dicarloj | 17:c9afe1a7b423 | 357 | } else { |
dicarloj | 17:c9afe1a7b423 | 358 | globalVideoState.mode = 2; // oam |
dicarloj | 17:c9afe1a7b423 | 359 | } |
dicarloj | 17:c9afe1a7b423 | 360 | } |
dicarloj | 17:c9afe1a7b423 | 361 | break; |
dicarloj | 17:c9afe1a7b423 | 362 | case 1: // vblank |
dicarloj | 17:c9afe1a7b423 | 363 | if(globalVideoState.modeClock >= 456) { |
dicarloj | 17:c9afe1a7b423 | 364 | globalVideoState.modeClock = 0; |
dicarloj | 17:c9afe1a7b423 | 365 | globalVideoState.line++; |
dicarloj | 17:c9afe1a7b423 | 366 | |
dicarloj | 17:c9afe1a7b423 | 367 | if(globalVideoState.line > 153) { |
dicarloj | 17:c9afe1a7b423 | 368 | globalVideoState.mode = 2; |
dicarloj | 17:c9afe1a7b423 | 369 | globalVideoState.line = 0; |
dicarloj | 17:c9afe1a7b423 | 370 | } |
dicarloj | 17:c9afe1a7b423 | 371 | } |
dicarloj | 17:c9afe1a7b423 | 372 | break; |
dicarloj | 17:c9afe1a7b423 | 373 | default: |
dicarloj | 17:c9afe1a7b423 | 374 | assert(false); |
dicarloj | 17:c9afe1a7b423 | 375 | } |
dicarloj | 17:c9afe1a7b423 | 376 | |
dicarloj | 17:c9afe1a7b423 | 377 | globalMemState.ioRegs[IO_LY] = (u8)globalVideoState.line; // update current line |
dicarloj | 17:c9afe1a7b423 | 378 | |
dicarloj | 17:c9afe1a7b423 | 379 | |
dicarloj | 17:c9afe1a7b423 | 380 | // this is likely somewhat wrong. |
dicarloj | 17:c9afe1a7b423 | 381 | u8 stat = globalMemState.ioRegs[IO_STAT]; // update STAT register (this is likely the source of issue on bubble bobble) |
dicarloj | 17:c9afe1a7b423 | 382 | stat &= ~7; // clear mode, coincidence |
dicarloj | 17:c9afe1a7b423 | 383 | stat += globalVideoState.mode; // set current mode |
dicarloj | 17:c9afe1a7b423 | 384 | if(globalMemState.ioRegs[IO_LYC] == globalMemState.ioRegs[IO_LY]) { // check coincidence |
dicarloj | 17:c9afe1a7b423 | 385 | stat += 4; |
dicarloj | 17:c9afe1a7b423 | 386 | if((stat >> 6) & 1) { |
dicarloj | 17:c9afe1a7b423 | 387 | globalMemState.ioRegs[IO_IF] |= 2; // stat interrupt |
dicarloj | 17:c9afe1a7b423 | 388 | } |
dicarloj | 17:c9afe1a7b423 | 389 | } |
dicarloj | 17:c9afe1a7b423 | 390 | } |