GBHawk: performance optimizations

This commit is contained in:
alyosha-tas 2020-05-29 10:25:03 -04:00
parent 4494caac50
commit 527334f7e6
4 changed files with 258 additions and 262 deletions

View File

@ -59,6 +59,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
public int WAVE_decay_counter;
public bool WAVE_decay_done;
public bool sound_update_needed;
// Audio Variables
// derived
public bool WAVE_DAC_pow;
@ -625,8 +627,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
SQ1_output = DUTY_CYCLES[SQ1_duty * 8 + SQ1_duty_cntr] ? (SQ1_vol_state + DAC_OFST) : DAC_OFST;
// avoid aliasing at high frequenices
//if (SQ1_frq > 0x7F0) { SQ1_output = 0; }
sound_update_needed = true;
}
}
@ -642,8 +643,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
SQ2_output = DUTY_CYCLES[SQ2_duty * 8 + SQ2_duty_cntr] ? (SQ2_vol_state + DAC_OFST) : DAC_OFST;
// avoid aliasing at high frequenices
//if (SQ2_frq > 0x7F0) { SQ2_output = 0; }
sound_update_needed = true;
}
}
@ -687,6 +687,8 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
WAVE_wave_cntr++;
WAVE_wave_cntr &= 0x1F;
sample = Wave_RAM[WAVE_wave_cntr >> 1];
sound_update_needed = true;
}
}
else if (!WAVE_decay_done && (++WAVE_decay_counter == 200))
@ -696,18 +698,18 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
// wave state must decay slow enough that games that turn on and off the wave channel to fill wave RAM don't buzz too much
if (!WAVE_DAC_pow)
{
if (WAVE_output > 0) { WAVE_output--; }
if (WAVE_output > 0) { WAVE_output--; sound_update_needed = true; }
else { WAVE_decay_done = true; }
}
else
{
if (WAVE_output > DAC_OFST)
{
WAVE_output--;
WAVE_output--; sound_update_needed = true;
}
else if (WAVE_output < DAC_OFST)
{
WAVE_output++;
WAVE_output++; sound_update_needed = true;
}
else { WAVE_decay_done = true; }
}
@ -732,40 +734,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
}
NOISE_output = (NOISE_LFSR & 1) > 0 ? DAC_OFST : (NOISE_vol_state + DAC_OFST);
sound_update_needed = true;
}
}
// add up components to each channel
int L_final = 0;
int R_final = 0;
if (AUD_CTRL_sq1_L_en) { L_final += SQ1_output; }
if (AUD_CTRL_sq2_L_en) { L_final += SQ2_output; }
if (AUD_CTRL_wave_L_en) { L_final += WAVE_output;}
if (AUD_CTRL_noise_L_en) { L_final += NOISE_output; }
if (AUD_CTRL_sq1_R_en) { R_final += SQ1_output; }
if (AUD_CTRL_sq2_R_en) { R_final += SQ2_output; }
if (AUD_CTRL_wave_R_en) { R_final += WAVE_output; }
if (AUD_CTRL_noise_R_en) { R_final += NOISE_output; }
L_final *= (AUD_CTRL_vol_L + 1) * 40;
R_final *= (AUD_CTRL_vol_R + 1) * 40;
if (L_final != latched_sample_L)
{
_blip_L.AddDelta(master_audio_clock, L_final - latched_sample_L);
latched_sample_L = L_final;
}
if (R_final != latched_sample_R)
{
_blip_R.AddDelta(master_audio_clock, R_final - latched_sample_R);
latched_sample_R = R_final;
}
master_audio_clock++;
// frame sequencer ticks at a rate of 512 hz (or every time a 13 bit counter rolls over)
// the sequencer is actually the timer DIV register
// so if it's constantly written to, these values won't update
@ -867,12 +839,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
{
if (SQ1_env_add)
{
if (SQ1_vol_state < 15) { SQ1_vol_state++; }
if (SQ1_vol_state < 15) { SQ1_vol_state++; sound_update_needed = true; }
else { SQ1_vol_done = true; }
}
else
{
if (SQ1_vol_state >= 1) { SQ1_vol_state--; }
if (SQ1_vol_state >= 1) { SQ1_vol_state--; sound_update_needed = true; }
else { SQ1_vol_done = true; }
}
}
@ -889,12 +861,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
{
if (SQ2_env_add)
{
if (SQ2_vol_state < 15) { SQ2_vol_state++; }
if (SQ2_vol_state < 15) { SQ2_vol_state++; sound_update_needed = true; }
else { SQ2_vol_done = true; }
}
else
{
if (SQ2_vol_state >= 1) { SQ2_vol_state--; }
if (SQ2_vol_state >= 1) { SQ2_vol_state--; sound_update_needed = true; }
else { SQ2_vol_done = true; }
}
}
@ -911,12 +883,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
{
if (NOISE_env_add)
{
if (NOISE_vol_state < 15) { NOISE_vol_state++; }
if (NOISE_vol_state < 15) { NOISE_vol_state++; sound_update_needed = true; }
else { NOISE_vol_done = true; }
}
else
{
if (NOISE_vol_state >= 1) { NOISE_vol_state--; }
if (NOISE_vol_state >= 1) { NOISE_vol_state--; sound_update_needed = true; }
else { NOISE_vol_done = true; }
}
}
@ -946,6 +918,40 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
}
}
}
if (sound_update_needed)
{
// add up components to each channel
int L_final = 0;
int R_final = 0;
if (AUD_CTRL_sq1_L_en) { L_final += SQ1_output; }
if (AUD_CTRL_sq2_L_en) { L_final += SQ2_output; }
if (AUD_CTRL_wave_L_en) { L_final += WAVE_output; }
if (AUD_CTRL_noise_L_en) { L_final += NOISE_output; }
if (AUD_CTRL_sq1_R_en) { R_final += SQ1_output; }
if (AUD_CTRL_sq2_R_en) { R_final += SQ2_output; }
if (AUD_CTRL_wave_R_en) { R_final += WAVE_output; }
if (AUD_CTRL_noise_R_en) { R_final += NOISE_output; }
L_final *= (AUD_CTRL_vol_L + 1) * 40;
R_final *= (AUD_CTRL_vol_R + 1) * 40;
if (L_final != latched_sample_L)
{
_blip_L.AddDelta(master_audio_clock, L_final - latched_sample_L);
latched_sample_L = L_final;
}
if (R_final != latched_sample_R)
{
_blip_R.AddDelta(master_audio_clock, R_final - latched_sample_R);
latched_sample_R = R_final;
}
}
master_audio_clock++;
sound_update_needed = false;
}
public void power_off()
@ -1034,30 +1040,50 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
if (!NOISE_enable && ((Audio_Regs[NR42] & 0xF8) > 0)) { NOISE_output = DAC_OFST; }
else if ((Audio_Regs[NR42] & 0xF8) == 0) { NOISE_output = 0; }
sound_update_needed = true;
}
public void calculate_bias_gain_1()
{
if (!SQ1_enable && ((Audio_Regs[NR12] & 0xF8) > 0)) { SQ1_output = DAC_OFST; }
else if ((Audio_Regs[NR12] & 0xF8) == 0) { SQ1_output = 0; }
sound_update_needed = true;
}
public void calculate_bias_gain_2()
{
if (!SQ2_enable && ((Audio_Regs[NR22] & 0xF8) > 0)) { SQ2_output = DAC_OFST; }
else if ((Audio_Regs[NR22] & 0xF8) == 0) { SQ2_output = 0; }
sound_update_needed = true;
}
public void calculate_bias_gain_w()
{
if (!WAVE_enable && WAVE_DAC_pow) { WAVE_decay_counter = 0; WAVE_decay_done = false; }
else if (!WAVE_DAC_pow) { WAVE_decay_counter = 0; WAVE_decay_done = false; }
sound_update_needed = true;
}
public void calculate_bias_gain_n()
{
if (!NOISE_enable && ((Audio_Regs[NR42] & 0xF8) > 0)) { NOISE_output = DAC_OFST; }
else if ((Audio_Regs[NR42] & 0xF8) == 0) { NOISE_output = 0; }
sound_update_needed = true;
}
public void update_sound()
{
if (sound_update_needed)
{
}
}
public void SyncState(Serializer ser)
@ -1145,6 +1171,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
ser.Sync(nameof(WAVE_decay_counter), ref WAVE_decay_counter);
ser.Sync(nameof(WAVE_decay_done), ref WAVE_decay_done);
ser.Sync(nameof(sound_update_needed), ref sound_update_needed);
ser.Sync(nameof(master_audio_clock), ref master_audio_clock);
ser.Sync(nameof(sample), ref sample);

