Code: Select all
private static final int MODE0 = 3;
private static final int MODE1 = 4;
private static final int MODE2 = 5;
private static final int LCDC_BGDISPLAY = 0;
private static final int LCDC_OBJENABLE = 1;
private static final int LCDC_OBJ_SPRIT = 2; //Bit 2 - OBJ (Sprite) Size (0=8x8, 1=8x16)
private static final int LCDC_BG_TILE_M = 3; //Bit 3 - BG Tile Map Display Select (0=9800-9BFF, 1=9C00-9FFF)
private static final int LCDC_BG_TILE_S = 4; //Bit 4 - BG & Window Tile Data Select (0=8800-97FF, 1=8000-8FFF)
private static final int LCDC_WINDOW_EN = 5; //Bit 5 - Window Display Enable (0=Off, 1=On)
private static final int LCDC_WINDOW_SE = 6; //Bit 6 - Window Tile Map Display Select (0=9800-9BFF, 1=9C00-9FFF)
public void updateGraphics(int cycles){
setLCDStatus();
if(isLCDEnabled())
scanlineCounter -= cycles;
else
return;
if(scanlineCounter <= 0){
int currentLine = read_reg(0xFF44); // LY
scanlineCounter = 456;
if(currentLine == 144){ // V-Blank periodo
MMU.getInstance().requestInterrupter(0);
}else if(currentLine > 153){ // se passou de 153, reset to 0
gpu_registros[4] = 0;
}else if( currentLine < 144){
drawScanLine() ;
}
gpu_registros[4] += 1;
}
}
///////////////////////////////////////////////////////////////////////////////////////
private void drawScanLine() {
int control = read_reg(0xFF40); // 0xFF40
if(testBit(control, LCDC_BGDISPLAY))
renderTiles(control);
if(testBit(control, LCDC_OBJENABLE))
renderSprites();
}
//////////////////////////////////////////////////////////////////////
private void renderTiles(int c){
int tileData = 0;
int backgroundMemory = 0;
boolean using = true;
int lcdControl = c;
// onde vou desenhar o visual area no window
int scrollY = read_reg(0xFF42); // SCY
int scrollX = read_reg(0xFF43); // SCX
int windowY = read_reg(0xff4A); // WY
int windowX = read_reg(0xFF4B) - 7; // WX
boolean usingWindow = false;
// background enable
if(testBit(lcdControl, LCDC_WINDOW_EN)){ // Bit 5 - Window Display Enable (0=Off, 1=On)
// is the current scanline we're drawing within the windows Y pos?,
if(windowY <= read_reg(0xFF44))
usingWindow = true;
}
// qual tile data estamos usando?
if(testBit(lcdControl, LCDC_BG_TILE_S)){
tileData = 0x8000;
}else{
// IMPORTANT: This memory region uses signed bytes as tile identifiers
tileData = 0x8800;
using = false;
}
// qual background regiao da memoria?
if(false == usingWindow){
if(testBit(lcdControl, LCDC_BG_TILE_M))
backgroundMemory = 0x9C00;
else
backgroundMemory = 0x9800;
}else{
// qual window memory
if(testBit(lcdControl, LCDC_WINDOW_SE))
backgroundMemory = 0x9C00;
else
backgroundMemory = 0x9800;
}
int yPos = 0;
//yPos é usado para calcular qual de 32 vertcal tiles o corrente scanline esta desenhando
if(!usingWindow)
yPos = scrollY + read_reg(0xFF44);
else
yPos = read_reg(0xFF44) - windowY;
// qual dos 8 vertical pixels do corrente tile esta no scanline
int tileRow = (yPos / 8)*32;
//hora de iniciar a desenhar a 160 horizontal pixels para este scanline
for(int pixel = 0; pixel < 160; pixel++){
int xPos = pixel + scrollX;
// traduz o corrente X pos para a janela de space se necessario
if(usingWindow){
if(pixel >= windowX){
xPos = pixel - windowX;
}
}
// qual dos 32 horizontais tiles este xPos esta dentro
int tileCol = (xPos / 8);
int tileNumUnsigned=0;
byte tileNumSigned=0;
// get the tile identity number. Remember it can be signed or unsigned
int tileAddress = backgroundMemory + tileRow + tileCol;
if(using){
tileNumUnsigned = MMU.getInstance().readByte(tileAddress); // signed
}else{
tileNumSigned = (byte)MMU.getInstance().readByte(tileAddress); // unsigned
}
// deduce where this tile identifier is in memory.
int tileLocation = tileData;
if(using)
tileLocation += (tileNumUnsigned * 16);
else
tileLocation += (tileNumSigned + 128) * 16;
// find the correct vertical line we're on of the tile to get the tile data from in memory
int line = yPos % 8;
line *= 2;// each vertical line takes up two bytes of memory
int data1 = MMU.getInstance().readByte(tileLocation + line) ;
int data2 = MMU.getInstance().readByte(tileLocation + line + 1) ;
// pixel 0 in the tile is it 7 of data 1 and data2. Pixel 1 is bit 6 etc..
int colourBit = xPos % 8 ;
colourBit -= 7 ;
colourBit *= -1 ;
// combine data 2 and data 1 to get the colour id for this pixel in the tile
int colourNum = testBit(data2,colourBit) ? 1:0 ;
colourNum <<= 1;
colourNum |= testBit(data1,colourBit) ? 1:0 ;
// now we have the colour id get the actual colour from palette 0xFF47
int col = GetColour(colourNum, 0xFF47) ;
int red = 0;
int green = 0;
int blue = 0;
switch(col){
case 0: red = 0xFF; green = 0xFF ; blue = 0xFF; break ;
case 1: red = 0xCC; green = 0xCC ; blue = 0xCC; break ;
case 2: red = 0x77; green = 0x77 ; blue = 0x77; break ;
}
int finaly = read_reg(0xFF44);
if ((finaly<0)||(finaly>143)||(pixel<0)||(pixel>159))
{
return;
}
screenData[pixel][finaly][0] = red;
screenData[pixel][finaly][1] = green;
screenData[pixel][finaly][2] = blue;
}
}
/////////////////////////////////////////////////////////////////////////
public void setLCDStatus(){
int status = read_reg(0xFF41);
if(isLCDEnabled() == false){
// set o modo para 1 durante lcd disable. e reset scanline
scanlineCounter = 456; // reset scanline
gpu_registros[4] = 0; // scanline = 0;
status &= 252;
status = setBit(status, 0);
write_reg(0xFF41, status); // set interrupter H-Blank
}
int currentLine = read_reg(0xFF44);
int currentMode = status & 0x3;
int mode = 0;
boolean reqInt = false;
// se estiver em V-Blank, então set MODO para 1
if(currentLine >= 144){
mode = 1;
status = setBit(status, 0);
status = resetBit(status, 1);
reqInt = testBit(status, MODE1);
}else{
int mode2bounds = 456-80;
int mode3bounds = mode2bounds - 172;
// MODO 2
if(scanlineCounter >= mode2bounds){
mode = 2;
status = setBit(status, 1); // enable V-Blank
status = resetBit(status, 0);// disable H-Blank
reqInt = testBit(status, MODE2);
}// MODO 3
else if(scanlineCounter >= mode3bounds){
mode = 3;
status = setBit(status, 1);
status = setBit(status, 0);
}//MODO 0
else{
mode = 0;
status = resetBit(status, 1);
status = resetBit(status, 0);
reqInt = testBit(status, MODE0);
}
}
// entrada em um novo modo, então solicita Interrupt
if(reqInt && (mode != currentMode))
MMU.getInstance().requestInterrupter(1); // Bit 1: LCD Interupt
if(currentLine == read_reg(0xFF45)){
status = setBit(status, 2);
if(testBit(status, 6))
MMU.getInstance().requestInterrupter(1);
}else{
status = resetBit(status, 2);
}
write_reg(0xFF41, status);
}
thx!