gameboy wormboy manboy gameworm gameman wormgame mangame manworm

Dependencies:   mbed SDFileSystem2

Committer:
dicarloj
Date:
Sun Jan 13 19:00:10 2019 +0000
Revision:
17:c9afe1a7b423
a

Who changed what in which revision?

UserRevisionLine numberNew 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 }