View File

@ -1374,6 +1374,39 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
}
public override void DMA_tick()
{
if (DMA_clock >= 4)
{
DMA_OAM_access = false;
if ((DMA_clock % 4) == 1)
{
// the cpu can't access memory during this time, but we still need the ppu to be able to.
DMA_start = false;
// Gekkio reports that A14 being high on DMA transfers always represent WRAM accesses
// So transfers nominally from higher memory areas are actually still from there (i.e. FF -> DF)
byte DMA_actual = DMA_addr;
if (DMA_addr > 0xDF) { DMA_actual &= 0xDF; }
DMA_byte = Core.ReadMemory((ushort)((DMA_actual << 8) + DMA_inc));
DMA_start = true;
}
else if ((DMA_clock % 4) == 3)
{
Core.OAM[DMA_inc] = DMA_byte;
if (DMA_inc < (0xA0 - 1)) { DMA_inc++; }
}
}
DMA_clock++;
if (DMA_clock == 648)
{
DMA_start = false;
DMA_OAM_access = true;
}
}
public override void process_sprite()
{
int y;
@ -1414,66 +1447,27 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
if (SL_sprites[sl_use_index * 4 + 3].Bit(5))
{
int b0, b1, b2, b3, b4, b5, b6, b7 = 0;
for (int i = 0; i < 2; i++)
{
b0 = (sprite_sel[i] & 0x01) << 7;
b1 = (sprite_sel[i] & 0x02) << 5;
b2 = (sprite_sel[i] & 0x04) << 3;
b3 = (sprite_sel[i] & 0x08) << 1;
b4 = (sprite_sel[i] & 0x10) >> 1;
b5 = (sprite_sel[i] & 0x20) >> 3;
b6 = (sprite_sel[i] & 0x40) >> 5;
b7 = (sprite_sel[i] & 0x80) >> 7;
sprite_sel[i] = (byte)(b0 | b1 | b2 | b3 | b4 | b5 | b6 | b7);
sprite_sel[i] = (byte)(((sprite_sel[i] & 0x01) << 7) |
((sprite_sel[i] & 0x02) << 5) |
((sprite_sel[i] & 0x04) << 3) |
((sprite_sel[i] & 0x08) << 1) |
((sprite_sel[i] & 0x10) >> 1) |
((sprite_sel[i] & 0x20) >> 3) |
((sprite_sel[i] & 0x40) >> 5) |
((sprite_sel[i] & 0x80) >> 7));
}
}
}
// normal DMA moves twice as fast in double speed mode on GBC
// So give it it's own function so we can seperate it from PPU tick
public override void DMA_tick()
{
if (DMA_clock >= 4)
{
DMA_OAM_access = false;
if ((DMA_clock % 4) == 1)
{
// the cpu can't access memory during this time, but we still need the ppu to be able to.
DMA_start = false;
// Gekkio reports that A14 being high on DMA transfers always represent WRAM accesses
// So transfers nominally from higher memory areas are actually still from there (i.e. FF -> DF)
byte DMA_actual = DMA_addr;
if (DMA_addr > 0xDF) { DMA_actual &= 0xDF; }
DMA_byte = Core.ReadMemory((ushort)((DMA_actual << 8) + DMA_inc));
DMA_start = true;
}
else if ((DMA_clock % 4) == 3)
{
Core.OAM[DMA_inc] = DMA_byte;
if (DMA_inc < (0xA0 - 1)) { DMA_inc++; }
}
}
DMA_clock++;
if (DMA_clock == 648)
{
DMA_start = false;
DMA_OAM_access = true;
}
}
// order sprites according to x coordinate
// order sprites according to x coordinate (in GB mode)
// note that for sprites of equal x coordinate, priority goes to first on the list
public override void reorder_and_assemble_sprites()
{
sprite_ordered_index = 0;
// In CGB mode, sprites are ordered solely based on their position in OAM, so they are already ordered
if (Core.GBC_compat)
{
for (int j = 0; j < SL_sprites_index; j++)
@ -1507,48 +1501,44 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
}
}
bool have_pixel = false;
byte s_pixel = 0;
byte sprite_attr = 0;
int low_bound = 0;
int high_bound = 0;
int t_index = 0;
for (int i = 0; i < 160; i++)
{
have_pixel = false;
for (int j = 0; j < SL_sprites_index; j++)
sprite_present_list[i] = 0;
}
for (int i = (SL_sprites_index - 1); i >= 0; i--)
{
if ((SL_sprites_ordered[i * 4] > 0) && ((SL_sprites_ordered[i * 4] - 8) < 160))
{
if ((i >= (SL_sprites_ordered[j * 4] - 8)) &&
(i < SL_sprites_ordered[j * 4]) &&
!have_pixel)
low_bound = (SL_sprites_ordered[i * 4] >= 8) ? 0 : (8 - SL_sprites_ordered[i * 4]);
high_bound = ((SL_sprites_ordered[i * 4] - 8) <= 152) ? 7 : (159 - (SL_sprites_ordered[i * 4] - 8));
for (int j = low_bound; j <= high_bound; j++)
{
// we can use the current sprite, so pick out a pixel for it
int t_index = i - (SL_sprites_ordered[j * 4] - 8);
t_index = 7 - j;
t_index = 7 - t_index;
sprite_data[0] = (byte)((SL_sprites_ordered[j * 4 + 1] >> t_index) & 1);
sprite_data[1] = (byte)(((SL_sprites_ordered[j * 4 + 2] >> t_index) & 1) << 1);
sprite_data[0] = (byte)((SL_sprites_ordered[i * 4 + 1] >> t_index) & 1);
sprite_data[1] = (byte)(((SL_sprites_ordered[i * 4 + 2] >> t_index) & 1) << 1);
s_pixel = (byte)(sprite_data[0] + sprite_data[1]);
sprite_attr = (byte)SL_sprites_ordered[j * 4 + 3];
sprite_attr = (byte)SL_sprites_ordered[i * 4 + 3];
// pixel color of 0 is transparent, so if this is the case we don't have a pixel
if (s_pixel != 0)
{
have_pixel = true;
sprite_present_list[SL_sprites_ordered[i * 4] - (8 - j)] = 1;
sprite_pixel_list[SL_sprites_ordered[i * 4] - (8 - j)] = s_pixel;
sprite_attr_list[SL_sprites_ordered[i * 4] - (8 - j)] = sprite_attr;
}
}
}
if (have_pixel)
{
sprite_present_list[i] = 1;
sprite_pixel_list[i] = s_pixel;
sprite_attr_list[i] = sprite_attr;
}
else
{
sprite_present_list[i] = 0;
}
}
}

View File

@ -1333,8 +1333,40 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
fetch_sprite = false;
}
}
}
}
public override void DMA_tick()
{
if (DMA_clock >= 4)
{
DMA_OAM_access = false;
if ((DMA_clock % 4) == 1)
{
// the cpu can't access memory during this time, but we still need the ppu to be able to.
DMA_start = false;
// Gekkio reports that A14 being high on DMA transfers always represent WRAM accesses
// So transfers nominally from higher memory areas are actually still from there (i.e. FF -> DF)
byte DMA_actual = DMA_addr;
if (DMA_addr > 0xDF) { DMA_actual &= 0xDF; }
DMA_byte = Core.ReadMemory((ushort)((DMA_actual << 8) + DMA_inc));
DMA_start = true;
}
else if ((DMA_clock % 4) == 3)
{
Core.OAM[DMA_inc] = DMA_byte;
if (DMA_inc < (0xA0 - 1)) { DMA_inc++; }
}
}
DMA_clock++;
if (DMA_clock == 648)
{
DMA_start = false;
DMA_OAM_access = true;
}
}
public override void process_sprite()
@ -1377,60 +1409,20 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
if (SL_sprites[sl_use_index * 4 + 3].Bit(5))
{
int b0, b1, b2, b3, b4, b5, b6, b7 = 0;
for (int i = 0; i < 2; i++)
{
b0 = (sprite_sel[i] & 0x01) << 7;
b1 = (sprite_sel[i] & 0x02) << 5;
b2 = (sprite_sel[i] & 0x04) << 3;
b3 = (sprite_sel[i] & 0x08) << 1;
b4 = (sprite_sel[i] & 0x10) >> 1;
b5 = (sprite_sel[i] & 0x20) >> 3;
b6 = (sprite_sel[i] & 0x40) >> 5;
b7 = (sprite_sel[i] & 0x80) >> 7;
sprite_sel[i] = (byte)(b0 | b1 | b2 | b3 | b4 | b5 | b6 | b7);
sprite_sel[i] = (byte)(((sprite_sel[i] & 0x01) << 7) |
((sprite_sel[i] & 0x02) << 5) |
((sprite_sel[i] & 0x04) << 3) |
((sprite_sel[i] & 0x08) << 1) |
((sprite_sel[i] & 0x10) >> 1) |
((sprite_sel[i] & 0x20) >> 3) |
((sprite_sel[i] & 0x40) >> 5) |
((sprite_sel[i] & 0x80) >> 7));
}
}
}
// normal DMA moves twice as fast in double speed mode on GBC
// So give it it's own function so we can seperate it from PPU tick
public override void DMA_tick()
{
if (DMA_clock >= 4)
{
DMA_OAM_access = false;
if ((DMA_clock % 4) == 1)
{
// the cpu can't access memory during this time, but we still need the ppu to be able to.
DMA_start = false;
// Gekkio reports that A14 being high on DMA transfers always represent WRAM accesses
// So transfers nominally from higher memory areas are actually still from there (i.e. FF -> DF)
byte DMA_actual = DMA_addr;
if (DMA_addr > 0xDF) { DMA_actual &= 0xDF; }
DMA_byte = Core.ReadMemory((ushort)((DMA_actual << 8) + DMA_inc));
DMA_start = true;
}
else if ((DMA_clock % 4) == 3)
{
Core.OAM[DMA_inc] = DMA_byte;
if (DMA_inc < (0xA0 - 1)) { DMA_inc++; }
}
}
DMA_clock++;
if (DMA_clock == 648)
{
DMA_start = false;
DMA_OAM_access = true;
}
}
// order sprites according to x coordinate
// note that for sprites of equal x coordinate, priority goes to first on the list
public override void reorder_and_assemble_sprites()
{
sprite_ordered_index = 0;
@ -1447,48 +1439,44 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
sprite_ordered_index++;
}
bool have_pixel = false;
byte s_pixel = 0;
byte sprite_attr = 0;
int low_bound = 0;
int high_bound = 0;
int t_index = 0;
for (int i = 0; i < 160; i++)
{
have_pixel = false;
for (int j = 0; j < SL_sprites_index; j++)
sprite_present_list[i] = 0;
}
for (int i = (SL_sprites_index - 1); i >= 0; i--)
{
if ((SL_sprites_ordered[i * 4] > 0) && ((SL_sprites_ordered[i * 4] - 8) < 160))
{
if ((i >= (SL_sprites_ordered[j * 4] - 8)) &&
(i < SL_sprites_ordered[j * 4]) &&
!have_pixel)
low_bound = (SL_sprites_ordered[i * 4] >= 8) ? 0 : (8 - SL_sprites_ordered[i * 4]);
high_bound = ((SL_sprites_ordered[i * 4] - 8) <= 152) ? 7 : (159 - (SL_sprites_ordered[i * 4] - 8));
for (int j = low_bound; j <= high_bound; j++)
{
// we can use the current sprite, so pick out a pixel for it
int t_index = i - (SL_sprites_ordered[j * 4] - 8);
t_index = 7 - j;
t_index = 7 - t_index;
sprite_data[0] = (byte)((SL_sprites_ordered[j * 4 + 1] >> t_index) & 1);
sprite_data[1] = (byte)(((SL_sprites_ordered[j * 4 + 2] >> t_index) & 1) << 1);
sprite_data[0] = (byte)((SL_sprites_ordered[i * 4 + 1] >> t_index) & 1);
sprite_data[1] = (byte)(((SL_sprites_ordered[i * 4 + 2] >> t_index) & 1) << 1);
s_pixel = (byte)(sprite_data[0] + sprite_data[1]);
sprite_attr = (byte)SL_sprites_ordered[j * 4 + 3];
sprite_attr = (byte)SL_sprites_ordered[i * 4 + 3];
// pixel color of 0 is transparent, so if this is the case we don't have a pixel
if (s_pixel != 0)
{
have_pixel = true;
sprite_present_list[SL_sprites_ordered[i * 4] - (8 - j)] = 1;
sprite_pixel_list[SL_sprites_ordered[i * 4] - (8 - j)] = s_pixel;
sprite_attr_list[SL_sprites_ordered[i * 4] - (8 - j)] = sprite_attr;
}
}
}
if (have_pixel)
{
sprite_present_list[i] = 1;
sprite_pixel_list[i] = s_pixel;
sprite_attr_list[i] = sprite_attr;
}
else
{
sprite_present_list[i] = 0;
}
}
}

View File

@ -984,6 +984,39 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
}
}
public override void DMA_tick()
{
if (DMA_clock >= 4)
{
DMA_OAM_access = false;
if ((DMA_clock % 4) == 1)
{
// the cpu can't access memory during this time, but we still need the ppu to be able to.
DMA_start = false;
// Gekkio reports that A14 being high on DMA transfers always represent WRAM accesses
// So transfers nominally from higher memory areas are actually still from there (i.e. FF -> DF)
byte DMA_actual = DMA_addr;
if (DMA_addr > 0xDF) { DMA_actual &= 0xDF; }
DMA_byte = Core.ReadMemory((ushort)((DMA_actual << 8) + DMA_inc));
DMA_start = true;
}
else if ((DMA_clock % 4) == 3)
{
Core.OAM[DMA_inc] = DMA_byte;
if (DMA_inc < (0xA0 - 1)) { DMA_inc++; }
}
}
DMA_clock++;
if (DMA_clock == 648)
{
DMA_start = false;
DMA_OAM_access = true;
}
}
public override void process_sprite()
{
int y;
@ -1023,58 +1056,20 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
if (SL_sprites[sl_use_index * 4 + 3].Bit(5))
{
int b0, b1, b2, b3, b4, b5, b6, b7 = 0;
for (int i = 0; i < 2; i++)
{
b0 = (sprite_sel[i] & 0x01) << 7;
b1 = (sprite_sel[i] & 0x02) << 5;
b2 = (sprite_sel[i] & 0x04) << 3;
b3 = (sprite_sel[i] & 0x08) << 1;
b4 = (sprite_sel[i] & 0x10) >> 1;
b5 = (sprite_sel[i] & 0x20) >> 3;
b6 = (sprite_sel[i] & 0x40) >> 5;
b7 = (sprite_sel[i] & 0x80) >> 7;
sprite_sel[i] = (byte)(b0 | b1 | b2 | b3 | b4 | b5 | b6 | b7);
sprite_sel[i] = (byte)(((sprite_sel[i] & 0x01) << 7) |
((sprite_sel[i] & 0x02) << 5) |
((sprite_sel[i] & 0x04) << 3) |
((sprite_sel[i] & 0x08) << 1) |
((sprite_sel[i] & 0x10) >> 1) |
((sprite_sel[i] & 0x20) >> 3) |
((sprite_sel[i] & 0x40) >> 5) |
((sprite_sel[i] & 0x80) >> 7));
}
}
}
// normal DMA moves twice as fast in double speed mode on GBC
// So give it it's own function so we can seperate it from PPU tick
public override void DMA_tick()
{
if (DMA_clock >= 4)
{
DMA_OAM_access = false;
if ((DMA_clock % 4) == 1)
{
// the cpu can't access memory during this time, but we still need the ppu to be able to.
DMA_start = false;
// Gekkio reports that A14 being high on DMA transfers always represent WRAM accesses
// So transfers nominally from higher memory areas are actually still from there (i.e. FF -> DF)
byte DMA_actual = DMA_addr;
if (DMA_addr > 0xDF) { DMA_actual &= 0xDF; }
DMA_byte = Core.ReadMemory((ushort)((DMA_actual << 8) + DMA_inc));
DMA_start = true;
}
else if ((DMA_clock % 4) == 3)
{
Core.OAM[DMA_inc] = DMA_byte;
if (DMA_inc < (0xA0 - 1)) { DMA_inc++; }
}
}
DMA_clock++;
if (DMA_clock == 648)
{
DMA_start = false;
DMA_OAM_access = true;
}
}
// order sprites according to x coordinate
// note that for sprites of equal x coordinate, priority goes to first on the list
public override void reorder_and_assemble_sprites()
@ -1098,48 +1093,44 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBHawk
}
}
bool have_pixel = false;
byte s_pixel = 0;
byte sprite_attr = 0;
int low_bound = 0;
int high_bound = 0;
int t_index = 0;
for (int i = 0; i < 160; i++)
{
have_pixel = false;
for (int j = 0; j < SL_sprites_index; j++)
sprite_present_list[i] = 0;
}
for (int i = (SL_sprites_index - 1); i >= 0; i--)
{
if ((SL_sprites_ordered[i * 4] > 0) && ((SL_sprites_ordered[i * 4] - 8) < 160))
{
if ((i >= (SL_sprites_ordered[j * 4] - 8)) &&
(i < SL_sprites_ordered[j * 4]) &&
!have_pixel)
low_bound = (SL_sprites_ordered[i * 4] >= 8) ? 0 : (8 - SL_sprites_ordered[i * 4]);
high_bound = ((SL_sprites_ordered[i * 4] - 8) <= 152) ? 7 : (159 - (SL_sprites_ordered[i * 4] - 8));
for (int j = low_bound; j <= high_bound; j++)
{
// we can use the current sprite, so pick out a pixel for it
int t_index = i - (SL_sprites_ordered[j * 4] - 8);
t_index = 7 - j;
t_index = 7 - t_index;
sprite_data[0] = (byte)((SL_sprites_ordered[j * 4 + 1] >> t_index) & 1);
sprite_data[1] = (byte)(((SL_sprites_ordered[j * 4 + 2] >> t_index) & 1) << 1);
sprite_data[0] = (byte)((SL_sprites_ordered[i * 4 + 1] >> t_index) & 1);
sprite_data[1] = (byte)(((SL_sprites_ordered[i * 4 + 2] >> t_index) & 1) << 1);
s_pixel = (byte)(sprite_data[0] + sprite_data[1]);
sprite_attr = (byte)SL_sprites_ordered[j * 4 + 3];
sprite_attr = (byte)SL_sprites_ordered[i * 4 + 3];
// pixel color of 0 is transparent, so if this is the case we don't have a pixel
if (s_pixel != 0)
{
have_pixel = true;
sprite_present_list[SL_sprites_ordered[i * 4] - (8 - j)] = 1;
sprite_pixel_list[SL_sprites_ordered[i * 4] - (8 - j)] = s_pixel;
sprite_attr_list[SL_sprites_ordered[i * 4] - (8 - j)] = sprite_attr;
}
}
}
if (have_pixel)
{
sprite_present_list[i] = 1;
sprite_pixel_list[i] = s_pixel;
sprite_attr_list[i] = sprite_attr;
}
else
{
sprite_present_list[i] = 0;
}
}
}