From b6173db95c492bcfd19b3a2013eb83573adbbde7 Mon Sep 17 00:00:00 2001 From: Asnivor Date: Sat, 26 Jan 2019 20:07:00 +0000 Subject: [PATCH] Linux VBANext - is built and should work once the mono default attribute issue is sorted --- Assets/libvbanext.dll.so | Bin 0 -> 950584 bytes vbanext/instance.cpp | 23189 +++++++++++++++++++------------------ vbanext/mingw/Makefile | 2 +- 3 files changed, 11598 insertions(+), 11593 deletions(-) create mode 100755 Assets/libvbanext.dll.so diff --git a/Assets/libvbanext.dll.so b/Assets/libvbanext.dll.so new file mode 100755 index 0000000000000000000000000000000000000000..54610699b54a64675a5b9422fab7d2d62514ab45 GIT binary patch literal 950584 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%0A~f83W6TT}m&wbq_o}Bn+Sb^y72# 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 z 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

r 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 literal 0 HcmV?d00001 diff --git a/vbanext/instance.cpp b/vbanext/instance.cpp index f381b865f5..05195ab122 100644 --- a/vbanext/instance.cpp +++ b/vbanext/instance.cpp @@ -15,23 +15,23 @@ #define HAVE_HLE_BIOS #include "port.h" - -#include "instance.h" - -#include "sound_blargg.h" - -#include "constarrays.h" - -#include "newstate.h" - -#define INLINE - -class Gigazoid -{ - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// BEGIN MEMORY.CPP -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "instance.h" + +#include "sound_blargg.h" + +#include "constarrays.h" + +#include "newstate.h" + +#define INLINE + +class Gigazoid +{ + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// BEGIN MEMORY.CPP +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /*============================================================ FLASH @@ -659,14 +659,14 @@ void AdvanceRTC(int ticks) rtcInternalTime.Increment(); } } - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// END MEMORY.CPP -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// BEGIN SOUND.CPP -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// END MEMORY.CPP +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// BEGIN SOUND.CPP +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #define NR10 0x60 #define NR11 0x62 #define NR12 0x63 @@ -883,58 +883,58 @@ class Gb_Osc NSS(enabled); } - void Gb_Osc::clock_length() - { - if ( (regs [4] & LENGTH_ENABLED) && length_ctr ) - { - if ( --length_ctr <= 0 ) - enabled = false; - } - } - void Gb_Osc::reset() - { - output = 0; - last_amp = 0; - delay = 0; - phase = 0; - enabled = false; - } + void Gb_Osc::clock_length() + { + if ( (regs [4] & LENGTH_ENABLED) && length_ctr ) + { + if ( --length_ctr <= 0 ) + enabled = false; + } + } + void Gb_Osc::reset() + { + output = 0; + last_amp = 0; + delay = 0; + phase = 0; + enabled = false; + } protected: - INLINE void Gb_Osc::update_amp( int32_t time, int new_amp ) - { - int delta = new_amp - last_amp; - if ( delta ) - { - last_amp = new_amp; - med_synth->offset( time, delta, output ); - } - } - int Gb_Osc::write_trig( int frame_phase, int max_len, int old_data ) - { - int data = regs [4]; - - if ( (frame_phase & 1) && !(old_data & LENGTH_ENABLED) && length_ctr ) - { - if ( (data & LENGTH_ENABLED)) - length_ctr--; - } - - if ( data & TRIGGER_MASK ) - { - enabled = true; - if ( !length_ctr ) - { - length_ctr = max_len; - if ( (frame_phase & 1) && (data & LENGTH_ENABLED) ) - length_ctr--; - } - } - - if ( !length_ctr ) - enabled = false; - - return data & TRIGGER_MASK; - } + INLINE void Gb_Osc::update_amp( int32_t time, int new_amp ) + { + int delta = new_amp - last_amp; + if ( delta ) + { + last_amp = new_amp; + med_synth->offset( time, delta, output ); + } + } + int Gb_Osc::write_trig( int frame_phase, int max_len, int old_data ) + { + int data = regs [4]; + + if ( (frame_phase & 1) && !(old_data & LENGTH_ENABLED) && length_ctr ) + { + if ( (data & LENGTH_ENABLED)) + length_ctr--; + } + + if ( data & TRIGGER_MASK ) + { + enabled = true; + if ( !length_ctr ) + { + length_ctr = max_len; + if ( (frame_phase & 1) && (data & LENGTH_ENABLED) ) + length_ctr--; + } + } + + if ( !length_ctr ) + enabled = false; + + return data & TRIGGER_MASK; + } }; class Gb_Env : public Gb_Osc @@ -952,55 +952,55 @@ class Gb_Env : public Gb_Osc NSS(env_enabled); } - void Gb_Env::clock_envelope() - { - if ( env_enabled && --env_delay <= 0 && reload_env_timer() ) - { - int v = volume + (regs [2] & 0x08 ? +1 : -1); - if ( 0 <= v && v <= 15 ) - volume = v; - else - env_enabled = false; - } - } - bool Gb_Env::write_register( int frame_phase, int reg, int old, int data ) - { - int const max_len = 64; - - switch ( reg ) - { - case 1: - length_ctr = max_len - (data & (max_len - 1)); - break; - - case 2: - if ( !GB_ENV_DAC_ENABLED() ) - enabled = false; - - zombie_volume( old, data ); - - if ( (data & 7) && env_delay == 8 ) - { - env_delay = 1; - clock_envelope(); // TODO: really happens at next length clock - } - break; - - case 4: - if ( write_trig( frame_phase, max_len, old ) ) - { - volume = regs [2] >> 4; - reload_env_timer(); - env_enabled = true; - if ( frame_phase == 7 ) - env_delay++; - if ( !GB_ENV_DAC_ENABLED() ) - enabled = false; - return true; - } - } - return false; - } + void Gb_Env::clock_envelope() + { + if ( env_enabled && --env_delay <= 0 && reload_env_timer() ) + { + int v = volume + (regs [2] & 0x08 ? +1 : -1); + if ( 0 <= v && v <= 15 ) + volume = v; + else + env_enabled = false; + } + } + bool Gb_Env::write_register( int frame_phase, int reg, int old, int data ) + { + int const max_len = 64; + + switch ( reg ) + { + case 1: + length_ctr = max_len - (data & (max_len - 1)); + break; + + case 2: + if ( !GB_ENV_DAC_ENABLED() ) + enabled = false; + + zombie_volume( old, data ); + + if ( (data & 7) && env_delay == 8 ) + { + env_delay = 1; + clock_envelope(); // TODO: really happens at next length clock + } + break; + + case 4: + if ( write_trig( frame_phase, max_len, old ) ) + { + volume = regs [2] >> 4; + reload_env_timer(); + env_enabled = true; + if ( frame_phase == 7 ) + env_delay++; + if ( !GB_ENV_DAC_ENABLED() ) + enabled = false; + return true; + } + } + return false; + } void reset() { @@ -1009,32 +1009,32 @@ class Gb_Env : public Gb_Osc Gb_Osc::reset(); } private: - INLINE void Gb_Env::zombie_volume( int old, int data ) - { - int v = volume; - - // CGB-05 behavior, very close to AGB behavior as well - if ( (old ^ data) & 8 ) - { - if ( !(old & 8) ) - { - v++; - if ( old & 7 ) - v++; - } - - v = 16 - v; - } - else if ( (old & 0x0F) == 8 ) - v++; - volume = v & 0x0F; - } - INLINE int Gb_Env::reload_env_timer() - { - int raw = regs [2] & 7; - env_delay = (raw ? raw : 8); - return raw; - } + INLINE void Gb_Env::zombie_volume( int old, int data ) + { + int v = volume; + + // CGB-05 behavior, very close to AGB behavior as well + if ( (old ^ data) & 8 ) + { + if ( !(old & 8) ) + { + v++; + if ( old & 7 ) + v++; + } + + v = 16 - v; + } + else if ( (old & 0x0F) == 8 ) + v++; + volume = v & 0x0F; + } + INLINE int Gb_Env::reload_env_timer() + { + int raw = regs [2] & 7; + env_delay = (raw ? raw : 8); + return raw; + } }; class Gb_Square : public Gb_Env @@ -1045,90 +1045,90 @@ public: Gb_Env::SyncState(ns); } - bool Gb_Square::write_register( int frame_phase, int reg, int old_data, int data ) - { - bool result = Gb_Env::write_register( frame_phase, reg, old_data, data ); - if ( result ) - delay = (delay & (CLK_MUL_MUL_4 - 1)) + period(); - return result; - } - void Gb_Square::run( int32_t time, int32_t end_time ) - { - /* Calc duty and phase*/ - static unsigned char const duty_offsets [4] = { 1, 1, 3, 7 }; - static unsigned char const duties [4] = { 1, 2, 4, 6 }; - int const duty_code = regs [1] >> 6; - int32_t duty_offset = duty_offsets [duty_code]; - int32_t duty = duties [duty_code]; - /* AGB uses inverted duty*/ - duty_offset -= duty; - duty = 8 - duty; - int ph = (phase + duty_offset) & 7; - - /* Determine what will be generated*/ - int vol = 0; - Blip_Buffer* const out = output; - if ( out ) - { - int amp = dac_off_amp; - if ( GB_ENV_DAC_ENABLED() ) - { - if ( enabled ) - vol = volume; - - amp = -(vol >> 1); - - /* Play inaudible frequencies as constant amplitude*/ - if ( GB_OSC_FREQUENCY() >= 0x7FA && delay < CLK_MUL_MUL_32 ) - { - amp += (vol * duty) >> 3; - vol = 0; - } - - if ( ph < duty ) - { - amp += vol; - vol = -vol; - } - } - update_amp( time, amp ); - } - - /* Generate wave*/ - time += delay; - if ( time < end_time ) - { - int const per = period(); - if ( !vol ) - { - /* Maintain phase when not playing*/ - int count = (end_time - time + per - 1) / per; - ph += count; /* will be masked below*/ - time += (int32_t) count * per; - } - else - { - /* Output amplitude transitions*/ - int delta = vol; - do - { - ph = (ph + 1) & 7; - if ( ph == 0 || ph == duty ) - { - good_synth->offset_inline( time, delta, out ); - delta = -delta; - } - time += per; - } - while ( time < end_time ); - - if ( delta != vol ) - last_amp -= delta; - } - phase = (ph - duty_offset) & 7; - } - delay = time - end_time; - } + bool Gb_Square::write_register( int frame_phase, int reg, int old_data, int data ) + { + bool result = Gb_Env::write_register( frame_phase, reg, old_data, data ); + if ( result ) + delay = (delay & (CLK_MUL_MUL_4 - 1)) + period(); + return result; + } + void Gb_Square::run( int32_t time, int32_t end_time ) + { + /* Calc duty and phase*/ + static unsigned char const duty_offsets [4] = { 1, 1, 3, 7 }; + static unsigned char const duties [4] = { 1, 2, 4, 6 }; + int const duty_code = regs [1] >> 6; + int32_t duty_offset = duty_offsets [duty_code]; + int32_t duty = duties [duty_code]; + /* AGB uses inverted duty*/ + duty_offset -= duty; + duty = 8 - duty; + int ph = (phase + duty_offset) & 7; + + /* Determine what will be generated*/ + int vol = 0; + Blip_Buffer* const out = output; + if ( out ) + { + int amp = dac_off_amp; + if ( GB_ENV_DAC_ENABLED() ) + { + if ( enabled ) + vol = volume; + + amp = -(vol >> 1); + + /* Play inaudible frequencies as constant amplitude*/ + if ( GB_OSC_FREQUENCY() >= 0x7FA && delay < CLK_MUL_MUL_32 ) + { + amp += (vol * duty) >> 3; + vol = 0; + } + + if ( ph < duty ) + { + amp += vol; + vol = -vol; + } + } + update_amp( time, amp ); + } + + /* Generate wave*/ + time += delay; + if ( time < end_time ) + { + int const per = period(); + if ( !vol ) + { + /* Maintain phase when not playing*/ + int count = (end_time - time + per - 1) / per; + ph += count; /* will be masked below*/ + time += (int32_t) count * per; + } + else + { + /* Output amplitude transitions*/ + int delta = vol; + do + { + ph = (ph + 1) & 7; + if ( ph == 0 || ph == duty ) + { + good_synth->offset_inline( time, delta, out ); + delta = -delta; + } + time += per; + } + while ( time < end_time ); + + if ( delta != vol ) + last_amp -= delta; + } + phase = (ph - duty_offset) & 7; + } + delay = time - end_time; + } void reset() { @@ -1157,33 +1157,33 @@ class Gb_Sweep_Square : public Gb_Square NSS(sweep_neg); } - void Gb_Sweep_Square::clock_sweep() - { - if ( --sweep_delay <= 0 ) - { - reload_sweep_timer(); - if ( sweep_enabled && (regs [0] & PERIOD_MASK) ) - { - calc_sweep( true ); - calc_sweep( false ); - } - } - } - INLINE void Gb_Sweep_Square::write_register( int frame_phase, int reg, int old_data, int data ) - { - if ( reg == 0 && sweep_enabled && sweep_neg && !(data & 0x08) ) - enabled = false; // sweep negate disabled after used - - if ( Gb_Square::write_register( frame_phase, reg, old_data, data ) ) - { - sweep_freq = GB_OSC_FREQUENCY(); - sweep_neg = false; - reload_sweep_timer(); - sweep_enabled = (regs [0] & (PERIOD_MASK | SHIFT_MASK)) != 0; - if ( regs [0] & SHIFT_MASK ) - calc_sweep( false ); - } - } + void Gb_Sweep_Square::clock_sweep() + { + if ( --sweep_delay <= 0 ) + { + reload_sweep_timer(); + if ( sweep_enabled && (regs [0] & PERIOD_MASK) ) + { + calc_sweep( true ); + calc_sweep( false ); + } + } + } + INLINE void Gb_Sweep_Square::write_register( int frame_phase, int reg, int old_data, int data ) + { + if ( reg == 0 && sweep_enabled && sweep_neg && !(data & 0x08) ) + enabled = false; // sweep negate disabled after used + + if ( Gb_Square::write_register( frame_phase, reg, old_data, data ) ) + { + sweep_freq = GB_OSC_FREQUENCY(); + sweep_neg = false; + reload_sweep_timer(); + sweep_enabled = (regs [0] & (PERIOD_MASK | SHIFT_MASK)) != 0; + if ( regs [0] & SHIFT_MASK ) + calc_sweep( false ); + } + } void reset() { @@ -1194,25 +1194,25 @@ class Gb_Sweep_Square : public Gb_Square Gb_Square::reset(); } private: - void Gb_Sweep_Square::calc_sweep( bool update ) - { - int shift, delta, freq; - - shift = regs [0] & SHIFT_MASK; - delta = sweep_freq >> shift; - sweep_neg = (regs [0] & 0x08) != 0; - freq = sweep_freq + (sweep_neg ? -delta : delta); - - if ( freq > 0x7FF ) - enabled = false; - else if ( shift && update ) - { - sweep_freq = freq; - - regs [3] = freq & 0xFF; - regs [4] = (regs [4] & ~0x07) | (freq >> 8 & 0x07); - } - } + void Gb_Sweep_Square::calc_sweep( bool update ) + { + int shift, delta, freq; + + shift = regs [0] & SHIFT_MASK; + delta = sweep_freq >> shift; + sweep_neg = (regs [0] & 0x08) != 0; + freq = sweep_freq + (sweep_neg ? -delta : delta); + + if ( freq > 0x7FF ) + enabled = false; + else if ( shift && update ) + { + sweep_freq = freq; + + regs [3] = freq & 0xFF; + regs [4] = (regs [4] & ~0x07) | (freq >> 8 & 0x07); + } + } }; class Gb_Noise : public Gb_Env @@ -1304,98 +1304,98 @@ class Gb_Noise : public Gb_Env return s; } - void Gb_Noise::run( int32_t time, int32_t end_time ) - { - /* Determine what will be generated*/ - int vol = 0; - Blip_Buffer* const out = output; - if ( out ) - { - int amp = dac_off_amp; - if ( GB_ENV_DAC_ENABLED() ) - { - if ( enabled ) - vol = volume; - - amp = -(vol >> 1); - - if ( !(phase & 1) ) - { - amp += vol; - vol = -vol; - } - } - - /* AGB negates final output*/ - vol = -vol; - amp = -amp; - - update_amp( time, amp ); - } - - /* Run timer and calculate time of next LFSR clock*/ - static unsigned char const period1s [8] = { 1, 2, 4, 6, 8, 10, 12, 14 }; - int const period1 = period1s [regs [3] & 7] * CLK_MUL; - { - int extra = (end_time - time) - delay; - int const per2 = GB_NOISE_PERIOD2(8); - time += delay + ((divider ^ (per2 >> 1)) & (per2 - 1)) * period1; - - int count = (extra < 0 ? 0 : (extra + period1 - 1) / period1); - divider = (divider - count) & PERIOD2_MASK; - delay = count * period1 - extra; - } - - /* Generate wave*/ - if ( time < end_time ) - { - unsigned const mask = GB_NOISE_LFSR_MASK(); - unsigned bits = phase; - - int per = GB_NOISE_PERIOD2( period1 * 8 ); - if ( GB_NOISE_PERIOD2_INDEX() >= 0xE ) - { - time = end_time; - } - else if ( !vol ) - { - /* Maintain phase when not playing*/ - int count = (end_time - time + per - 1) / per; - time += (int32_t) count * per; - bits = run_lfsr( bits, ~mask, count ); - } - else - { - /* Output amplitude transitions*/ - int delta = -vol; - do - { - unsigned changed = bits + 1; - bits = bits >> 1 & mask; - if ( changed & 2 ) - { - bits |= ~mask; - delta = -delta; - med_synth->offset_inline( time, delta, out ); - } - time += per; - } - while ( time < end_time ); - - if ( delta == vol ) - last_amp += delta; - } - phase = bits; - } - } - INLINE void Gb_Noise::write_register( int frame_phase, int reg, int old_data, int data ) - { - if ( Gb_Env::write_register( frame_phase, reg, old_data, data ) ) - { - phase = 0x7FFF; - delay += CLK_MUL_MUL_8; - } - } + void Gb_Noise::run( int32_t time, int32_t end_time ) + { + /* Determine what will be generated*/ + int vol = 0; + Blip_Buffer* const out = output; + if ( out ) + { + int amp = dac_off_amp; + if ( GB_ENV_DAC_ENABLED() ) + { + if ( enabled ) + vol = volume; + + amp = -(vol >> 1); + + if ( !(phase & 1) ) + { + amp += vol; + vol = -vol; + } + } + + /* AGB negates final output*/ + vol = -vol; + amp = -amp; + + update_amp( time, amp ); + } + + /* Run timer and calculate time of next LFSR clock*/ + static unsigned char const period1s [8] = { 1, 2, 4, 6, 8, 10, 12, 14 }; + int const period1 = period1s [regs [3] & 7] * CLK_MUL; + { + int extra = (end_time - time) - delay; + int const per2 = GB_NOISE_PERIOD2(8); + time += delay + ((divider ^ (per2 >> 1)) & (per2 - 1)) * period1; + + int count = (extra < 0 ? 0 : (extra + period1 - 1) / period1); + divider = (divider - count) & PERIOD2_MASK; + delay = count * period1 - extra; + } + + /* Generate wave*/ + if ( time < end_time ) + { + unsigned const mask = GB_NOISE_LFSR_MASK(); + unsigned bits = phase; + + int per = GB_NOISE_PERIOD2( period1 * 8 ); + if ( GB_NOISE_PERIOD2_INDEX() >= 0xE ) + { + time = end_time; + } + else if ( !vol ) + { + /* Maintain phase when not playing*/ + int count = (end_time - time + per - 1) / per; + time += (int32_t) count * per; + bits = run_lfsr( bits, ~mask, count ); + } + else + { + /* Output amplitude transitions*/ + int delta = -vol; + do + { + unsigned changed = bits + 1; + bits = bits >> 1 & mask; + if ( changed & 2 ) + { + bits |= ~mask; + delta = -delta; + med_synth->offset_inline( time, delta, out ); + } + time += per; + } + while ( time < end_time ); + + if ( delta == vol ) + last_amp += delta; + } + phase = bits; + } + } + INLINE void Gb_Noise::write_register( int frame_phase, int reg, int old_data, int data ) + { + if ( Gb_Env::write_register( frame_phase, reg, old_data, data ) ) + { + phase = 0x7FFF; + delay += CLK_MUL_MUL_8; + } + } void reset() { @@ -1419,122 +1419,122 @@ class Gb_Wave : public Gb_Osc NSS(agb_mask); } - INLINE void Gb_Wave::write_register( int frame_phase, int reg, int old_data, int data ) - { - switch ( reg ) - { - case 0: - if ( !GB_WAVE_DAC_ENABLED() ) - enabled = false; - break; - - case 1: - length_ctr = 256 - data; - break; - - case 4: - bool was_enabled = enabled; - if ( write_trig( frame_phase, 256, old_data ) ) - { - if ( !GB_WAVE_DAC_ENABLED() ) - enabled = false; - phase = 0; - delay = period() + CLK_MUL_MUL_6; - } - } - } - void Gb_Wave::run( int32_t time, int32_t end_time ) - { - /* Calc volume*/ - static unsigned char const volumes [8] = { 0, 4, 2, 1, 3, 3, 3, 3 }; - int const volume_idx = regs [2] >> 5 & (agb_mask | 3); /* 2 bits on DMG/CGB, 3 on AGB*/ - int const volume_mul = volumes [volume_idx]; - - /* Determine what will be generated*/ - int playing = false; - Blip_Buffer* const out = output; - if ( out ) - { - int amp = dac_off_amp; - if ( GB_WAVE_DAC_ENABLED() ) - { - /* Play inaudible frequencies as constant amplitude*/ - amp = 128; /* really depends on average of all samples in wave*/ - - /* if delay is larger, constant amplitude won't start yet*/ - if ( GB_OSC_FREQUENCY() <= 0x7FB || delay > CLK_MUL_MUL_15 ) - { - if ( volume_mul ) - playing = (int) enabled; - - amp = (sample_buf << (phase << 2 & 4) & 0xF0) * playing; - } - - amp = ((amp * volume_mul) >> VOLUME_SHIFT_PLUS_FOUR) - DAC_BIAS; - } - update_amp( time, amp ); - } - - /* Generate wave*/ - time += delay; - if ( time < end_time ) - { - unsigned char const* wave = wave_ram; - - /* wave size and bank*/ - int const flags = regs [0] & agb_mask; - int const wave_mask = (flags & SIZE20_MASK) | 0x1F; - int swap_banks = 0; - if ( flags & BANK40_MASK) - { - swap_banks = flags & SIZE20_MASK; - wave += BANK_SIZE_DIV_TWO - (swap_banks >> 1); - } - - int ph = phase ^ swap_banks; - ph = (ph + 1) & wave_mask; /* pre-advance*/ - - int const per = period(); - if ( !playing ) - { - /* Maintain phase when not playing*/ - int count = (end_time - time + per - 1) / per; - ph += count; /* will be masked below*/ - time += (int32_t) count * per; - } - else - { - /* Output amplitude transitions*/ - int lamp = last_amp + DAC_BIAS; - do - { - /* Extract nybble*/ - int nybble = wave [ph >> 1] << (ph << 2 & 4) & 0xF0; - ph = (ph + 1) & wave_mask; - - /* Scale by volume*/ - int amp = (nybble * volume_mul) >> VOLUME_SHIFT_PLUS_FOUR; - - int delta = amp - lamp; - if ( delta ) - { - lamp = amp; - med_synth->offset_inline( time, delta, out ); - } - time += per; - } - while ( time < end_time ); - last_amp = lamp - DAC_BIAS; - } - ph = (ph - 1) & wave_mask; /* undo pre-advance and mask position*/ - - /* Keep track of last byte read*/ - if ( enabled ) - sample_buf = wave [ph >> 1]; - - phase = ph ^ swap_banks; /* undo swapped banks*/ - } - delay = time - end_time; + INLINE void Gb_Wave::write_register( int frame_phase, int reg, int old_data, int data ) + { + switch ( reg ) + { + case 0: + if ( !GB_WAVE_DAC_ENABLED() ) + enabled = false; + break; + + case 1: + length_ctr = 256 - data; + break; + + case 4: + bool was_enabled = enabled; + if ( write_trig( frame_phase, 256, old_data ) ) + { + if ( !GB_WAVE_DAC_ENABLED() ) + enabled = false; + phase = 0; + delay = period() + CLK_MUL_MUL_6; + } + } + } + void Gb_Wave::run( int32_t time, int32_t end_time ) + { + /* Calc volume*/ + static unsigned char const volumes [8] = { 0, 4, 2, 1, 3, 3, 3, 3 }; + int const volume_idx = regs [2] >> 5 & (agb_mask | 3); /* 2 bits on DMG/CGB, 3 on AGB*/ + int const volume_mul = volumes [volume_idx]; + + /* Determine what will be generated*/ + int playing = false; + Blip_Buffer* const out = output; + if ( out ) + { + int amp = dac_off_amp; + if ( GB_WAVE_DAC_ENABLED() ) + { + /* Play inaudible frequencies as constant amplitude*/ + amp = 128; /* really depends on average of all samples in wave*/ + + /* if delay is larger, constant amplitude won't start yet*/ + if ( GB_OSC_FREQUENCY() <= 0x7FB || delay > CLK_MUL_MUL_15 ) + { + if ( volume_mul ) + playing = (int) enabled; + + amp = (sample_buf << (phase << 2 & 4) & 0xF0) * playing; + } + + amp = ((amp * volume_mul) >> VOLUME_SHIFT_PLUS_FOUR) - DAC_BIAS; + } + update_amp( time, amp ); + } + + /* Generate wave*/ + time += delay; + if ( time < end_time ) + { + unsigned char const* wave = wave_ram; + + /* wave size and bank*/ + int const flags = regs [0] & agb_mask; + int const wave_mask = (flags & SIZE20_MASK) | 0x1F; + int swap_banks = 0; + if ( flags & BANK40_MASK) + { + swap_banks = flags & SIZE20_MASK; + wave += BANK_SIZE_DIV_TWO - (swap_banks >> 1); + } + + int ph = phase ^ swap_banks; + ph = (ph + 1) & wave_mask; /* pre-advance*/ + + int const per = period(); + if ( !playing ) + { + /* Maintain phase when not playing*/ + int count = (end_time - time + per - 1) / per; + ph += count; /* will be masked below*/ + time += (int32_t) count * per; + } + else + { + /* Output amplitude transitions*/ + int lamp = last_amp + DAC_BIAS; + do + { + /* Extract nybble*/ + int nybble = wave [ph >> 1] << (ph << 2 & 4) & 0xF0; + ph = (ph + 1) & wave_mask; + + /* Scale by volume*/ + int amp = (nybble * volume_mul) >> VOLUME_SHIFT_PLUS_FOUR; + + int delta = amp - lamp; + if ( delta ) + { + lamp = amp; + med_synth->offset_inline( time, delta, out ); + } + time += per; + } + while ( time < end_time ); + last_amp = lamp - DAC_BIAS; + } + ph = (ph - 1) & wave_mask; /* undo pre-advance and mask position*/ + + /* Keep track of last byte read*/ + if ( enabled ) + sample_buf = wave [ph >> 1]; + + phase = ph ^ swap_banks; /* undo swapped banks*/ + } + delay = time - end_time; } /* Reads/writes wave RAM*/ @@ -1580,25 +1580,25 @@ class Gb_Wave : public Gb_Osc /* Frequency timer period*/ int period() const { return (2048 - GB_OSC_FREQUENCY()) * (CLK_MUL_MUL_2); } - void Gb_Wave::corrupt_wave() - { - int pos = ((phase + 1) & BANK_SIZE_MIN_ONE) >> 1; - if ( pos < 4 ) - wave_ram [0] = wave_ram [pos]; - else - for ( int i = 4; --i >= 0; ) - wave_ram [i] = wave_ram [(pos & ~3) + i]; - } + void Gb_Wave::corrupt_wave() + { + int pos = ((phase + 1) & BANK_SIZE_MIN_ONE) >> 1; + if ( pos < 4 ) + wave_ram [0] = wave_ram [pos]; + else + for ( int i = 4; --i >= 0; ) + wave_ram [i] = wave_ram [(pos & ~3) + i]; + } /* Wave index that would be accessed, or -1 if no access would occur*/ - int Gb_Wave::access( unsigned addr ) const - { - //if ( mode != MODE_AGB ) - //{ - // addr = (phase & BANK_SIZE_MIN_ONE) >> 1; - //} - return addr & 0x0F; - } + int Gb_Wave::access( unsigned addr ) const + { + //if ( mode != MODE_AGB ) + //{ + // addr = (phase & BANK_SIZE_MIN_ONE) >> 1; + //} + return addr & 0x0F; + } }; /*============================================================ @@ -2603,11093 +2603,11098 @@ void soundSetSampleRate(long sampleRate) remake_stereo_buffer(); } } -*/ - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// END SOUND.CPP -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// BEGIN GBA.CPP -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -/*============================================================ - GBA INLINE -============================================================ */ - -#define UPDATE_REG(address, value) WRITE16LE(((u16 *)&ioMem[address]),value); -#define ARM_PREFETCH_NEXT cpuPrefetch[1] = CPUReadMemoryQuick(bus.armNextPC+4); -#define THUMB_PREFETCH_NEXT cpuPrefetch[1] = CPUReadHalfWordQuick(bus.armNextPC+2); - -#define ARM_PREFETCH \ - {\ - cpuPrefetch[0] = CPUReadMemoryQuick(bus.armNextPC);\ - cpuPrefetch[1] = CPUReadMemoryQuick(bus.armNextPC+4);\ - } - -#define THUMB_PREFETCH \ - {\ - cpuPrefetch[0] = CPUReadHalfWordQuick(bus.armNextPC);\ - cpuPrefetch[1] = CPUReadHalfWordQuick(bus.armNextPC+2);\ - } - -int cpuNextEvent; // = 0; -bool holdState; // = false; -uint32_t cpuPrefetch[2]; -int cpuTotalTicks; // = 0; -uint8_t memoryWait[16]; -uint8_t memoryWaitSeq[16]; -uint8_t memoryWait32[16]; -uint8_t memoryWaitSeq32[16]; - -uint8_t biosProtected[4]; -uint8_t cpuBitsSet[256]; - -bool N_FLAG; // = 0; -bool C_FLAG; // = 0; -bool Z_FLAG; // = 0; -bool V_FLAG; // = 0; -bool armState; // = true; -bool armIrqEnable; // = true; -int armMode; // = 0x1f; - -typedef enum -{ - REG_DISPCNT = 0x000, - REG_DISPSTAT = 0x002, - REG_VCOUNT = 0x003, - REG_BG0CNT = 0x004, - REG_BG1CNT = 0x005, - REG_BG2CNT = 0x006, - REG_BG3CNT = 0x007, - REG_BG0HOFS = 0x08, - REG_BG0VOFS = 0x09, - REG_BG1HOFS = 0x0A, - REG_BG1VOFS = 0x0B, - REG_BG2HOFS = 0x0C, - REG_BG2VOFS = 0x0D, - REG_BG3HOFS = 0x0E, - REG_BG3VOFS = 0x0F, - REG_BG2PA = 0x10, - REG_BG2PB = 0x11, - REG_BG2PC = 0x12, - REG_BG2PD = 0x13, - REG_BG2X_L = 0x14, - REG_BG2X_H = 0x15, - REG_BG2Y_L = 0x16, - REG_BG2Y_H = 0x17, - REG_BG3PA = 0x18, - REG_BG3PB = 0x19, - REG_BG3PC = 0x1A, - REG_BG3PD = 0x1B, - REG_BG3X_L = 0x1C, - REG_BG3X_H = 0x1D, - REG_BG3Y_L = 0x1E, - REG_BG3Y_H = 0x1F, - REG_WIN0H = 0x20, - REG_WIN1H = 0x21, - REG_WIN0V = 0x22, - REG_WIN1V = 0x23, - REG_WININ = 0x24, - REG_WINOUT = 0x25, - REG_BLDCNT = 0x28, - REG_BLDALPHA = 0x29, - REG_BLDY = 0x2A, - REG_TM0D = 0x80, - REG_TM0CNT = 0x81, - REG_TM1D = 0x82, - REG_TM1CNT = 0x83, - REG_TM2D = 0x84, - REG_TM2CNT = 0x85, - REG_TM3D = 0x86, - REG_TM3CNT = 0x87, - REG_P1 = 0x098, - REG_P1CNT = 0x099, - REG_RCNT = 0x9A, - REG_IE = 0x100, - REG_IF = 0x101, - REG_IME = 0x104, - REG_HALTCNT = 0x180 -} hardware_register; - -uint16_t io_registers[1024 * 16]; - -u16 MOSAIC; - -uint16_t BG2X_L ; -uint16_t BG2X_H ; -uint16_t BG2Y_L ; -uint16_t BG2Y_H ; -uint16_t BG3X_L ; -uint16_t BG3X_H ; -uint16_t BG3Y_L ; -uint16_t BG3Y_H ; -uint16_t BLDMOD ; -uint16_t COLEV ; -uint16_t COLY ; -uint16_t DM0SAD_L ; -uint16_t DM0SAD_H ; -uint16_t DM0DAD_L ; -uint16_t DM0DAD_H ; -uint16_t DM0CNT_L ; -uint16_t DM0CNT_H ; -uint16_t DM1SAD_L ; -uint16_t DM1SAD_H ; -uint16_t DM1DAD_L ; -uint16_t DM1DAD_H ; -uint16_t DM1CNT_L ; -uint16_t DM1CNT_H ; -uint16_t DM2SAD_L ; -uint16_t DM2SAD_H ; -uint16_t DM2DAD_L ; -uint16_t DM2DAD_H ; -uint16_t DM2CNT_L ; -uint16_t DM2CNT_H ; -uint16_t DM3SAD_L ; -uint16_t DM3SAD_H ; -uint16_t DM3DAD_L ; -uint16_t DM3DAD_H ; -uint16_t DM3CNT_L ; -uint16_t DM3CNT_H ; - -uint8_t timerOnOffDelay ; -uint16_t timer0Value ; -uint32_t dma0Source ; -uint32_t dma0Dest ; -uint32_t dma1Source ; -uint32_t dma1Dest ; -uint32_t dma2Source ; -uint32_t dma2Dest ; -uint32_t dma3Source ; -uint32_t dma3Dest ; -void (Gigazoid::*cpuSaveGameFunc)(uint32_t,uint8_t); -bool fxOn ; -bool windowOn ; - -int cpuDmaTicksToUpdate; - -int IRQTicks; -bool intState; - -bus_t bus; -graphics_t graphics; - -memoryMap map[256]; -int clockTicks; - -int romSize; // = 0x2000000; -uint32_t line[6][240]; -bool gfxInWin[2][240]; -int lineOBJpixleft[128]; -int joy; - -int gfxBG2Changed; -int gfxBG3Changed; - -int gfxBG2X; -int gfxBG2Y; -int gfxBG3X; -int gfxBG3Y; - -bool ioReadable[0x400]; - -//int gfxLastVCOUNT = 0; - -// Waitstates when accessing data - -#define DATATICKS_ACCESS_BUS_PREFETCH(address, value) \ - int addr = (address >> 24) & 15; \ - if ((addr>=0x08) || (addr < 0x02)) \ - { \ - bus.busPrefetchCount=0; \ - bus.busPrefetch=false; \ - } \ - else if (bus.busPrefetch) \ - { \ - int waitState = value; \ - waitState = (1 & ~waitState) | (waitState & waitState); \ - bus.busPrefetchCount = ((bus.busPrefetchCount+1)<> 24) & 15]) -#define DATATICKS_ACCESS_32BIT_SEQ(address) (memoryWaitSeq32[(address >> 24) & 15]) -#define DATATICKS_ACCESS_16BIT(address) (memoryWait[(address >> 24) & 15]) -#define DATATICKS_ACCESS_16BIT_SEQ(address) (memoryWaitSeq[(address >> 24) & 15]) - -// Waitstates when executing opcode -INLINE int codeTicksAccess(u32 address, u8 bit32) // THUMB NON SEQ -{ - int addr, ret; - - addr = (address>>24) & 15; - - if (unsigned(addr - 0x08) <= 5) - { - if (bus.busPrefetchCount&0x1) - { - if (bus.busPrefetchCount&0x2) - { - bus.busPrefetchCount = ((bus.busPrefetchCount&0xFF)>>2) | (bus.busPrefetchCount&0xFFFFFF00); - return 0; - } - bus.busPrefetchCount = ((bus.busPrefetchCount&0xFF)>>1) | (bus.busPrefetchCount&0xFFFFFF00); - return memoryWaitSeq[addr]-1; - } - } - bus.busPrefetchCount = 0; - - if(bit32) /* ARM NON SEQ */ - ret = memoryWait32[addr]; - else /* THUMB NON SEQ */ - ret = memoryWait[addr]; - - return ret; -} - -INLINE int codeTicksAccessSeq16(u32 address) // THUMB SEQ -{ - int addr = (address>>24) & 15; - - if (unsigned(addr - 0x08) <= 5) - { - if (bus.busPrefetchCount&0x1) - { - bus.busPrefetchCount = ((bus.busPrefetchCount&0xFF)>>1) | (bus.busPrefetchCount&0xFFFFFF00); - return 0; - } - else if (bus.busPrefetchCount>0xFF) - { - bus.busPrefetchCount=0; - return memoryWait[addr]; - } - } - else - bus.busPrefetchCount = 0; - - return memoryWaitSeq[addr]; -} - -INLINE int codeTicksAccessSeq32(u32 address) // ARM SEQ -{ - int addr = (address>>24)&15; - - if (unsigned(addr - 0x08) <= 5) - { - if (bus.busPrefetchCount&0x1) - { - if (bus.busPrefetchCount&0x2) - { - bus.busPrefetchCount = ((bus.busPrefetchCount&0xFF)>>2) | (bus.busPrefetchCount&0xFFFFFF00); - return 0; - } - bus.busPrefetchCount = ((bus.busPrefetchCount&0xFF)>>1) | (bus.busPrefetchCount&0xFFFFFF00); - return memoryWaitSeq[addr]; - } - else if (bus.busPrefetchCount > 0xFF) - { - bus.busPrefetchCount=0; - return memoryWait32[addr]; - } - } - return memoryWaitSeq32[addr]; -} - -#define CPUReadByteQuick(addr) map[(addr)>>24].address[(addr) & map[(addr)>>24].mask] -#define CPUReadHalfWordQuick(addr) READ16LE(((u16*)&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask])) -#define CPUReadMemoryQuick(addr) READ32LE(((u32*)&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask])) - -bool stopState; -#ifdef USE_MOTION_SENSOR -extern bool cpuEEPROMSensorEnabled; -#endif -bool timer0On ; -int timer0Ticks ; -int timer0Reload ; -int timer0ClockReload ; -uint16_t timer1Value ; -bool timer1On ; -int timer1Ticks ; -int timer1Reload ; -int timer1ClockReload ; -uint16_t timer2Value ; -bool timer2On ; -int timer2Ticks ; -int timer2Reload ; -int timer2ClockReload ; -uint16_t timer3Value ; -bool timer3On ; -int timer3Ticks ; -int timer3Reload ; -int timer3ClockReload ; - -INLINE u32 CPUReadMemory(u32 address) -{ - if (readCallback) - readCallback(address); - - u32 value; - switch(address >> 24) - { - case 0: - /* BIOS */ - if(bus.reg[15].I >> 24) - { - if(address < 0x4000) - value = READ32LE(((u32 *)&biosProtected)); - else goto unreadable; - } - else - value = READ32LE(((u32 *)&bios[address & 0x3FFC])); - break; - case 0x02: - /* external work RAM */ - value = READ32LE(((u32 *)&workRAM[address & 0x3FFFC])); - break; - case 0x03: - /* internal work RAM */ - value = READ32LE(((u32 *)&internalRAM[address & 0x7ffC])); - break; - case 0x04: - /* I/O registers */ - if (address == 0x4000130) - { - if (padCallback) - padCallback(); - lagged = false; - } - if((address < 0x4000400) && ioReadable[address & 0x3fc]) - { - if(ioReadable[(address & 0x3fc) + 2]) - value = READ32LE(((u32 *)&ioMem[address & 0x3fC])); - else - value = READ16LE(((u16 *)&ioMem[address & 0x3fc])); - } - else - goto unreadable; - break; - case 0x05: - /* palette RAM */ - value = READ32LE(((u32 *)&graphics.paletteRAM[address & 0x3fC])); - break; - case 0x06: - /* VRAM */ - address = (address & 0x1fffc); - if (((io_registers[REG_DISPCNT] & 7) >2) && ((address & 0x1C000) == 0x18000)) - { - value = 0; - break; - } - if ((address & 0x18000) == 0x18000) - address &= 0x17fff; - value = READ32LE(((u32 *)&vram[address])); - break; - case 0x07: - /* OAM RAM */ - value = READ32LE(((u32 *)&oam[address & 0x3FC])); - break; - case 0x08: - case 0x09: - case 0x0A: - case 0x0B: - case 0x0C: - /* gamepak ROM */ - value = READ32LE(((u32 *)&rom[address&0x1FFFFFC])); - break; - case 0x0D: - value = eepromRead(); - break; - case 14: - case 15: - value = flashRead(address) * 0x01010101; - break; - default: -unreadable: - if(armState) - value = CPUReadHalfWordQuick(bus.reg[15].I + (address & 2)); - else - value = CPUReadHalfWordQuick(bus.reg[15].I); - } - - if(address & 3) { - int shift = (address & 3) << 3; - value = (value >> shift) | (value << (32 - shift)); - } - return value; -} - -INLINE u32 CPUReadHalfWord(u32 address) -{ - if (readCallback) - readCallback(address); - - u32 value; - switch(address >> 24) - { - case 0: - if (bus.reg[15].I >> 24) - { - if(address < 0x4000) - value = READ16LE(((u16 *)&biosProtected[address&2])); - else - goto unreadable; - } - else - value = READ16LE(((u16 *)&bios[address & 0x3FFE])); - break; - case 2: - value = READ16LE(((u16 *)&workRAM[address & 0x3FFFE])); - break; - case 3: - value = READ16LE(((u16 *)&internalRAM[address & 0x7ffe])); - break; - case 4: - if (address == 0x4000130) - { - if (padCallback) - padCallback(); - lagged = false; - } - - if((address < 0x4000400) && ioReadable[address & 0x3fe]) - { - value = READ16LE(((u16 *)&ioMem[address & 0x3fe])); - if (((address & 0x3fe)>0xFF) && ((address & 0x3fe)<0x10E)) - { - if (((address & 0x3fe) == 0x100) && timer0On) - value = 0xFFFF - ((timer0Ticks-cpuTotalTicks) >> timer0ClockReload); - else - if (((address & 0x3fe) == 0x104) && timer1On && !(io_registers[REG_TM1CNT] & 4)) - value = 0xFFFF - ((timer1Ticks-cpuTotalTicks) >> timer1ClockReload); - else - if (((address & 0x3fe) == 0x108) && timer2On && !(io_registers[REG_TM2CNT] & 4)) - value = 0xFFFF - ((timer2Ticks-cpuTotalTicks) >> timer2ClockReload); - else - if (((address & 0x3fe) == 0x10C) && timer3On && !(io_registers[REG_TM3CNT] & 4)) - value = 0xFFFF - ((timer3Ticks-cpuTotalTicks) >> timer3ClockReload); - } - } - else goto unreadable; - break; - case 5: - value = READ16LE(((u16 *)&graphics.paletteRAM[address & 0x3fe])); - break; - case 6: - address = (address & 0x1fffe); - if (((io_registers[REG_DISPCNT] & 7) >2) && ((address & 0x1C000) == 0x18000)) - { - value = 0; - break; - } - if ((address & 0x18000) == 0x18000) - address &= 0x17fff; - value = READ16LE(((u16 *)&vram[address])); - break; - case 7: - value = READ16LE(((u16 *)&oam[address & 0x3fe])); - break; - case 8: - case 9: - case 10: - case 11: - case 12: - if(rtcEnabled && (address == 0x80000c4 || address == 0x80000c6 || address == 0x80000c8)) - value = rtcRead(address); - else - value = READ16LE(((u16 *)&rom[address & 0x1FFFFFE])); - break; - case 13: - value = eepromRead(); - break; - case 14: - value = flashRead(address) * 0x0101; - break; - default: -unreadable: - { - int param = bus.reg[15].I; - if(armState) - param += (address & 2); - value = CPUReadHalfWordQuick(param); - } - break; - } - - if(address & 1) - value = (value >> 8) | (value << 24); - - return value; -} - -INLINE u16 CPUReadHalfWordSigned(u32 address) -{ - u16 value = CPUReadHalfWord(address); - if((address & 1)) - value = (s8)value; - return value; -} - -INLINE u8 CPUReadByte(u32 address) -{ - if (readCallback) - readCallback(address); - - switch(address >> 24) - { - case 0: - if (bus.reg[15].I >> 24) - { - if(address < 0x4000) - return biosProtected[address & 3]; - else - goto unreadable; - } - return bios[address & 0x3FFF]; - case 2: - return workRAM[address & 0x3FFFF]; - case 3: - return internalRAM[address & 0x7fff]; - case 4: - if (address == 0x4000130 || address == 0x4000131) - { - if (padCallback) - padCallback(); - lagged = false; - } - - if((address < 0x4000400) && ioReadable[address & 0x3ff]) - return ioMem[address & 0x3ff]; - else goto unreadable; - case 5: - return graphics.paletteRAM[address & 0x3ff]; - case 6: - address = (address & 0x1ffff); - if (((io_registers[REG_DISPCNT] & 7) >2) && ((address & 0x1C000) == 0x18000)) - return 0; - if ((address & 0x18000) == 0x18000) - address &= 0x17fff; - return vram[address]; - case 7: - return oam[address & 0x3ff]; - case 8: - case 9: - case 10: - case 11: - case 12: - return rom[address & 0x1FFFFFF]; - case 13: - return eepromRead(); - case 14: -#ifdef USE_MOTION_SENSOR - if(cpuEEPROMSensorEnabled) - { - switch(address & 0x00008f00) - { - case 0x8200: - return systemGetSensorX() & 255; - case 0x8300: - return (systemGetSensorX() >> 8)|0x80; - case 0x8400: - return systemGetSensorY() & 255; - case 0x8500: - return systemGetSensorY() >> 8; - } - } -#endif - return flashRead(address); - default: -unreadable: - if(armState) - return CPUReadByteQuick(bus.reg[15].I+(address & 3)); - else - return CPUReadByteQuick(bus.reg[15].I+(address & 1)); - } -} - -INLINE void CPUWriteMemory(u32 address, u32 value) -{ - if (writeCallback) - writeCallback(address); - - switch(address >> 24) - { - case 0x02: - WRITE32LE(((u32 *)&workRAM[address & 0x3FFFC]), value); - break; - case 0x03: - WRITE32LE(((u32 *)&internalRAM[address & 0x7ffC]), value); - break; - case 0x04: - if(address < 0x4000400) - { - CPUUpdateRegister((address & 0x3FC), value & 0xFFFF); - CPUUpdateRegister((address & 0x3FC) + 2, (value >> 16)); - } - break; - case 0x05: - WRITE32LE(((u32 *)&graphics.paletteRAM[address & 0x3FC]), value); - break; - case 0x06: - address = (address & 0x1fffc); - if (((io_registers[REG_DISPCNT] & 7) >2) && ((address & 0x1C000) == 0x18000)) - return; - if ((address & 0x18000) == 0x18000) - address &= 0x17fff; - - - WRITE32LE(((u32 *)&vram[address]), value); - break; - case 0x07: - WRITE32LE(((u32 *)&oam[address & 0x3fc]), value); - break; - case 0x0D: - if (cpuEEPROMEnabled) - eepromWrite(value); - break; - case 0x0E: - (this->*cpuSaveGameFunc)(address, (u8)value); - break; - default: - break; - } -} - -INLINE void CPUWriteHalfWord(u32 address, u16 value) -{ - if (writeCallback) - writeCallback(address); - - switch(address >> 24) - { - case 2: - WRITE16LE(((u16 *)&workRAM[address & 0x3FFFE]),value); - break; - case 3: - WRITE16LE(((u16 *)&internalRAM[address & 0x7ffe]), value); - break; - case 4: - if(address < 0x4000400) - CPUUpdateRegister(address & 0x3fe, value); - break; - case 5: - WRITE16LE(((u16 *)&graphics.paletteRAM[address & 0x3fe]), value); - break; - case 6: - address = (address & 0x1fffe); - if (((io_registers[REG_DISPCNT] & 7) >2) && ((address & 0x1C000) == 0x18000)) - return; - if ((address & 0x18000) == 0x18000) - address &= 0x17fff; - WRITE16LE(((u16 *)&vram[address]), value); - break; - case 7: - WRITE16LE(((u16 *)&oam[address & 0x3fe]), value); - break; - case 8: - case 9: - if(address == 0x80000c4 || address == 0x80000c6 || address == 0x80000c8) - if(!rtcWrite(address, value)) - break; - break; - case 13: - if(cpuEEPROMEnabled) - eepromWrite((u8)value); - break; - case 14: - (this->*cpuSaveGameFunc)(address, (u8)value); - break; - default: - break; - } -} - -INLINE void CPUWriteByte(u32 address, u8 b) -{ - if (writeCallback) - writeCallback(address); - - switch(address >> 24) - { - case 2: - workRAM[address & 0x3FFFF] = b; - break; - case 3: - internalRAM[address & 0x7fff] = b; - break; - case 4: - if(address < 0x4000400) - { - switch(address & 0x3FF) - { - case 0x60: - case 0x61: - case 0x62: - case 0x63: - case 0x64: - case 0x65: - case 0x68: - case 0x69: - case 0x6c: - case 0x6d: - case 0x70: - case 0x71: - case 0x72: - case 0x73: - case 0x74: - case 0x75: - case 0x78: - case 0x79: - case 0x7c: - case 0x7d: - case 0x80: - case 0x81: - case 0x84: - case 0x85: - case 0x90: - case 0x91: - case 0x92: - case 0x93: - case 0x94: - case 0x95: - case 0x96: - case 0x97: - case 0x98: - case 0x99: - case 0x9a: - case 0x9b: - case 0x9c: - case 0x9d: - case 0x9e: - case 0x9f: - { - int gb_addr = table[(address & 0xFF) - 0x60]; - soundEvent_u8(gb_addr, address&0xFF, b); - } - break; - case 0x301: // HALTCNT, undocumented - if(b == 0x80) - stopState = true; - holdState = 1; - cpuNextEvent = cpuTotalTicks; - break; - default: // every other register - { - u32 lowerBits = address & 0x3fe; - uint16_t param; - if(address & 1) - param = (READ16LE(&ioMem[lowerBits]) & 0x00FF) | (b << 8); - else - param = (READ16LE(&ioMem[lowerBits]) & 0xFF00) | b; - - CPUUpdateRegister(lowerBits, param); - } - break; - } - } - break; - case 5: - // no need to switch - *((u16 *)&graphics.paletteRAM[address & 0x3FE]) = (b << 8) | b; - break; - case 6: - address = (address & 0x1fffe); - if (((io_registers[REG_DISPCNT] & 7) >2) && ((address & 0x1C000) == 0x18000)) - return; - if ((address & 0x18000) == 0x18000) - address &= 0x17fff; - - // no need to switch - // byte writes to OBJ VRAM are ignored - if ((address) < objTilesAddress[((io_registers[REG_DISPCNT] & 7)+1)>>2]) - *((u16 *)&vram[address]) = (b << 8) | b; - break; - case 7: - // no need to switch - // byte writes to OAM are ignored - // *((u16 *)&oam[address & 0x3FE]) = (b << 8) | b; - break; - case 13: - if(cpuEEPROMEnabled) - eepromWrite(b); - break; - case 14: - (this->*cpuSaveGameFunc)(address, b); - break; - default: - break; - } -} - - -/*============================================================ - BIOS -============================================================ */ - -void BIOS_RegisterRamReset(u32 flags) -{ - // no need to trace here. this is only called directly from GBA.cpp - // to emulate bios initialization - - CPUUpdateRegister(0x0, 0x80); - - if(flags) - { - if(flags & 0x01) - memset(workRAM, 0, 0x40000); // clear work RAM - - if(flags & 0x02) - memset(internalRAM, 0, 0x7e00); // don't clear 0x7e00-0x7fff, clear internal RAM - - if(flags & 0x04) - memset(graphics.paletteRAM, 0, 0x400); // clear palette RAM - - if(flags & 0x08) - memset(vram, 0, 0x18000); // clear VRAM - - if(flags & 0x10) - memset(oam, 0, 0x400); // clean OAM - - if(flags & 0x80) { - int i; - for(i = 0; i < 0x10; i++) - CPUUpdateRegister(0x200+i*2, 0); - - for(i = 0; i < 0xF; i++) - CPUUpdateRegister(0x4+i*2, 0); - - for(i = 0; i < 0x20; i++) - CPUUpdateRegister(0x20+i*2, 0); - - for(i = 0; i < 0x18; i++) - CPUUpdateRegister(0xb0+i*2, 0); - - CPUUpdateRegister(0x130, 0); - CPUUpdateRegister(0x20, 0x100); - CPUUpdateRegister(0x30, 0x100); - CPUUpdateRegister(0x26, 0x100); - CPUUpdateRegister(0x36, 0x100); - } - - if(flags & 0x20) { - int i; - for(i = 0; i < 8; i++) - CPUUpdateRegister(0x110+i*2, 0); - CPUUpdateRegister(0x134, 0x8000); - for(i = 0; i < 7; i++) - CPUUpdateRegister(0x140+i*2, 0); - } - - if(flags & 0x40) { - int i; - CPUWriteByte(0x4000084, 0); - CPUWriteByte(0x4000084, 0x80); - CPUWriteMemory(0x4000080, 0x880e0000); - CPUUpdateRegister(0x88, CPUReadHalfWord(0x4000088)&0x3ff); - CPUWriteByte(0x4000070, 0x70); - for(i = 0; i < 8; i++) - CPUUpdateRegister(0x90+i*2, 0); - CPUWriteByte(0x4000070, 0); - for(i = 0; i < 8; i++) - CPUUpdateRegister(0x90+i*2, 0); - CPUWriteByte(0x4000084, 0); - } - } -} - -void BIOS_SoftReset (void) -{ - armState = true; - armMode = 0x1F; - armIrqEnable = false; - C_FLAG = V_FLAG = N_FLAG = Z_FLAG = false; - bus.reg[13].I = 0x03007F00; - bus.reg[14].I = 0x00000000; - bus.reg[16].I = 0x00000000; - bus.reg[R13_IRQ].I = 0x03007FA0; - bus.reg[R14_IRQ].I = 0x00000000; - bus.reg[SPSR_IRQ].I = 0x00000000; - bus.reg[R13_SVC].I = 0x03007FE0; - bus.reg[R14_SVC].I = 0x00000000; - bus.reg[SPSR_SVC].I = 0x00000000; - u8 b = internalRAM[0x7ffa]; - - memset(&internalRAM[0x7e00], 0, 0x200); - - if(b) { - bus.armNextPC = 0x02000000; - bus.reg[15].I = 0x02000004; - } else { - bus.armNextPC = 0x08000000; - bus.reg[15].I = 0x08000004; - } -} - -#define BIOS_REGISTER_RAM_RESET() BIOS_RegisterRamReset(bus.reg[0].I); - -#define CPU_UPDATE_CPSR() \ -{ \ - uint32_t CPSR; \ - CPSR = bus.reg[16].I & 0x40; \ - if(N_FLAG) \ - CPSR |= 0x80000000; \ - if(Z_FLAG) \ - CPSR |= 0x40000000; \ - if(C_FLAG) \ - CPSR |= 0x20000000; \ - if(V_FLAG) \ - CPSR |= 0x10000000; \ - if(!armState) \ - CPSR |= 0x00000020; \ - if(!armIrqEnable) \ - CPSR |= 0x80; \ - CPSR |= (armMode & 0x1F); \ - bus.reg[16].I = CPSR; \ -} - -#define CPU_SOFTWARE_INTERRUPT() \ -{ \ - uint32_t PC = bus.reg[15].I; \ - bool savedArmState = armState; \ - if(armMode != 0x13) \ - CPUSwitchMode(0x13, true, false); \ - bus.reg[14].I = PC - (savedArmState ? 4 : 2); \ - bus.reg[15].I = 0x08; \ - armState = true; \ - armIrqEnable = false; \ - bus.armNextPC = 0x08; \ - ARM_PREFETCH; \ - bus.reg[15].I += 4; \ -} - -void CPUUpdateFlags(bool breakLoop) -{ - uint32_t CPSR = bus.reg[16].I; - - N_FLAG = (CPSR & 0x80000000) ? true: false; - Z_FLAG = (CPSR & 0x40000000) ? true: false; - C_FLAG = (CPSR & 0x20000000) ? true: false; - V_FLAG = (CPSR & 0x10000000) ? true: false; - armState = (CPSR & 0x20) ? false : true; - armIrqEnable = (CPSR & 0x80) ? false : true; - if (breakLoop && armIrqEnable && (io_registers[REG_IF] & io_registers[REG_IE]) && (io_registers[REG_IME] & 1)) - cpuNextEvent = cpuTotalTicks; -} - -void CPUSoftwareInterrupt(int comment) -{ - if(armState) - comment >>= 16; - - CPU_SOFTWARE_INTERRUPT(); -} - - -/*============================================================ - GBA ARM CORE -============================================================ */ - -#ifdef _MSC_VER - // Disable "empty statement" warnings - #pragma warning(disable: 4390) - // Visual C's inline assembler treats "offset" as a reserved word, so we - // tell it otherwise. If you want to use it, write "OFFSET" in capitals. - #define offset offset_ -#endif - -void armUnknownInsn(u32 opcode) -{ - u32 PC = bus.reg[15].I; - bool savedArmState = armState; - if(armMode != 0x1b ) - CPUSwitchMode(0x1b, true, false); - bus.reg[14].I = PC - (savedArmState ? 4 : 2); - bus.reg[15].I = 0x04; - armState = true; - armIrqEnable = false; - bus.armNextPC = 0x04; - ARM_PREFETCH; - bus.reg[15].I += 4; -} - -// Common macros ////////////////////////////////////////////////////////// - -#define NEG(i) ((i) >> 31) -#define POS(i) ((~(i)) >> 31) - -// The following macros are used for optimization; any not defined for a -// particular compiler/CPU combination default to the C core versions. -// -// ALU_INIT_C: Used at the beginning of ALU instructions (AND/EOR/...). -// (ALU_INIT_NC) Can consist of variable declarations, like the C core, -// or the start of a continued assembly block, like the -// x86-optimized version. The _C version is used when the -// carry flag from the shift operation is needed (logical -// operations that set condition codes, like ANDS); the -// _NC version is used when the carry result is ignored. -// VALUE_XXX: Retrieve the second operand's value for an ALU instruction. -// The _C and _NC versions are used the same way as ALU_INIT. -// OP_XXX: ALU operations. XXX is the instruction name. -// SETCOND_NONE: Used in multiply instructions in place of SETCOND_MUL -// when the condition codes are not set. Usually empty. -// SETCOND_MUL: Used in multiply instructions to set the condition codes. -// ROR_IMM_MSR: Used to rotate the immediate operand for MSR. -// ROR_OFFSET: Used to rotate the `offset' parameter for LDR and STR -// instructions. -// RRX_OFFSET: Used to rotate (RRX) the `offset' parameter for LDR and -// STR instructions. - -// C core - -#define C_SETCOND_LOGICAL \ - N_FLAG = ((s32)res < 0) ? true : false; \ - Z_FLAG = (res == 0) ? true : false; \ - C_FLAG = C_OUT; -#define C_SETCOND_ADD \ - N_FLAG = ((s32)res < 0) ? true : false; \ - Z_FLAG = (res == 0) ? true : false; \ - V_FLAG = ((NEG(lhs) & NEG(rhs) & POS(res)) | \ - (POS(lhs) & POS(rhs) & NEG(res))) ? true : false;\ - C_FLAG = ((NEG(lhs) & NEG(rhs)) | \ - (NEG(lhs) & POS(res)) | \ - (NEG(rhs) & POS(res))) ? true : false; -#define C_SETCOND_SUB \ - N_FLAG = ((s32)res < 0) ? true : false; \ - Z_FLAG = (res == 0) ? true : false; \ - V_FLAG = ((NEG(lhs) & POS(rhs) & POS(res)) | \ - (POS(lhs) & NEG(rhs) & NEG(res))) ? true : false;\ - C_FLAG = ((NEG(lhs) & POS(rhs)) | \ - (NEG(lhs) & POS(res)) | \ - (POS(rhs) & POS(res))) ? true : false; - -#ifndef ALU_INIT_C - #define ALU_INIT_C \ - int dest = (opcode>>12) & 15; \ - bool C_OUT = C_FLAG; \ - u32 value; -#endif -// OP Rd,Rb,Rm LSL # -#ifndef VALUE_LSL_IMM_C - #define VALUE_LSL_IMM_C \ - unsigned int shift = (opcode >> 7) & 0x1F; \ - if (!shift) { /* LSL #0 most common? */ \ - value = bus.reg[opcode & 0x0F].I; \ - } else { \ - u32 v = bus.reg[opcode & 0x0F].I; \ - C_OUT = (v >> (32 - shift)) & 1 ? true : false; \ - value = v << shift; \ - } -#endif -// OP Rd,Rb,Rm LSL Rs -#ifndef VALUE_LSL_REG_C - #define VALUE_LSL_REG_C \ - unsigned int shift = bus.reg[(opcode >> 8)&15].B.B0; \ - if (shift) { \ - if (shift == 32) { \ - value = 0; \ - C_OUT = (bus.reg[opcode & 0x0F].I & 1 ? true : false);\ - } else if (shift < 32) { \ - u32 v = bus.reg[opcode & 0x0F].I; \ - C_OUT = (v >> (32 - shift)) & 1 ? true : false;\ - value = v << shift; \ - } else { \ - value = 0; \ - C_OUT = false; \ - } \ - } else { \ - value = bus.reg[opcode & 0x0F].I; \ - } -#endif -// OP Rd,Rb,Rm LSR # -#ifndef VALUE_LSR_IMM_C - #define VALUE_LSR_IMM_C \ - unsigned int shift = (opcode >> 7) & 0x1F; \ - if (shift) { \ - u32 v = bus.reg[opcode & 0x0F].I; \ - C_OUT = (v >> (shift - 1)) & 1 ? true : false; \ - value = v >> shift; \ - } else { \ - value = 0; \ - C_OUT = (bus.reg[opcode & 0x0F].I & 0x80000000) ? true : false;\ - } -#endif -// OP Rd,Rb,Rm LSR Rs -#ifndef VALUE_LSR_REG_C - #define VALUE_LSR_REG_C \ - unsigned int shift = bus.reg[(opcode >> 8)&15].B.B0; \ - if (shift) { \ - if (shift == 32) { \ - value = 0; \ - C_OUT = (bus.reg[opcode & 0x0F].I & 0x80000000 ? true : false);\ - } else if (shift < 32) { \ - u32 v = bus.reg[opcode & 0x0F].I; \ - C_OUT = (v >> (shift - 1)) & 1 ? true : false;\ - value = v >> shift; \ - } else { \ - value = 0; \ - C_OUT = false; \ - } \ - } else { \ - value = bus.reg[opcode & 0x0F].I; \ - } -#endif -// OP Rd,Rb,Rm ASR # -#ifndef VALUE_ASR_IMM_C - #define VALUE_ASR_IMM_C \ - unsigned int shift = (opcode >> 7) & 0x1F; \ - if (shift) { \ - s32 v = bus.reg[opcode & 0x0F].I; \ - C_OUT = (v >> (int)(shift - 1)) & 1 ? true : false;\ - value = v >> (int)shift; \ - } else { \ - if (bus.reg[opcode & 0x0F].I & 0x80000000) { \ - value = 0xFFFFFFFF; \ - C_OUT = true; \ - } else { \ - value = 0; \ - C_OUT = false; \ - } \ - } -#endif -// OP Rd,Rb,Rm ASR Rs -#ifndef VALUE_ASR_REG_C - #define VALUE_ASR_REG_C \ - unsigned int shift = bus.reg[(opcode >> 8)&15].B.B0; \ - if (shift < 32) { \ - if (shift) { \ - s32 v = bus.reg[opcode & 0x0F].I; \ - C_OUT = (v >> (int)(shift - 1)) & 1 ? true : false;\ - value = v >> (int)shift; \ - } else { \ - value = bus.reg[opcode & 0x0F].I; \ - } \ - } else { \ - if (bus.reg[opcode & 0x0F].I & 0x80000000) { \ - value = 0xFFFFFFFF; \ - C_OUT = true; \ - } else { \ - value = 0; \ - C_OUT = false; \ - } \ - } -#endif -// OP Rd,Rb,Rm ROR # -#ifndef VALUE_ROR_IMM_C - #define VALUE_ROR_IMM_C \ - unsigned int shift = (opcode >> 7) & 0x1F; \ - if (shift) { \ - u32 v = bus.reg[opcode & 0x0F].I; \ - C_OUT = (v >> (shift - 1)) & 1 ? true : false; \ - value = ((v << (32 - shift)) | \ - (v >> shift)); \ - } else { \ - u32 v = bus.reg[opcode & 0x0F].I; \ - C_OUT = (v & 1) ? true : false; \ - value = ((v >> 1) | \ - (C_FLAG << 31)); \ - } -#endif -// OP Rd,Rb,Rm ROR Rs -#ifndef VALUE_ROR_REG_C - #define VALUE_ROR_REG_C \ - unsigned int shift = bus.reg[(opcode >> 8)&15].B.B0; \ - if (shift & 0x1F) { \ - u32 v = bus.reg[opcode & 0x0F].I; \ - C_OUT = (v >> (shift - 1)) & 1 ? true : false; \ - value = ((v << (32 - shift)) | \ - (v >> shift)); \ - } else { \ - value = bus.reg[opcode & 0x0F].I; \ - if (shift) \ - C_OUT = (value & 0x80000000 ? true : false);\ - } -#endif -// OP Rd,Rb,# ROR # -#ifndef VALUE_IMM_C - #define VALUE_IMM_C \ - int shift = (opcode & 0xF00) >> 7; \ - if (shift) { \ - u32 v = opcode & 0xFF; \ - C_OUT = (v >> (shift - 1)) & 1 ? true : false; \ - value = ((v << (32 - shift)) | \ - (v >> shift)); \ - } else { \ - value = opcode & 0xFF; \ - } -#endif - -// Make the non-carry versions default to the carry versions -// (this is fine for C--the compiler will optimize the dead code out) -#ifndef ALU_INIT_NC - #define ALU_INIT_NC ALU_INIT_C -#endif -#ifndef VALUE_LSL_IMM_NC - #define VALUE_LSL_IMM_NC VALUE_LSL_IMM_C -#endif -#ifndef VALUE_LSL_REG_NC - #define VALUE_LSL_REG_NC VALUE_LSL_REG_C -#endif -#ifndef VALUE_LSR_IMM_NC - #define VALUE_LSR_IMM_NC VALUE_LSR_IMM_C -#endif -#ifndef VALUE_LSR_REG_NC - #define VALUE_LSR_REG_NC VALUE_LSR_REG_C -#endif -#ifndef VALUE_ASR_IMM_NC - #define VALUE_ASR_IMM_NC VALUE_ASR_IMM_C -#endif -#ifndef VALUE_ASR_REG_NC - #define VALUE_ASR_REG_NC VALUE_ASR_REG_C -#endif -#ifndef VALUE_ROR_IMM_NC - #define VALUE_ROR_IMM_NC VALUE_ROR_IMM_C -#endif -#ifndef VALUE_ROR_REG_NC - #define VALUE_ROR_REG_NC VALUE_ROR_REG_C -#endif -#ifndef VALUE_IMM_NC - #define VALUE_IMM_NC VALUE_IMM_C -#endif - -#define C_CHECK_PC(SETCOND) if (dest != 15) { SETCOND } -#ifndef OP_AND - #define OP_AND \ - u32 res = bus.reg[(opcode>>16)&15].I & value; \ - bus.reg[dest].I = res; -#endif -#ifndef OP_ANDS - #define OP_ANDS OP_AND C_CHECK_PC(C_SETCOND_LOGICAL) -#endif -#ifndef OP_EOR - #define OP_EOR \ - u32 res = bus.reg[(opcode>>16)&15].I ^ value; \ - bus.reg[dest].I = res; -#endif -#ifndef OP_EORS - #define OP_EORS OP_EOR C_CHECK_PC(C_SETCOND_LOGICAL) -#endif -#ifndef OP_SUB - #define OP_SUB \ - u32 lhs = bus.reg[(opcode>>16)&15].I; \ - u32 rhs = value; \ - u32 res = lhs - rhs; \ - bus.reg[dest].I = res; -#endif -#ifndef OP_SUBS - #define OP_SUBS OP_SUB C_CHECK_PC(C_SETCOND_SUB) -#endif -#ifndef OP_RSB - #define OP_RSB \ - u32 lhs = bus.reg[(opcode>>16)&15].I; \ - u32 rhs = value; \ - u32 res = rhs - lhs; \ - bus.reg[dest].I = res; -#endif -#ifndef OP_RSBS - #define OP_RSBS OP_RSB C_CHECK_PC(C_SETCOND_SUB) -#endif -#ifndef OP_ADD - #define OP_ADD \ - u32 lhs = bus.reg[(opcode>>16)&15].I; \ - u32 rhs = value; \ - u32 res = lhs + rhs; \ - bus.reg[dest].I = res; -#endif -#ifndef OP_ADDS - #define OP_ADDS OP_ADD C_CHECK_PC(C_SETCOND_ADD) -#endif -#ifndef OP_ADC - #define OP_ADC \ - u32 lhs = bus.reg[(opcode>>16)&15].I; \ - u32 rhs = value; \ - u32 res = lhs + rhs + (u32)C_FLAG; \ - bus.reg[dest].I = res; -#endif -#ifndef OP_ADCS - #define OP_ADCS OP_ADC C_CHECK_PC(C_SETCOND_ADD) -#endif -#ifndef OP_SBC - #define OP_SBC \ - u32 lhs = bus.reg[(opcode>>16)&15].I; \ - u32 rhs = value; \ - u32 res = lhs - rhs - !((u32)C_FLAG); \ - bus.reg[dest].I = res; -#endif -#ifndef OP_SBCS - #define OP_SBCS OP_SBC C_CHECK_PC(C_SETCOND_SUB) -#endif -#ifndef OP_RSC - #define OP_RSC \ - u32 lhs = bus.reg[(opcode>>16)&15].I; \ - u32 rhs = value; \ - u32 res = rhs - lhs - !((u32)C_FLAG); \ - bus.reg[dest].I = res; -#endif -#ifndef OP_RSCS - #define OP_RSCS OP_RSC C_CHECK_PC(C_SETCOND_SUB) -#endif -#ifndef OP_TST - #define OP_TST \ - u32 res = bus.reg[(opcode >> 16) & 0x0F].I & value; \ - C_SETCOND_LOGICAL; -#endif -#ifndef OP_TEQ - #define OP_TEQ \ - u32 res = bus.reg[(opcode >> 16) & 0x0F].I ^ value; \ - C_SETCOND_LOGICAL; -#endif -#ifndef OP_CMP - #define OP_CMP \ - u32 lhs = bus.reg[(opcode>>16)&15].I; \ - u32 rhs = value; \ - u32 res = lhs - rhs; \ - C_SETCOND_SUB; -#endif -#ifndef OP_CMN - #define OP_CMN \ - u32 lhs = bus.reg[(opcode>>16)&15].I; \ - u32 rhs = value; \ - u32 res = lhs + rhs; \ - C_SETCOND_ADD; -#endif -#ifndef OP_ORR - #define OP_ORR \ - u32 res = bus.reg[(opcode >> 16) & 0x0F].I | value; \ - bus.reg[dest].I = res; -#endif -#ifndef OP_ORRS - #define OP_ORRS OP_ORR C_CHECK_PC(C_SETCOND_LOGICAL) -#endif -#ifndef OP_MOV - #define OP_MOV \ - u32 res = value; \ - bus.reg[dest].I = res; -#endif -#ifndef OP_MOVS - #define OP_MOVS OP_MOV C_CHECK_PC(C_SETCOND_LOGICAL) -#endif -#ifndef OP_BIC - #define OP_BIC \ - u32 res = bus.reg[(opcode >> 16) & 0x0F].I & (~value); \ - bus.reg[dest].I = res; -#endif -#ifndef OP_BICS - #define OP_BICS OP_BIC C_CHECK_PC(C_SETCOND_LOGICAL) -#endif -#ifndef OP_MVN - #define OP_MVN \ - u32 res = ~value; \ - bus.reg[dest].I = res; -#endif -#ifndef OP_MVNS - #define OP_MVNS OP_MVN C_CHECK_PC(C_SETCOND_LOGICAL) -#endif - -#ifndef SETCOND_NONE - #define SETCOND_NONE /*nothing*/ -#endif -#ifndef SETCOND_MUL - #define SETCOND_MUL \ - N_FLAG = ((s32)bus.reg[dest].I < 0) ? true : false; \ - Z_FLAG = bus.reg[dest].I ? false : true; -#endif -#ifndef SETCOND_MULL - #define SETCOND_MULL \ - N_FLAG = (bus.reg[dest].I & 0x80000000) ? true : false;\ - Z_FLAG = bus.reg[dest].I || bus.reg[acc].I ? false : true; -#endif - -#ifndef ROR_IMM_MSR - #define ROR_IMM_MSR \ - u32 v = opcode & 0xff; \ - value = ((v << (32 - shift)) | (v >> shift)); -#endif -#ifndef ROR_OFFSET - #define ROR_OFFSET \ - offset = ((offset << (32 - shift)) | (offset >> shift)); -#endif -#ifndef RRX_OFFSET - #define RRX_OFFSET \ - offset = ((offset >> 1) | ((int)C_FLAG << 31)); -#endif - -// ALU ops (except multiply) ////////////////////////////////////////////// - -// ALU_INIT: init code (ALU_INIT_C or ALU_INIT_NC) -// GETVALUE: load value and shift/rotate (VALUE_XXX) -// OP: ALU operation (OP_XXX) -// MODECHANGE: MODECHANGE_NO or MODECHANGE_YES -// ISREGSHIFT: 1 for insns of the form ...,Rn LSL/etc Rs; 0 otherwise -// ALU_INIT, GETVALUE and OP are concatenated in order. -#define ALU_INSN(ALU_INIT, GETVALUE, OP, MODECHANGE, ISREGSHIFT) \ - ALU_INIT GETVALUE OP; \ - if ((opcode & 0x0000F000) != 0x0000F000) { \ - clockTicks = 1 + ISREGSHIFT \ - + codeTicksAccessSeq32(bus.armNextPC); \ - } else { \ - MODECHANGE; \ - if (armState) { \ - bus.reg[15].I &= 0xFFFFFFFC; \ - bus.armNextPC = bus.reg[15].I; \ - bus.reg[15].I += 4; \ - ARM_PREFETCH; \ - } else { \ - bus.reg[15].I &= 0xFFFFFFFE; \ - bus.armNextPC = bus.reg[15].I; \ - bus.reg[15].I += 2; \ - THUMB_PREFETCH; \ - } \ - clockTicks = 3 + ISREGSHIFT \ - + codeTicksAccess(bus.armNextPC, BITS_32) \ - + ((codeTicksAccessSeq32(bus.armNextPC)) << 1); \ - } - -#define MODECHANGE_NO /*nothing*/ -#define MODECHANGE_YES if(armMode != (bus.reg[17].I & 0x1f)) CPUSwitchMode(bus.reg[17].I & 0x1f, false, true); - -#define DEFINE_ALU_INSN_C(CODE1, CODE2, OP, MODECHANGE) \ - void arm##CODE1##0(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_LSL_IMM_C, OP_##OP, MODECHANGE_##MODECHANGE, 0); }\ - void arm##CODE1##1(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_LSL_REG_C, OP_##OP, MODECHANGE_##MODECHANGE, 1); }\ - void arm##CODE1##2(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_LSR_IMM_C, OP_##OP, MODECHANGE_##MODECHANGE, 0); }\ - void arm##CODE1##3(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_LSR_REG_C, OP_##OP, MODECHANGE_##MODECHANGE, 1); }\ - void arm##CODE1##4(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_ASR_IMM_C, OP_##OP, MODECHANGE_##MODECHANGE, 0); }\ - void arm##CODE1##5(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_ASR_REG_C, OP_##OP, MODECHANGE_##MODECHANGE, 1); }\ - void arm##CODE1##6(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_ROR_IMM_C, OP_##OP, MODECHANGE_##MODECHANGE, 0); }\ - void arm##CODE1##7(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_ROR_REG_C, OP_##OP, MODECHANGE_##MODECHANGE, 1); }\ - void arm##CODE2##0(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_IMM_C, OP_##OP, MODECHANGE_##MODECHANGE, 0); } -#define DEFINE_ALU_INSN_NC(CODE1, CODE2, OP, MODECHANGE) \ - void arm##CODE1##0(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_LSL_IMM_NC, OP_##OP, MODECHANGE_##MODECHANGE, 0); }\ - void arm##CODE1##1(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_LSL_REG_NC, OP_##OP, MODECHANGE_##MODECHANGE, 1); }\ - void arm##CODE1##2(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_LSR_IMM_NC, OP_##OP, MODECHANGE_##MODECHANGE, 0); }\ - void arm##CODE1##3(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_LSR_REG_NC, OP_##OP, MODECHANGE_##MODECHANGE, 1); }\ - void arm##CODE1##4(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_ASR_IMM_NC, OP_##OP, MODECHANGE_##MODECHANGE, 0); }\ - void arm##CODE1##5(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_ASR_REG_NC, OP_##OP, MODECHANGE_##MODECHANGE, 1); }\ - void arm##CODE1##6(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_ROR_IMM_NC, OP_##OP, MODECHANGE_##MODECHANGE, 0); }\ - void arm##CODE1##7(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_ROR_REG_NC, OP_##OP, MODECHANGE_##MODECHANGE, 1); }\ - void arm##CODE2##0(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_IMM_NC, OP_##OP, MODECHANGE_##MODECHANGE, 0); } - -// AND -DEFINE_ALU_INSN_NC(00, 20, AND, NO) -// ANDS -DEFINE_ALU_INSN_C (01, 21, ANDS, YES) - -// EOR -DEFINE_ALU_INSN_NC(02, 22, EOR, NO) -// EORS -DEFINE_ALU_INSN_C (03, 23, EORS, YES) - -// SUB -DEFINE_ALU_INSN_NC(04, 24, SUB, NO) -// SUBS -DEFINE_ALU_INSN_NC(05, 25, SUBS, YES) - -// RSB -DEFINE_ALU_INSN_NC(06, 26, RSB, NO) -// RSBS -DEFINE_ALU_INSN_NC(07, 27, RSBS, YES) - -// ADD -DEFINE_ALU_INSN_NC(08, 28, ADD, NO) -// ADDS -DEFINE_ALU_INSN_NC(09, 29, ADDS, YES) - -// ADC -DEFINE_ALU_INSN_NC(0A, 2A, ADC, NO) -// ADCS -DEFINE_ALU_INSN_NC(0B, 2B, ADCS, YES) - -// SBC -DEFINE_ALU_INSN_NC(0C, 2C, SBC, NO) -// SBCS -DEFINE_ALU_INSN_NC(0D, 2D, SBCS, YES) - -// RSC -DEFINE_ALU_INSN_NC(0E, 2E, RSC, NO) -// RSCS -DEFINE_ALU_INSN_NC(0F, 2F, RSCS, YES) - -// TST -DEFINE_ALU_INSN_C (11, 31, TST, NO) - -// TEQ -DEFINE_ALU_INSN_C (13, 33, TEQ, NO) - -// CMP -DEFINE_ALU_INSN_NC(15, 35, CMP, NO) - -// CMN -DEFINE_ALU_INSN_NC(17, 37, CMN, NO) - -// ORR -DEFINE_ALU_INSN_NC(18, 38, ORR, NO) -// ORRS -DEFINE_ALU_INSN_C (19, 39, ORRS, YES) - -// MOV -DEFINE_ALU_INSN_NC(1A, 3A, MOV, NO) -// MOVS -DEFINE_ALU_INSN_C (1B, 3B, MOVS, YES) - -// BIC -DEFINE_ALU_INSN_NC(1C, 3C, BIC, NO) -// BICS -DEFINE_ALU_INSN_C (1D, 3D, BICS, YES) - -// MVN -DEFINE_ALU_INSN_NC(1E, 3E, MVN, NO) -// MVNS -DEFINE_ALU_INSN_C (1F, 3F, MVNS, YES) - -// Multiply instructions ////////////////////////////////////////////////// - -// OP: OP_MUL, OP_MLA etc. -// SETCOND: SETCOND_NONE, SETCOND_MUL, or SETCOND_MULL -// CYCLES: base cycle count (1, 2, or 3) -#define MUL_INSN(OP, SETCOND, CYCLES) \ - int mult = (opcode & 0x0F); \ - u32 rs = bus.reg[(opcode >> 8) & 0x0F].I; \ - int acc = (opcode >> 12) & 0x0F; /* or destLo */ \ - int dest = (opcode >> 16) & 0x0F; /* or destHi */ \ - OP; \ - SETCOND; \ - if ((s32)rs < 0) \ - rs = ~rs; \ - if ((rs & 0xFFFF0000) == 0) \ - clockTicks += 1; \ - else if ((rs & 0xFF000000) == 0) \ - clockTicks += 2; \ - else \ - clockTicks += 3; \ - if (bus.busPrefetchCount == 0) \ - bus.busPrefetchCount = ((bus.busPrefetchCount+1)<> 32); -#define OP_MLAL(SIGN) \ - SIGN##64 res = ((SIGN##64)bus.reg[dest].I<<32 | bus.reg[acc].I)\ - + ((SIGN##64)(SIGN##32)bus.reg[mult].I \ - * (SIGN##64)(SIGN##32)rs); \ - bus.reg[acc].I = (u32)res; \ - bus.reg[dest].I = (u32)(res >> 32); -#define OP_UMULL OP_MULL(u) -#define OP_UMLAL OP_MLAL(u) -#define OP_SMULL OP_MULL(s) -#define OP_SMLAL OP_MLAL(s) - -// MUL Rd, Rm, Rs - void arm009(u32 opcode) { MUL_INSN(OP_MUL, SETCOND_NONE, 1); } -// MULS Rd, Rm, Rs - void arm019(u32 opcode) { MUL_INSN(OP_MUL, SETCOND_MUL, 1); } - -// MLA Rd, Rm, Rs, Rn - void arm029(u32 opcode) { MUL_INSN(OP_MLA, SETCOND_NONE, 2); } -// MLAS Rd, Rm, Rs, Rn - void arm039(u32 opcode) { MUL_INSN(OP_MLA, SETCOND_MUL, 2); } - -// UMULL RdLo, RdHi, Rn, Rs - void arm089(u32 opcode) { MUL_INSN(OP_UMULL, SETCOND_NONE, 2); } -// UMULLS RdLo, RdHi, Rn, Rs - void arm099(u32 opcode) { MUL_INSN(OP_UMULL, SETCOND_MULL, 2); } - -// UMLAL RdLo, RdHi, Rn, Rs - void arm0A9(u32 opcode) { MUL_INSN(OP_UMLAL, SETCOND_NONE, 3); } -// UMLALS RdLo, RdHi, Rn, Rs - void arm0B9(u32 opcode) { MUL_INSN(OP_UMLAL, SETCOND_MULL, 3); } - -// SMULL RdLo, RdHi, Rm, Rs - void arm0C9(u32 opcode) { MUL_INSN(OP_SMULL, SETCOND_NONE, 2); } -// SMULLS RdLo, RdHi, Rm, Rs - void arm0D9(u32 opcode) { MUL_INSN(OP_SMULL, SETCOND_MULL, 2); } - -// SMLAL RdLo, RdHi, Rm, Rs - void arm0E9(u32 opcode) { MUL_INSN(OP_SMLAL, SETCOND_NONE, 3); } -// SMLALS RdLo, RdHi, Rm, Rs - void arm0F9(u32 opcode) { MUL_INSN(OP_SMLAL, SETCOND_MULL, 3); } - -// Misc instructions ////////////////////////////////////////////////////// - -// SWP Rd, Rm, [Rn] - void arm109(u32 opcode) -{ - u32 address = bus.reg[(opcode >> 16) & 15].I; - u32 temp = CPUReadMemory(address); - CPUWriteMemory(address, bus.reg[opcode&15].I); - bus.reg[(opcode >> 12) & 15].I = temp; - int dataticks_value = DATATICKS_ACCESS_32BIT(address); - DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); - clockTicks = 4 + (dataticks_value << 1) + codeTicksAccess(bus.armNextPC, BITS_32); -} - -// SWPB Rd, Rm, [Rn] - void arm149(u32 opcode) -{ - u32 address = bus.reg[(opcode >> 16) & 15].I; - u32 temp = CPUReadByte(address); - CPUWriteByte(address, bus.reg[opcode&15].B.B0); - bus.reg[(opcode>>12)&15].I = temp; - u32 dataticks_value = DATATICKS_ACCESS_32BIT(address); - DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); - clockTicks = 4 + (dataticks_value << 1) + codeTicksAccess(bus.armNextPC, BITS_32); -} - -// MRS Rd, CPSR - void arm100(u32 opcode) -{ - if ((opcode & 0x0FFF0FFF) == 0x010F0000) - { - CPU_UPDATE_CPSR(); - bus.reg[(opcode >> 12) & 0x0F].I = bus.reg[16].I; - } - else - armUnknownInsn(opcode); -} - -// MRS Rd, SPSR - void arm140(u32 opcode) -{ - if ((opcode & 0x0FFF0FFF) == 0x014F0000) - bus.reg[(opcode >> 12) & 0x0F].I = bus.reg[17].I; - else - armUnknownInsn(opcode); -} - -// MSR CPSR_fields, Rm - void arm120(u32 opcode) -{ - if ((opcode & 0x0FF0FFF0) == 0x0120F000) - { - CPU_UPDATE_CPSR(); - u32 value = bus.reg[opcode & 15].I; - u32 newValue = bus.reg[16].I; - if (armMode > 0x10) { - if (opcode & 0x00010000) - newValue = (newValue & 0xFFFFFF00) | (value & 0x000000FF); - if (opcode & 0x00020000) - newValue = (newValue & 0xFFFF00FF) | (value & 0x0000FF00); - if (opcode & 0x00040000) - newValue = (newValue & 0xFF00FFFF) | (value & 0x00FF0000); - } - if (opcode & 0x00080000) - newValue = (newValue & 0x00FFFFFF) | (value & 0xFF000000); - newValue |= 0x10; - if(armMode != (newValue & 0x1F)) - CPUSwitchMode(newValue & 0x1F, false, true); - bus.reg[16].I = newValue; - CPUUpdateFlags(1); - if (!armState) { // this should not be allowed, but it seems to work - THUMB_PREFETCH; - bus.reg[15].I = bus.armNextPC + 2; - } - } - else - armUnknownInsn(opcode); -} - -// MSR SPSR_fields, Rm - void arm160(u32 opcode) -{ - if ((opcode & 0x0FF0FFF0) == 0x0160F000) - { - u32 value = bus.reg[opcode & 15].I; - if (armMode > 0x10 && armMode < 0x1F) - { - if (opcode & 0x00010000) - bus.reg[17].I = (bus.reg[17].I & 0xFFFFFF00) | (value & 0x000000FF); - if (opcode & 0x00020000) - bus.reg[17].I = (bus.reg[17].I & 0xFFFF00FF) | (value & 0x0000FF00); - if (opcode & 0x00040000) - bus.reg[17].I = (bus.reg[17].I & 0xFF00FFFF) | (value & 0x00FF0000); - if (opcode & 0x00080000) - bus.reg[17].I = (bus.reg[17].I & 0x00FFFFFF) | (value & 0xFF000000); - } - } - else - armUnknownInsn(opcode); -} - -// MSR CPSR_fields, # - void arm320(u32 opcode) -{ - if ((opcode & 0x0FF0F000) == 0x0320F000) - { - CPU_UPDATE_CPSR(); - u32 value = opcode & 0xFF; - int shift = (opcode & 0xF00) >> 7; - if (shift) { - ROR_IMM_MSR; - } - u32 newValue = bus.reg[16].I; - if (armMode > 0x10) { - if (opcode & 0x00010000) - newValue = (newValue & 0xFFFFFF00) | (value & 0x000000FF); - if (opcode & 0x00020000) - newValue = (newValue & 0xFFFF00FF) | (value & 0x0000FF00); - if (opcode & 0x00040000) - newValue = (newValue & 0xFF00FFFF) | (value & 0x00FF0000); - } - if (opcode & 0x00080000) - newValue = (newValue & 0x00FFFFFF) | (value & 0xFF000000); - - newValue |= 0x10; - - if(armMode != (newValue & 0x1F)) - CPUSwitchMode(newValue & 0x1F, false, true); - bus.reg[16].I = newValue; - CPUUpdateFlags(1); - if (!armState) { // this should not be allowed, but it seems to work - THUMB_PREFETCH; - bus.reg[15].I = bus.armNextPC + 2; - } - } - else - armUnknownInsn(opcode); -} - -// MSR SPSR_fields, # - void arm360(u32 opcode) -{ - if ((opcode & 0x0FF0F000) == 0x0360F000) { - if (armMode > 0x10 && armMode < 0x1F) { - u32 value = opcode & 0xFF; - int shift = (opcode & 0xF00) >> 7; - if (shift) { - ROR_IMM_MSR; - } - if (opcode & 0x00010000) - bus.reg[17].I = (bus.reg[17].I & 0xFFFFFF00) | (value & 0x000000FF); - if (opcode & 0x00020000) - bus.reg[17].I = (bus.reg[17].I & 0xFFFF00FF) | (value & 0x0000FF00); - if (opcode & 0x00040000) - bus.reg[17].I = (bus.reg[17].I & 0xFF00FFFF) | (value & 0x00FF0000); - if (opcode & 0x00080000) - bus.reg[17].I = (bus.reg[17].I & 0x00FFFFFF) | (value & 0xFF000000); - } - } - else - armUnknownInsn(opcode); -} - -// BX Rm - void arm121(u32 opcode) -{ - if ((opcode & 0x0FFFFFF0) == 0x012FFF10) { - int base = opcode & 0x0F; - bus.busPrefetchCount = 0; - armState = bus.reg[base].I & 1 ? false : true; - if (armState) { - bus.reg[15].I = bus.reg[base].I & 0xFFFFFFFC; - bus.armNextPC = bus.reg[15].I; - bus.reg[15].I += 4; - ARM_PREFETCH; - clockTicks = 3 + (codeTicksAccessSeq32(bus.armNextPC)<<1) - + codeTicksAccess(bus.armNextPC, BITS_32); - } else { - bus.reg[15].I = bus.reg[base].I & 0xFFFFFFFE; - bus.armNextPC = bus.reg[15].I; - bus.reg[15].I += 2; - THUMB_PREFETCH; - clockTicks = 3 + (codeTicksAccessSeq16(bus.armNextPC)<<1) - + codeTicksAccess(bus.armNextPC, BITS_16); - } - } - else - armUnknownInsn(opcode); -} - -// Load/store ///////////////////////////////////////////////////////////// - -#define OFFSET_IMM \ - int offset = opcode & 0xFFF; -#define OFFSET_IMM8 \ - int offset = ((opcode & 0x0F) | ((opcode>>4) & 0xF0)); -#define OFFSET_REG \ - int offset = bus.reg[opcode & 15].I; -#define OFFSET_LSL \ - int offset = bus.reg[opcode & 15].I << ((opcode>>7) & 31); -#define OFFSET_LSR \ - int shift = (opcode >> 7) & 31; \ - int offset = shift ? bus.reg[opcode & 15].I >> shift : 0; -#define OFFSET_ASR \ - int shift = (opcode >> 7) & 31; \ - int offset; \ - if (shift) \ - offset = (int)((s32)bus.reg[opcode & 15].I >> shift);\ - else if (bus.reg[opcode & 15].I & 0x80000000) \ - offset = 0xFFFFFFFF; \ - else \ - offset = 0; -#define OFFSET_ROR \ - int shift = (opcode >> 7) & 31; \ - u32 offset = bus.reg[opcode & 15].I; \ - if (shift) { \ - ROR_OFFSET; \ - } else { \ - RRX_OFFSET; \ - } - -#define ADDRESS_POST (bus.reg[base].I) -#define ADDRESS_PREDEC (bus.reg[base].I - offset) -#define ADDRESS_PREINC (bus.reg[base].I + offset) - -#define OP_STR CPUWriteMemory(address, bus.reg[dest].I) -#define OP_STRH CPUWriteHalfWord(address, bus.reg[dest].W.W0) -#define OP_STRB CPUWriteByte(address, bus.reg[dest].B.B0) -#define OP_LDR bus.reg[dest].I = CPUReadMemory(address) -#define OP_LDRH bus.reg[dest].I = CPUReadHalfWord(address) -#define OP_LDRB bus.reg[dest].I = CPUReadByte(address) -#define OP_LDRSH bus.reg[dest].I = (s16)CPUReadHalfWordSigned(address) -#define OP_LDRSB bus.reg[dest].I = (s8)CPUReadByte(address) - -#define WRITEBACK_NONE /*nothing*/ -#define WRITEBACK_PRE bus.reg[base].I = address -#define WRITEBACK_POSTDEC bus.reg[base].I = address - offset -#define WRITEBACK_POSTINC bus.reg[base].I = address + offset - -#define LDRSTR_INIT(CALC_OFFSET, CALC_ADDRESS) \ - if (bus.busPrefetchCount == 0) \ - bus.busPrefetch = bus.busPrefetchEnable; \ - int dest = (opcode >> 12) & 15; \ - int base = (opcode >> 16) & 15; \ - CALC_OFFSET; \ - u32 address = CALC_ADDRESS; - -#define STR(CALC_OFFSET, CALC_ADDRESS, STORE_DATA, WRITEBACK1, WRITEBACK2, SIZE) \ - LDRSTR_INIT(CALC_OFFSET, CALC_ADDRESS); \ - WRITEBACK1; \ - STORE_DATA; \ - WRITEBACK2; \ - int dataticks_val; \ - if(SIZE == 32) \ - dataticks_val = DATATICKS_ACCESS_32BIT(address); \ - else \ - dataticks_val = DATATICKS_ACCESS_16BIT(address); \ - DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_val); \ - clockTicks = 2 + dataticks_val + codeTicksAccess(bus.armNextPC, BITS_32); - -#define LDR(CALC_OFFSET, CALC_ADDRESS, LOAD_DATA, WRITEBACK, SIZE) \ - LDRSTR_INIT(CALC_OFFSET, CALC_ADDRESS); \ - LOAD_DATA; \ - if (dest != base) \ - { \ - WRITEBACK; \ - } \ - clockTicks = 0; \ - int dataticks_value; \ - if (dest == 15) { \ - bus.reg[15].I &= 0xFFFFFFFC; \ - bus.armNextPC = bus.reg[15].I; \ - bus.reg[15].I += 4; \ - ARM_PREFETCH; \ - dataticks_value = DATATICKS_ACCESS_32BIT_SEQ(address); \ - DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); \ - clockTicks += 2 + (dataticks_value << 1);\ - } \ - if(SIZE == 32) \ - dataticks_value = DATATICKS_ACCESS_32BIT(address); \ - else \ - dataticks_value = DATATICKS_ACCESS_16BIT(address); \ - DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); \ - clockTicks += 3 + dataticks_value + codeTicksAccess(bus.armNextPC, BITS_32); -#define STR_POSTDEC(CALC_OFFSET, STORE_DATA, SIZE) \ - STR(CALC_OFFSET, ADDRESS_POST, STORE_DATA, WRITEBACK_NONE, WRITEBACK_POSTDEC, SIZE) -#define STR_POSTINC(CALC_OFFSET, STORE_DATA, SIZE) \ - STR(CALC_OFFSET, ADDRESS_POST, STORE_DATA, WRITEBACK_NONE, WRITEBACK_POSTINC, SIZE) -#define STR_PREDEC(CALC_OFFSET, STORE_DATA, SIZE) \ - STR(CALC_OFFSET, ADDRESS_PREDEC, STORE_DATA, WRITEBACK_NONE, WRITEBACK_NONE, SIZE) -#define STR_PREDEC_WB(CALC_OFFSET, STORE_DATA, SIZE) \ - STR(CALC_OFFSET, ADDRESS_PREDEC, STORE_DATA, WRITEBACK_PRE, WRITEBACK_NONE, SIZE) -#define STR_PREINC(CALC_OFFSET, STORE_DATA, SIZE) \ - STR(CALC_OFFSET, ADDRESS_PREINC, STORE_DATA, WRITEBACK_NONE, WRITEBACK_NONE, SIZE) -#define STR_PREINC_WB(CALC_OFFSET, STORE_DATA, SIZE) \ - STR(CALC_OFFSET, ADDRESS_PREINC, STORE_DATA, WRITEBACK_PRE, WRITEBACK_NONE, SIZE) -#define LDR_POSTDEC(CALC_OFFSET, LOAD_DATA, SIZE) \ - LDR(CALC_OFFSET, ADDRESS_POST, LOAD_DATA, WRITEBACK_POSTDEC, SIZE) -#define LDR_POSTINC(CALC_OFFSET, LOAD_DATA, SIZE) \ - LDR(CALC_OFFSET, ADDRESS_POST, LOAD_DATA, WRITEBACK_POSTINC, SIZE) -#define LDR_PREDEC(CALC_OFFSET, LOAD_DATA, SIZE) \ - LDR(CALC_OFFSET, ADDRESS_PREDEC, LOAD_DATA, WRITEBACK_NONE, SIZE) -#define LDR_PREDEC_WB(CALC_OFFSET, LOAD_DATA, SIZE) \ - LDR(CALC_OFFSET, ADDRESS_PREDEC, LOAD_DATA, WRITEBACK_PRE, SIZE) -#define LDR_PREINC(CALC_OFFSET, LOAD_DATA, SIZE) \ - LDR(CALC_OFFSET, ADDRESS_PREINC, LOAD_DATA, WRITEBACK_NONE, SIZE) -#define LDR_PREINC_WB(CALC_OFFSET, LOAD_DATA, SIZE) \ - LDR(CALC_OFFSET, ADDRESS_PREINC, LOAD_DATA, WRITEBACK_PRE, SIZE) - -// STRH Rd, [Rn], -Rm - void arm00B(u32 opcode) { STR_POSTDEC(OFFSET_REG, OP_STRH, 16); } -// STRH Rd, [Rn], #-offset - void arm04B(u32 opcode) { STR_POSTDEC(OFFSET_IMM8, OP_STRH, 16); } -// STRH Rd, [Rn], Rm - void arm08B(u32 opcode) { STR_POSTINC(OFFSET_REG, OP_STRH, 16); } -// STRH Rd, [Rn], #offset - void arm0CB(u32 opcode) { STR_POSTINC(OFFSET_IMM8, OP_STRH, 16); } -// STRH Rd, [Rn, -Rm] - void arm10B(u32 opcode) { STR_PREDEC(OFFSET_REG, OP_STRH, 16); } -// STRH Rd, [Rn, -Rm]! - void arm12B(u32 opcode) { STR_PREDEC_WB(OFFSET_REG, OP_STRH, 16); } -// STRH Rd, [Rn, -#offset] - void arm14B(u32 opcode) { STR_PREDEC(OFFSET_IMM8, OP_STRH, 16); } -// STRH Rd, [Rn, -#offset]! - void arm16B(u32 opcode) { STR_PREDEC_WB(OFFSET_IMM8, OP_STRH, 16); } -// STRH Rd, [Rn, Rm] - void arm18B(u32 opcode) { STR_PREINC(OFFSET_REG, OP_STRH, 16); } -// STRH Rd, [Rn, Rm]! - void arm1AB(u32 opcode) { STR_PREINC_WB(OFFSET_REG, OP_STRH, 16); } -// STRH Rd, [Rn, #offset] - void arm1CB(u32 opcode) { STR_PREINC(OFFSET_IMM8, OP_STRH, 16); } -// STRH Rd, [Rn, #offset]! - void arm1EB(u32 opcode) { STR_PREINC_WB(OFFSET_IMM8, OP_STRH, 16); } - -// LDRH Rd, [Rn], -Rm - void arm01B(u32 opcode) { LDR_POSTDEC(OFFSET_REG, OP_LDRH, 16); } -// LDRH Rd, [Rn], #-offset - void arm05B(u32 opcode) { LDR_POSTDEC(OFFSET_IMM8, OP_LDRH, 16); } -// LDRH Rd, [Rn], Rm - void arm09B(u32 opcode) { LDR_POSTINC(OFFSET_REG, OP_LDRH, 16); } -// LDRH Rd, [Rn], #offset - void arm0DB(u32 opcode) { LDR_POSTINC(OFFSET_IMM8, OP_LDRH, 16); } -// LDRH Rd, [Rn, -Rm] - void arm11B(u32 opcode) { LDR_PREDEC(OFFSET_REG, OP_LDRH, 16); } -// LDRH Rd, [Rn, -Rm]! - void arm13B(u32 opcode) { LDR_PREDEC_WB(OFFSET_REG, OP_LDRH, 16); } -// LDRH Rd, [Rn, -#offset] - void arm15B(u32 opcode) { LDR_PREDEC(OFFSET_IMM8, OP_LDRH, 16); } -// LDRH Rd, [Rn, -#offset]! - void arm17B(u32 opcode) { LDR_PREDEC_WB(OFFSET_IMM8, OP_LDRH, 16); } -// LDRH Rd, [Rn, Rm] - void arm19B(u32 opcode) { LDR_PREINC(OFFSET_REG, OP_LDRH, 16); } -// LDRH Rd, [Rn, Rm]! - void arm1BB(u32 opcode) { LDR_PREINC_WB(OFFSET_REG, OP_LDRH, 16); } -// LDRH Rd, [Rn, #offset] - void arm1DB(u32 opcode) { LDR_PREINC(OFFSET_IMM8, OP_LDRH, 16); } -// LDRH Rd, [Rn, #offset]! - void arm1FB(u32 opcode) { LDR_PREINC_WB(OFFSET_IMM8, OP_LDRH, 16); } - -// LDRSB Rd, [Rn], -Rm - void arm01D(u32 opcode) { LDR_POSTDEC(OFFSET_REG, OP_LDRSB, 16); } -// LDRSB Rd, [Rn], #-offset - void arm05D(u32 opcode) { LDR_POSTDEC(OFFSET_IMM8, OP_LDRSB, 16); } -// LDRSB Rd, [Rn], Rm - void arm09D(u32 opcode) { LDR_POSTINC(OFFSET_REG, OP_LDRSB, 16); } -// LDRSB Rd, [Rn], #offset - void arm0DD(u32 opcode) { LDR_POSTINC(OFFSET_IMM8, OP_LDRSB, 16); } -// LDRSB Rd, [Rn, -Rm] - void arm11D(u32 opcode) { LDR_PREDEC(OFFSET_REG, OP_LDRSB, 16); } -// LDRSB Rd, [Rn, -Rm]! - void arm13D(u32 opcode) { LDR_PREDEC_WB(OFFSET_REG, OP_LDRSB, 16); } -// LDRSB Rd, [Rn, -#offset] - void arm15D(u32 opcode) { LDR_PREDEC(OFFSET_IMM8, OP_LDRSB, 16); } -// LDRSB Rd, [Rn, -#offset]! - void arm17D(u32 opcode) { LDR_PREDEC_WB(OFFSET_IMM8, OP_LDRSB, 16); } -// LDRSB Rd, [Rn, Rm] - void arm19D(u32 opcode) { LDR_PREINC(OFFSET_REG, OP_LDRSB, 16); } -// LDRSB Rd, [Rn, Rm]! - void arm1BD(u32 opcode) { LDR_PREINC_WB(OFFSET_REG, OP_LDRSB, 16); } -// LDRSB Rd, [Rn, #offset] - void arm1DD(u32 opcode) { LDR_PREINC(OFFSET_IMM8, OP_LDRSB, 16); } -// LDRSB Rd, [Rn, #offset]! - void arm1FD(u32 opcode) { LDR_PREINC_WB(OFFSET_IMM8, OP_LDRSB, 16); } - -// LDRSH Rd, [Rn], -Rm - void arm01F(u32 opcode) { LDR_POSTDEC(OFFSET_REG, OP_LDRSH, 16); } -// LDRSH Rd, [Rn], #-offset - void arm05F(u32 opcode) { LDR_POSTDEC(OFFSET_IMM8, OP_LDRSH, 16); } -// LDRSH Rd, [Rn], Rm - void arm09F(u32 opcode) { LDR_POSTINC(OFFSET_REG, OP_LDRSH, 16); } -// LDRSH Rd, [Rn], #offset - void arm0DF(u32 opcode) { LDR_POSTINC(OFFSET_IMM8, OP_LDRSH, 16); } -// LDRSH Rd, [Rn, -Rm] - void arm11F(u32 opcode) { LDR_PREDEC(OFFSET_REG, OP_LDRSH, 16); } -// LDRSH Rd, [Rn, -Rm]! - void arm13F(u32 opcode) { LDR_PREDEC_WB(OFFSET_REG, OP_LDRSH, 16); } -// LDRSH Rd, [Rn, -#offset] - void arm15F(u32 opcode) { LDR_PREDEC(OFFSET_IMM8, OP_LDRSH, 16); } -// LDRSH Rd, [Rn, -#offset]! - void arm17F(u32 opcode) { LDR_PREDEC_WB(OFFSET_IMM8, OP_LDRSH, 16); } -// LDRSH Rd, [Rn, Rm] - void arm19F(u32 opcode) { LDR_PREINC(OFFSET_REG, OP_LDRSH, 16); } -// LDRSH Rd, [Rn, Rm]! - void arm1BF(u32 opcode) { LDR_PREINC_WB(OFFSET_REG, OP_LDRSH, 16); } -// LDRSH Rd, [Rn, #offset] - void arm1DF(u32 opcode) { LDR_PREINC(OFFSET_IMM8, OP_LDRSH, 16); } -// LDRSH Rd, [Rn, #offset]! - void arm1FF(u32 opcode) { LDR_PREINC_WB(OFFSET_IMM8, OP_LDRSH, 16); } - -// STR[T] Rd, [Rn], -# -// Note: STR and STRT do the same thing on the GBA (likewise for LDR/LDRT etc) - void arm400(u32 opcode) { STR_POSTDEC(OFFSET_IMM, OP_STR, 32); } -// LDR[T] Rd, [Rn], -# - void arm410(u32 opcode) { LDR_POSTDEC(OFFSET_IMM, OP_LDR, 32); } -// STRB[T] Rd, [Rn], -# - void arm440(u32 opcode) { STR_POSTDEC(OFFSET_IMM, OP_STRB, 16); } -// LDRB[T] Rd, [Rn], -# - void arm450(u32 opcode) { LDR_POSTDEC(OFFSET_IMM, OP_LDRB, 16); } -// STR[T] Rd, [Rn], # - void arm480(u32 opcode) { STR_POSTINC(OFFSET_IMM, OP_STR, 32); } -// LDR Rd, [Rn], # - void arm490(u32 opcode) { LDR_POSTINC(OFFSET_IMM, OP_LDR, 32); } -// STRB[T] Rd, [Rn], # - void arm4C0(u32 opcode) { STR_POSTINC(OFFSET_IMM, OP_STRB, 16); } -// LDRB[T] Rd, [Rn], # - void arm4D0(u32 opcode) { LDR_POSTINC(OFFSET_IMM, OP_LDRB, 16); } -// STR Rd, [Rn, -#] - void arm500(u32 opcode) { STR_PREDEC(OFFSET_IMM, OP_STR, 32); } -// LDR Rd, [Rn, -#] - void arm510(u32 opcode) { LDR_PREDEC(OFFSET_IMM, OP_LDR, 32); } -// STR Rd, [Rn, -#]! - void arm520(u32 opcode) { STR_PREDEC_WB(OFFSET_IMM, OP_STR, 32); } -// LDR Rd, [Rn, -#]! - void arm530(u32 opcode) { LDR_PREDEC_WB(OFFSET_IMM, OP_LDR, 32); } -// STRB Rd, [Rn, -#] - void arm540(u32 opcode) { STR_PREDEC(OFFSET_IMM, OP_STRB, 16); } -// LDRB Rd, [Rn, -#] - void arm550(u32 opcode) { LDR_PREDEC(OFFSET_IMM, OP_LDRB, 16); } -// STRB Rd, [Rn, -#]! - void arm560(u32 opcode) { STR_PREDEC_WB(OFFSET_IMM, OP_STRB, 16); } -// LDRB Rd, [Rn, -#]! - void arm570(u32 opcode) { LDR_PREDEC_WB(OFFSET_IMM, OP_LDRB, 16); } -// STR Rd, [Rn, #] - void arm580(u32 opcode) { STR_PREINC(OFFSET_IMM, OP_STR, 32); } -// LDR Rd, [Rn, #] - void arm590(u32 opcode) { LDR_PREINC(OFFSET_IMM, OP_LDR, 32); } -// STR Rd, [Rn, #]! - void arm5A0(u32 opcode) { STR_PREINC_WB(OFFSET_IMM, OP_STR, 32); } -// LDR Rd, [Rn, #]! - void arm5B0(u32 opcode) { LDR_PREINC_WB(OFFSET_IMM, OP_LDR, 32); } -// STRB Rd, [Rn, #] - void arm5C0(u32 opcode) { STR_PREINC(OFFSET_IMM, OP_STRB, 16); } -// LDRB Rd, [Rn, #] - void arm5D0(u32 opcode) { LDR_PREINC(OFFSET_IMM, OP_LDRB, 16); } -// STRB Rd, [Rn, #]! - void arm5E0(u32 opcode) { STR_PREINC_WB(OFFSET_IMM, OP_STRB, 16); } -// LDRB Rd, [Rn, #]! - void arm5F0(u32 opcode) { LDR_PREINC_WB(OFFSET_IMM, OP_LDRB, 16); } - -// STR[T] Rd, [Rn], -Rm, LSL # - void arm600(u32 opcode) { STR_POSTDEC(OFFSET_LSL, OP_STR, 32); } -// STR[T] Rd, [Rn], -Rm, LSR # - void arm602(u32 opcode) { STR_POSTDEC(OFFSET_LSR, OP_STR, 32); } -// STR[T] Rd, [Rn], -Rm, ASR # - void arm604(u32 opcode) { STR_POSTDEC(OFFSET_ASR, OP_STR, 32); } -// STR[T] Rd, [Rn], -Rm, ROR # - void arm606(u32 opcode) { STR_POSTDEC(OFFSET_ROR, OP_STR, 32); } -// LDR[T] Rd, [Rn], -Rm, LSL # - void arm610(u32 opcode) { LDR_POSTDEC(OFFSET_LSL, OP_LDR, 32); } -// LDR[T] Rd, [Rn], -Rm, LSR # - void arm612(u32 opcode) { LDR_POSTDEC(OFFSET_LSR, OP_LDR, 32); } -// LDR[T] Rd, [Rn], -Rm, ASR # - void arm614(u32 opcode) { LDR_POSTDEC(OFFSET_ASR, OP_LDR, 32); } -// LDR[T] Rd, [Rn], -Rm, ROR # - void arm616(u32 opcode) { LDR_POSTDEC(OFFSET_ROR, OP_LDR, 32); } -// STRB[T] Rd, [Rn], -Rm, LSL # - void arm640(u32 opcode) { STR_POSTDEC(OFFSET_LSL, OP_STRB, 16); } -// STRB[T] Rd, [Rn], -Rm, LSR # - void arm642(u32 opcode) { STR_POSTDEC(OFFSET_LSR, OP_STRB, 16); } -// STRB[T] Rd, [Rn], -Rm, ASR # - void arm644(u32 opcode) { STR_POSTDEC(OFFSET_ASR, OP_STRB, 16); } -// STRB[T] Rd, [Rn], -Rm, ROR # - void arm646(u32 opcode) { STR_POSTDEC(OFFSET_ROR, OP_STRB, 16); } -// LDRB[T] Rd, [Rn], -Rm, LSL # - void arm650(u32 opcode) { LDR_POSTDEC(OFFSET_LSL, OP_LDRB, 16); } -// LDRB[T] Rd, [Rn], -Rm, LSR # - void arm652(u32 opcode) { LDR_POSTDEC(OFFSET_LSR, OP_LDRB, 16); } -// LDRB[T] Rd, [Rn], -Rm, ASR # - void arm654(u32 opcode) { LDR_POSTDEC(OFFSET_ASR, OP_LDRB, 16); } -// LDRB Rd, [Rn], -Rm, ROR # - void arm656(u32 opcode) { LDR_POSTDEC(OFFSET_ROR, OP_LDRB, 16); } -// STR[T] Rd, [Rn], Rm, LSL # - void arm680(u32 opcode) { STR_POSTINC(OFFSET_LSL, OP_STR, 32); } -// STR[T] Rd, [Rn], Rm, LSR # - void arm682(u32 opcode) { STR_POSTINC(OFFSET_LSR, OP_STR, 32); } -// STR[T] Rd, [Rn], Rm, ASR # - void arm684(u32 opcode) { STR_POSTINC(OFFSET_ASR, OP_STR, 32); } -// STR[T] Rd, [Rn], Rm, ROR # - void arm686(u32 opcode) { STR_POSTINC(OFFSET_ROR, OP_STR, 32); } -// LDR[T] Rd, [Rn], Rm, LSL # - void arm690(u32 opcode) { LDR_POSTINC(OFFSET_LSL, OP_LDR, 32); } -// LDR[T] Rd, [Rn], Rm, LSR # - void arm692(u32 opcode) { LDR_POSTINC(OFFSET_LSR, OP_LDR, 32); } -// LDR[T] Rd, [Rn], Rm, ASR # - void arm694(u32 opcode) { LDR_POSTINC(OFFSET_ASR, OP_LDR, 32); } -// LDR[T] Rd, [Rn], Rm, ROR # - void arm696(u32 opcode) { LDR_POSTINC(OFFSET_ROR, OP_LDR, 32); } -// STRB[T] Rd, [Rn], Rm, LSL # - void arm6C0(u32 opcode) { STR_POSTINC(OFFSET_LSL, OP_STRB, 16); } -// STRB[T] Rd, [Rn], Rm, LSR # - void arm6C2(u32 opcode) { STR_POSTINC(OFFSET_LSR, OP_STRB, 16); } -// STRB[T] Rd, [Rn], Rm, ASR # - void arm6C4(u32 opcode) { STR_POSTINC(OFFSET_ASR, OP_STRB, 16); } -// STRB[T] Rd, [Rn], Rm, ROR # - void arm6C6(u32 opcode) { STR_POSTINC(OFFSET_ROR, OP_STRB, 16); } -// LDRB[T] Rd, [Rn], Rm, LSL # - void arm6D0(u32 opcode) { LDR_POSTINC(OFFSET_LSL, OP_LDRB, 16); } -// LDRB[T] Rd, [Rn], Rm, LSR # - void arm6D2(u32 opcode) { LDR_POSTINC(OFFSET_LSR, OP_LDRB, 16); } -// LDRB[T] Rd, [Rn], Rm, ASR # - void arm6D4(u32 opcode) { LDR_POSTINC(OFFSET_ASR, OP_LDRB, 16); } -// LDRB[T] Rd, [Rn], Rm, ROR # - void arm6D6(u32 opcode) { LDR_POSTINC(OFFSET_ROR, OP_LDRB, 16); } -// STR Rd, [Rn, -Rm, LSL #] - void arm700(u32 opcode) { STR_PREDEC(OFFSET_LSL, OP_STR, 32); } -// STR Rd, [Rn, -Rm, LSR #] - void arm702(u32 opcode) { STR_PREDEC(OFFSET_LSR, OP_STR, 32); } -// STR Rd, [Rn, -Rm, ASR #] - void arm704(u32 opcode) { STR_PREDEC(OFFSET_ASR, OP_STR, 32); } -// STR Rd, [Rn, -Rm, ROR #] - void arm706(u32 opcode) { STR_PREDEC(OFFSET_ROR, OP_STR, 32); } -// LDR Rd, [Rn, -Rm, LSL #] - void arm710(u32 opcode) { LDR_PREDEC(OFFSET_LSL, OP_LDR, 32); } -// LDR Rd, [Rn, -Rm, LSR #] - void arm712(u32 opcode) { LDR_PREDEC(OFFSET_LSR, OP_LDR, 32); } -// LDR Rd, [Rn, -Rm, ASR #] - void arm714(u32 opcode) { LDR_PREDEC(OFFSET_ASR, OP_LDR, 32); } -// LDR Rd, [Rn, -Rm, ROR #] - void arm716(u32 opcode) { LDR_PREDEC(OFFSET_ROR, OP_LDR, 32); } -// STR Rd, [Rn, -Rm, LSL #]! - void arm720(u32 opcode) { STR_PREDEC_WB(OFFSET_LSL, OP_STR, 32); } -// STR Rd, [Rn, -Rm, LSR #]! - void arm722(u32 opcode) { STR_PREDEC_WB(OFFSET_LSR, OP_STR, 32); } -// STR Rd, [Rn, -Rm, ASR #]! - void arm724(u32 opcode) { STR_PREDEC_WB(OFFSET_ASR, OP_STR, 32); } -// STR Rd, [Rn, -Rm, ROR #]! - void arm726(u32 opcode) { STR_PREDEC_WB(OFFSET_ROR, OP_STR, 32); } -// LDR Rd, [Rn, -Rm, LSL #]! - void arm730(u32 opcode) { LDR_PREDEC_WB(OFFSET_LSL, OP_LDR, 32); } -// LDR Rd, [Rn, -Rm, LSR #]! - void arm732(u32 opcode) { LDR_PREDEC_WB(OFFSET_LSR, OP_LDR, 32); } -// LDR Rd, [Rn, -Rm, ASR #]! - void arm734(u32 opcode) { LDR_PREDEC_WB(OFFSET_ASR, OP_LDR, 32); } -// LDR Rd, [Rn, -Rm, ROR #]! - void arm736(u32 opcode) { LDR_PREDEC_WB(OFFSET_ROR, OP_LDR, 32); } -// STRB Rd, [Rn, -Rm, LSL #] - void arm740(u32 opcode) { STR_PREDEC(OFFSET_LSL, OP_STRB, 16); } -// STRB Rd, [Rn, -Rm, LSR #] - void arm742(u32 opcode) { STR_PREDEC(OFFSET_LSR, OP_STRB, 16); } -// STRB Rd, [Rn, -Rm, ASR #] - void arm744(u32 opcode) { STR_PREDEC(OFFSET_ASR, OP_STRB, 16); } -// STRB Rd, [Rn, -Rm, ROR #] - void arm746(u32 opcode) { STR_PREDEC(OFFSET_ROR, OP_STRB, 16); } -// LDRB Rd, [Rn, -Rm, LSL #] - void arm750(u32 opcode) { LDR_PREDEC(OFFSET_LSL, OP_LDRB, 16); } -// LDRB Rd, [Rn, -Rm, LSR #] - void arm752(u32 opcode) { LDR_PREDEC(OFFSET_LSR, OP_LDRB, 16); } -// LDRB Rd, [Rn, -Rm, ASR #] - void arm754(u32 opcode) { LDR_PREDEC(OFFSET_ASR, OP_LDRB, 16); } -// LDRB Rd, [Rn, -Rm, ROR #] - void arm756(u32 opcode) { LDR_PREDEC(OFFSET_ROR, OP_LDRB, 16); } -// STRB Rd, [Rn, -Rm, LSL #]! - void arm760(u32 opcode) { STR_PREDEC_WB(OFFSET_LSL, OP_STRB, 16); } -// STRB Rd, [Rn, -Rm, LSR #]! - void arm762(u32 opcode) { STR_PREDEC_WB(OFFSET_LSR, OP_STRB, 16); } -// STRB Rd, [Rn, -Rm, ASR #]! - void arm764(u32 opcode) { STR_PREDEC_WB(OFFSET_ASR, OP_STRB, 16); } -// STRB Rd, [Rn, -Rm, ROR #]! - void arm766(u32 opcode) { STR_PREDEC_WB(OFFSET_ROR, OP_STRB, 16); } -// LDRB Rd, [Rn, -Rm, LSL #]! - void arm770(u32 opcode) { LDR_PREDEC_WB(OFFSET_LSL, OP_LDRB, 16); } -// LDRB Rd, [Rn, -Rm, LSR #]! - void arm772(u32 opcode) { LDR_PREDEC_WB(OFFSET_LSR, OP_LDRB, 16); } -// LDRB Rd, [Rn, -Rm, ASR #]! - void arm774(u32 opcode) { LDR_PREDEC_WB(OFFSET_ASR, OP_LDRB, 16); } -// LDRB Rd, [Rn, -Rm, ROR #]! - void arm776(u32 opcode) { LDR_PREDEC_WB(OFFSET_ROR, OP_LDRB, 16); } -// STR Rd, [Rn, Rm, LSL #] - void arm780(u32 opcode) { STR_PREINC(OFFSET_LSL, OP_STR, 32); } -// STR Rd, [Rn, Rm, LSR #] - void arm782(u32 opcode) { STR_PREINC(OFFSET_LSR, OP_STR, 32); } -// STR Rd, [Rn, Rm, ASR #] - void arm784(u32 opcode) { STR_PREINC(OFFSET_ASR, OP_STR, 32); } -// STR Rd, [Rn, Rm, ROR #] - void arm786(u32 opcode) { STR_PREINC(OFFSET_ROR, OP_STR, 32); } -// LDR Rd, [Rn, Rm, LSL #] - void arm790(u32 opcode) { LDR_PREINC(OFFSET_LSL, OP_LDR, 32); } -// LDR Rd, [Rn, Rm, LSR #] - void arm792(u32 opcode) { LDR_PREINC(OFFSET_LSR, OP_LDR, 32); } -// LDR Rd, [Rn, Rm, ASR #] - void arm794(u32 opcode) { LDR_PREINC(OFFSET_ASR, OP_LDR, 32); } -// LDR Rd, [Rn, Rm, ROR #] - void arm796(u32 opcode) { LDR_PREINC(OFFSET_ROR, OP_LDR, 32); } -// STR Rd, [Rn, Rm, LSL #]! - void arm7A0(u32 opcode) { STR_PREINC_WB(OFFSET_LSL, OP_STR, 32); } -// STR Rd, [Rn, Rm, LSR #]! - void arm7A2(u32 opcode) { STR_PREINC_WB(OFFSET_LSR, OP_STR, 32); } -// STR Rd, [Rn, Rm, ASR #]! - void arm7A4(u32 opcode) { STR_PREINC_WB(OFFSET_ASR, OP_STR, 32); } -// STR Rd, [Rn, Rm, ROR #]! - void arm7A6(u32 opcode) { STR_PREINC_WB(OFFSET_ROR, OP_STR, 32); } -// LDR Rd, [Rn, Rm, LSL #]! - void arm7B0(u32 opcode) { LDR_PREINC_WB(OFFSET_LSL, OP_LDR, 32); } -// LDR Rd, [Rn, Rm, LSR #]! - void arm7B2(u32 opcode) { LDR_PREINC_WB(OFFSET_LSR, OP_LDR, 32); } -// LDR Rd, [Rn, Rm, ASR #]! - void arm7B4(u32 opcode) { LDR_PREINC_WB(OFFSET_ASR, OP_LDR, 32); } -// LDR Rd, [Rn, Rm, ROR #]! - void arm7B6(u32 opcode) { LDR_PREINC_WB(OFFSET_ROR, OP_LDR, 32); } -// STRB Rd, [Rn, Rm, LSL #] - void arm7C0(u32 opcode) { STR_PREINC(OFFSET_LSL, OP_STRB, 16); } -// STRB Rd, [Rn, Rm, LSR #] - void arm7C2(u32 opcode) { STR_PREINC(OFFSET_LSR, OP_STRB, 16); } -// STRB Rd, [Rn, Rm, ASR #] - void arm7C4(u32 opcode) { STR_PREINC(OFFSET_ASR, OP_STRB, 16); } -// STRB Rd, [Rn, Rm, ROR #] - void arm7C6(u32 opcode) { STR_PREINC(OFFSET_ROR, OP_STRB, 16); } -// LDRB Rd, [Rn, Rm, LSL #] - void arm7D0(u32 opcode) { LDR_PREINC(OFFSET_LSL, OP_LDRB, 16); } -// LDRB Rd, [Rn, Rm, LSR #] - void arm7D2(u32 opcode) { LDR_PREINC(OFFSET_LSR, OP_LDRB, 16); } -// LDRB Rd, [Rn, Rm, ASR #] - void arm7D4(u32 opcode) { LDR_PREINC(OFFSET_ASR, OP_LDRB, 16); } -// LDRB Rd, [Rn, Rm, ROR #] - void arm7D6(u32 opcode) { LDR_PREINC(OFFSET_ROR, OP_LDRB, 16); } -// STRB Rd, [Rn, Rm, LSL #]! - void arm7E0(u32 opcode) { STR_PREINC_WB(OFFSET_LSL, OP_STRB, 16); } -// STRB Rd, [Rn, Rm, LSR #]! - void arm7E2(u32 opcode) { STR_PREINC_WB(OFFSET_LSR, OP_STRB, 16); } -// STRB Rd, [Rn, Rm, ASR #]! - void arm7E4(u32 opcode) { STR_PREINC_WB(OFFSET_ASR, OP_STRB, 16); } -// STRB Rd, [Rn, Rm, ROR #]! - void arm7E6(u32 opcode) { STR_PREINC_WB(OFFSET_ROR, OP_STRB, 16); } -// LDRB Rd, [Rn, Rm, LSL #]! - void arm7F0(u32 opcode) { LDR_PREINC_WB(OFFSET_LSL, OP_LDRB, 16); } -// LDRB Rd, [Rn, Rm, LSR #]! - void arm7F2(u32 opcode) { LDR_PREINC_WB(OFFSET_LSR, OP_LDRB, 16); } -// LDRB Rd, [Rn, Rm, ASR #]! - void arm7F4(u32 opcode) { LDR_PREINC_WB(OFFSET_ASR, OP_LDRB, 16); } -// LDRB Rd, [Rn, Rm, ROR #]! - void arm7F6(u32 opcode) { LDR_PREINC_WB(OFFSET_ROR, OP_LDRB, 16); } - -// STM/LDM //////////////////////////////////////////////////////////////// - -#define STM_REG(bit,num) \ - if (opcode & (1U<<(bit))) { \ - CPUWriteMemory(address, bus.reg[(num)].I); \ - int dataticks_value = count ? DATATICKS_ACCESS_32BIT_SEQ(address) : DATATICKS_ACCESS_32BIT(address); \ - DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); \ - clockTicks += 1 + dataticks_value; \ - count++; \ - address += 4; \ - } -#define STMW_REG(bit,num) \ - if (opcode & (1U<<(bit))) { \ - CPUWriteMemory(address, bus.reg[(num)].I); \ - int dataticks_value = count ? DATATICKS_ACCESS_32BIT_SEQ(address) : DATATICKS_ACCESS_32BIT(address); \ - DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); \ - clockTicks += 1 + dataticks_value; \ - bus.reg[base].I = temp; \ - count++; \ - address += 4; \ - } -#define LDM_REG(bit,num) \ - if (opcode & (1U<<(bit))) { \ - int dataticks_value; \ - bus.reg[(num)].I = CPUReadMemory(address); \ - dataticks_value = count ? DATATICKS_ACCESS_32BIT_SEQ(address) : DATATICKS_ACCESS_32BIT(address); \ - DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); \ - clockTicks += 1 + dataticks_value; \ - count++; \ - address += 4; \ - } -#define STM_LOW(STORE_REG) \ - STORE_REG(0, 0); \ - STORE_REG(1, 1); \ - STORE_REG(2, 2); \ - STORE_REG(3, 3); \ - STORE_REG(4, 4); \ - STORE_REG(5, 5); \ - STORE_REG(6, 6); \ - STORE_REG(7, 7); -#define STM_HIGH(STORE_REG) \ - STORE_REG(8, 8); \ - STORE_REG(9, 9); \ - STORE_REG(10, 10); \ - STORE_REG(11, 11); \ - STORE_REG(12, 12); \ - STORE_REG(13, 13); \ - STORE_REG(14, 14); -#define STM_HIGH_2(STORE_REG) \ - if (armMode == 0x11) { \ - STORE_REG(8, R8_FIQ); \ - STORE_REG(9, R9_FIQ); \ - STORE_REG(10, R10_FIQ); \ - STORE_REG(11, R11_FIQ); \ - STORE_REG(12, R12_FIQ); \ - } else { \ - STORE_REG(8, 8); \ - STORE_REG(9, 9); \ - STORE_REG(10, 10); \ - STORE_REG(11, 11); \ - STORE_REG(12, 12); \ - } \ - if (armMode != 0x10 && armMode != 0x1F) { \ - STORE_REG(13, R13_USR); \ - STORE_REG(14, R14_USR); \ - } else { \ - STORE_REG(13, 13); \ - STORE_REG(14, 14); \ - } -#define STM_PC \ - if (opcode & (1U<<15)) { \ - CPUWriteMemory(address, bus.reg[15].I+4); \ - int dataticks_value = count ? DATATICKS_ACCESS_32BIT_SEQ(address) : DATATICKS_ACCESS_32BIT(address); \ - DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); \ - clockTicks += 1 + dataticks_value; \ - count++; \ - } -#define STMW_PC \ - if (opcode & (1U<<15)) { \ - CPUWriteMemory(address, bus.reg[15].I+4); \ - int dataticks_value = count ? DATATICKS_ACCESS_32BIT_SEQ(address) : DATATICKS_ACCESS_32BIT(address); \ - DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); \ - clockTicks += 1 + dataticks_value; \ - bus.reg[base].I = temp; \ - count++; \ - } -#define LDM_LOW \ - LDM_REG(0, 0); \ - LDM_REG(1, 1); \ - LDM_REG(2, 2); \ - LDM_REG(3, 3); \ - LDM_REG(4, 4); \ - LDM_REG(5, 5); \ - LDM_REG(6, 6); \ - LDM_REG(7, 7); -#define LDM_HIGH \ - LDM_REG(8, 8); \ - LDM_REG(9, 9); \ - LDM_REG(10, 10); \ - LDM_REG(11, 11); \ - LDM_REG(12, 12); \ - LDM_REG(13, 13); \ - LDM_REG(14, 14); -#define LDM_HIGH_2 \ - if (armMode == 0x11) { \ - LDM_REG(8, R8_FIQ); \ - LDM_REG(9, R9_FIQ); \ - LDM_REG(10, R10_FIQ); \ - LDM_REG(11, R11_FIQ); \ - LDM_REG(12, R12_FIQ); \ - } else { \ - LDM_REG(8, 8); \ - LDM_REG(9, 9); \ - LDM_REG(10, 10); \ - LDM_REG(11, 11); \ - LDM_REG(12, 12); \ - } \ - if (armMode != 0x10 && armMode != 0x1F) { \ - LDM_REG(13, R13_USR); \ - LDM_REG(14, R14_USR); \ - } else { \ - LDM_REG(13, 13); \ - LDM_REG(14, 14); \ - } -#define STM_ALL \ - STM_LOW(STM_REG); \ - STM_HIGH(STM_REG); \ - STM_PC; -#define STMW_ALL \ - STM_LOW(STMW_REG); \ - STM_HIGH(STMW_REG); \ - STMW_PC; -#define LDM_ALL \ - LDM_LOW; \ - LDM_HIGH; \ - if (opcode & (1U<<15)) { \ - bus.reg[15].I = CPUReadMemory(address); \ - int dataticks_value = count ? DATATICKS_ACCESS_32BIT_SEQ(address) : DATATICKS_ACCESS_32BIT(address); \ - DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); \ - clockTicks += 1 + dataticks_value; \ - count++; \ - } \ - if (opcode & (1U<<15)) { \ - bus.armNextPC = bus.reg[15].I; \ - bus.reg[15].I += 4; \ - ARM_PREFETCH; \ - clockTicks += 1 + codeTicksAccessSeq32(bus.armNextPC);\ - } -#define STM_ALL_2 \ - STM_LOW(STM_REG); \ - STM_HIGH_2(STM_REG); \ - STM_PC; -#define STMW_ALL_2 \ - STM_LOW(STMW_REG); \ - STM_HIGH_2(STMW_REG); \ - STMW_PC; -#define LDM_ALL_2 \ - LDM_LOW; \ - if (opcode & (1U<<15)) { \ - LDM_HIGH; \ - bus.reg[15].I = CPUReadMemory(address); \ - int dataticks_value = count ? DATATICKS_ACCESS_32BIT_SEQ(address) : DATATICKS_ACCESS_32BIT(address); \ - DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); \ - clockTicks += 1 + dataticks_value; \ - count++; \ - } else { \ - LDM_HIGH_2; \ - } -#define LDM_ALL_2B \ - if (opcode & (1U<<15)) { \ - if(armMode != (bus.reg[17].I & 0x1F)) \ - CPUSwitchMode(bus.reg[17].I & 0x1F, false, true); \ - if (armState) { \ - bus.armNextPC = bus.reg[15].I & 0xFFFFFFFC; \ - bus.reg[15].I = bus.armNextPC + 4; \ - ARM_PREFETCH; \ - } else { \ - bus.armNextPC = bus.reg[15].I & 0xFFFFFFFE; \ - bus.reg[15].I = bus.armNextPC + 2; \ - THUMB_PREFETCH; \ - } \ - clockTicks += 1 + codeTicksAccessSeq32(bus.armNextPC);\ - } - - -// STMDA Rn, {Rlist} - void arm800(u32 opcode) -{ - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - int base = (opcode & 0x000F0000) >> 16; - u32 temp = bus.reg[base].I - - 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); - u32 address = (temp + 4) & 0xFFFFFFFC; - int count = 0; - STM_ALL; - clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32); -} - -// LDMDA Rn, {Rlist} - void arm810(u32 opcode) -{ - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - int base = (opcode & 0x000F0000) >> 16; - u32 temp = bus.reg[base].I - - 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); - u32 address = (temp + 4) & 0xFFFFFFFC; - int count = 0; - LDM_ALL; - clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32); -} - -// STMDA Rn!, {Rlist} - void arm820(u32 opcode) -{ - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - int base = (opcode & 0x000F0000) >> 16; - u32 temp = bus.reg[base].I - - 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); - u32 address = (temp+4) & 0xFFFFFFFC; - int count = 0; - STMW_ALL; - clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32); -} - -// LDMDA Rn!, {Rlist} - void arm830(u32 opcode) -{ - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - int base = (opcode & 0x000F0000) >> 16; - u32 temp = bus.reg[base].I - - 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); - u32 address = (temp + 4) & 0xFFFFFFFC; - int count = 0; - LDM_ALL; - clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32); - if (!(opcode & (1U << base))) - bus.reg[base].I = temp; -} - -// STMDA Rn, {Rlist}^ - void arm840(u32 opcode) -{ - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - int base = (opcode & 0x000F0000) >> 16; - u32 temp = bus.reg[base].I - - 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); - u32 address = (temp+4) & 0xFFFFFFFC; - int count = 0; - STM_ALL_2; - clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32); -} - -// LDMDA Rn, {Rlist}^ - void arm850(u32 opcode) -{ - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - int base = (opcode & 0x000F0000) >> 16; - u32 temp = bus.reg[base].I - - 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); - u32 address = (temp + 4) & 0xFFFFFFFC; - int count = 0; - LDM_ALL_2; - LDM_ALL_2B; - clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32); -} - -// STMDA Rn!, {Rlist}^ - void arm860(u32 opcode) -{ - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - int base = (opcode & 0x000F0000) >> 16; - u32 temp = bus.reg[base].I - - 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); - u32 address = (temp+4) & 0xFFFFFFFC; - int count = 0; - STMW_ALL_2; - clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32); -} - -// LDMDA Rn!, {Rlist}^ - void arm870(u32 opcode) -{ - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - int base = (opcode & 0x000F0000) >> 16; - u32 temp = bus.reg[base].I - - 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); - u32 address = (temp + 4) & 0xFFFFFFFC; - int count = 0; - LDM_ALL_2; - if (!(opcode & (1U << base))) - bus.reg[base].I = temp; - LDM_ALL_2B; - clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32); -} - -// STMIA Rn, {Rlist} - void arm880(u32 opcode) -{ - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - int base = (opcode & 0x000F0000) >> 16; - u32 address = bus.reg[base].I & 0xFFFFFFFC; - int count = 0; - STM_ALL; - clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32); -} - -// LDMIA Rn, {Rlist} - void arm890(u32 opcode) -{ - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - int base = (opcode & 0x000F0000) >> 16; - u32 address = bus.reg[base].I & 0xFFFFFFFC; - int count = 0; - LDM_ALL; - clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32); -} - -// STMIA Rn!, {Rlist} - void arm8A0(u32 opcode) -{ - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - int base = (opcode & 0x000F0000) >> 16; - u32 address = bus.reg[base].I & 0xFFFFFFFC; - int count = 0; - u32 temp = bus.reg[base].I + - 4 * (cpuBitsSet[opcode & 0xFF] + cpuBitsSet[(opcode >> 8) & 255]); - STMW_ALL; - clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32); -} - -// LDMIA Rn!, {Rlist} - void arm8B0(u32 opcode) -{ - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - int base = (opcode & 0x000F0000) >> 16; - u32 temp = bus.reg[base].I + - 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); - u32 address = bus.reg[base].I & 0xFFFFFFFC; - int count = 0; - LDM_ALL; - clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32); - if (!(opcode & (1U << base))) - bus.reg[base].I = temp; -} - -// STMIA Rn, {Rlist}^ - void arm8C0(u32 opcode) -{ - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - int base = (opcode & 0x000F0000) >> 16; - u32 address = bus.reg[base].I & 0xFFFFFFFC; - int count = 0; - STM_ALL_2; - clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32); -} - -// LDMIA Rn, {Rlist}^ - void arm8D0(u32 opcode) -{ - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - int base = (opcode & 0x000F0000) >> 16; - u32 address = bus.reg[base].I & 0xFFFFFFFC; - int count = 0; - LDM_ALL_2; - LDM_ALL_2B; - clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32); -} - -// STMIA Rn!, {Rlist}^ - void arm8E0(u32 opcode) -{ - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - int base = (opcode & 0x000F0000) >> 16; - u32 address = bus.reg[base].I & 0xFFFFFFFC; - int count = 0; - u32 temp = bus.reg[base].I + - 4 * (cpuBitsSet[opcode & 0xFF] + cpuBitsSet[(opcode >> 8) & 255]); - STMW_ALL_2; - clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32); -} - -// LDMIA Rn!, {Rlist}^ - void arm8F0(u32 opcode) -{ - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - int base = (opcode & 0x000F0000) >> 16; - u32 temp = bus.reg[base].I + - 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); - u32 address = bus.reg[base].I & 0xFFFFFFFC; - int count = 0; - LDM_ALL_2; - if (!(opcode & (1U << base))) - bus.reg[base].I = temp; - LDM_ALL_2B; - clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32); -} - -// STMDB Rn, {Rlist} - void arm900(u32 opcode) -{ - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - int base = (opcode & 0x000F0000) >> 16; - u32 temp = bus.reg[base].I - - 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); - u32 address = temp & 0xFFFFFFFC; - int count = 0; - STM_ALL; - clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32); -} - -// LDMDB Rn, {Rlist} - void arm910(u32 opcode) -{ - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - int base = (opcode & 0x000F0000) >> 16; - u32 temp = bus.reg[base].I - - 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); - u32 address = temp & 0xFFFFFFFC; - int count = 0; - LDM_ALL; - clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32); -} - -// STMDB Rn!, {Rlist} - void arm920(u32 opcode) -{ - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - int base = (opcode & 0x000F0000) >> 16; - u32 temp = bus.reg[base].I - - 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); - u32 address = temp & 0xFFFFFFFC; - int count = 0; - STMW_ALL; - clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32); -} - -// LDMDB Rn!, {Rlist} - void arm930(u32 opcode) -{ - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - int base = (opcode & 0x000F0000) >> 16; - u32 temp = bus.reg[base].I - - 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); - u32 address = temp & 0xFFFFFFFC; - int count = 0; - LDM_ALL; - clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32); - if (!(opcode & (1U << base))) - bus.reg[base].I = temp; -} - -// STMDB Rn, {Rlist}^ - void arm940(u32 opcode) -{ - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - int base = (opcode & 0x000F0000) >> 16; - u32 temp = bus.reg[base].I - - 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); - u32 address = temp & 0xFFFFFFFC; - int count = 0; - STM_ALL_2; - clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32); -} - -// LDMDB Rn, {Rlist}^ - void arm950(u32 opcode) -{ - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - int base = (opcode & 0x000F0000) >> 16; - u32 temp = bus.reg[base].I - - 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); - u32 address = temp & 0xFFFFFFFC; - int count = 0; - LDM_ALL_2; - LDM_ALL_2B; - clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32); -} - -// STMDB Rn!, {Rlist}^ - void arm960(u32 opcode) -{ - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - int base = (opcode & 0x000F0000) >> 16; - u32 temp = bus.reg[base].I - - 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); - u32 address = temp & 0xFFFFFFFC; - int count = 0; - STMW_ALL_2; - clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32); -} - -// LDMDB Rn!, {Rlist}^ - void arm970(u32 opcode) -{ - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - int base = (opcode & 0x000F0000) >> 16; - u32 temp = bus.reg[base].I - - 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); - u32 address = temp & 0xFFFFFFFC; - int count = 0; - LDM_ALL_2; - if (!(opcode & (1U << base))) - bus.reg[base].I = temp; - LDM_ALL_2B; - clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32); -} - -// STMIB Rn, {Rlist} - void arm980(u32 opcode) -{ - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - int base = (opcode & 0x000F0000) >> 16; - u32 address = (bus.reg[base].I+4) & 0xFFFFFFFC; - int count = 0; - STM_ALL; - clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32); -} - -// LDMIB Rn, {Rlist} - void arm990(u32 opcode) -{ - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - int base = (opcode & 0x000F0000) >> 16; - u32 address = (bus.reg[base].I+4) & 0xFFFFFFFC; - int count = 0; - LDM_ALL; - clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32); -} - -// STMIB Rn!, {Rlist} - void arm9A0(u32 opcode) -{ - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - int base = (opcode & 0x000F0000) >> 16; - u32 address = (bus.reg[base].I+4) & 0xFFFFFFFC; - int count = 0; - u32 temp = bus.reg[base].I + - 4 * (cpuBitsSet[opcode & 0xFF] + cpuBitsSet[(opcode >> 8) & 255]); - STMW_ALL; - clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32); -} - -// LDMIB Rn!, {Rlist} - void arm9B0(u32 opcode) -{ - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - int base = (opcode & 0x000F0000) >> 16; - u32 temp = bus.reg[base].I + - 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); - u32 address = (bus.reg[base].I+4) & 0xFFFFFFFC; - int count = 0; - LDM_ALL; - clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32); - if (!(opcode & (1U << base))) - bus.reg[base].I = temp; -} - -// STMIB Rn, {Rlist}^ - void arm9C0(u32 opcode) -{ - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - int base = (opcode & 0x000F0000) >> 16; - u32 address = (bus.reg[base].I+4) & 0xFFFFFFFC; - int count = 0; - STM_ALL_2; - clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32); -} - -// LDMIB Rn, {Rlist}^ - void arm9D0(u32 opcode) -{ - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - int base = (opcode & 0x000F0000) >> 16; - u32 address = (bus.reg[base].I+4) & 0xFFFFFFFC; - int count = 0; - LDM_ALL_2; - LDM_ALL_2B; - clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32); -} - -// STMIB Rn!, {Rlist}^ - void arm9E0(u32 opcode) -{ - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - int base = (opcode & 0x000F0000) >> 16; - u32 address = (bus.reg[base].I+4) & 0xFFFFFFFC; - int count = 0; - u32 temp = bus.reg[base].I + - 4 * (cpuBitsSet[opcode & 0xFF] + cpuBitsSet[(opcode >> 8) & 255]); - STMW_ALL_2; - clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32); -} - -// LDMIB Rn!, {Rlist}^ - void arm9F0(u32 opcode) -{ - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - int base = (opcode & 0x000F0000) >> 16; - u32 temp = bus.reg[base].I + - 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); - u32 address = (bus.reg[base].I+4) & 0xFFFFFFFC; - int count = 0; - LDM_ALL_2; - if (!(opcode & (1U << base))) - bus.reg[base].I = temp; - LDM_ALL_2B; - clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32); -} - -// B/BL/SWI and (unimplemented) coproc support //////////////////////////// - -// B - void armA00(u32 opcode) -{ - int codeTicksVal = 0; - int ct = 0; - int offset = opcode & 0x00FFFFFF; - if (offset & 0x00800000) - offset |= 0xFF000000; // negative offset - bus.reg[15].I += offset<<2; - bus.armNextPC = bus.reg[15].I; - bus.reg[15].I += 4; - ARM_PREFETCH; - - codeTicksVal = codeTicksAccessSeq32(bus.armNextPC); - ct = codeTicksVal + 3; - ct += 2 + codeTicksAccess(bus.armNextPC, BITS_32) + codeTicksVal; - - bus.busPrefetchCount = 0; - clockTicks = ct; -} - -// BL - void armB00(u32 opcode) -{ - int codeTicksVal = 0; - int ct = 0; - - int offset = opcode & 0x00FFFFFF; - if (offset & 0x00800000) - offset |= 0xFF000000; // negative offset - bus.reg[14].I = bus.reg[15].I - 4; - bus.reg[15].I += offset<<2; - bus.armNextPC = bus.reg[15].I; - bus.reg[15].I += 4; - ARM_PREFETCH; - - codeTicksVal = codeTicksAccessSeq32(bus.armNextPC); - ct = codeTicksVal + 3; - ct += 2 + codeTicksAccess(bus.armNextPC, BITS_32) + codeTicksVal; - - bus.busPrefetchCount = 0; - clockTicks = ct; -} - -#define armE01 armUnknownInsn - -// SWI - void armF00(u32 opcode) -{ - int codeTicksVal = 0; - int ct = 0; - - codeTicksVal = codeTicksAccessSeq32(bus.armNextPC); - ct = codeTicksVal + 3; - ct += 2 + codeTicksAccess(bus.armNextPC, BITS_32) + codeTicksVal; - - bus.busPrefetchCount = 0; - - clockTicks = ct; - CPUSoftwareInterrupt(opcode & 0x00FFFFFF); - -} - -// Instruction table ////////////////////////////////////////////////////// - -static void (Gigazoid::*const armInsnTable[4096])(u32 opcode); - -// Wrapper routine (execution loop) /////////////////////////////////////// -int armExecute (void) -{ - CACHE_PREFETCH(clockTicks); - - u32 cond1; - u32 cond2; - - int ct = 0; - - do - { - - clockTicks = 0; - - if ((bus.armNextPC & 0x0803FFFF) == 0x08020000) - bus.busPrefetchCount = 0x100; - - u32 opcode = cpuPrefetch[0]; - cpuPrefetch[0] = cpuPrefetch[1]; - - bus.busPrefetch = false; - int32_t busprefetch_mask = ((bus.busPrefetchCount & 0xFFFFFE00) | -(bus.busPrefetchCount & 0xFFFFFE00)) >> 31; - bus.busPrefetchCount = (0x100 | (bus.busPrefetchCount & 0xFF) & busprefetch_mask) | (bus.busPrefetchCount & ~busprefetch_mask); -#if 0 - if (bus.busPrefetchCount & 0xFFFFFE00) - bus.busPrefetchCount = 0x100 | (bus.busPrefetchCount & 0xFF); -#endif - - - int oldArmNextPC = bus.armNextPC; - - bus.armNextPC = bus.reg[15].I; - if (traceCallback) - traceCallback(bus.armNextPC, opcode); - if (fetchCallback) - fetchCallback(bus.armNextPC); - bus.reg[15].I += 4; - ARM_PREFETCH_NEXT; - - int cond = opcode >> 28; - bool cond_res = true; - if (cond != 0x0E) { // most opcodes are AL (always) - switch(cond) { - case 0x00: // EQ - cond_res = Z_FLAG; - break; - case 0x01: // NE - cond_res = !Z_FLAG; - break; - case 0x02: // CS - cond_res = C_FLAG; - break; - case 0x03: // CC - cond_res = !C_FLAG; - break; - case 0x04: // MI - cond_res = N_FLAG; - break; - case 0x05: // PL - cond_res = !N_FLAG; - break; - case 0x06: // VS - cond_res = V_FLAG; - break; - case 0x07: // VC - cond_res = !V_FLAG; - break; - case 0x08: // HI - cond_res = C_FLAG && !Z_FLAG; - break; - case 0x09: // LS - cond_res = !C_FLAG || Z_FLAG; - break; - case 0x0A: // GE - cond_res = N_FLAG == V_FLAG; - break; - case 0x0B: // LT - cond_res = N_FLAG != V_FLAG; - break; - case 0x0C: // GT - cond_res = !Z_FLAG &&(N_FLAG == V_FLAG); - break; - case 0x0D: // LE - cond_res = Z_FLAG || (N_FLAG != V_FLAG); - break; - case 0x0E: // AL (impossible, checked above) - cond_res = true; - break; - case 0x0F: - default: - // ??? - cond_res = false; - break; - } - } - - if (cond_res) - { - cond1 = (opcode>>16)&0xFF0; - cond2 = (opcode>>4)&0x0F; - - (this->*armInsnTable[(cond1| cond2)])(opcode); - - } - ct = clockTicks; - - if (ct < 0) - return 0; - - /// better pipelining - - if (ct == 0) - clockTicks = 1 + codeTicksAccessSeq32(oldArmNextPC); - - cpuTotalTicks += clockTicks; - -} while ((cpuTotalTicks < cpuNextEvent) & armState & ~holdState); - return 1; -} - - -/*============================================================ - GBA THUMB CORE -============================================================ */ - - void thumbUnknownInsn(u32 opcode) -{ - u32 PC = bus.reg[15].I; - bool savedArmState = armState; - if(armMode != 0x1b) - CPUSwitchMode(0x1b, true, false); - bus.reg[14].I = PC - (savedArmState ? 4 : 2); - bus.reg[15].I = 0x04; - armState = true; - armIrqEnable = false; - bus.armNextPC = 0x04; - ARM_PREFETCH; - bus.reg[15].I += 4; -} - -#define NEG(i) ((i) >> 31) -#define POS(i) ((~(i)) >> 31) - -// C core -#ifndef ADDCARRY - #define ADDCARRY(a, b, c) \ - C_FLAG = ((NEG(a) & NEG(b)) |\ - (NEG(a) & POS(c)) |\ - (NEG(b) & POS(c))) ? true : false; -#endif - -#ifndef ADDOVERFLOW - #define ADDOVERFLOW(a, b, c) \ - V_FLAG = ((NEG(a) & NEG(b) & POS(c)) |\ - (POS(a) & POS(b) & NEG(c))) ? true : false; -#endif - -#ifndef SUBCARRY - #define SUBCARRY(a, b, c) \ - C_FLAG = ((NEG(a) & POS(b)) |\ - (NEG(a) & POS(c)) |\ - (POS(b) & POS(c))) ? true : false; -#endif - -#ifndef SUBOVERFLOW - #define SUBOVERFLOW(a, b, c)\ - V_FLAG = ((NEG(a) & POS(b) & POS(c)) |\ - (POS(a) & NEG(b) & NEG(c))) ? true : false; -#endif - -#ifndef ADD_RD_RS_RN - #define ADD_RD_RS_RN(N) \ - {\ - u32 lhs = bus.reg[source].I;\ - u32 rhs = bus.reg[N].I;\ - u32 res = lhs + rhs;\ - bus.reg[dest].I = res;\ - Z_FLAG = (res == 0) ? true : false;\ - N_FLAG = NEG(res) ? true : false;\ - ADDCARRY(lhs, rhs, res);\ - ADDOVERFLOW(lhs, rhs, res);\ - } -#endif - -#ifndef ADD_RD_RS_O3 - #define ADD_RD_RS_O3(N) \ - {\ - u32 lhs = bus.reg[source].I;\ - u32 rhs = N;\ - u32 res = lhs + rhs;\ - bus.reg[dest].I = res;\ - Z_FLAG = (res == 0) ? true : false;\ - N_FLAG = NEG(res) ? true : false;\ - ADDCARRY(lhs, rhs, res);\ - ADDOVERFLOW(lhs, rhs, res);\ - } -#endif - -#ifndef ADD_RD_RS_O3_0 -# define ADD_RD_RS_O3_0 ADD_RD_RS_O3 -#endif - -#ifndef ADD_RN_O8 - #define ADD_RN_O8(d) \ - {\ - u32 lhs = bus.reg[(d)].I;\ - u32 rhs = (opcode & 255);\ - u32 res = lhs + rhs;\ - bus.reg[(d)].I = res;\ - Z_FLAG = (res == 0) ? true : false;\ - N_FLAG = NEG(res) ? true : false;\ - ADDCARRY(lhs, rhs, res);\ - ADDOVERFLOW(lhs, rhs, res);\ - } -#endif - -#ifndef CMN_RD_RS - #define CMN_RD_RS \ - {\ - u32 lhs = bus.reg[dest].I;\ - u32 rhs = value;\ - u32 res = lhs + rhs;\ - Z_FLAG = (res == 0) ? true : false;\ - N_FLAG = NEG(res) ? true : false;\ - ADDCARRY(lhs, rhs, res);\ - ADDOVERFLOW(lhs, rhs, res);\ - } -#endif - -#ifndef ADC_RD_RS - #define ADC_RD_RS \ - {\ - u32 lhs = bus.reg[dest].I;\ - u32 rhs = value;\ - u32 res = lhs + rhs + (u32)C_FLAG;\ - bus.reg[dest].I = res;\ - Z_FLAG = (res == 0) ? true : false;\ - N_FLAG = NEG(res) ? true : false;\ - ADDCARRY(lhs, rhs, res);\ - ADDOVERFLOW(lhs, rhs, res);\ - } -#endif - -#ifndef SUB_RD_RS_RN - #define SUB_RD_RS_RN(N) \ - {\ - u32 lhs = bus.reg[source].I;\ - u32 rhs = bus.reg[N].I;\ - u32 res = lhs - rhs;\ - bus.reg[dest].I = res;\ - Z_FLAG = (res == 0) ? true : false;\ - N_FLAG = NEG(res) ? true : false;\ - SUBCARRY(lhs, rhs, res);\ - SUBOVERFLOW(lhs, rhs, res);\ - } -#endif - -#ifndef SUB_RD_RS_O3 - #define SUB_RD_RS_O3(N) \ - {\ - u32 lhs = bus.reg[source].I;\ - u32 rhs = N;\ - u32 res = lhs - rhs;\ - bus.reg[dest].I = res;\ - Z_FLAG = (res == 0) ? true : false;\ - N_FLAG = NEG(res) ? true : false;\ - SUBCARRY(lhs, rhs, res);\ - SUBOVERFLOW(lhs, rhs, res);\ - } -#endif - -#ifndef SUB_RD_RS_O3_0 -# define SUB_RD_RS_O3_0 SUB_RD_RS_O3 -#endif -#ifndef SUB_RN_O8 - #define SUB_RN_O8(d) \ - {\ - u32 lhs = bus.reg[(d)].I;\ - u32 rhs = (opcode & 255);\ - u32 res = lhs - rhs;\ - bus.reg[(d)].I = res;\ - Z_FLAG = (res == 0) ? true : false;\ - N_FLAG = NEG(res) ? true : false;\ - SUBCARRY(lhs, rhs, res);\ - SUBOVERFLOW(lhs, rhs, res);\ - } -#endif -#ifndef MOV_RN_O8 - #define MOV_RN_O8(d) \ - {\ - u32 val;\ - val = (opcode & 255);\ - bus.reg[d].I = val;\ - N_FLAG = false;\ - Z_FLAG = (val ? false : true);\ - } -#endif -#ifndef CMP_RN_O8 - #define CMP_RN_O8(d) \ - {\ - u32 lhs = bus.reg[(d)].I;\ - u32 rhs = (opcode & 255);\ - u32 res = lhs - rhs;\ - Z_FLAG = (res == 0) ? true : false;\ - N_FLAG = NEG(res) ? true : false;\ - SUBCARRY(lhs, rhs, res);\ - SUBOVERFLOW(lhs, rhs, res);\ - } -#endif -#ifndef SBC_RD_RS - #define SBC_RD_RS \ - {\ - u32 lhs = bus.reg[dest].I;\ - u32 rhs = value;\ - u32 res = lhs - rhs - !((u32)C_FLAG);\ - bus.reg[dest].I = res;\ - Z_FLAG = (res == 0) ? true : false;\ - N_FLAG = NEG(res) ? true : false;\ - SUBCARRY(lhs, rhs, res);\ - SUBOVERFLOW(lhs, rhs, res);\ - } -#endif -#ifndef LSL_RD_RM_I5 - #define LSL_RD_RM_I5 \ - {\ - C_FLAG = (bus.reg[source].I >> (32 - shift)) & 1 ? true : false;\ - value = bus.reg[source].I << shift;\ - } -#endif -#ifndef LSL_RD_RS - #define LSL_RD_RS \ - {\ - C_FLAG = (bus.reg[dest].I >> (32 - value)) & 1 ? true : false;\ - value = bus.reg[dest].I << value;\ - } -#endif -#ifndef LSR_RD_RM_I5 - #define LSR_RD_RM_I5 \ - {\ - C_FLAG = (bus.reg[source].I >> (shift - 1)) & 1 ? true : false;\ - value = bus.reg[source].I >> shift;\ - } -#endif -#ifndef LSR_RD_RS - #define LSR_RD_RS \ - {\ - C_FLAG = (bus.reg[dest].I >> (value - 1)) & 1 ? true : false;\ - value = bus.reg[dest].I >> value;\ - } -#endif -#ifndef ASR_RD_RM_I5 - #define ASR_RD_RM_I5 \ - {\ - C_FLAG = ((s32)bus.reg[source].I >> (int)(shift - 1)) & 1 ? true : false;\ - value = (s32)bus.reg[source].I >> (int)shift;\ - } -#endif -#ifndef ASR_RD_RS - #define ASR_RD_RS \ - {\ - C_FLAG = ((s32)bus.reg[dest].I >> (int)(value - 1)) & 1 ? true : false;\ - value = (s32)bus.reg[dest].I >> (int)value;\ - } -#endif -#ifndef ROR_RD_RS - #define ROR_RD_RS \ - {\ - C_FLAG = (bus.reg[dest].I >> (value - 1)) & 1 ? true : false;\ - value = ((bus.reg[dest].I << (32 - value)) |\ - (bus.reg[dest].I >> value));\ - } -#endif -#ifndef NEG_RD_RS - #define NEG_RD_RS \ - {\ - u32 lhs = bus.reg[source].I;\ - u32 rhs = 0;\ - u32 res = rhs - lhs;\ - bus.reg[dest].I = res;\ - Z_FLAG = (res == 0) ? true : false;\ - N_FLAG = NEG(res) ? true : false;\ - SUBCARRY(rhs, lhs, res);\ - SUBOVERFLOW(rhs, lhs, res);\ - } -#endif -#ifndef CMP_RD_RS - #define CMP_RD_RS \ - {\ - u32 lhs = bus.reg[dest].I;\ - u32 rhs = value;\ - u32 res = lhs - rhs;\ - Z_FLAG = (res == 0) ? true : false;\ - N_FLAG = NEG(res) ? true : false;\ - SUBCARRY(lhs, rhs, res);\ - SUBOVERFLOW(lhs, rhs, res);\ - } -#endif -#ifndef IMM5_INSN - #define IMM5_INSN(OP,N) \ - int dest = opcode & 0x07;\ - int source = (opcode >> 3) & 0x07;\ - u32 value;\ - OP(N);\ - bus.reg[dest].I = value;\ - N_FLAG = (value & 0x80000000 ? true : false);\ - Z_FLAG = (value ? false : true); - #define IMM5_INSN_0(OP) \ - int dest = opcode & 0x07;\ - int source = (opcode >> 3) & 0x07;\ - u32 value;\ - OP;\ - bus.reg[dest].I = value;\ - N_FLAG = (value & 0x80000000 ? true : false);\ - Z_FLAG = (value ? false : true); - #define IMM5_LSL(N) \ - int shift = N;\ - LSL_RD_RM_I5; - #define IMM5_LSL_0 \ - value = bus.reg[source].I; - #define IMM5_LSR(N) \ - int shift = N;\ - LSR_RD_RM_I5; - #define IMM5_LSR_0 \ - C_FLAG = bus.reg[source].I & 0x80000000 ? true : false;\ - value = 0; - #define IMM5_ASR(N) \ - int shift = N;\ - ASR_RD_RM_I5; - #define IMM5_ASR_0 \ - if(bus.reg[source].I & 0x80000000) {\ - value = 0xFFFFFFFF;\ - C_FLAG = true;\ - } else {\ - value = 0;\ - C_FLAG = false;\ - } -#endif -#ifndef THREEARG_INSN - #define THREEARG_INSN(OP,N) \ - int dest = opcode & 0x07; \ - int source = (opcode >> 3) & 0x07; \ - OP(N); -#endif - -// Shift instructions ///////////////////////////////////////////////////// - -#define DEFINE_IMM5_INSN(OP,BASE) \ - void thumb##BASE##_00(u32 opcode) { IMM5_INSN_0(OP##_0); } \ - void thumb##BASE##_01(u32 opcode) { IMM5_INSN(OP, 1); } \ - void thumb##BASE##_02(u32 opcode) { IMM5_INSN(OP, 2); } \ - void thumb##BASE##_03(u32 opcode) { IMM5_INSN(OP, 3); } \ - void thumb##BASE##_04(u32 opcode) { IMM5_INSN(OP, 4); } \ - void thumb##BASE##_05(u32 opcode) { IMM5_INSN(OP, 5); } \ - void thumb##BASE##_06(u32 opcode) { IMM5_INSN(OP, 6); } \ - void thumb##BASE##_07(u32 opcode) { IMM5_INSN(OP, 7); } \ - void thumb##BASE##_08(u32 opcode) { IMM5_INSN(OP, 8); } \ - void thumb##BASE##_09(u32 opcode) { IMM5_INSN(OP, 9); } \ - void thumb##BASE##_0A(u32 opcode) { IMM5_INSN(OP,10); } \ - void thumb##BASE##_0B(u32 opcode) { IMM5_INSN(OP,11); } \ - void thumb##BASE##_0C(u32 opcode) { IMM5_INSN(OP,12); } \ - void thumb##BASE##_0D(u32 opcode) { IMM5_INSN(OP,13); } \ - void thumb##BASE##_0E(u32 opcode) { IMM5_INSN(OP,14); } \ - void thumb##BASE##_0F(u32 opcode) { IMM5_INSN(OP,15); } \ - void thumb##BASE##_10(u32 opcode) { IMM5_INSN(OP,16); } \ - void thumb##BASE##_11(u32 opcode) { IMM5_INSN(OP,17); } \ - void thumb##BASE##_12(u32 opcode) { IMM5_INSN(OP,18); } \ - void thumb##BASE##_13(u32 opcode) { IMM5_INSN(OP,19); } \ - void thumb##BASE##_14(u32 opcode) { IMM5_INSN(OP,20); } \ - void thumb##BASE##_15(u32 opcode) { IMM5_INSN(OP,21); } \ - void thumb##BASE##_16(u32 opcode) { IMM5_INSN(OP,22); } \ - void thumb##BASE##_17(u32 opcode) { IMM5_INSN(OP,23); } \ - void thumb##BASE##_18(u32 opcode) { IMM5_INSN(OP,24); } \ - void thumb##BASE##_19(u32 opcode) { IMM5_INSN(OP,25); } \ - void thumb##BASE##_1A(u32 opcode) { IMM5_INSN(OP,26); } \ - void thumb##BASE##_1B(u32 opcode) { IMM5_INSN(OP,27); } \ - void thumb##BASE##_1C(u32 opcode) { IMM5_INSN(OP,28); } \ - void thumb##BASE##_1D(u32 opcode) { IMM5_INSN(OP,29); } \ - void thumb##BASE##_1E(u32 opcode) { IMM5_INSN(OP,30); } \ - void thumb##BASE##_1F(u32 opcode) { IMM5_INSN(OP,31); } - -// LSL Rd, Rm, #Imm 5 -DEFINE_IMM5_INSN(IMM5_LSL,00) -// LSR Rd, Rm, #Imm 5 -DEFINE_IMM5_INSN(IMM5_LSR,08) -// ASR Rd, Rm, #Imm 5 -DEFINE_IMM5_INSN(IMM5_ASR,10) - -// 3-argument ADD/SUB ///////////////////////////////////////////////////// - -#define DEFINE_REG3_INSN(OP,BASE) \ - void thumb##BASE##_0(u32 opcode) { THREEARG_INSN(OP,0); } \ - void thumb##BASE##_1(u32 opcode) { THREEARG_INSN(OP,1); } \ - void thumb##BASE##_2(u32 opcode) { THREEARG_INSN(OP,2); } \ - void thumb##BASE##_3(u32 opcode) { THREEARG_INSN(OP,3); } \ - void thumb##BASE##_4(u32 opcode) { THREEARG_INSN(OP,4); } \ - void thumb##BASE##_5(u32 opcode) { THREEARG_INSN(OP,5); } \ - void thumb##BASE##_6(u32 opcode) { THREEARG_INSN(OP,6); } \ - void thumb##BASE##_7(u32 opcode) { THREEARG_INSN(OP,7); } - -#define DEFINE_IMM3_INSN(OP,BASE) \ - void thumb##BASE##_0(u32 opcode) { THREEARG_INSN(OP##_0,0); } \ - void thumb##BASE##_1(u32 opcode) { THREEARG_INSN(OP,1); } \ - void thumb##BASE##_2(u32 opcode) { THREEARG_INSN(OP,2); } \ - void thumb##BASE##_3(u32 opcode) { THREEARG_INSN(OP,3); } \ - void thumb##BASE##_4(u32 opcode) { THREEARG_INSN(OP,4); } \ - void thumb##BASE##_5(u32 opcode) { THREEARG_INSN(OP,5); } \ - void thumb##BASE##_6(u32 opcode) { THREEARG_INSN(OP,6); } \ - void thumb##BASE##_7(u32 opcode) { THREEARG_INSN(OP,7); } - -// ADD Rd, Rs, Rn -DEFINE_REG3_INSN(ADD_RD_RS_RN,18) -// SUB Rd, Rs, Rn -DEFINE_REG3_INSN(SUB_RD_RS_RN,1A) -// ADD Rd, Rs, #Offset3 -DEFINE_IMM3_INSN(ADD_RD_RS_O3,1C) -// SUB Rd, Rs, #Offset3 -DEFINE_IMM3_INSN(SUB_RD_RS_O3,1E) - -// MOV/CMP/ADD/SUB immediate ////////////////////////////////////////////// - -// MOV R0, #Offset8 - void thumb20(u32 opcode) { MOV_RN_O8(0); } -// MOV R1, #Offset8 - void thumb21(u32 opcode) { MOV_RN_O8(1); } -// MOV R2, #Offset8 - void thumb22(u32 opcode) { MOV_RN_O8(2); } -// MOV R3, #Offset8 - void thumb23(u32 opcode) { MOV_RN_O8(3); } -// MOV R4, #Offset8 - void thumb24(u32 opcode) { MOV_RN_O8(4); } -// MOV R5, #Offset8 - void thumb25(u32 opcode) { MOV_RN_O8(5); } -// MOV R6, #Offset8 - void thumb26(u32 opcode) { MOV_RN_O8(6); } -// MOV R7, #Offset8 - void thumb27(u32 opcode) { MOV_RN_O8(7); } - -// CMP R0, #Offset8 - void thumb28(u32 opcode) { CMP_RN_O8(0); } -// CMP R1, #Offset8 - void thumb29(u32 opcode) { CMP_RN_O8(1); } -// CMP R2, #Offset8 - void thumb2A(u32 opcode) { CMP_RN_O8(2); } -// CMP R3, #Offset8 - void thumb2B(u32 opcode) { CMP_RN_O8(3); } -// CMP R4, #Offset8 - void thumb2C(u32 opcode) { CMP_RN_O8(4); } -// CMP R5, #Offset8 - void thumb2D(u32 opcode) { CMP_RN_O8(5); } -// CMP R6, #Offset8 - void thumb2E(u32 opcode) { CMP_RN_O8(6); } -// CMP R7, #Offset8 - void thumb2F(u32 opcode) { CMP_RN_O8(7); } - -// ADD R0,#Offset8 - void thumb30(u32 opcode) { ADD_RN_O8(0); } -// ADD R1,#Offset8 - void thumb31(u32 opcode) { ADD_RN_O8(1); } -// ADD R2,#Offset8 - void thumb32(u32 opcode) { ADD_RN_O8(2); } -// ADD R3,#Offset8 - void thumb33(u32 opcode) { ADD_RN_O8(3); } -// ADD R4,#Offset8 - void thumb34(u32 opcode) { ADD_RN_O8(4); } -// ADD R5,#Offset8 - void thumb35(u32 opcode) { ADD_RN_O8(5); } -// ADD R6,#Offset8 - void thumb36(u32 opcode) { ADD_RN_O8(6); } -// ADD R7,#Offset8 - void thumb37(u32 opcode) { ADD_RN_O8(7); } - -// SUB R0,#Offset8 - void thumb38(u32 opcode) { SUB_RN_O8(0); } -// SUB R1,#Offset8 - void thumb39(u32 opcode) { SUB_RN_O8(1); } -// SUB R2,#Offset8 - void thumb3A(u32 opcode) { SUB_RN_O8(2); } -// SUB R3,#Offset8 - void thumb3B(u32 opcode) { SUB_RN_O8(3); } -// SUB R4,#Offset8 - void thumb3C(u32 opcode) { SUB_RN_O8(4); } -// SUB R5,#Offset8 - void thumb3D(u32 opcode) { SUB_RN_O8(5); } -// SUB R6,#Offset8 - void thumb3E(u32 opcode) { SUB_RN_O8(6); } -// SUB R7,#Offset8 - void thumb3F(u32 opcode) { SUB_RN_O8(7); } - -// ALU operations ///////////////////////////////////////////////////////// - -// AND Rd, Rs - void thumb40_0(u32 opcode) -{ - int dest = opcode & 7; - u32 val = (bus.reg[dest].I & bus.reg[(opcode >> 3)&7].I); - - //bus.reg[dest].I &= bus.reg[(opcode >> 3)&7].I; - N_FLAG = val & 0x80000000 ? true : false; - Z_FLAG = val ? false : true; - - bus.reg[dest].I = val; - -} - -// EOR Rd, Rs - void thumb40_1(u32 opcode) -{ - int dest = opcode & 7; - bus.reg[dest].I ^= bus.reg[(opcode >> 3)&7].I; - N_FLAG = bus.reg[dest].I & 0x80000000 ? true : false; - Z_FLAG = bus.reg[dest].I ? false : true; -} - -// LSL Rd, Rs - void thumb40_2(u32 opcode) -{ - int dest = opcode & 7; - u32 value = bus.reg[(opcode >> 3)&7].B.B0; - u32 val = value; - if(val) { - if(val == 32) { - value = 0; - C_FLAG = (bus.reg[dest].I & 1 ? true : false); - } else if(val < 32) { - LSL_RD_RS; - } else { - value = 0; - C_FLAG = false; - } - bus.reg[dest].I = value; - } - N_FLAG = bus.reg[dest].I & 0x80000000 ? true : false; - Z_FLAG = bus.reg[dest].I ? false : true; - clockTicks = codeTicksAccess(bus.armNextPC, BITS_16)+2; -} - -// LSR Rd, Rs - void thumb40_3(u32 opcode) -{ - int dest = opcode & 7; - u32 value = bus.reg[(opcode >> 3)&7].B.B0; - u32 val = value; - if(val) { - if(val == 32) { - value = 0; - C_FLAG = (bus.reg[dest].I & 0x80000000 ? true : false); - } else if(val < 32) { - LSR_RD_RS; - } else { - value = 0; - C_FLAG = false; - } - bus.reg[dest].I = value; - } - N_FLAG = bus.reg[dest].I & 0x80000000 ? true : false; - Z_FLAG = bus.reg[dest].I ? false : true; - clockTicks = codeTicksAccess(bus.armNextPC, BITS_16)+2; -} - -// ASR Rd, Rs - void thumb41_0(u32 opcode) -{ - int dest = opcode & 7; - u32 value = bus.reg[(opcode >> 3)&7].B.B0; - - if(value) { - if(value < 32) { - ASR_RD_RS; - bus.reg[dest].I = value; - } else { - if(bus.reg[dest].I & 0x80000000){ - bus.reg[dest].I = 0xFFFFFFFF; - C_FLAG = true; - } else { - bus.reg[dest].I = 0x00000000; - C_FLAG = false; - } - } - } - N_FLAG = bus.reg[dest].I & 0x80000000 ? true : false; - Z_FLAG = bus.reg[dest].I ? false : true; - clockTicks = codeTicksAccess(bus.armNextPC, BITS_16)+2; -} - -// ADC Rd, Rs - void thumb41_1(u32 opcode) -{ - int dest = opcode & 0x07; - u32 value = bus.reg[(opcode >> 3)&7].I; - ADC_RD_RS; -} - -// SBC Rd, Rs - void thumb41_2(u32 opcode) -{ - int dest = opcode & 0x07; - u32 value = bus.reg[(opcode >> 3)&7].I; - SBC_RD_RS; -} - -// ROR Rd, Rs - void thumb41_3(u32 opcode) -{ - int dest = opcode & 7; - u32 value = bus.reg[(opcode >> 3)&7].B.B0; - u32 val = value; - if(val) { - value = value & 0x1f; - if(val == 0) { - C_FLAG = (bus.reg[dest].I & 0x80000000 ? true : false); - } else { - ROR_RD_RS; - bus.reg[dest].I = value; - } - } - clockTicks = codeTicksAccess(bus.armNextPC, BITS_16)+2; - N_FLAG = bus.reg[dest].I & 0x80000000 ? true : false; - Z_FLAG = bus.reg[dest].I ? false : true; -} - -// TST Rd, Rs - void thumb42_0(u32 opcode) -{ - u32 value = bus.reg[opcode & 7].I & bus.reg[(opcode >> 3) & 7].I; - N_FLAG = value & 0x80000000 ? true : false; - Z_FLAG = value ? false : true; -} - -// NEG Rd, Rs - void thumb42_1(u32 opcode) -{ - int dest = opcode & 7; - int source = (opcode >> 3) & 7; - NEG_RD_RS; -} - -// CMP Rd, Rs - void thumb42_2(u32 opcode) -{ - int dest = opcode & 7; - u32 value = bus.reg[(opcode >> 3)&7].I; - CMP_RD_RS; -} - -// CMN Rd, Rs - void thumb42_3(u32 opcode) -{ - int dest = opcode & 7; - u32 value = bus.reg[(opcode >> 3)&7].I; - CMN_RD_RS; -} - -// ORR Rd, Rs - void thumb43_0(u32 opcode) -{ - int dest = opcode & 7; - bus.reg[dest].I |= bus.reg[(opcode >> 3) & 7].I; - Z_FLAG = bus.reg[dest].I ? false : true; - N_FLAG = bus.reg[dest].I & 0x80000000 ? true : false; -} - -// MUL Rd, Rs - void thumb43_1(u32 opcode) -{ - clockTicks = 1; - int dest = opcode & 7; - u32 rm = bus.reg[dest].I; - bus.reg[dest].I = bus.reg[(opcode >> 3) & 7].I * rm; - if (((s32)rm) < 0) - rm = ~rm; - if ((rm & 0xFFFF0000) == 0) - clockTicks += 1; - else if ((rm & 0xFF000000) == 0) - clockTicks += 2; - else - clockTicks += 3; - bus.busPrefetchCount = (bus.busPrefetchCount<>(8-clockTicks)); - clockTicks += codeTicksAccess(bus.armNextPC, BITS_16) + 1; - Z_FLAG = bus.reg[dest].I ? false : true; - N_FLAG = bus.reg[dest].I & 0x80000000 ? true : false; -} - -// BIC Rd, Rs - void thumb43_2(u32 opcode) -{ - int dest = opcode & 7; - bus.reg[dest].I &= (~bus.reg[(opcode >> 3) & 7].I); - Z_FLAG = bus.reg[dest].I ? false : true; - N_FLAG = bus.reg[dest].I & 0x80000000 ? true : false; -} - -// MVN Rd, Rs - void thumb43_3(u32 opcode) -{ - int dest = opcode & 7; - bus.reg[dest].I = ~bus.reg[(opcode >> 3) & 7].I; - Z_FLAG = bus.reg[dest].I ? false : true; - N_FLAG = bus.reg[dest].I & 0x80000000 ? true : false; -} - -// High-register instructions and BX ////////////////////////////////////// - -// ADD Rd, Hs - void thumb44_1(u32 opcode) -{ - bus.reg[opcode&7].I += bus.reg[((opcode>>3)&7)+8].I; -} - -// ADD Hd, Rs - void thumb44_2(u32 opcode) -{ - bus.reg[(opcode&7)+8].I += bus.reg[(opcode>>3)&7].I; - if((opcode&7) == 7) { - bus.reg[15].I &= 0xFFFFFFFE; - bus.armNextPC = bus.reg[15].I; - bus.reg[15].I += 2; - THUMB_PREFETCH; - clockTicks = codeTicksAccessSeq16(bus.armNextPC)<<1 - + codeTicksAccess(bus.armNextPC, BITS_16) + 3; - } -} - -// ADD Hd, Hs - void thumb44_3(u32 opcode) -{ - bus.reg[(opcode&7)+8].I += bus.reg[((opcode>>3)&7)+8].I; - if((opcode&7) == 7) { - bus.reg[15].I &= 0xFFFFFFFE; - bus.armNextPC = bus.reg[15].I; - bus.reg[15].I += 2; - THUMB_PREFETCH; - clockTicks = codeTicksAccessSeq16(bus.armNextPC)<<1 - + codeTicksAccess(bus.armNextPC, BITS_16) + 3; - } -} - -// CMP Rd, Hs - void thumb45_1(u32 opcode) -{ - int dest = opcode & 7; - u32 value = bus.reg[((opcode>>3)&7)+8].I; - CMP_RD_RS; -} - -// CMP Hd, Rs - void thumb45_2(u32 opcode) -{ - int dest = (opcode & 7) + 8; - u32 value = bus.reg[(opcode>>3)&7].I; - CMP_RD_RS; -} - -// CMP Hd, Hs - void thumb45_3(u32 opcode) -{ - int dest = (opcode & 7) + 8; - u32 value = bus.reg[((opcode>>3)&7)+8].I; - CMP_RD_RS; -} - -// MOV Rd, Hs - void thumb46_1(u32 opcode) -{ - bus.reg[opcode&7].I = bus.reg[((opcode>>3)&7)+8].I; -} - -// MOV Hd, Rs - void thumb46_2(u32 opcode) -{ - bus.reg[(opcode&7)+8].I = bus.reg[(opcode>>3)&7].I; - if((opcode&7) == 7) { - bus.reg[15].I &= 0xFFFFFFFE; - bus.armNextPC = bus.reg[15].I; - bus.reg[15].I += 2; - THUMB_PREFETCH; - clockTicks = codeTicksAccessSeq16(bus.armNextPC)<<1 - + codeTicksAccess(bus.armNextPC, BITS_16) + 3; - } -} - -// MOV Hd, Hs - void thumb46_3(u32 opcode) -{ - bus.reg[(opcode&7)+8].I = bus.reg[((opcode>>3)&7)+8].I; - if((opcode&7) == 7) { - bus.reg[15].I &= 0xFFFFFFFE; - bus.armNextPC = bus.reg[15].I; - bus.reg[15].I += 2; - THUMB_PREFETCH; - clockTicks = codeTicksAccessSeq16(bus.armNextPC)<<1 - + codeTicksAccess(bus.armNextPC, BITS_16) + 3; - } -} - - -// BX Rs - void thumb47(u32 opcode) -{ - int base = (opcode >> 3) & 15; - bus.busPrefetchCount=0; - bus.reg[15].I = bus.reg[base].I; - if(bus.reg[base].I & 1) { - armState = false; - bus.reg[15].I &= 0xFFFFFFFE; - bus.armNextPC = bus.reg[15].I; - bus.reg[15].I += 2; - THUMB_PREFETCH; - clockTicks = ((codeTicksAccessSeq16(bus.armNextPC)) << 1) - + codeTicksAccess(bus.armNextPC, BITS_16) + 3; - } else { - armState = true; - bus.reg[15].I &= 0xFFFFFFFC; - bus.armNextPC = bus.reg[15].I; - bus.reg[15].I += 4; - ARM_PREFETCH; - clockTicks = ((codeTicksAccessSeq32(bus.armNextPC)) << 1) - + codeTicksAccess(bus.armNextPC, BITS_32) + 3; - } -} - -// Load/store instructions //////////////////////////////////////////////// - -// LDR R0~R7,[PC, #Imm] - void thumb48(u32 opcode) -{ - u8 regist = (opcode >> 8) & 7; - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - u32 address = (bus.reg[15].I & 0xFFFFFFFC) + ((opcode & 0xFF) << 2); - // why quick? - // bus.reg[regist].I = CPUReadMemoryQuick(address); - bus.reg[regist].I = CPUReadMemory(address); - bus.busPrefetchCount=0; - int dataticks_value = DATATICKS_ACCESS_32BIT(address); - DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); - clockTicks = 3 + dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16); -} - -// STR Rd, [Rs, Rn] - void thumb50(u32 opcode) -{ - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - u32 address = bus.reg[(opcode>>3)&7].I + bus.reg[(opcode>>6)&7].I; - CPUWriteMemory(address, bus.reg[opcode & 7].I); - int dataticks_value = DATATICKS_ACCESS_32BIT(address); - DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); - clockTicks = dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16) + 2; -} - -// STRH Rd, [Rs, Rn] - void thumb52(u32 opcode) -{ - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - u32 address = bus.reg[(opcode>>3)&7].I + bus.reg[(opcode>>6)&7].I; - CPUWriteHalfWord(address, bus.reg[opcode&7].W.W0); - int dataticks_value = DATATICKS_ACCESS_16BIT(address); - DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); - clockTicks = dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16) + 2; -} - -// STRB Rd, [Rs, Rn] - void thumb54(u32 opcode) -{ - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - u32 address = bus.reg[(opcode>>3)&7].I + bus.reg[(opcode >>6)&7].I; - CPUWriteByte(address, bus.reg[opcode & 7].B.B0); - int dataticks_value = DATATICKS_ACCESS_16BIT(address); - DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); - clockTicks = dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16) + 2; -} - -// LDSB Rd, [Rs, Rn] - void thumb56(u32 opcode) -{ - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - u32 address = bus.reg[(opcode>>3)&7].I + bus.reg[(opcode>>6)&7].I; - bus.reg[opcode&7].I = (s8)CPUReadByte(address); - int dataticks_value = DATATICKS_ACCESS_16BIT(address); - DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); - clockTicks = 3 + dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16); -} - -// LDR Rd, [Rs, Rn] - void thumb58(u32 opcode) -{ - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - u32 address = bus.reg[(opcode>>3)&7].I + bus.reg[(opcode>>6)&7].I; - bus.reg[opcode&7].I = CPUReadMemory(address); - int dataticks_value = DATATICKS_ACCESS_32BIT(address); - DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); - clockTicks = 3 + dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16); -} - -// LDRH Rd, [Rs, Rn] - void thumb5A(u32 opcode) -{ - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - u32 address = bus.reg[(opcode>>3)&7].I + bus.reg[(opcode>>6)&7].I; - bus.reg[opcode&7].I = CPUReadHalfWord(address); - int dataticks_value = DATATICKS_ACCESS_32BIT(address); - DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); - clockTicks = 3 + dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16); -} - -// LDRB Rd, [Rs, Rn] - void thumb5C(u32 opcode) -{ - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - u32 address = bus.reg[(opcode>>3)&7].I + bus.reg[(opcode>>6)&7].I; - bus.reg[opcode&7].I = CPUReadByte(address); - int dataticks_value = DATATICKS_ACCESS_16BIT(address); - DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); - clockTicks = 3 + dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16); -} - -// LDSH Rd, [Rs, Rn] - void thumb5E(u32 opcode) -{ - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - u32 address = bus.reg[(opcode>>3)&7].I + bus.reg[(opcode>>6)&7].I; - bus.reg[opcode&7].I = (s16)CPUReadHalfWordSigned(address); - int dataticks_value = DATATICKS_ACCESS_16BIT(address); - DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); - clockTicks = 3 + dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16); -} - -// STR Rd, [Rs, #Imm] - void thumb60(u32 opcode) -{ - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - u32 address = bus.reg[(opcode>>3)&7].I + (((opcode>>6)&31)<<2); - CPUWriteMemory(address, bus.reg[opcode&7].I); - int dataticks_value = DATATICKS_ACCESS_32BIT(address); - DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); - clockTicks = dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16) + 2; -} - -// LDR Rd, [Rs, #Imm] - void thumb68(u32 opcode) -{ - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - u32 address = bus.reg[(opcode>>3)&7].I + (((opcode>>6)&31)<<2); - bus.reg[opcode&7].I = CPUReadMemory(address); - int dataticks_value = DATATICKS_ACCESS_32BIT(address); - DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); - clockTicks = 3 + dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16); -} - -// STRB Rd, [Rs, #Imm] - void thumb70(u32 opcode) -{ - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - u32 address = bus.reg[(opcode>>3)&7].I + (((opcode>>6)&31)); - CPUWriteByte(address, bus.reg[opcode&7].B.B0); - int dataticks_value = DATATICKS_ACCESS_16BIT(address); - DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); - clockTicks = dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16) + 2; -} - -// LDRB Rd, [Rs, #Imm] - void thumb78(u32 opcode) -{ - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - u32 address = bus.reg[(opcode>>3)&7].I + (((opcode>>6)&31)); - bus.reg[opcode&7].I = CPUReadByte(address); - int dataticks_value = DATATICKS_ACCESS_16BIT(address); - DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); - clockTicks = 3 + dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16); -} - -// STRH Rd, [Rs, #Imm] - void thumb80(u32 opcode) -{ - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - u32 address = bus.reg[(opcode>>3)&7].I + (((opcode>>6)&31)<<1); - CPUWriteHalfWord(address, bus.reg[opcode&7].W.W0); - int dataticks_value = DATATICKS_ACCESS_16BIT(address); - DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); - clockTicks = dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16) + 2; -} - -// LDRH Rd, [Rs, #Imm] - void thumb88(u32 opcode) -{ - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - u32 address = bus.reg[(opcode>>3)&7].I + (((opcode>>6)&31)<<1); - bus.reg[opcode&7].I = CPUReadHalfWord(address); - int dataticks_value = DATATICKS_ACCESS_16BIT(address); - DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); - clockTicks = 3 + dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16); -} - -// STR R0~R7, [SP, #Imm] - void thumb90(u32 opcode) -{ - u8 regist = (opcode >> 8) & 7; - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - u32 address = bus.reg[13].I + ((opcode&255)<<2); - CPUWriteMemory(address, bus.reg[regist].I); - int dataticks_value = DATATICKS_ACCESS_32BIT(address); - DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); - clockTicks = dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16) + 2; -} - -// LDR R0~R7, [SP, #Imm] - void thumb98(u32 opcode) -{ - u8 regist = (opcode >> 8) & 7; - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - u32 address = bus.reg[13].I + ((opcode&255)<<2); - // why quick? - // bus.reg[regist].I = CPUReadMemoryQuick(address); - bus.reg[regist].I = CPUReadMemory(address); - int dataticks_value = DATATICKS_ACCESS_32BIT(address); - DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); - clockTicks = 3 + dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16); -} - -// PC/stack-related /////////////////////////////////////////////////////// - -// ADD R0~R7, PC, Imm - void thumbA0(u32 opcode) -{ - u8 regist = (opcode >> 8) & 7; - bus.reg[regist].I = (bus.reg[15].I & 0xFFFFFFFC) + ((opcode&255)<<2); -} - -// ADD R0~R7, SP, Imm - void thumbA8(u32 opcode) -{ - u8 regist = (opcode >> 8) & 7; - bus.reg[regist].I = bus.reg[13].I + ((opcode&255)<<2); -} - -// ADD SP, Imm - void thumbB0(u32 opcode) -{ - int offset = (opcode & 127) << 2; - if(opcode & 0x80) - offset = -offset; - bus.reg[13].I += offset; -} - -// Push and pop /////////////////////////////////////////////////////////// - -#define PUSH_REG(val, r) \ - if (opcode & (val)) { \ - CPUWriteMemory(address, bus.reg[(r)].I); \ - int dataticks_value = count ? DATATICKS_ACCESS_32BIT_SEQ(address) : DATATICKS_ACCESS_32BIT(address); \ - DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); \ - clockTicks += 1 + dataticks_value; \ - count++; \ - address += 4; \ - } - -#define POP_REG(val, r) \ - if (opcode & (val)) { \ - bus.reg[(r)].I = CPUReadMemory(address); \ -int dataticks_value = count ? DATATICKS_ACCESS_32BIT_SEQ(address) : DATATICKS_ACCESS_32BIT(address); \ - DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); \ - clockTicks += 1 + dataticks_value; \ - count++; \ - address += 4; \ - } - -// PUSH {Rlist} - void thumbB4(u32 opcode) -{ - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - int count = 0; - u32 temp = bus.reg[13].I - 4 * cpuBitsSet[opcode & 0xff]; - u32 address = temp & 0xFFFFFFFC; - PUSH_REG(1, 0); - PUSH_REG(2, 1); - PUSH_REG(4, 2); - PUSH_REG(8, 3); - PUSH_REG(16, 4); - PUSH_REG(32, 5); - PUSH_REG(64, 6); - PUSH_REG(128, 7); - clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_16); - bus.reg[13].I = temp; -} - -// PUSH {Rlist, LR} - void thumbB5(u32 opcode) -{ - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - int count = 0; - u32 temp = bus.reg[13].I - 4 - 4 * cpuBitsSet[opcode & 0xff]; - u32 address = temp & 0xFFFFFFFC; - PUSH_REG(1, 0); - PUSH_REG(2, 1); - PUSH_REG(4, 2); - PUSH_REG(8, 3); - PUSH_REG(16, 4); - PUSH_REG(32, 5); - PUSH_REG(64, 6); - PUSH_REG(128, 7); - PUSH_REG(256, 14); - clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_16); - bus.reg[13].I = temp; -} - -// POP {Rlist} - void thumbBC(u32 opcode) -{ - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - int count = 0; - u32 address = bus.reg[13].I & 0xFFFFFFFC; - u32 temp = bus.reg[13].I + 4*cpuBitsSet[opcode & 0xFF]; - POP_REG(1, 0); - POP_REG(2, 1); - POP_REG(4, 2); - POP_REG(8, 3); - POP_REG(16, 4); - POP_REG(32, 5); - POP_REG(64, 6); - POP_REG(128, 7); - bus.reg[13].I = temp; - clockTicks = 2 + codeTicksAccess(bus.armNextPC, BITS_16); -} - -// POP {Rlist, PC} - void thumbBD(u32 opcode) -{ - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - int count = 0; - u32 address = bus.reg[13].I & 0xFFFFFFFC; - u32 temp = bus.reg[13].I + 4 + 4*cpuBitsSet[opcode & 0xFF]; - POP_REG(1, 0); - POP_REG(2, 1); - POP_REG(4, 2); - POP_REG(8, 3); - POP_REG(16, 4); - POP_REG(32, 5); - POP_REG(64, 6); - POP_REG(128, 7); - bus.reg[15].I = (CPUReadMemory(address) & 0xFFFFFFFE); - int dataticks_value = count ? DATATICKS_ACCESS_32BIT_SEQ(address) : DATATICKS_ACCESS_32BIT(address); - DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); - clockTicks += 1 + dataticks_value; - count++; - bus.armNextPC = bus.reg[15].I; - bus.reg[15].I += 2; - bus.reg[13].I = temp; - THUMB_PREFETCH; - bus.busPrefetchCount = 0; - clockTicks += 3 + ((codeTicksAccess(bus.armNextPC, BITS_16)) << 1); -} - -// Load/store multiple //////////////////////////////////////////////////// - -#define THUMB_STM_REG(val,r,b) \ - if(opcode & (val)) { \ - CPUWriteMemory(address, bus.reg[(r)].I); \ - bus.reg[(b)].I = temp; \ - int dataticks_val = count ? DATATICKS_ACCESS_32BIT_SEQ(address) : DATATICKS_ACCESS_32BIT(address); \ - DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_val); \ - clockTicks += 1 + dataticks_val; \ - count++; \ - address += 4; \ - } - -#define THUMB_LDM_REG(val,r) \ - if(opcode & (val)) { \ - bus.reg[(r)].I = CPUReadMemory(address); \ - int dataticks_val = count ? DATATICKS_ACCESS_32BIT_SEQ(address) : DATATICKS_ACCESS_32BIT(address); \ - DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_val); \ - clockTicks += 1 + dataticks_val; \ - count++; \ - address += 4; \ - } - -// STM R0~7!, {Rlist} - void thumbC0(u32 opcode) -{ - u8 regist = (opcode >> 8) & 7; - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - u32 address = bus.reg[regist].I & 0xFFFFFFFC; - u32 temp = bus.reg[regist].I + 4*cpuBitsSet[opcode & 0xff]; - int count = 0; - // store - THUMB_STM_REG(1, 0, regist); - THUMB_STM_REG(2, 1, regist); - THUMB_STM_REG(4, 2, regist); - THUMB_STM_REG(8, 3, regist); - THUMB_STM_REG(16, 4, regist); - THUMB_STM_REG(32, 5, regist); - THUMB_STM_REG(64, 6, regist); - THUMB_STM_REG(128, 7, regist); - clockTicks = 1 + codeTicksAccess(bus.armNextPC, BITS_16); -} - -// LDM R0~R7!, {Rlist} - void thumbC8(u32 opcode) -{ - u8 regist = (opcode >> 8) & 7; - if (bus.busPrefetchCount == 0) - bus.busPrefetch = bus.busPrefetchEnable; - u32 address = bus.reg[regist].I & 0xFFFFFFFC; - u32 temp = bus.reg[regist].I + 4*cpuBitsSet[opcode & 0xFF]; - int count = 0; - // load - THUMB_LDM_REG(1, 0); - THUMB_LDM_REG(2, 1); - THUMB_LDM_REG(4, 2); - THUMB_LDM_REG(8, 3); - THUMB_LDM_REG(16, 4); - THUMB_LDM_REG(32, 5); - THUMB_LDM_REG(64, 6); - THUMB_LDM_REG(128, 7); - clockTicks = 2 + codeTicksAccess(bus.armNextPC, BITS_16); - if(!(opcode & (1<*thumbInsnTable[opcode>>6])(opcode); - - ct = clockTicks; - - if (ct < 0) - return 0; - - /// better pipelining - if (ct==0) - clockTicks = codeTicksAccessSeq16(oldArmNextPC) + 1; - - cpuTotalTicks += clockTicks; - -} while ((cpuTotalTicks < cpuNextEvent) & ~armState & ~holdState); - return 1; -} - - -/*============================================================ - GBA GFX -============================================================ */ - -#ifdef TILED_RENDERING -#ifdef _MSC_VER -union u8h -{ - __pragma( pack(push, 1)); - struct -#ifdef LSB_FIRST - { - /* 0*/ unsigned char lo:4; - /* 4*/ unsigned char hi:4; -#else - { - /* 4*/ unsigned char hi:4; - /* 0*/ unsigned char lo:4; -#endif - } - __pragma(pack(pop)); - u8 val; -}; -#else -union u8h -{ - struct -#ifdef LSB_FIRST - { - /* 0*/ unsigned char lo:4; - /* 4*/ unsigned char hi:4; -#else - { - /* 4*/ unsigned char hi:4; - /* 0*/ unsigned char lo:4; -#endif - } __attribute__ ((packed)); - u8 val; -}; -#endif - -union TileEntry -{ -#ifdef LSB_FIRST - struct - { - /* 0*/ unsigned tileNum:10; - /*12*/ unsigned hFlip:1; - /*13*/ unsigned vFlip:1; - /*14*/ unsigned palette:4; - }; -#else - struct - { - /*14*/ unsigned palette:4; - /*13*/ unsigned vFlip:1; - /*12*/ unsigned hFlip:1; - /* 0*/ unsigned tileNum:10; - }; -#endif - u16 val; -}; - -struct TileLine -{ - u32 pixels[8]; -}; - -typedef const TileLine (*TileReader) (const u16 *, const int, const u8 *, u16 *, const u32); - -static inline void gfxDrawPixel(u32 *dest, const u8 color, const u16 *palette, const u32 prio) -{ - *dest = color ? (READ16LE(&palette[color]) | prio): 0x80000000; -} - -static inline const TileLine gfxReadTile(const u16 *screenSource, const int yyy, const u8 *charBase, u16 *palette, const u32 prio) -{ - TileEntry tile; - tile.val = READ16LE(screenSource); - - int tileY = yyy & 7; - if (tile.vFlip) tileY = 7 - tileY; - TileLine tileLine; - - const u8 *tileBase = &charBase[tile.tileNum * 64 + tileY * 8]; - - if (!tile.hFlip) - { - gfxDrawPixel(&tileLine.pixels[0], tileBase[0], palette, prio); - gfxDrawPixel(&tileLine.pixels[1], tileBase[1], palette, prio); - gfxDrawPixel(&tileLine.pixels[2], tileBase[2], palette, prio); - gfxDrawPixel(&tileLine.pixels[3], tileBase[3], palette, prio); - gfxDrawPixel(&tileLine.pixels[4], tileBase[4], palette, prio); - gfxDrawPixel(&tileLine.pixels[5], tileBase[5], palette, prio); - gfxDrawPixel(&tileLine.pixels[6], tileBase[6], palette, prio); - gfxDrawPixel(&tileLine.pixels[7], tileBase[7], palette, prio); - } - else - { - gfxDrawPixel(&tileLine.pixels[0], tileBase[7], palette, prio); - gfxDrawPixel(&tileLine.pixels[1], tileBase[6], palette, prio); - gfxDrawPixel(&tileLine.pixels[2], tileBase[5], palette, prio); - gfxDrawPixel(&tileLine.pixels[3], tileBase[4], palette, prio); - gfxDrawPixel(&tileLine.pixels[4], tileBase[3], palette, prio); - gfxDrawPixel(&tileLine.pixels[5], tileBase[2], palette, prio); - gfxDrawPixel(&tileLine.pixels[6], tileBase[1], palette, prio); - gfxDrawPixel(&tileLine.pixels[7], tileBase[0], palette, prio); - } - - return tileLine; -} - -static inline const TileLine gfxReadTilePal(const u16 *screenSource, const int yyy, const u8 *charBase, u16 *palette, const u32 prio) -{ - TileEntry tile; - tile.val = READ16LE(screenSource); - - int tileY = yyy & 7; - if (tile.vFlip) tileY = 7 - tileY; - palette += tile.palette * 16; - TileLine tileLine; - - const u8h *tileBase = (u8h*) &charBase[tile.tileNum * 32 + tileY * 4]; - - if (!tile.hFlip) - { - gfxDrawPixel(&tileLine.pixels[0], tileBase[0].lo, palette, prio); - gfxDrawPixel(&tileLine.pixels[1], tileBase[0].hi, palette, prio); - gfxDrawPixel(&tileLine.pixels[2], tileBase[1].lo, palette, prio); - gfxDrawPixel(&tileLine.pixels[3], tileBase[1].hi, palette, prio); - gfxDrawPixel(&tileLine.pixels[4], tileBase[2].lo, palette, prio); - gfxDrawPixel(&tileLine.pixels[5], tileBase[2].hi, palette, prio); - gfxDrawPixel(&tileLine.pixels[6], tileBase[3].lo, palette, prio); - gfxDrawPixel(&tileLine.pixels[7], tileBase[3].hi, palette, prio); - } - else - { - gfxDrawPixel(&tileLine.pixels[0], tileBase[3].hi, palette, prio); - gfxDrawPixel(&tileLine.pixels[1], tileBase[3].lo, palette, prio); - gfxDrawPixel(&tileLine.pixels[2], tileBase[2].hi, palette, prio); - gfxDrawPixel(&tileLine.pixels[3], tileBase[2].lo, palette, prio); - gfxDrawPixel(&tileLine.pixels[4], tileBase[1].hi, palette, prio); - gfxDrawPixel(&tileLine.pixels[5], tileBase[1].lo, palette, prio); - gfxDrawPixel(&tileLine.pixels[6], tileBase[0].hi, palette, prio); - gfxDrawPixel(&tileLine.pixels[7], tileBase[0].lo, palette, prio); - } - - return tileLine; -} - -static inline void gfxDrawTile(const TileLine &tileLine, u32 *line) -{ - memcpy(line, tileLine.pixels, sizeof(tileLine.pixels)); -} - -static inline void gfxDrawTileClipped(const TileLine &tileLine, u32 *line, const int start, int w) -{ - memcpy(line, tileLine.pixels + start, w * sizeof(u32)); -} - -template -void gfxDrawTextScreen(u16 control, u16 hofs, u16 vofs, - u32 *line) -{ - u16 *palette = (u16 *)graphics.paletteRAM; - u8 *charBase = &vram[((control >> 2) & 0x03) * 0x4000]; - u16 *screenBase = (u16 *)&vram[((control >> 8) & 0x1f) * 0x800]; - u32 prio = ((control & 3)<<25) + 0x1000000; - int sizeX = 256; - int sizeY = 256; - switch ((control >> 14) & 3) - { - case 0: - break; - case 1: - sizeX = 512; - break; - case 2: - sizeY = 512; - break; - case 3: - sizeX = 512; - sizeY = 512; - break; - } - - int maskX = sizeX-1; - int maskY = sizeY-1; - - bool mosaicOn = (control & 0x40) ? true : false; - - int xxx = hofs & maskX; - int yyy = (vofs + io_registers[REG_VCOUNT]) & maskY; - int mosaicX = (MOSAIC & 0x000F)+1; - int mosaicY = ((MOSAIC & 0x00F0)>>4)+1; - - if (mosaicOn) - { - if ((io_registers[REG_VCOUNT] % mosaicY) != 0) - { - mosaicY = io_registers[REG_VCOUNT] - (io_registers[REG_VCOUNT] % mosaicY); - yyy = (vofs + mosaicY) & maskY; - } - } - - if (yyy > 255 && sizeY > 256) - { - yyy &= 255; - screenBase += 0x400; - if (sizeX > 256) - screenBase += 0x400; - } - - int yshift = ((yyy>>3)<<5); - - u16 *screenSource = screenBase + 0x400 * (xxx>>8) + ((xxx & 255)>>3) + yshift; - int x = 0; - const int firstTileX = xxx & 7; - - // First tile, if clipped - if (firstTileX) - { - gfxDrawTileClipped(readTile(screenSource, yyy, charBase, palette, prio), &line[x], firstTileX, 8 - firstTileX); - screenSource++; - x += 8 - firstTileX; - xxx += 8 - firstTileX; - - if (xxx == 256 && sizeX > 256) - { - screenSource = screenBase + 0x400 + yshift; - } - else if (xxx >= sizeX) - { - xxx = 0; - screenSource = screenBase + yshift; - } - } - - // Middle tiles, full - while (x < 240 - firstTileX) - { - gfxDrawTile(readTile(screenSource, yyy, charBase, palette, prio), &line[x]); - screenSource++; - xxx += 8; - x += 8; - - if (xxx == 256 && sizeX > 256) - { - screenSource = screenBase + 0x400 + yshift; - } - else if (xxx >= sizeX) - { - xxx = 0; - screenSource = screenBase + yshift; - } - } - - // Last tile, if clipped - if (firstTileX) - { - gfxDrawTileClipped(readTile(screenSource, yyy, charBase, palette, prio), &line[x], 0, firstTileX); - } - - if (mosaicOn) - { - if (mosaicX > 1) - { - int m = 1; - for (int i = 0; i < 239; i++) - { - line[i+1] = line[i]; - m++; - if (m == mosaicX) - { - m = 1; - i++; - } - } - } - } -} - -void gfxDrawTextScreen(u16 control, u16 hofs, u16 vofs, u32 *line) -{ - if (control & 0x80) // 1 pal / 256 col - gfxDrawTextScreen(control, hofs, vofs, line); - else // 16 pal / 16 col - gfxDrawTextScreen(control, hofs, vofs, line); -} -#else -inline void gfxDrawTextScreen(u16 control, u16 hofs, u16 vofs, - u32 *line) -{ - u16 *palette = (u16 *)graphics.paletteRAM; - u8 *charBase = &vram[((control >> 2) & 0x03) * 0x4000]; - u16 *screenBase = (u16 *)&vram[((control >> 8) & 0x1f) * 0x800]; - u32 prio = ((control & 3)<<25) + 0x1000000; - int sizeX = 256; - int sizeY = 256; - switch((control >> 14) & 3) { - case 0: - break; - case 1: - sizeX = 512; - break; - case 2: - sizeY = 512; - break; - case 3: - sizeX = 512; - sizeY = 512; - break; - } - - int maskX = sizeX-1; - int maskY = sizeY-1; - - bool mosaicOn = (control & 0x40) ? true : false; - - int xxx = hofs & maskX; - int yyy = (vofs + io_registers[REG_VCOUNT]) & maskY; - int mosaicX = (MOSAIC & 0x000F)+1; - int mosaicY = ((MOSAIC & 0x00F0)>>4)+1; - - if(mosaicOn) { - if((io_registers[REG_VCOUNT] % mosaicY) != 0) { - mosaicY = io_registers[REG_VCOUNT] - (io_registers[REG_VCOUNT] % mosaicY); - yyy = (vofs + mosaicY) & maskY; - } - } - - if(yyy > 255 && sizeY > 256) { - yyy &= 255; - screenBase += 0x400; - if(sizeX > 256) - screenBase += 0x400; - } - - int yshift = ((yyy>>3)<<5); - if((control) & 0x80) { - u16 *screenSource = screenBase + 0x400 * (xxx>>8) + ((xxx & 255)>>3) + yshift; - for(int x = 0; x < 240; x++) { - u16 data = READ16LE(screenSource); - - int tile = data & 0x3FF; - int tileX = (xxx & 7); - int tileY = yyy & 7; - - if(tileX == 7) - screenSource++; - - if(data & 0x0400) - tileX = 7 - tileX; - if(data & 0x0800) - tileY = 7 - tileY; - - u8 color = charBase[tile * 64 + tileY * 8 + tileX]; - - line[x] = color ? (READ16LE(&palette[color]) | prio): 0x80000000; - - xxx++; - if(xxx == 256) { - if(sizeX > 256) - screenSource = screenBase + 0x400 + yshift; - else { - screenSource = screenBase + yshift; - xxx = 0; - } - } else if(xxx >= sizeX) { - xxx = 0; - screenSource = screenBase + yshift; - } - } - } else { - u16 *screenSource = screenBase + 0x400*(xxx>>8)+((xxx&255)>>3) + - yshift; - for(int x = 0; x < 240; x++) { - u16 data = READ16LE(screenSource); - - int tile = data & 0x3FF; - int tileX = (xxx & 7); - int tileY = yyy & 7; - - if(tileX == 7) - screenSource++; - - if(data & 0x0400) - tileX = 7 - tileX; - if(data & 0x0800) - tileY = 7 - tileY; - - u8 color = charBase[(tile<<5) + (tileY<<2) + (tileX>>1)]; - - if(tileX & 1) { - color = (color >> 4); - } else { - color &= 0x0F; - } - - int pal = (data>>8) & 0xF0; - line[x] = color ? (READ16LE(&palette[pal + color])|prio): 0x80000000; - - xxx++; - if(xxx == 256) { - if(sizeX > 256) - screenSource = screenBase + 0x400 + yshift; - else { - screenSource = screenBase + yshift; - xxx = 0; - } - } else if(xxx >= sizeX) { - xxx = 0; - screenSource = screenBase + yshift; - } - } - } - if(mosaicOn) { - if(mosaicX > 1) { - int m = 1; - for(int i = 0; i < 239; i++) { - line[i+1] = line[i]; - m++; - if(m == mosaicX) { - m = 1; - i++; - } - } - } - } -} -#endif - -INLINE void gfxDrawRotScreen(u16 control, u16 x_l, u16 x_h, u16 y_l, u16 y_h, -u16 pa, u16 pb, u16 pc, u16 pd, int& currentX, int& currentY, int changed, u32 *line) -{ - u16 *palette = (u16 *)graphics.paletteRAM; - u8 *charBase = &vram[((control >> 2) & 0x03) << 14]; - u8 *screenBase = (u8 *)&vram[((control >> 8) & 0x1f) << 11]; - int prio = ((control & 3) << 25) + 0x1000000; - - u32 map_size = (control >> 14) & 3; - u32 sizeX = map_sizes_rot[map_size]; - u32 sizeY = map_sizes_rot[map_size]; - - int maskX = sizeX-1; - int maskY = sizeY-1; - - int yshift = ((control >> 14) & 3)+4; - -#ifdef BRANCHLESS_GBA_GFX - int dx = pa & 0x7FFF; - int dmx = pb & 0x7FFF; - int dy = pc & 0x7FFF; - int dmy = pd & 0x7FFF; - - dx |= isel(-(pa & 0x8000), 0, 0xFFFF8000); - - dmx |= isel(-(pb & 0x8000), 0, 0xFFFF8000); - - dy |= isel(-(pc & 0x8000), 0, 0xFFFF8000); - - dmy |= isel(-(pd & 0x8000), 0, 0xFFFF8000); -#else - int dx = pa & 0x7FFF; - if(pa & 0x8000) - dx |= 0xFFFF8000; - int dmx = pb & 0x7FFF; - if(pb & 0x8000) - dmx |= 0xFFFF8000; - int dy = pc & 0x7FFF; - if(pc & 0x8000) - dy |= 0xFFFF8000; - int dmy = pd & 0x7FFF; - if(pd & 0x8000) - dmy |= 0xFFFF8000; -#endif - - if(io_registers[REG_VCOUNT] == 0) - changed = 3; - - currentX += dmx; - currentY += dmy; - - if(changed & 1) - { - currentX = (x_l) | ((x_h & 0x07FF)<<16); - if(x_h & 0x0800) - currentX |= 0xF8000000; - } - - if(changed & 2) - { - currentY = (y_l) | ((y_h & 0x07FF)<<16); - if(y_h & 0x0800) - currentY |= 0xF8000000; - } - - int realX = currentX; - int realY = currentY; - - if(control & 0x40) - { - int mosaicY = ((MOSAIC & 0xF0)>>4) + 1; - int y = (io_registers[REG_VCOUNT] % mosaicY); - realX -= y*dmx; - realY -= y*dmy; - } - - memset(line, -1, 240 * sizeof(u32)); - if(control & 0x2000) - { - for(u32 x = 0; x < 240u; ++x) - { - int xxx = (realX >> 8) & maskX; - int yyy = (realY >> 8) & maskY; - - int tile = screenBase[(xxx>>3) + ((yyy>>3)<> 8); - unsigned yyy = (realY >> 8); - - if(xxx < sizeX && yyy < sizeY) - { - int tile = screenBase[(xxx>>3) + ((yyy>>3)< 1) - { - int m = 1; - for(u32 i = 0; i < 239u; ++i) - { - line[i+1] = line[i]; - if(++m == mosaicX) - { - m = 1; - ++i; - } - } - } - } -} - -INLINE void gfxDrawRotScreen16Bit( int& currentX, int& currentY, int changed) -{ - u16 *screenBase = (u16 *)&vram[0]; - int prio = ((io_registers[REG_BG2CNT] & 3) << 25) + 0x1000000; - - u32 sizeX = 240; - u32 sizeY = 160; - - int startX = (BG2X_L) | ((BG2X_H & 0x07FF)<<16); - if(BG2X_H & 0x0800) - startX |= 0xF8000000; - int startY = (BG2Y_L) | ((BG2Y_H & 0x07FF)<<16); - if(BG2Y_H & 0x0800) - startY |= 0xF8000000; - -#ifdef BRANCHLESS_GBA_GFX - int dx = io_registers[REG_BG2PA] & 0x7FFF; - dx |= isel(-(io_registers[REG_BG2PA] & 0x8000), 0, 0xFFFF8000); - - int dmx = io_registers[REG_BG2PB] & 0x7FFF; - dmx |= isel(-(io_registers[REG_BG2PB] & 0x8000), 0, 0xFFFF8000); - - int dy = io_registers[REG_BG2PC] & 0x7FFF; - dy |= isel(-(io_registers[REG_BG2PC] & 0x8000), 0, 0xFFFF8000); - - int dmy = io_registers[REG_BG2PD] & 0x7FFF; - dmy |= isel(-(io_registers[REG_BG2PD] & 0x8000), 0, 0xFFFF8000); -#else - int dx = io_registers[REG_BG2PA] & 0x7FFF; - if(io_registers[REG_BG2PA] & 0x8000) - dx |= 0xFFFF8000; - int dmx = io_registers[REG_BG2PB] & 0x7FFF; - if(io_registers[REG_BG2PB] & 0x8000) - dmx |= 0xFFFF8000; - int dy = io_registers[REG_BG2PC] & 0x7FFF; - if(io_registers[REG_BG2PC] & 0x8000) - dy |= 0xFFFF8000; - int dmy = io_registers[REG_BG2PD] & 0x7FFF; - if(io_registers[REG_BG2PD] & 0x8000) - dmy |= 0xFFFF8000; -#endif - - if(io_registers[REG_VCOUNT] == 0) - changed = 3; - - currentX += dmx; - currentY += dmy; - - if(changed & 1) - { - currentX = (BG2X_L) | ((BG2X_H & 0x07FF)<<16); - if(BG2X_H & 0x0800) - currentX |= 0xF8000000; - } - - if(changed & 2) - { - currentY = (BG2Y_L) | ((BG2Y_H & 0x07FF)<<16); - if(BG2Y_H & 0x0800) - currentY |= 0xF8000000; - } - - int realX = currentX; - int realY = currentY; - - if(io_registers[REG_BG2CNT] & 0x40) { - int mosaicY = ((MOSAIC & 0xF0)>>4) + 1; - int y = (io_registers[REG_VCOUNT] % mosaicY); - realX -= y*dmx; - realY -= y*dmy; - } - - unsigned xxx = (realX >> 8); - unsigned yyy = (realY >> 8); - - memset(line[2], -1, 240 * sizeof(u32)); - for(u32 x = 0; x < 240u; ++x) - { - if(xxx < sizeX && yyy < sizeY) - line[2][x] = (READ16LE(&screenBase[yyy * sizeX + xxx]) | prio); - - realX += dx; - realY += dy; - - xxx = (realX >> 8); - yyy = (realY >> 8); - } - - if(io_registers[REG_BG2CNT] & 0x40) { - int mosaicX = (MOSAIC & 0xF) + 1; - if(mosaicX > 1) { - int m = 1; - for(u32 i = 0; i < 239u; ++i) - { - line[2][i+1] = line[2][i]; - if(++m == mosaicX) - { - m = 1; - ++i; - } - } - } - } -} - -INLINE void gfxDrawRotScreen256(int ¤tX, int& currentY, int changed) -{ - u16 *palette = (u16 *)graphics.paletteRAM; - u8 *screenBase = (io_registers[REG_DISPCNT] & 0x0010) ? &vram[0xA000] : &vram[0x0000]; - int prio = ((io_registers[REG_BG2CNT] & 3) << 25) + 0x1000000; - u32 sizeX = 240; - u32 sizeY = 160; - - int startX = (BG2X_L) | ((BG2X_H & 0x07FF)<<16); - if(BG2X_H & 0x0800) - startX |= 0xF8000000; - int startY = (BG2Y_L) | ((BG2Y_H & 0x07FF)<<16); - if(BG2Y_H & 0x0800) - startY |= 0xF8000000; - -#ifdef BRANCHLESS_GBA_GFX - int dx = io_registers[REG_BG2PA] & 0x7FFF; - dx |= isel(-(io_registers[REG_BG2PA] & 0x8000), 0, 0xFFFF8000); - - int dmx = io_registers[REG_BG2PB] & 0x7FFF; - dmx |= isel(-(io_registers[REG_BG2PB] & 0x8000), 0, 0xFFFF8000); - - int dy = io_registers[REG_BG2PC] & 0x7FFF; - dy |= isel(-(io_registers[REG_BG2PC] & 0x8000), 0, 0xFFFF8000); - - int dmy = io_registers[REG_BG2PD] & 0x7FFF; - dmy |= isel(-(io_registers[REG_BG2PD] & 0x8000), 0, 0xFFFF8000); -#else - int dx = io_registers[REG_BG2PA] & 0x7FFF; - if(io_registers[REG_BG2PA] & 0x8000) - dx |= 0xFFFF8000; - int dmx = io_registers[REG_BG2PB] & 0x7FFF; - if(io_registers[REG_BG2PB] & 0x8000) - dmx |= 0xFFFF8000; - int dy = io_registers[REG_BG2PC] & 0x7FFF; - if(io_registers[REG_BG2PC] & 0x8000) - dy |= 0xFFFF8000; - int dmy = io_registers[REG_BG2PD] & 0x7FFF; - if(io_registers[REG_BG2PD] & 0x8000) - dmy |= 0xFFFF8000; -#endif - - if(io_registers[REG_VCOUNT] == 0) - changed = 3; - - currentX += dmx; - currentY += dmy; - - if(changed & 1) - { - currentX = (BG2X_L) | ((BG2X_H & 0x07FF)<<16); - if(BG2X_H & 0x0800) - currentX |= 0xF8000000; - } - - if(changed & 2) - { - currentY = (BG2Y_L) | ((BG2Y_H & 0x07FF)<<16); - if(BG2Y_H & 0x0800) - currentY |= 0xF8000000; - } - - int realX = currentX; - int realY = currentY; - - if(io_registers[REG_BG2CNT] & 0x40) { - int mosaicY = ((MOSAIC & 0xF0)>>4) + 1; - int y = io_registers[REG_VCOUNT] - (io_registers[REG_VCOUNT] % mosaicY); - realX = startX + y*dmx; - realY = startY + y*dmy; - } - - int xxx = (realX >> 8); - int yyy = (realY >> 8); - - memset(line[2], -1, 240 * sizeof(u32)); - for(u32 x = 0; x < 240; ++x) - { - u8 color = screenBase[yyy * 240 + xxx]; - if(unsigned(xxx) < sizeX && unsigned(yyy) < sizeY && color) - line[2][x] = (READ16LE(&palette[color])|prio); - realX += dx; - realY += dy; - - xxx = (realX >> 8); - yyy = (realY >> 8); - } - - if(io_registers[REG_BG2CNT] & 0x40) - { - int mosaicX = (MOSAIC & 0xF) + 1; - if(mosaicX > 1) - { - int m = 1; - for(u32 i = 0; i < 239u; ++i) - { - line[2][i+1] = line[2][i]; - if(++m == mosaicX) - { - m = 1; - ++i; - } - } - } - } -} - -INLINE void gfxDrawRotScreen16Bit160(int& currentX, int& currentY, int changed) -{ - u16 *screenBase = (io_registers[REG_DISPCNT] & 0x0010) ? (u16 *)&vram[0xa000] : - (u16 *)&vram[0]; - int prio = ((io_registers[REG_BG2CNT] & 3) << 25) + 0x1000000; - u32 sizeX = 160; - u32 sizeY = 128; - - int startX = (BG2X_L) | ((BG2X_H & 0x07FF)<<16); - if(BG2X_H & 0x0800) - startX |= 0xF8000000; - int startY = (BG2Y_L) | ((BG2Y_H & 0x07FF)<<16); - if(BG2Y_H & 0x0800) - startY |= 0xF8000000; - -#ifdef BRANCHLESS_GBA_GFX - int dx = io_registers[REG_BG2PA] & 0x7FFF; - dx |= isel(-(io_registers[REG_BG2PA] & 0x8000), 0, 0xFFFF8000); - - int dmx = io_registers[REG_BG2PB] & 0x7FFF; - dmx |= isel(-(io_registers[REG_BG2PB] & 0x8000), 0, 0xFFFF8000); - - int dy = io_registers[REG_BG2PC] & 0x7FFF; - dy |= isel(-(io_registers[REG_BG2PC] & 0x8000), 0, 0xFFFF8000); - - int dmy = io_registers[REG_BG2PD] & 0x7FFF; - dmy |= isel(-(io_registers[REG_BG2PD] & 0x8000), 0, 0xFFFF8000); -#else - int dx = io_registers[REG_BG2PA] & 0x7FFF; - if(io_registers[REG_BG2PA] & 0x8000) - dx |= 0xFFFF8000; - int dmx = io_registers[REG_BG2PB] & 0x7FFF; - if(io_registers[REG_BG2PB] & 0x8000) - dmx |= 0xFFFF8000; - int dy = io_registers[REG_BG2PC] & 0x7FFF; - if(io_registers[REG_BG2PC] & 0x8000) - dy |= 0xFFFF8000; - int dmy = io_registers[REG_BG2PD] & 0x7FFF; - if(io_registers[REG_BG2PD] & 0x8000) - dmy |= 0xFFFF8000; -#endif - - if(io_registers[REG_VCOUNT] == 0) - changed = 3; - - currentX += dmx; - currentY += dmy; - - if(changed & 1) - { - currentX = (BG2X_L) | ((BG2X_H & 0x07FF)<<16); - if(BG2X_H & 0x0800) - currentX |= 0xF8000000; - } - - if(changed & 2) - { - currentY = (BG2Y_L) | ((BG2Y_H & 0x07FF)<<16); - if(BG2Y_H & 0x0800) - currentY |= 0xF8000000; - } - - int realX = currentX; - int realY = currentY; - - if(io_registers[REG_BG2CNT] & 0x40) { - int mosaicY = ((MOSAIC & 0xF0)>>4) + 1; - int y = io_registers[REG_VCOUNT] - (io_registers[REG_VCOUNT] % mosaicY); - realX = startX + y*dmx; - realY = startY + y*dmy; - } - - int xxx = (realX >> 8); - int yyy = (realY >> 8); - - memset(line[2], -1, 240 * sizeof(u32)); - for(u32 x = 0; x < 240u; ++x) - { - if(unsigned(xxx) < sizeX && unsigned(yyy) < sizeY) - line[2][x] = (READ16LE(&screenBase[yyy * sizeX + xxx]) | prio); - - realX += dx; - realY += dy; - - xxx = (realX >> 8); - yyy = (realY >> 8); - } - - - int mosaicX = (MOSAIC & 0xF) + 1; - if(io_registers[REG_BG2CNT] & 0x40 && (mosaicX > 1)) - { - int m = 1; - for(u32 i = 0; i < 239u; ++i) - { - line[2][i+1] = line[2][i]; - if(++m == mosaicX) - { - m = 1; - ++i; - } - } - } -} - -/* lineOBJpix is used to keep track of the drawn OBJs - and to stop drawing them if the 'maximum number of OBJ per line' - has been reached. */ - -INLINE void gfxDrawSprites (void) -{ - unsigned lineOBJpix, m; - - lineOBJpix = (io_registers[REG_DISPCNT] & 0x20) ? 954 : 1226; - m = 0; - - u16 *sprites = (u16 *)oam; - u16 *spritePalette = &((u16 *)graphics.paletteRAM)[256]; - int mosaicY = ((MOSAIC & 0xF000)>>12) + 1; - int mosaicX = ((MOSAIC & 0xF00)>>8) + 1; - for(u32 x = 0; x < 128; x++) - { - u16 a0 = READ16LE(sprites++); - u16 a1 = READ16LE(sprites++); - u16 a2 = READ16LE(sprites++); - ++sprites; - - lineOBJpixleft[x]=lineOBJpix; - - lineOBJpix-=2; - if (lineOBJpix<=0) - return; - - if ((a0 & 0x0c00) == 0x0c00) - a0 &=0xF3FF; - - u16 a0val = a0>>14; - - if (a0val == 3) - { - a0 &= 0x3FFF; - a1 &= 0x3FFF; - } - - u32 sizeX = 8<<(a1>>14); - u32 sizeY = sizeX; - - - if (a0val & 1) - { -#ifdef BRANCHLESS_GBA_GFX - sizeX <<= isel(-(sizeX & (~31u)), 1, 0); - sizeY >>= isel(-(sizeY>8), 0, 1); -#else - if (sizeX<32) - sizeX<<=1; - if (sizeY>8) - sizeY>>=1; -#endif - } - else if (a0val & 2) - { -#ifdef BRANCHLESS_GBA_GFX - sizeX >>= isel(-(sizeX>8), 0, 1); - sizeY <<= isel(-(sizeY & (~31u)), 1, 0); -#else - if (sizeX>8) - sizeX>>=1; - if (sizeY<32) - sizeY<<=1; -#endif - - } - - - int sy = (a0 & 255); - int sx = (a1 & 0x1FF); - - // computes ticks used by OBJ-WIN if OBJWIN is enabled - if (((a0 & 0x0c00) == 0x0800) && (graphics.layerEnable & 0x8000)) - { - if ((a0 & 0x0300) == 0x0300) - { - sizeX<<=1; - sizeY<<=1; - } - -#ifdef BRANCHLESS_GBA_GFX - sy -= isel(256 - sy - sizeY, 0, 256); - sx -= isel(512 - sx - sizeX, 0, 512); -#else - if((sy+sizeY) > 256) - sy -= 256; - if ((sx+sizeX)> 512) - sx -= 512; -#endif - - if (sx < 0) - { - sizeX+=sx; - sx = 0; - } - else if ((sx+sizeX)>240) - sizeX=240-sx; - - if ((io_registers[REG_VCOUNT]>=sy) && (io_registers[REG_VCOUNT] 256) - sy -= 256; - int t = io_registers[REG_VCOUNT] - sy; - if(unsigned(t) < fieldY) - { - u32 startpix = 0; - if ((sx+fieldX)> 512) - startpix=512-sx; - - if (lineOBJpix && ((sx < 240) || startpix)) - { - lineOBJpix-=8; - int rot = (((a1 >> 9) & 0x1F) << 4); - u16 *OAM = (u16 *)oam; - int dx = READ16LE(&OAM[3 + rot]); - if(dx & 0x8000) - dx |= 0xFFFF8000; - int dmx = READ16LE(&OAM[7 + rot]); - if(dmx & 0x8000) - dmx |= 0xFFFF8000; - int dy = READ16LE(&OAM[11 + rot]); - if(dy & 0x8000) - dy |= 0xFFFF8000; - int dmy = READ16LE(&OAM[15 + rot]); - if(dmy & 0x8000) - dmy |= 0xFFFF8000; - - if(a0 & 0x1000) - t -= (t % mosaicY); - - int realX = ((sizeX) << 7) - (fieldX >> 1)*dx + ((t - (fieldY>>1))* dmx); - int realY = ((sizeY) << 7) - (fieldX >> 1)*dy + ((t - (fieldY>>1))* dmy); - - u32 prio = (((a2 >> 10) & 3) << 25) | ((a0 & 0x0c00)<<6); - - int c = (a2 & 0x3FF); - if((io_registers[REG_DISPCNT] & 7) > 2 && (c < 512)) - continue; - - if(a0 & 0x2000) - { - int inc = 32; - if(io_registers[REG_DISPCNT] & 0x40) - inc = sizeX >> 2; - else - c &= 0x3FE; - for(u32 x = 0; x < fieldX; x++) - { - if (x >= startpix) - lineOBJpix-=2; - unsigned xxx = realX >> 8; - unsigned yyy = realY >> 8; - if(xxx < sizeX && yyy < sizeY && sx < 240) - { - - u32 color = vram[0x10000 + ((((c + (yyy>>3) * inc)<<5) - + ((yyy & 7)<<3) + ((xxx >> 3)<<6) + (xxx & 7))&0x7FFF)]; - - if ((color==0) && (((prio >> 25)&3) < ((line[4][sx]>>25)&3))) - { - line[4][sx] = (line[4][sx] & 0xF9FFFFFF) | prio; - if((a0 & 0x1000) && m) - line[4][sx]=(line[4][sx-1] & 0xF9FFFFFF) | prio; - } - else if((color) && (prio < (line[4][sx]&0xFF000000))) - { - line[4][sx] = READ16LE(&spritePalette[color]) | prio; - if((a0 & 0x1000) && m) - line[4][sx]=(line[4][sx-1] & 0xF9FFFFFF) | prio; - } - - if ((a0 & 0x1000) && ((m+1) == mosaicX)) - m = 0; - } - sx = (sx+1)&511; - realX += dx; - realY += dy; - } - } - else - { - int inc = 32; - if(io_registers[REG_DISPCNT] & 0x40) - inc = sizeX >> 3; - int palette = (a2 >> 8) & 0xF0; - for(u32 x = 0; x < fieldX; ++x) - { - if (x >= startpix) - lineOBJpix-=2; - unsigned xxx = realX >> 8; - unsigned yyy = realY >> 8; - if(xxx < sizeX && yyy < sizeY && sx < 240) - { - - u32 color = vram[0x10000 + ((((c + (yyy>>3) * inc)<<5) - + ((yyy & 7)<<2) + ((xxx >> 3)<<5) - + ((xxx & 7)>>1))&0x7FFF)]; - if(xxx & 1) - color >>= 4; - else - color &= 0x0F; - - if ((color==0) && (((prio >> 25)&3) < - ((line[4][sx]>>25)&3))) - { - line[4][sx] = (line[4][sx] & 0xF9FFFFFF) | prio; - if((a0 & 0x1000) && m) - line[4][sx]=(line[4][sx-1] & 0xF9FFFFFF) | prio; - } - else if((color) && (prio < (line[4][sx]&0xFF000000))) - { - line[4][sx] = READ16LE(&spritePalette[palette+color]) | prio; - if((a0 & 0x1000) && m) - line[4][sx]=(line[4][sx-1] & 0xF9FFFFFF) | prio; - } - } - if((a0 & 0x1000) && m) - { - if (++m==mosaicX) - m=0; - } - - sx = (sx+1)&511; - realX += dx; - realY += dy; - - } - } - } - } - } - else - { - if(sy+sizeY > 256) - sy -= 256; - int t = io_registers[REG_VCOUNT] - sy; - if(unsigned(t) < sizeY) - { - u32 startpix = 0; - if ((sx+sizeX)> 512) - startpix=512-sx; - - if((sx < 240) || startpix) - { - lineOBJpix+=2; - - if(a1 & 0x2000) - t = sizeY - t - 1; - - int c = (a2 & 0x3FF); - if((io_registers[REG_DISPCNT] & 7) > 2 && (c < 512)) - continue; - - int inc = 32; - int xxx = 0; - if(a1 & 0x1000) - xxx = sizeX-1; - - if(a0 & 0x1000) - t -= (t % mosaicY); - - if(a0 & 0x2000) - { - if(io_registers[REG_DISPCNT] & 0x40) - inc = sizeX >> 2; - else - c &= 0x3FE; - - int address = 0x10000 + ((((c+ (t>>3) * inc) << 5) - + ((t & 7) << 3) + ((xxx>>3)<<6) + (xxx & 7)) & 0x7FFF); - - if(a1 & 0x1000) - xxx = 7; - u32 prio = (((a2 >> 10) & 3) << 25) | ((a0 & 0x0c00)<<6); - - for(u32 xx = 0; xx < sizeX; xx++) - { - if (xx >= startpix) - --lineOBJpix; - if(sx < 240) - { - u8 color = vram[address]; - if ((color==0) && (((prio >> 25)&3) < - ((line[4][sx]>>25)&3))) - { - line[4][sx] = (line[4][sx] & 0xF9FFFFFF) | prio; - if((a0 & 0x1000) && m) - line[4][sx]=(line[4][sx-1] & 0xF9FFFFFF) | prio; - } - else if((color) && (prio < (line[4][sx]&0xFF000000))) - { - line[4][sx] = READ16LE(&spritePalette[color]) | prio; - if((a0 & 0x1000) && m) - line[4][sx]=(line[4][sx-1] & 0xF9FFFFFF) | prio; - } - - if ((a0 & 0x1000) && ((m+1) == mosaicX)) - m = 0; - } - - sx = (sx+1) & 511; - if(a1 & 0x1000) - { - --address; - if(--xxx == -1) - { - address -= 56; - xxx = 7; - } - if(address < 0x10000) - address += 0x8000; - } - else - { - ++address; - if(++xxx == 8) - { - address += 56; - xxx = 0; - } - if(address > 0x17fff) - address -= 0x8000; - } - } - } - else - { - if(io_registers[REG_DISPCNT] & 0x40) - inc = sizeX >> 3; - - int address = 0x10000 + ((((c + (t>>3) * inc)<<5) - + ((t & 7)<<2) + ((xxx>>3)<<5) + ((xxx & 7) >> 1))&0x7FFF); - - u32 prio = (((a2 >> 10) & 3) << 25) | ((a0 & 0x0c00)<<6); - int palette = (a2 >> 8) & 0xF0; - if(a1 & 0x1000) - { - xxx = 7; - int xx = sizeX - 1; - do - { - if (xx >= (int)(startpix)) - --lineOBJpix; - //if (lineOBJpix<0) - // continue; - if(sx < 240) - { - u8 color = vram[address]; - if(xx & 1) - color >>= 4; - else - color &= 0x0F; - - if ((color==0) && (((prio >> 25)&3) < - ((line[4][sx]>>25)&3))) - { - line[4][sx] = (line[4][sx] & 0xF9FFFFFF) | prio; - if((a0 & 0x1000) && m) - line[4][sx]=(line[4][sx-1] & 0xF9FFFFFF) | prio; - } - else if((color) && (prio < (line[4][sx]&0xFF000000))) - { - line[4][sx] = READ16LE(&spritePalette[palette + color]) | prio; - if((a0 & 0x1000) && m) - line[4][sx]=(line[4][sx-1] & 0xF9FFFFFF) | prio; - } - } - - if ((a0 & 0x1000) && ((m+1) == mosaicX)) - m=0; - - sx = (sx+1) & 511; - if(!(xx & 1)) - --address; - if(--xxx == -1) - { - xxx = 7; - address -= 28; - } - if(address < 0x10000) - address += 0x8000; - }while(--xx >= 0); - } - else - { - for(u32 xx = 0; xx < sizeX; ++xx) - { - if (xx >= startpix) - --lineOBJpix; - //if (lineOBJpix<0) - // continue; - if(sx < 240) - { - u8 color = vram[address]; - if(xx & 1) - color >>= 4; - else - color &= 0x0F; - - if ((color==0) && (((prio >> 25)&3) < - ((line[4][sx]>>25)&3))) - { - line[4][sx] = (line[4][sx] & 0xF9FFFFFF) | prio; - if((a0 & 0x1000) && m) - line[4][sx]=(line[4][sx-1] & 0xF9FFFFFF) | prio; - } - else if((color) && (prio < (line[4][sx]&0xFF000000))) - { - line[4][sx] = READ16LE(&spritePalette[palette + color]) | prio; - if((a0 & 0x1000) && m) - line[4][sx]=(line[4][sx-1] & 0xF9FFFFFF) | prio; - - } - } - if ((a0 & 0x1000) && ((m+1) == mosaicX)) - m=0; - - sx = (sx+1) & 511; - if(xx & 1) - ++address; - if(++xxx == 8) - { - address += 28; - xxx = 0; - } - if(address > 0x17fff) - address -= 0x8000; - } - } - } - } - } - } - } -} - -INLINE void gfxDrawOBJWin (void) -{ - u16 *sprites = (u16 *)oam; - for(int x = 0; x < 128 ; x++) - { - int lineOBJpix = lineOBJpixleft[x]; - u16 a0 = READ16LE(sprites++); - u16 a1 = READ16LE(sprites++); - u16 a2 = READ16LE(sprites++); - sprites++; - - if (lineOBJpix<=0) - return; - - // ignores non OBJ-WIN and disabled OBJ-WIN - if(((a0 & 0x0c00) != 0x0800) || ((a0 & 0x0300) == 0x0200)) - continue; - - u16 a0val = a0>>14; - - if ((a0 & 0x0c00) == 0x0c00) - a0 &=0xF3FF; - - if (a0val == 3) - { - a0 &= 0x3FFF; - a1 &= 0x3FFF; - } - - int sizeX = 8<<(a1>>14); - int sizeY = sizeX; - - if (a0val & 1) - { -#ifdef BRANCHLESS_GBA_GFX - sizeX <<= isel(-(sizeX & (~31u)), 1, 0); - sizeY >>= isel(-(sizeY>8), 0, 1); -#else - if (sizeX<32) - sizeX<<=1; - if (sizeY>8) - sizeY>>=1; -#endif - } - else if (a0val & 2) - { -#ifdef BRANCHLESS_GBA_GFX - sizeX >>= isel(-(sizeX>8), 0, 1); - sizeY <<= isel(-(sizeY & (~31u)), 1, 0); -#else - if (sizeX>8) - sizeX>>=1; - if (sizeY<32) - sizeY<<=1; -#endif - - } - - int sy = (a0 & 255); - - if(a0 & 0x0100) - { - int fieldX = sizeX; - int fieldY = sizeY; - if(a0 & 0x0200) - { - fieldX <<= 1; - fieldY <<= 1; - } - if((sy+fieldY) > 256) - sy -= 256; - int t = io_registers[REG_VCOUNT] - sy; - if((t >= 0) && (t < fieldY)) - { - int sx = (a1 & 0x1FF); - int startpix = 0; - if ((sx+fieldX)> 512) - startpix=512-sx; - - if((sx < 240) || startpix) - { - lineOBJpix-=8; - // int t2 = t - (fieldY >> 1); - int rot = (a1 >> 9) & 0x1F; - u16 *OAM = (u16 *)oam; - int dx = READ16LE(&OAM[3 + (rot << 4)]); - if(dx & 0x8000) - dx |= 0xFFFF8000; - int dmx = READ16LE(&OAM[7 + (rot << 4)]); - if(dmx & 0x8000) - dmx |= 0xFFFF8000; - int dy = READ16LE(&OAM[11 + (rot << 4)]); - if(dy & 0x8000) - dy |= 0xFFFF8000; - int dmy = READ16LE(&OAM[15 + (rot << 4)]); - if(dmy & 0x8000) - dmy |= 0xFFFF8000; - - int realX = ((sizeX) << 7) - (fieldX >> 1)*dx - (fieldY>>1)*dmx - + t * dmx; - int realY = ((sizeY) << 7) - (fieldX >> 1)*dy - (fieldY>>1)*dmy - + t * dmy; - - int c = (a2 & 0x3FF); - if((io_registers[REG_DISPCNT] & 7) > 2 && (c < 512)) - continue; - - int inc = 32; - bool condition1 = a0 & 0x2000; - - if(io_registers[REG_DISPCNT] & 0x40) - inc = sizeX >> 3; - - for(int x = 0; x < fieldX; x++) - { - bool cont = true; - if (x >= startpix) - lineOBJpix-=2; - if (lineOBJpix<0) - continue; - int xxx = realX >> 8; - int yyy = realY >> 8; - - if(xxx < 0 || xxx >= sizeX || yyy < 0 || yyy >= sizeY || sx >= 240) - cont = false; - - if(cont) - { - u32 color; - if(condition1) - color = vram[0x10000 + ((((c + (yyy>>3) * inc)<<5) - + ((yyy & 7)<<3) + ((xxx >> 3)<<6) + - (xxx & 7))&0x7fff)]; - else - { - color = vram[0x10000 + ((((c + (yyy>>3) * inc)<<5) - + ((yyy & 7)<<2) + ((xxx >> 3)<<5) + - ((xxx & 7)>>1))&0x7fff)]; - if(xxx & 1) - color >>= 4; - else - color &= 0x0F; - } - - if(color) - line[5][sx] = 1; - } - sx = (sx+1)&511; - realX += dx; - realY += dy; - } - } - } - } - else - { - if((sy+sizeY) > 256) - sy -= 256; - int t = io_registers[REG_VCOUNT] - sy; - if((t >= 0) && (t < sizeY)) - { - int sx = (a1 & 0x1FF); - int startpix = 0; - if ((sx+sizeX)> 512) - startpix=512-sx; - - if((sx < 240) || startpix) - { - lineOBJpix+=2; - if(a1 & 0x2000) - t = sizeY - t - 1; - int c = (a2 & 0x3FF); - if((io_registers[REG_DISPCNT] & 7) > 2 && (c < 512)) - continue; - if(a0 & 0x2000) - { - - int inc = 32; - if(io_registers[REG_DISPCNT] & 0x40) - inc = sizeX >> 2; - else - c &= 0x3FE; - - int xxx = 0; - if(a1 & 0x1000) - xxx = sizeX-1; - int address = 0x10000 + ((((c+ (t>>3) * inc) << 5) - + ((t & 7) << 3) + ((xxx>>3)<<6) + (xxx & 7))&0x7fff); - if(a1 & 0x1000) - xxx = 7; - for(int xx = 0; xx < sizeX; xx++) - { - if (xx >= startpix) - lineOBJpix--; - if (lineOBJpix<0) - continue; - if(sx < 240) - { - u8 color = vram[address]; - if(color) - line[5][sx] = 1; - } - - sx = (sx+1) & 511; - if(a1 & 0x1000) { - xxx--; - address--; - if(xxx == -1) { - address -= 56; - xxx = 7; - } - if(address < 0x10000) - address += 0x8000; - } else { - xxx++; - address++; - if(xxx == 8) { - address += 56; - xxx = 0; - } - if(address > 0x17fff) - address -= 0x8000; - } - } - } - else - { - int inc = 32; - if(io_registers[REG_DISPCNT] & 0x40) - inc = sizeX >> 3; - int xxx = 0; - if(a1 & 0x1000) - xxx = sizeX - 1; - int address = 0x10000 + ((((c + (t>>3) * inc)<<5) - + ((t & 7)<<2) + ((xxx>>3)<<5) + ((xxx & 7) >> 1))&0x7fff); - // u32 prio = (((a2 >> 10) & 3) << 25) | ((a0 & 0x0c00)<<6); - // int palette = (a2 >> 8) & 0xF0; - if(a1 & 0x1000) - { - xxx = 7; - for(int xx = sizeX - 1; xx >= 0; xx--) - { - if (xx >= startpix) - lineOBJpix--; - if (lineOBJpix<0) - continue; - if(sx < 240) - { - u8 color = vram[address]; - if(xx & 1) - color = (color >> 4); - else - color &= 0x0F; - - if(color) - line[5][sx] = 1; - } - sx = (sx+1) & 511; - xxx--; - if(!(xx & 1)) - address--; - if(xxx == -1) { - xxx = 7; - address -= 28; - } - if(address < 0x10000) - address += 0x8000; - } - } - else - { - for(int xx = 0; xx < sizeX; xx++) - { - if (xx >= startpix) - lineOBJpix--; - if (lineOBJpix<0) - continue; - if(sx < 240) - { - u8 color = vram[address]; - if(xx & 1) - color = (color >> 4); - else - color &= 0x0F; - - if(color) - line[5][sx] = 1; - } - sx = (sx+1) & 511; - xxx++; - if(xx & 1) - address++; - if(xxx == 8) { - address += 28; - xxx = 0; - } - if(address > 0x17fff) - address -= 0x8000; - } - } - } - } - } - } - } -} - -INLINE u32 gfxIncreaseBrightness(u32 color, int coeff) -{ - color = (((color & 0xffff) << 16) | (color & 0xffff)) & 0x3E07C1F; - - color += ((((0x3E07C1F - color) * coeff) >> 4) & 0x3E07C1F); - - return (color >> 16) | color; -} - -INLINE u32 gfxDecreaseBrightness(u32 color, int coeff) -{ - color = (((color & 0xffff) << 16) | (color & 0xffff)) & 0x3E07C1F; - - color -= (((color * coeff) >> 4) & 0x3E07C1F); - - return (color >> 16) | color; -} - -#define GFX_ALPHA_BLEND(color, color2, ca, cb) \ - int r = AlphaClampLUT[(((color & 0x1F) * ca) >> 4) + (((color2 & 0x1F) * cb) >> 4)]; \ - int g = AlphaClampLUT[((((color >> 5) & 0x1F) * ca) >> 4) + ((((color2 >> 5) & 0x1F) * cb) >> 4)]; \ - int b = AlphaClampLUT[((((color >> 10) & 0x1F) * ca) >> 4) + ((((color2 >> 10) & 0x1F) * cb) >> 4)]; \ - color = (color & 0xFFFF0000) | (b << 10) | (g << 5) | r; - -/*============================================================ - GBA.CPP -============================================================ */ -static const bool useBios = true; -bool skipBios; -// it's a few bytes in the linkscript to make a multiboot image work in normal boot as well, -// and most of the ones i've seen have done that, so this is not terribly useful -static const bool cpuIsMultiBoot = false; -int cpuSaveType; // used only in init() to set up function pointers and for save file determination -bool mirroringEnable; - -int cpuDmaCount; - -uint8_t bios[0x4000]; -uint8_t rom[0x2000000]; -uint8_t internalRAM[0x8000]; -uint8_t workRAM[0x40000]; -uint8_t vram[0x20000]; -u16 pix[2 * PIX_BUFFER_SCREEN_WIDTH * 160]; -uint8_t oam[0x400]; -uint8_t ioMem[0x400]; - -bool cpuEEPROMEnabled; // true to process writes to EEPROM at 0dxxxxxx -bool cpuEEPROMSensorEnabled; // eeprom motion sensor? code is mostly disabled - -#ifndef LSB_FIRST -bool cpuBiosSwapped = false; -#endif - -INLINE int CPUUpdateTicks (void) -{ - int cpuLoopTicks = graphics.lcdTicks; - - if(timer0On && (timer0Ticks < cpuLoopTicks)) - cpuLoopTicks = timer0Ticks; - - if(timer1On && !(io_registers[REG_TM1CNT] & 4) && (timer1Ticks < cpuLoopTicks)) - cpuLoopTicks = timer1Ticks; - - if(timer2On && !(io_registers[REG_TM2CNT] & 4) && (timer2Ticks < cpuLoopTicks)) - cpuLoopTicks = timer2Ticks; - - if(timer3On && !(io_registers[REG_TM3CNT] & 4) && (timer3Ticks < cpuLoopTicks)) - cpuLoopTicks = timer3Ticks; - - if (IRQTicks) - { - if (IRQTicks < cpuLoopTicks) - cpuLoopTicks = IRQTicks; - } - - return cpuLoopTicks; -} - -#define CPUUpdateWindow0() \ -{ \ - int x00_window0 = io_registers[REG_WIN0H] >>8; \ - int x01_window0 = io_registers[REG_WIN0H] & 255; \ - int x00_lte_x01 = x00_window0 <= x01_window0; \ - for(int i = 0; i < 240; i++) \ - gfxInWin[0][i] = ((i >= x00_window0 && i < x01_window0) & x00_lte_x01) | ((i >= x00_window0 || i < x01_window0) & ~x00_lte_x01); \ -} - -#define CPUUpdateWindow1() \ -{ \ - int x00_window1 = io_registers[REG_WIN1H]>>8; \ - int x01_window1 = io_registers[REG_WIN1H] & 255; \ - int x00_lte_x01 = x00_window1 <= x01_window1; \ - for(int i = 0; i < 240; i++) \ - gfxInWin[1][i] = ((i >= x00_window1 && i < x01_window1) & x00_lte_x01) | ((i >= x00_window1 || i < x01_window1) & ~x00_lte_x01); \ -} - -#define CPUCompareVCOUNT() \ - if(io_registers[REG_VCOUNT] == (io_registers[REG_DISPSTAT] >> 8)) \ - { \ - io_registers[REG_DISPSTAT] |= 4; \ - UPDATE_REG(0x04, io_registers[REG_DISPSTAT]); \ - if(io_registers[REG_DISPSTAT] & 0x20) \ - { \ - io_registers[REG_IF] |= 4; \ - UPDATE_REG(0x202, io_registers[REG_IF]); \ - } \ - } \ - else \ - { \ - io_registers[REG_DISPSTAT] &= 0xFFFB; \ - UPDATE_REG(0x4, io_registers[REG_DISPSTAT]); \ - } \ - if (graphics.layerEnableDelay > 0) \ - { \ - graphics.layerEnableDelay--; \ - if (graphics.layerEnableDelay == 1) \ - graphics.layerEnable = io_registers[REG_DISPCNT]; \ - } - -int CPULoadRom(const u8 *romfile, const u32 romfilelen) -{ - if (cpuIsMultiBoot) - { - if (romfilelen > 0x40000) - return 0; - } - else - { - if (romfilelen > 0x2000000) - return 0; - } - - uint8_t *whereToLoad = cpuIsMultiBoot ? workRAM : rom; - - memcpy(whereToLoad, romfile, romfilelen); - romSize = romfilelen; - - uint16_t *temp = (uint16_t *)(rom+((romSize+1)&~1)); - int i; - for(i = (romSize+1)&~1; i < 0x2000000; i+=2) { - WRITE16LE(temp, (i >> 1) & 0xFFFF); - temp++; - } - - - flashInit(); - eepromInit(); - - memset(line[0], -1, 240 * sizeof(u32)); - memset(line[1], -1, 240 * sizeof(u32)); - memset(line[2], -1, 240 * sizeof(u32)); - memset(line[3], -1, 240 * sizeof(u32)); - - return romSize; -} - -void doMirroring (bool b) -{ - uint32_t mirroredRomSize = (((romSize)>>20) & 0x3F)<<20; - uint32_t mirroredRomAddress = romSize; - if ((mirroredRomSize <=0x800000) && (b)) - { - mirroredRomAddress = mirroredRomSize; - if (mirroredRomSize==0) - mirroredRomSize=0x100000; - while (mirroredRomAddress<0x01000000) - { - memcpy((uint16_t *)(rom+mirroredRomAddress), (uint16_t *)(rom), mirroredRomSize); - mirroredRomAddress+=mirroredRomSize; - } - } -} - -#define brightness_switch() \ - switch((BLDMOD >> 6) & 3) \ - { \ - case 2: \ - color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); \ - break; \ - case 3: \ - color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); \ - break; \ - } - -#define alpha_blend_brightness_switch() \ - if(top2 & (BLDMOD>>8)) \ - if(color < 0x80000000) \ - { \ - GFX_ALPHA_BLEND(color, back, coeff[COLEV & 0x1F], coeff[(COLEV >> 8) & 0x1F]); \ - } \ - else if(BLDMOD & top) \ - { \ - brightness_switch(); \ - } - -/* we only use 16bit color depth */ -#define INIT_COLOR_DEPTH_LINE_MIX() uint16_t * lineMix = (pix + PIX_BUFFER_SCREEN_WIDTH * io_registers[REG_VCOUNT]) - -void mode0RenderLine (void) -{ -#ifdef REPORT_VIDEO_MODES - fprintf(stderr, "MODE 0: Render Line\n"); -#endif - INIT_COLOR_DEPTH_LINE_MIX(); - - uint16_t *palette = (uint16_t *)graphics.paletteRAM; - - if(graphics.layerEnable & 0x0100) { - gfxDrawTextScreen(io_registers[REG_BG0CNT], io_registers[REG_BG0HOFS], io_registers[REG_BG0VOFS], line[0]); - } - - if(graphics.layerEnable & 0x0200) { - gfxDrawTextScreen(io_registers[REG_BG1CNT], io_registers[REG_BG1HOFS], io_registers[REG_BG1VOFS], line[1]); - } - - if(graphics.layerEnable & 0x0400) { - gfxDrawTextScreen(io_registers[REG_BG2CNT], io_registers[REG_BG2HOFS], io_registers[REG_BG2VOFS], line[2]); - } - - if(graphics.layerEnable & 0x0800) { - gfxDrawTextScreen(io_registers[REG_BG3CNT], io_registers[REG_BG3HOFS], io_registers[REG_BG3VOFS], line[3]); - } - - - uint32_t backdrop = (READ16LE(&palette[0]) | 0x30000000); - - for(int x = 0; x < 240; x++) - { - uint32_t color = backdrop; - uint8_t top = 0x20; - - if(line[0][x] < color) { - color = line[0][x]; - top = 0x01; - } - - if((uint8_t)(line[1][x]>>24) < (uint8_t)(color >> 24)) { - color = line[1][x]; - top = 0x02; - } - - if((uint8_t)(line[2][x]>>24) < (uint8_t)(color >> 24)) { - color = line[2][x]; - top = 0x04; - } - - if((uint8_t)(line[3][x]>>24) < (uint8_t)(color >> 24)) { - color = line[3][x]; - top = 0x08; - } - - if((uint8_t)(line[4][x]>>24) < (uint8_t)(color >> 24)) { - color = line[4][x]; - top = 0x10; - - if(color & 0x00010000) { - // semi-transparent OBJ - uint32_t back = backdrop; - uint8_t top2 = 0x20; - - if((uint8_t)(line[0][x]>>24) < (uint8_t)(back >> 24)) { - back = line[0][x]; - top2 = 0x01; - } - - if((uint8_t)(line[1][x]>>24) < (uint8_t)(back >> 24)) { - back = line[1][x]; - top2 = 0x02; - } - - if((uint8_t)(line[2][x]>>24) < (uint8_t)(back >> 24)) { - back = line[2][x]; - top2 = 0x04; - } - - if((uint8_t)(line[3][x]>>24) < (uint8_t)(back >> 24)) { - back = line[3][x]; - top2 = 0x08; - } - - alpha_blend_brightness_switch(); - } - } - - - lineMix[x] = CONVERT_COLOR(color); - } -} - -void mode0RenderLineNoWindow (void) -{ -#ifdef REPORT_VIDEO_MODES - fprintf(stderr, "MODE 0: Render Line No Window\n"); -#endif - INIT_COLOR_DEPTH_LINE_MIX(); - - uint16_t *palette = (uint16_t *)graphics.paletteRAM; - uint32_t backdrop = (READ16LE(&palette[0]) | 0x30000000); - - if(graphics.layerEnable & 0x0100) { - gfxDrawTextScreen(io_registers[REG_BG0CNT], io_registers[REG_BG0HOFS], io_registers[REG_BG0VOFS], line[0]); - } - - if(graphics.layerEnable & 0x0200) { - gfxDrawTextScreen(io_registers[REG_BG1CNT], io_registers[REG_BG1HOFS], io_registers[REG_BG1VOFS], line[1]); - } - - if(graphics.layerEnable & 0x0400) { - gfxDrawTextScreen(io_registers[REG_BG2CNT], io_registers[REG_BG2HOFS], io_registers[REG_BG2VOFS], line[2]); - } - - if(graphics.layerEnable & 0x0800) { - gfxDrawTextScreen(io_registers[REG_BG3CNT], io_registers[REG_BG3HOFS], io_registers[REG_BG3VOFS], line[3]); - } - - int effect = (BLDMOD >> 6) & 3; - - for(int x = 0; x < 240; x++) { - uint32_t color = backdrop; - uint8_t top = 0x20; - - if(line[0][x] < color) { - color = line[0][x]; - top = 0x01; - } - - if(line[1][x] < (color & 0xFF000000)) { - color = line[1][x]; - top = 0x02; - } - - if(line[2][x] < (color & 0xFF000000)) { - color = line[2][x]; - top = 0x04; - } - - if(line[3][x] < (color & 0xFF000000)) { - color = line[3][x]; - top = 0x08; - } - - if(line[4][x] < (color & 0xFF000000)) { - color = line[4][x]; - top = 0x10; - } - - if(!(color & 0x00010000)) { - switch(effect) { - case 0: - break; - case 1: - if(top & BLDMOD) - { - uint32_t back = backdrop; - uint8_t top2 = 0x20; - if((line[0][x] < back) && (top != 0x01)) - { - back = line[0][x]; - top2 = 0x01; - } - - if((line[1][x] < (back & 0xFF000000)) && (top != 0x02)) - { - back = line[1][x]; - top2 = 0x02; - } - - if((line[2][x] < (back & 0xFF000000)) && (top != 0x04)) - { - back = line[2][x]; - top2 = 0x04; - } - - if((line[3][x] < (back & 0xFF000000)) && (top != 0x08)) - { - back = line[3][x]; - top2 = 0x08; - } - - if((line[4][x] < (back & 0xFF000000)) && (top != 0x10)) - { - back = line[4][x]; - top2 = 0x10; - } - - if(top2 & (BLDMOD>>8) && color < 0x80000000) - { - GFX_ALPHA_BLEND(color, back, coeff[COLEV & 0x1F], coeff[(COLEV >> 8) & 0x1F]); - } - - } - break; - case 2: - if(BLDMOD & top) - color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); - break; - case 3: - if(BLDMOD & top) - color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); - break; - } - } else { - // semi-transparent OBJ - uint32_t back = backdrop; - uint8_t top2 = 0x20; - - if(line[0][x] < back) { - back = line[0][x]; - top2 = 0x01; - } - - if(line[1][x] < (back & 0xFF000000)) { - back = line[1][x]; - top2 = 0x02; - } - - if(line[2][x] < (back & 0xFF000000)) { - back = line[2][x]; - top2 = 0x04; - } - - if(line[3][x] < (back & 0xFF000000)) { - back = line[3][x]; - top2 = 0x08; - } - - alpha_blend_brightness_switch(); - } - - lineMix[x] = CONVERT_COLOR(color); - } -} - -void mode0RenderLineAll (void) -{ -#ifdef REPORT_VIDEO_MODES - fprintf(stderr, "MODE 0: Render Line All\n"); -#endif - INIT_COLOR_DEPTH_LINE_MIX(); - - uint16_t *palette = (uint16_t *)graphics.paletteRAM; - - bool inWindow0 = false; - bool inWindow1 = false; - - if(graphics.layerEnable & 0x2000) { - uint8_t v0 = io_registers[REG_WIN0V] >> 8; - uint8_t v1 = io_registers[REG_WIN0V] & 255; - inWindow0 = ((v0 == v1) && (v0 >= 0xe8)); - if(v1 >= v0) - inWindow0 |= (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1); - else - inWindow0 |= (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1); - } - if(graphics.layerEnable & 0x4000) { - uint8_t v0 = io_registers[REG_WIN1V] >> 8; - uint8_t v1 = io_registers[REG_WIN1V] & 255; - inWindow1 = ((v0 == v1) && (v0 >= 0xe8)); - if(v1 >= v0) - inWindow1 |= (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1); - else - inWindow1 |= (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1); - } - - if((graphics.layerEnable & 0x0100)) { - gfxDrawTextScreen(io_registers[REG_BG0CNT], io_registers[REG_BG0HOFS], io_registers[REG_BG0VOFS], line[0]); - } - - if((graphics.layerEnable & 0x0200)) { - gfxDrawTextScreen(io_registers[REG_BG1CNT], io_registers[REG_BG1HOFS], io_registers[REG_BG1VOFS], line[1]); - } - - if((graphics.layerEnable & 0x0400)) { - gfxDrawTextScreen(io_registers[REG_BG2CNT], io_registers[REG_BG2HOFS], io_registers[REG_BG2VOFS], line[2]); - } - - if((graphics.layerEnable & 0x0800)) { - gfxDrawTextScreen(io_registers[REG_BG3CNT], io_registers[REG_BG3HOFS], io_registers[REG_BG3VOFS], line[3]); - } - - uint32_t backdrop = (READ16LE(&palette[0]) | 0x30000000); - - uint8_t inWin0Mask = io_registers[REG_WININ] & 0xFF; - uint8_t inWin1Mask = io_registers[REG_WININ] >> 8; - uint8_t outMask = io_registers[REG_WINOUT] & 0xFF; - - for(int x = 0; x < 240; x++) { - uint32_t color = backdrop; - uint8_t top = 0x20; - uint8_t mask = outMask; - - if(!(line[5][x] & 0x80000000)) { - mask = io_registers[REG_WINOUT] >> 8; - } - - int32_t window1_mask = ((inWindow1 & gfxInWin[1][x]) | -(inWindow1 & gfxInWin[1][x])) >> 31; - int32_t window0_mask = ((inWindow0 & gfxInWin[0][x]) | -(inWindow0 & gfxInWin[0][x])) >> 31; - mask = (inWin1Mask & window1_mask) | (mask & ~window1_mask); - mask = (inWin0Mask & window0_mask) | (mask & ~window0_mask); - - if((mask & 1) && (line[0][x] < color)) { - color = line[0][x]; - top = 0x01; - } - - if((mask & 2) && ((uint8_t)(line[1][x]>>24) < (uint8_t)(color >> 24))) { - color = line[1][x]; - top = 0x02; - } - - if((mask & 4) && ((uint8_t)(line[2][x]>>24) < (uint8_t)(color >> 24))) { - color = line[2][x]; - top = 0x04; - } - - if((mask & 8) && ((uint8_t)(line[3][x]>>24) < (uint8_t)(color >> 24))) { - color = line[3][x]; - top = 0x08; - } - - if((mask & 16) && ((uint8_t)(line[4][x]>>24) < (uint8_t)(color >> 24))) { - color = line[4][x]; - top = 0x10; - } - - if(color & 0x00010000) - { - // semi-transparent OBJ - uint32_t back = backdrop; - uint8_t top2 = 0x20; - - if((mask & 1) && ((uint8_t)(line[0][x]>>24) < (uint8_t)(back >> 24))) { - back = line[0][x]; - top2 = 0x01; - } - - if((mask & 2) && ((uint8_t)(line[1][x]>>24) < (uint8_t)(back >> 24))) { - back = line[1][x]; - top2 = 0x02; - } - - if((mask & 4) && ((uint8_t)(line[2][x]>>24) < (uint8_t)(back >> 24))) { - back = line[2][x]; - top2 = 0x04; - } - - if((mask & 8) && ((uint8_t)(line[3][x]>>24) < (uint8_t)(back >> 24))) { - back = line[3][x]; - top2 = 0x08; - } - - alpha_blend_brightness_switch(); - } - else if((mask & 32) && (top & BLDMOD)) - { - // special FX on in the window - switch((BLDMOD >> 6) & 3) - { - case 0: - break; - case 1: - { - uint32_t back = backdrop; - uint8_t top2 = 0x20; - if(((mask & 1) && (uint8_t)(line[0][x]>>24) < (uint8_t)(back >> 24)) && top != 0x01) - { - back = line[0][x]; - top2 = 0x01; - } - - if(((mask & 2) && (uint8_t)(line[1][x]>>24) < (uint8_t)(back >> 24)) && top != 0x02) - { - back = line[1][x]; - top2 = 0x02; - } - - if(((mask & 4) && (uint8_t)(line[2][x]>>24) < (uint8_t)(back >> 24)) && top != 0x04) - { - back = line[2][x]; - top2 = 0x04; - } - - if(((mask & 8) && (uint8_t)(line[3][x]>>24) < (uint8_t)(back >> 24)) && top != 0x08) - { - back = line[3][x]; - top2 = 0x08; - } - - if(((mask & 16) && (uint8_t)(line[4][x]>>24) < (uint8_t)(back >> 24)) && top != 0x10) { - back = line[4][x]; - top2 = 0x10; - } - - if(top2 & (BLDMOD>>8) && color < 0x80000000) - { - GFX_ALPHA_BLEND(color, back, coeff[COLEV & 0x1F], coeff[(COLEV >> 8) & 0x1F]); - } - } - break; - case 2: - color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); - break; - case 3: - color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); - break; - } - } - - lineMix[x] = CONVERT_COLOR(color); - } -} - -/* -Mode 1 is a tiled graphics mode, but with background layer 2 supporting scaling and rotation. -There is no layer 3 in this mode. -Layers 0 and 1 can be either 16 colours (with 16 different palettes) or 256 colours. -There are 1024 tiles available. -Layer 2 is 256 colours and allows only 256 tiles. - -These routines only render a single line at a time, because of the way the GBA does events. -*/ - -void mode1RenderLine (void) -{ -#ifdef REPORT_VIDEO_MODES - fprintf(stderr, "MODE 1: Render Line\n"); -#endif - INIT_COLOR_DEPTH_LINE_MIX(); - - uint16_t *palette = (uint16_t *)graphics.paletteRAM; - - if(graphics.layerEnable & 0x0100) { - gfxDrawTextScreen(io_registers[REG_BG0CNT], io_registers[REG_BG0HOFS], io_registers[REG_BG0VOFS], line[0]); - } - - if(graphics.layerEnable & 0x0200) { - gfxDrawTextScreen(io_registers[REG_BG1CNT], io_registers[REG_BG1HOFS], io_registers[REG_BG1VOFS], line[1]); - } - - if(graphics.layerEnable & 0x0400) { - int changed = gfxBG2Changed; -#if 0 - if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) - changed = 3; -#endif - gfxDrawRotScreen(io_registers[REG_BG2CNT], BG2X_L, BG2X_H, BG2Y_L, BG2Y_H, - io_registers[REG_BG2PA], io_registers[REG_BG2PB], io_registers[REG_BG2PC], io_registers[REG_BG2PD], - gfxBG2X, gfxBG2Y, changed, line[2]); - } - - uint32_t backdrop = (READ16LE(&palette[0]) | 0x30000000); - - for(uint32_t x = 0; x < 240u; ++x) { - uint32_t color = backdrop; - uint8_t top = 0x20; - - uint8_t li1 = (uint8_t)(line[1][x]>>24); - uint8_t li2 = (uint8_t)(line[2][x]>>24); - uint8_t li4 = (uint8_t)(line[4][x]>>24); - - uint8_t r = (li2 < li1) ? (li2) : (li1); - - if(li4 < r){ - r = (li4); - } - - if(line[0][x] < backdrop) { - color = line[0][x]; - top = 0x01; - } - - if(r < (uint8_t)(color >> 24)) { - if(r == li1){ - color = line[1][x]; - top = 0x02; - }else if(r == li2){ - color = line[2][x]; - top = 0x04; - }else if(r == li4){ - color = line[4][x]; - top = 0x10; - if((color & 0x00010000)) - { - // semi-transparent OBJ - uint32_t back = backdrop; - uint8_t top2 = 0x20; - - uint8_t li0 = (uint8_t)(line[0][x]>>24); - uint8_t li1 = (uint8_t)(line[1][x]>>24); - uint8_t li2 = (uint8_t)(line[2][x]>>24); - uint8_t r = (li1 < li0) ? (li1) : (li0); - - if(li2 < r) { - r = (li2); - } - - if(r < (uint8_t)(back >> 24)) { - if(r == li0){ - back = line[0][x]; - top2 = 0x01; - }else if(r == li1){ - back = line[1][x]; - top2 = 0x02; - }else if(r == li2){ - back = line[2][x]; - top2 = 0x04; - } - } - - alpha_blend_brightness_switch(); - } - } - } - - - lineMix[x] = CONVERT_COLOR(color); - } - gfxBG2Changed = 0; - //gfxLastVCOUNT = io_registers[REG_VCOUNT]; -} - -void mode1RenderLineNoWindow (void) -{ -#ifdef REPORT_VIDEO_MODES - fprintf(stderr, "MODE 1: Render Line No Window\n"); -#endif - INIT_COLOR_DEPTH_LINE_MIX(); - - uint16_t *palette = (uint16_t *)graphics.paletteRAM; - - if(graphics.layerEnable & 0x0100) { - gfxDrawTextScreen(io_registers[REG_BG0CNT], io_registers[REG_BG0HOFS], io_registers[REG_BG0VOFS], line[0]); - } - - - if(graphics.layerEnable & 0x0200) { - gfxDrawTextScreen(io_registers[REG_BG1CNT], io_registers[REG_BG1HOFS], io_registers[REG_BG1VOFS], line[1]); - } - - if(graphics.layerEnable & 0x0400) { - int changed = gfxBG2Changed; -#if 0 - if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) - changed = 3; -#endif - gfxDrawRotScreen(io_registers[REG_BG2CNT], BG2X_L, BG2X_H, BG2Y_L, BG2Y_H, - io_registers[REG_BG2PA], io_registers[REG_BG2PB], io_registers[REG_BG2PC], io_registers[REG_BG2PD], - gfxBG2X, gfxBG2Y, changed, line[2]); - } - - uint32_t backdrop = (READ16LE(&palette[0]) | 0x30000000); - - for(int x = 0; x < 240; ++x) { - uint32_t color = backdrop; - uint8_t top = 0x20; - - uint8_t li1 = (uint8_t)(line[1][x]>>24); - uint8_t li2 = (uint8_t)(line[2][x]>>24); - uint8_t li4 = (uint8_t)(line[4][x]>>24); - - uint8_t r = (li2 < li1) ? (li2) : (li1); - - if(li4 < r){ - r = (li4); - } - - if(line[0][x] < backdrop) { - color = line[0][x]; - top = 0x01; - } - - if(r < (uint8_t)(color >> 24)) { - if(r == li1){ - color = line[1][x]; - top = 0x02; - }else if(r == li2){ - color = line[2][x]; - top = 0x04; - }else if(r == li4){ - color = line[4][x]; - top = 0x10; - } - } - - if(!(color & 0x00010000)) { - switch((BLDMOD >> 6) & 3) { - case 0: - break; - case 1: - if(top & BLDMOD) - { - uint32_t back = backdrop; - uint8_t top2 = 0x20; - - if((top != 0x01) && (uint8_t)(line[0][x]>>24) < (uint8_t)(back >> 24)) { - back = line[0][x]; - top2 = 0x01; - } - - if((top != 0x02) && (uint8_t)(line[1][x]>>24) < (uint8_t)(back >> 24)) { - back = line[1][x]; - top2 = 0x02; - } - - if((top != 0x04) && (uint8_t)(line[2][x]>>24) < (uint8_t)(back >> 24)) { - back = line[2][x]; - top2 = 0x04; - } - - if((top != 0x10) && (uint8_t)(line[4][x]>>24) < (uint8_t)(back >> 24)) { - back = line[4][x]; - top2 = 0x10; - } - - if(top2 & (BLDMOD>>8) && color < 0x80000000) - { - GFX_ALPHA_BLEND(color, back, coeff[COLEV & 0x1F], coeff[(COLEV >> 8) & 0x1F]); - } - } - break; - case 2: - if(BLDMOD & top) - color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); - break; - case 3: - if(BLDMOD & top) - color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); - break; - } - } else { - // semi-transparent OBJ - uint32_t back = backdrop; - uint8_t top2 = 0x20; - - uint8_t li0 = (uint8_t)(line[0][x]>>24); - uint8_t li1 = (uint8_t)(line[1][x]>>24); - uint8_t li2 = (uint8_t)(line[2][x]>>24); - - uint8_t r = (li1 < li0) ? (li1) : (li0); - - if(li2 < r) { - r = (li2); - } - - if(r < (uint8_t)(back >> 24)) - { - if(r == li0) - { - back = line[0][x]; - top2 = 0x01; - } - else if(r == li1) - { - back = line[1][x]; - top2 = 0x02; - } - else if(r == li2) - { - back = line[2][x]; - top2 = 0x04; - } - } - - alpha_blend_brightness_switch(); - } - - lineMix[x] = CONVERT_COLOR(color); - } - gfxBG2Changed = 0; - //gfxLastVCOUNT = io_registers[REG_VCOUNT]; -} - -void mode1RenderLineAll (void) -{ -#ifdef REPORT_VIDEO_MODES - fprintf(stderr, "MODE 1: Render Line All\n"); -#endif - INIT_COLOR_DEPTH_LINE_MIX(); - - uint16_t *palette = (uint16_t *)graphics.paletteRAM; - - bool inWindow0 = false; - bool inWindow1 = false; - - if(graphics.layerEnable & 0x2000) - { - uint8_t v0 = io_registers[REG_WIN0V] >> 8; - uint8_t v1 = io_registers[REG_WIN0V] & 255; - inWindow0 = ((v0 == v1) && (v0 >= 0xe8)); -#ifndef ORIGINAL_BRANCHES - uint32_t condition = v1 >= v0; - int32_t condition_mask = ((condition) | -(condition)) >> 31; - inWindow0 = (((inWindow0 | (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1)) & condition_mask) | (((inWindow0 | (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1)) & ~(condition_mask)))); -#else - if(v1 >= v0) - inWindow0 |= (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1); - else - inWindow0 |= (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1); -#endif - } - if(graphics.layerEnable & 0x4000) - { - uint8_t v0 = io_registers[REG_WIN1V] >> 8; - uint8_t v1 = io_registers[REG_WIN1V] & 255; - inWindow1 = ((v0 == v1) && (v0 >= 0xe8)); -#ifndef ORIGINAL_BRANCHES - uint32_t condition = v1 >= v0; - int32_t condition_mask = ((condition) | -(condition)) >> 31; - inWindow1 = (((inWindow1 | (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1)) & condition_mask) | (((inWindow1 | (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1)) & ~(condition_mask)))); -#else - if(v1 >= v0) - inWindow1 |= (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1); - else - inWindow1 |= (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1); -#endif - } - - if(graphics.layerEnable & 0x0100) { - gfxDrawTextScreen(io_registers[REG_BG0CNT], io_registers[REG_BG0HOFS], io_registers[REG_BG0VOFS], line[0]); - } - - if(graphics.layerEnable & 0x0200) { - gfxDrawTextScreen(io_registers[REG_BG1CNT], io_registers[REG_BG1HOFS], io_registers[REG_BG1VOFS], line[1]); - } - - if(graphics.layerEnable & 0x0400) { - int changed = gfxBG2Changed; -#if 0 - if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) - changed = 3; -#endif - gfxDrawRotScreen(io_registers[REG_BG2CNT], BG2X_L, BG2X_H, BG2Y_L, BG2Y_H, - io_registers[REG_BG2PA], io_registers[REG_BG2PB], io_registers[REG_BG2PC], io_registers[REG_BG2PD], - gfxBG2X, gfxBG2Y, changed, line[2]); - } - - uint32_t backdrop = (READ16LE(&palette[0]) | 0x30000000); - - uint8_t inWin0Mask = io_registers[REG_WININ] & 0xFF; - uint8_t inWin1Mask = io_registers[REG_WININ] >> 8; - uint8_t outMask = io_registers[REG_WINOUT] & 0xFF; - - for(int x = 0; x < 240; ++x) { - uint32_t color = backdrop; - uint8_t top = 0x20; - uint8_t mask = outMask; - - if(!(line[5][x] & 0x80000000)) { - mask = io_registers[REG_WINOUT] >> 8; - } - - int32_t window1_mask = ((inWindow1 & gfxInWin[1][x]) | -(inWindow1 & gfxInWin[1][x])) >> 31; - int32_t window0_mask = ((inWindow0 & gfxInWin[0][x]) | -(inWindow0 & gfxInWin[0][x])) >> 31; - mask = (inWin1Mask & window1_mask) | (mask & ~window1_mask); - mask = (inWin0Mask & window0_mask) | (mask & ~window0_mask); - - // At the very least, move the inexpensive 'mask' operation up front - if((mask & 1) && line[0][x] < backdrop) { - color = line[0][x]; - top = 0x01; - } - - if((mask & 2) && (uint8_t)(line[1][x]>>24) < (uint8_t)(color >> 24)) { - color = line[1][x]; - top = 0x02; - } - - if((mask & 4) && (uint8_t)(line[2][x]>>24) < (uint8_t)(color >> 24)) { - color = line[2][x]; - top = 0x04; - } - - if((mask & 16) && (uint8_t)(line[4][x]>>24) < (uint8_t)(color >> 24)) { - color = line[4][x]; - top = 0x10; - } - - if(color & 0x00010000) { - // semi-transparent OBJ - uint32_t back = backdrop; - uint8_t top2 = 0x20; - - if((mask & 1) && (uint8_t)(line[0][x]>>24) < (uint8_t)(backdrop >> 24)) { - back = line[0][x]; - top2 = 0x01; - } - - if((mask & 2) && (uint8_t)(line[1][x]>>24) < (uint8_t)(back >> 24)) { - back = line[1][x]; - top2 = 0x02; - } - - if((mask & 4) && (uint8_t)(line[2][x]>>24) < (uint8_t)(back >> 24)) { - back = line[2][x]; - top2 = 0x04; - } - - alpha_blend_brightness_switch(); - } else if(mask & 32) { - // special FX on the window - switch((BLDMOD >> 6) & 3) { - case 0: - break; - case 1: - if(top & BLDMOD) - { - uint32_t back = backdrop; - uint8_t top2 = 0x20; - - if((mask & 1) && (top != 0x01) && (uint8_t)(line[0][x]>>24) < (uint8_t)(backdrop >> 24)) { - back = line[0][x]; - top2 = 0x01; - } - - if((mask & 2) && (top != 0x02) && (uint8_t)(line[1][x]>>24) < (uint8_t)(back >> 24)) { - back = line[1][x]; - top2 = 0x02; - } - - if((mask & 4) && (top != 0x04) && (uint8_t)(line[2][x]>>24) < (uint8_t)(back >> 24)) { - back = line[2][x]; - top2 = 0x04; - } - - if((mask & 16) && (top != 0x10) && (uint8_t)(line[4][x]>>24) < (uint8_t)(back >> 24)) { - back = line[4][x]; - top2 = 0x10; - } - - if(top2 & (BLDMOD>>8) && color < 0x80000000) - { - GFX_ALPHA_BLEND(color, back, coeff[COLEV & 0x1F], coeff[(COLEV >> 8) & 0x1F]); - } - } - break; - case 2: - if(BLDMOD & top) - color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); - break; - case 3: - if(BLDMOD & top) - color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); - break; - } - } - - lineMix[x] = CONVERT_COLOR(color); - } - gfxBG2Changed = 0; - //gfxLastVCOUNT = io_registers[REG_VCOUNT]; -} - -/* -Mode 2 is a 256 colour tiled graphics mode which supports scaling and rotation. -There is no background layer 0 or 1 in this mode. Only background layers 2 and 3. -There are 256 tiles available. -It does not support flipping. - -These routines only render a single line at a time, because of the way the GBA does events. -*/ - -void mode2RenderLine (void) -{ -#ifdef REPORT_VIDEO_MODES - fprintf(stderr, "MODE 2: Render Line\n"); -#endif - INIT_COLOR_DEPTH_LINE_MIX(); - - uint16_t *palette = (uint16_t *)graphics.paletteRAM; - - if(graphics.layerEnable & 0x0400) { - int changed = gfxBG2Changed; -#if 0 - if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) - changed = 3; -#endif - - gfxDrawRotScreen(io_registers[REG_BG2CNT], BG2X_L, BG2X_H, BG2Y_L, BG2Y_H, - io_registers[REG_BG2PA], io_registers[REG_BG2PB], io_registers[REG_BG2PC], io_registers[REG_BG2PD], gfxBG2X, gfxBG2Y, - changed, line[2]); - } - - if(graphics.layerEnable & 0x0800) { - int changed = gfxBG3Changed; -#if 0 - if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) - changed = 3; -#endif - - gfxDrawRotScreen(io_registers[REG_BG3CNT], BG3X_L, BG3X_H, BG3Y_L, BG3Y_H, - io_registers[REG_BG3PA], io_registers[REG_BG3PB], io_registers[REG_BG3PC], io_registers[REG_BG3PD], gfxBG3X, gfxBG3Y, - changed, line[3]); - } - - uint32_t backdrop = (READ16LE(&palette[0]) | 0x30000000); - - for(int x = 0; x < 240; ++x) { - uint32_t color = backdrop; - uint8_t top = 0x20; - - uint8_t li2 = (uint8_t)(line[2][x]>>24); - uint8_t li3 = (uint8_t)(line[3][x]>>24); - uint8_t li4 = (uint8_t)(line[4][x]>>24); - - uint8_t r = (li3 < li2) ? (li3) : (li2); - - if(li4 < r){ - r = (li4); - } - - if(r < (uint8_t)(color >> 24)) { - if(r == li2){ - color = line[2][x]; - top = 0x04; - }else if(r == li3){ - color = line[3][x]; - top = 0x08; - }else if(r == li4){ - color = line[4][x]; - top = 0x10; - - if(color & 0x00010000) { - // semi-transparent OBJ - uint32_t back = backdrop; - uint8_t top2 = 0x20; - - uint8_t li2 = (uint8_t)(line[2][x]>>24); - uint8_t li3 = (uint8_t)(line[3][x]>>24); - uint8_t r = (li3 < li2) ? (li3) : (li2); - - if(r < (uint8_t)(back >> 24)) { - if(r == li2){ - back = line[2][x]; - top2 = 0x04; - }else if(r == li3){ - back = line[3][x]; - top2 = 0x08; - } - } - - alpha_blend_brightness_switch(); - } - } - } - - - lineMix[x] = CONVERT_COLOR(color); - } - gfxBG2Changed = 0; - gfxBG3Changed = 0; - //gfxLastVCOUNT = io_registers[REG_VCOUNT]; -} - -void mode2RenderLineNoWindow (void) -{ -#ifdef REPORT_VIDEO_MODES - fprintf(stderr, "MODE 2: Render Line No Window\n"); -#endif - INIT_COLOR_DEPTH_LINE_MIX(); - - uint16_t *palette = (uint16_t *)graphics.paletteRAM; - - if(graphics.layerEnable & 0x0400) { - int changed = gfxBG2Changed; -#if 0 - if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) - changed = 3; -#endif - - gfxDrawRotScreen(io_registers[REG_BG2CNT], BG2X_L, BG2X_H, BG2Y_L, BG2Y_H, - io_registers[REG_BG2PA], io_registers[REG_BG2PB], io_registers[REG_BG2PC], io_registers[REG_BG2PD], gfxBG2X, gfxBG2Y, - changed, line[2]); - } - - if(graphics.layerEnable & 0x0800) { - int changed = gfxBG3Changed; -#if 0 - if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) - changed = 3; -#endif - - gfxDrawRotScreen(io_registers[REG_BG3CNT], BG3X_L, BG3X_H, BG3Y_L, BG3Y_H, - io_registers[REG_BG3PA], io_registers[REG_BG3PB], io_registers[REG_BG3PC], io_registers[REG_BG3PD], gfxBG3X, gfxBG3Y, - changed, line[3]); - } - - uint32_t backdrop = (READ16LE(&palette[0]) | 0x30000000); - - for(int x = 0; x < 240; ++x) { - uint32_t color = backdrop; - uint8_t top = 0x20; - - uint8_t li2 = (uint8_t)(line[2][x]>>24); - uint8_t li3 = (uint8_t)(line[3][x]>>24); - uint8_t li4 = (uint8_t)(line[4][x]>>24); - - uint8_t r = (li3 < li2) ? (li3) : (li2); - - if(li4 < r){ - r = (li4); - } - - if(r < (uint8_t)(color >> 24)) { - if(r == li2){ - color = line[2][x]; - top = 0x04; - }else if(r == li3){ - color = line[3][x]; - top = 0x08; - }else if(r == li4){ - color = line[4][x]; - top = 0x10; - } - } - - if(!(color & 0x00010000)) { - switch((BLDMOD >> 6) & 3) { - case 0: - break; - case 1: - if(top & BLDMOD) - { - uint32_t back = backdrop; - uint8_t top2 = 0x20; - - if((top != 0x04) && (uint8_t)(line[2][x]>>24) < (uint8_t)(back >> 24)) { - back = line[2][x]; - top2 = 0x04; - } - - if((top != 0x08) && (uint8_t)(line[3][x]>>24) < (uint8_t)(back >> 24)) { - back = line[3][x]; - top2 = 0x08; - } - - if((top != 0x10) && (uint8_t)(line[4][x]>>24) < (uint8_t)(back >> 24)) { - back = line[4][x]; - top2 = 0x10; - } - - if(top2 & (BLDMOD>>8) && color < 0x80000000) - { - GFX_ALPHA_BLEND(color, back, coeff[COLEV & 0x1F], coeff[(COLEV >> 8) & 0x1F]); - } - } - break; - case 2: - if(BLDMOD & top) - color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); - break; - case 3: - if(BLDMOD & top) - color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); - break; - } - } else { - // semi-transparent OBJ - uint32_t back = backdrop; - uint8_t top2 = 0x20; - - uint8_t li2 = (uint8_t)(line[2][x]>>24); - uint8_t li3 = (uint8_t)(line[3][x]>>24); - uint8_t r = (li3 < li2) ? (li3) : (li2); - - if(r < (uint8_t)(back >> 24)) { - if(r == li2){ - back = line[2][x]; - top2 = 0x04; - }else if(r == li3){ - back = line[3][x]; - top2 = 0x08; - } - } - - alpha_blend_brightness_switch(); - } - - lineMix[x] = CONVERT_COLOR(color); - } - gfxBG2Changed = 0; - gfxBG3Changed = 0; - //gfxLastVCOUNT = io_registers[REG_VCOUNT]; -} - -void mode2RenderLineAll (void) -{ -#ifdef REPORT_VIDEO_MODES - fprintf(stderr, "MODE 2: Render Line All\n"); -#endif - INIT_COLOR_DEPTH_LINE_MIX(); - - uint16_t *palette = (uint16_t *)graphics.paletteRAM; - - bool inWindow0 = false; - bool inWindow1 = false; - - if(graphics.layerEnable & 0x2000) - { - uint8_t v0 = io_registers[REG_WIN0V] >> 8; - uint8_t v1 = io_registers[REG_WIN0V] & 255; - inWindow0 = ((v0 == v1) && (v0 >= 0xe8)); -#ifndef ORIGINAL_BRANCHES - uint32_t condition = v1 >= v0; - int32_t condition_mask = ((condition) | -(condition)) >> 31; - inWindow0 = (((inWindow0 | (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1)) & condition_mask) | (((inWindow0 | (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1)) & ~(condition_mask)))); -#else - if(v1 >= v0) - inWindow0 |= (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1); - else - inWindow0 |= (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1); -#endif - } - if(graphics.layerEnable & 0x4000) - { - uint8_t v0 = io_registers[REG_WIN1V] >> 8; - uint8_t v1 = io_registers[REG_WIN1V] & 255; - inWindow1 = ((v0 == v1) && (v0 >= 0xe8)); -#ifndef ORIGINAL_BRANCHES - uint32_t condition = v1 >= v0; - int32_t condition_mask = ((condition) | -(condition)) >> 31; - inWindow1 = (((inWindow1 | (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1)) & condition_mask) | (((inWindow1 | (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1)) & ~(condition_mask)))); -#else - if(v1 >= v0) - inWindow1 |= (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1); - else - inWindow1 |= (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1); -#endif - } - - if(graphics.layerEnable & 0x0400) { - int changed = gfxBG2Changed; -#if 0 - if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) - changed = 3; -#endif - - gfxDrawRotScreen(io_registers[REG_BG2CNT], BG2X_L, BG2X_H, BG2Y_L, BG2Y_H, - io_registers[REG_BG2PA], io_registers[REG_BG2PB], io_registers[REG_BG2PC], io_registers[REG_BG2PD], gfxBG2X, gfxBG2Y, - changed, line[2]); - } - - if(graphics.layerEnable & 0x0800) { - int changed = gfxBG3Changed; -#if 0 - if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) - changed = 3; -#endif - - gfxDrawRotScreen(io_registers[REG_BG3CNT], BG3X_L, BG3X_H, BG3Y_L, BG3Y_H, - io_registers[REG_BG3PA], io_registers[REG_BG3PB], io_registers[REG_BG3PC], io_registers[REG_BG3PD], gfxBG3X, gfxBG3Y, - changed, line[3]); - } - - uint32_t backdrop = (READ16LE(&palette[0]) | 0x30000000); - - uint8_t inWin0Mask = io_registers[REG_WININ] & 0xFF; - uint8_t inWin1Mask = io_registers[REG_WININ] >> 8; - uint8_t outMask = io_registers[REG_WINOUT] & 0xFF; - - for(int x = 0; x < 240; x++) { - uint32_t color = backdrop; - uint8_t top = 0x20; - uint8_t mask = outMask; - - if(!(line[5][x] & 0x80000000)) { - mask = io_registers[REG_WINOUT] >> 8; - } - - int32_t window1_mask = ((inWindow1 & gfxInWin[1][x]) | -(inWindow1 & gfxInWin[1][x])) >> 31; - int32_t window0_mask = ((inWindow0 & gfxInWin[0][x]) | -(inWindow0 & gfxInWin[0][x])) >> 31; - mask = (inWin1Mask & window1_mask) | (mask & ~window1_mask); - mask = (inWin0Mask & window0_mask) | (mask & ~window0_mask); - - if((mask & 4) && line[2][x] < color) { - color = line[2][x]; - top = 0x04; - } - - if((mask & 8) && (uint8_t)(line[3][x]>>24) < (uint8_t)(color >> 24)) { - color = line[3][x]; - top = 0x08; - } - - if((mask & 16) && (uint8_t)(line[4][x]>>24) < (uint8_t)(color >> 24)) { - color = line[4][x]; - top = 0x10; - } - - if(color & 0x00010000) { - // semi-transparent OBJ - uint32_t back = backdrop; - uint8_t top2 = 0x20; - - if((mask & 4) && line[2][x] < back) { - back = line[2][x]; - top2 = 0x04; - } - - if((mask & 8) && (uint8_t)(line[3][x]>>24) < (uint8_t)(back >> 24)) { - back = line[3][x]; - top2 = 0x08; - } - - alpha_blend_brightness_switch(); - } else if(mask & 32) { - // special FX on the window - switch((BLDMOD >> 6) & 3) { - case 0: - break; - case 1: - if(top & BLDMOD) - { - uint32_t back = backdrop; - uint8_t top2 = 0x20; - - if((mask & 4) && (top != 0x04) && line[2][x] < back) { - back = line[2][x]; - top2 = 0x04; - } - - if((mask & 8) && (top != 0x08) && (uint8_t)(line[3][x]>>24) < (uint8_t)(back >> 24)) { - back = line[3][x]; - top2 = 0x08; - } - - if((mask & 16) && (top != 0x10) && (uint8_t)(line[4][x]>>24) < (uint8_t)(back >> 24)) { - back = line[4][x]; - top2 = 0x10; - } - - if(top2 & (BLDMOD>>8) && color < 0x80000000) - { - GFX_ALPHA_BLEND(color, back, coeff[COLEV & 0x1F], coeff[(COLEV >> 8) & 0x1F]); - } - } - break; - case 2: - if(BLDMOD & top) - color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); - break; - case 3: - if(BLDMOD & top) - color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); - break; - } - } - - lineMix[x] = CONVERT_COLOR(color); - } - gfxBG2Changed = 0; - gfxBG3Changed = 0; - //gfxLastVCOUNT = io_registers[REG_VCOUNT]; -} - -/* -Mode 3 is a 15-bit (32768) colour bitmap graphics mode. -It has a single layer, background layer 2, the same size as the screen. -It doesn't support paging, scrolling, flipping, rotation or tiles. - -These routines only render a single line at a time, because of the way the GBA does events. -*/ - -void mode3RenderLine (void) -{ -#ifdef REPORT_VIDEO_MODES - fprintf(stderr, "MODE 3: Render Line\n"); -#endif - INIT_COLOR_DEPTH_LINE_MIX(); - uint16_t *palette = (uint16_t *)graphics.paletteRAM; - - if(graphics.layerEnable & 0x0400) { - int changed = gfxBG2Changed; - -#if 0 - if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) - changed = 3; -#endif - - gfxDrawRotScreen16Bit(gfxBG2X, gfxBG2Y, changed); - } - - uint32_t background = (READ16LE(&palette[0]) | 0x30000000); - - for(int x = 0; x < 240; ++x) { - uint32_t color = background; - uint8_t top = 0x20; - - if(line[2][x] < color) { - color = line[2][x]; - top = 0x04; - } - - if((uint8_t)(line[4][x]>>24) < (uint8_t)(color >>24)) { - color = line[4][x]; - top = 0x10; - - if(color & 0x00010000) { - // semi-transparent OBJ - uint32_t back = background; - uint8_t top2 = 0x20; - - if(line[2][x] < background) { - back = line[2][x]; - top2 = 0x04; - } - - alpha_blend_brightness_switch(); - } - } - - - lineMix[x] = CONVERT_COLOR(color); - } - gfxBG2Changed = 0; - //gfxLastVCOUNT = io_registers[REG_VCOUNT]; -} - -void mode3RenderLineNoWindow (void) -{ -#ifdef REPORT_VIDEO_MODES - fprintf(stderr, "MODE 3: Render Line No Window\n"); -#endif - INIT_COLOR_DEPTH_LINE_MIX(); - uint16_t *palette = (uint16_t *)graphics.paletteRAM; - - if(graphics.layerEnable & 0x0400) { - int changed = gfxBG2Changed; - -#if 0 - if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) - changed = 3; -#endif - - gfxDrawRotScreen16Bit(gfxBG2X, gfxBG2Y, changed); - } - - uint32_t background = (READ16LE(&palette[0]) | 0x30000000); - - for(int x = 0; x < 240; ++x) { - uint32_t color = background; - uint8_t top = 0x20; - - if(line[2][x] < background) { - color = line[2][x]; - top = 0x04; - } - - if((uint8_t)(line[4][x]>>24) < (uint8_t)(color >>24)) { - color = line[4][x]; - top = 0x10; - } - - if(!(color & 0x00010000)) { - switch((BLDMOD >> 6) & 3) { - case 0: - break; - case 1: - if(top & BLDMOD) - { - uint32_t back = background; - uint8_t top2 = 0x20; - - if(top != 0x04 && (line[2][x] < background) ) { - back = line[2][x]; - top2 = 0x04; - } - - if(top != 0x10 && ((uint8_t)(line[4][x]>>24) < (uint8_t)(back >> 24))) { - back = line[4][x]; - top2 = 0x10; - } - - if(top2 & (BLDMOD>>8) && color < 0x80000000) - { - GFX_ALPHA_BLEND(color, back, coeff[COLEV & 0x1F], coeff[(COLEV >> 8) & 0x1F]); - } - - } - break; - case 2: - if(BLDMOD & top) - color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); - break; - case 3: - if(BLDMOD & top) - color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); - break; - } - } else { - // semi-transparent OBJ - uint32_t back = background; - uint8_t top2 = 0x20; - - if(line[2][x] < background) { - back = line[2][x]; - top2 = 0x04; - } - - alpha_blend_brightness_switch(); - } - - lineMix[x] = CONVERT_COLOR(color); - } - gfxBG2Changed = 0; - //gfxLastVCOUNT = io_registers[REG_VCOUNT]; -} - -void mode3RenderLineAll (void) -{ -#ifdef REPORT_VIDEO_MODES - fprintf(stderr, "MODE 3: Render Line All\n"); -#endif - INIT_COLOR_DEPTH_LINE_MIX(); - uint16_t *palette = (uint16_t *)graphics.paletteRAM; - - bool inWindow0 = false; - bool inWindow1 = false; - - if(graphics.layerEnable & 0x2000) - { - uint8_t v0 = io_registers[REG_WIN0V] >> 8; - uint8_t v1 = io_registers[REG_WIN0V] & 255; - inWindow0 = ((v0 == v1) && (v0 >= 0xe8)); -#ifndef ORIGINAL_BRANCHES - uint32_t condition = v1 >= v0; - int32_t condition_mask = ((condition) | -(condition)) >> 31; - inWindow0 = (((inWindow0 | (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1)) & condition_mask) | (((inWindow0 | (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1)) & ~(condition_mask)))); -#else - if(v1 >= v0) - inWindow0 |= (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1); - else - inWindow0 |= (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1); -#endif - } - - if(graphics.layerEnable & 0x4000) - { - uint8_t v0 = io_registers[REG_WIN1V] >> 8; - uint8_t v1 = io_registers[REG_WIN1V] & 255; - inWindow1 = ((v0 == v1) && (v0 >= 0xe8)); -#ifndef ORIGINAL_BRANCHES - uint32_t condition = v1 >= v0; - int32_t condition_mask = ((condition) | -(condition)) >> 31; - inWindow1 = (((inWindow1 | (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1)) & condition_mask) | (((inWindow1 | (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1)) & ~(condition_mask)))); -#else - if(v1 >= v0) - inWindow1 |= (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1); - else - inWindow1 |= (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1); -#endif - } - - if(graphics.layerEnable & 0x0400) { - int changed = gfxBG2Changed; - -#if 0 - if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) - changed = 3; -#endif - - gfxDrawRotScreen16Bit(gfxBG2X, gfxBG2Y, changed); - } - - uint8_t inWin0Mask = io_registers[REG_WININ] & 0xFF; - uint8_t inWin1Mask = io_registers[REG_WININ] >> 8; - uint8_t outMask = io_registers[REG_WINOUT] & 0xFF; - - uint32_t background = (READ16LE(&palette[0]) | 0x30000000); - - for(int x = 0; x < 240; ++x) { - uint32_t color = background; - uint8_t top = 0x20; - uint8_t mask = outMask; - - if(!(line[5][x] & 0x80000000)) { - mask = io_registers[REG_WINOUT] >> 8; - } - - int32_t window1_mask = ((inWindow1 & gfxInWin[1][x]) | -(inWindow1 & gfxInWin[1][x])) >> 31; - int32_t window0_mask = ((inWindow0 & gfxInWin[0][x]) | -(inWindow0 & gfxInWin[0][x])) >> 31; - mask = (inWin1Mask & window1_mask) | (mask & ~window1_mask); - mask = (inWin0Mask & window0_mask) | (mask & ~window0_mask); - - if((mask & 4) && line[2][x] < background) { - color = line[2][x]; - top = 0x04; - } - - if((mask & 16) && ((uint8_t)(line[4][x]>>24) < (uint8_t)(color >>24))) { - color = line[4][x]; - top = 0x10; - } - - if(color & 0x00010000) { - // semi-transparent OBJ - uint32_t back = background; - uint8_t top2 = 0x20; - - if((mask & 4) && line[2][x] < background) { - back = line[2][x]; - top2 = 0x04; - } - - alpha_blend_brightness_switch(); - } else if(mask & 32) { - switch((BLDMOD >> 6) & 3) { - case 0: - break; - case 1: - if(top & BLDMOD) - { - uint32_t back = background; - uint8_t top2 = 0x20; - - if((mask & 4) && (top != 0x04) && line[2][x] < back) { - back = line[2][x]; - top2 = 0x04; - } - - if((mask & 16) && (top != 0x10) && (uint8_t)(line[4][x]>>24) < (uint8_t)(back >> 24)) { - back = line[4][x]; - top2 = 0x10; - } - - if(top2 & (BLDMOD>>8) && color < 0x80000000) - { - GFX_ALPHA_BLEND(color, back, coeff[COLEV & 0x1F], coeff[(COLEV >> 8) & 0x1F]); - } - } - break; - case 2: - if(BLDMOD & top) - color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); - break; - case 3: - if(BLDMOD & top) - color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); - break; - } - } - - lineMix[x] = CONVERT_COLOR(color); - } - gfxBG2Changed = 0; - //gfxLastVCOUNT = io_registers[REG_VCOUNT]; -} - -/* -Mode 4 is a 256 colour bitmap graphics mode with 2 swappable pages. -It has a single layer, background layer 2, the same size as the screen. -It doesn't support scrolling, flipping, rotation or tiles. - -These routines only render a single line at a time, because of the way the GBA does events. -*/ - -void mode4RenderLine (void) -{ -#ifdef REPORT_VIDEO_MODES - fprintf(stderr, "MODE 4: Render Line\n"); -#endif - INIT_COLOR_DEPTH_LINE_MIX(); - uint16_t *palette = (uint16_t *)graphics.paletteRAM; - - if(graphics.layerEnable & 0x400) - { - int changed = gfxBG2Changed; - -#if 0 - if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) - changed = 3; -#endif - - gfxDrawRotScreen256(gfxBG2X, gfxBG2Y, changed); - } - - uint32_t backdrop = (READ16LE(&palette[0]) | 0x30000000); - - for(int x = 0; x < 240; ++x) - { - uint32_t color = backdrop; - uint8_t top = 0x20; - - if(line[2][x] < backdrop) { - color = line[2][x]; - top = 0x04; - } - - if((uint8_t)(line[4][x]>>24) < (uint8_t)(color >> 24)) { - color = line[4][x]; - top = 0x10; - - if(color & 0x00010000) { - // semi-transparent OBJ - uint32_t back = backdrop; - uint8_t top2 = 0x20; - - if(line[2][x] < backdrop) { - back = line[2][x]; - top2 = 0x04; - } - - alpha_blend_brightness_switch(); - } - } - - - lineMix[x] = CONVERT_COLOR(color); - } - gfxBG2Changed = 0; - //gfxLastVCOUNT = io_registers[REG_VCOUNT]; -} - -void mode4RenderLineNoWindow (void) -{ -#ifdef REPORT_VIDEO_MODES - fprintf(stderr, "MODE 4: Render Line No Window\n"); -#endif - INIT_COLOR_DEPTH_LINE_MIX(); - uint16_t *palette = (uint16_t *)graphics.paletteRAM; - - if(graphics.layerEnable & 0x400) - { - int changed = gfxBG2Changed; - -#if 0 - if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) - changed = 3; -#endif - - gfxDrawRotScreen256(gfxBG2X, gfxBG2Y, changed); - } - - uint32_t backdrop = (READ16LE(&palette[0]) | 0x30000000); - - for(int x = 0; x < 240; ++x) - { - uint32_t color = backdrop; - uint8_t top = 0x20; - - if(line[2][x] < backdrop) { - color = line[2][x]; - top = 0x04; - } - - if((uint8_t)(line[4][x]>>24) < (uint8_t)(color >> 24)) { - color = line[4][x]; - top = 0x10; - } - - if(!(color & 0x00010000)) { - switch((BLDMOD >> 6) & 3) { - case 0: - break; - case 1: - if(top & BLDMOD) - { - uint32_t back = backdrop; - uint8_t top2 = 0x20; - - if((top != 0x04) && line[2][x] < backdrop) { - back = line[2][x]; - top2 = 0x04; - } - - if((top != 0x10) && (uint8_t)(line[4][x]>>24) < (uint8_t)(back >> 24)) { - back = line[4][x]; - top2 = 0x10; - } - - if(top2 & (BLDMOD>>8) && color < 0x80000000) - { - GFX_ALPHA_BLEND(color, back, coeff[COLEV & 0x1F], coeff[(COLEV >> 8) & 0x1F]); - } - } - break; - case 2: - if(BLDMOD & top) - color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); - break; - case 3: - if(BLDMOD & top) - color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); - break; - } - } else { - // semi-transparent OBJ - uint32_t back = backdrop; - uint8_t top2 = 0x20; - - if(line[2][x] < back) { - back = line[2][x]; - top2 = 0x04; - } - - alpha_blend_brightness_switch(); - } - - lineMix[x] = CONVERT_COLOR(color); - } - gfxBG2Changed = 0; - //gfxLastVCOUNT = io_registers[REG_VCOUNT]; -} - -void mode4RenderLineAll (void) -{ -#ifdef REPORT_VIDEO_MODES - fprintf(stderr, "MODE 4: Render Line All\n"); -#endif - INIT_COLOR_DEPTH_LINE_MIX(); - uint16_t *palette = (uint16_t *)graphics.paletteRAM; - - bool inWindow0 = false; - bool inWindow1 = false; - - if(graphics.layerEnable & 0x2000) - { - uint8_t v0 = io_registers[REG_WIN0V] >> 8; - uint8_t v1 = io_registers[REG_WIN0V] & 255; - inWindow0 = ((v0 == v1) && (v0 >= 0xe8)); -#ifndef ORIGINAL_BRANCHES - uint32_t condition = v1 >= v0; - int32_t condition_mask = ((condition) | -(condition)) >> 31; - inWindow0 = (((inWindow0 | (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1)) & condition_mask) | (((inWindow0 | (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1)) & ~(condition_mask)))); -#else - if(v1 >= v0) - inWindow0 |= (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1); - else - inWindow0 |= (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1); -#endif - } - - if(graphics.layerEnable & 0x4000) - { - uint8_t v0 = io_registers[REG_WIN1V] >> 8; - uint8_t v1 = io_registers[REG_WIN1V] & 255; - inWindow1 = ((v0 == v1) && (v0 >= 0xe8)); -#ifndef ORIGINAL_BRANCHES - uint32_t condition = v1 >= v0; - int32_t condition_mask = ((condition) | -(condition)) >> 31; - inWindow1 = (((inWindow1 | (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1)) & condition_mask) | (((inWindow1 | (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1)) & ~(condition_mask)))); -#else - if(v1 >= v0) - inWindow1 |= (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1); - else - inWindow1 |= (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1); -#endif - } - - if(graphics.layerEnable & 0x400) - { - int changed = gfxBG2Changed; - -#if 0 - if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) - changed = 3; -#endif - - gfxDrawRotScreen256(gfxBG2X, gfxBG2Y, changed); - } - - uint32_t backdrop = (READ16LE(&palette[0]) | 0x30000000); - - uint8_t inWin0Mask = io_registers[REG_WININ] & 0xFF; - uint8_t inWin1Mask = io_registers[REG_WININ] >> 8; - uint8_t outMask = io_registers[REG_WINOUT] & 0xFF; - - for(int x = 0; x < 240; ++x) { - uint32_t color = backdrop; - uint8_t top = 0x20; - uint8_t mask = outMask; - - if(!(line[5][x] & 0x80000000)) - mask = io_registers[REG_WINOUT] >> 8; - - int32_t window1_mask = ((inWindow1 & gfxInWin[1][x]) | -(inWindow1 & gfxInWin[1][x])) >> 31; - int32_t window0_mask = ((inWindow0 & gfxInWin[0][x]) | -(inWindow0 & gfxInWin[0][x])) >> 31; - mask = (inWin1Mask & window1_mask) | (mask & ~window1_mask); - mask = (inWin0Mask & window0_mask) | (mask & ~window0_mask); - - if((mask & 4) && (line[2][x] < backdrop)) - { - color = line[2][x]; - top = 0x04; - } - - if((mask & 16) && ((uint8_t)(line[4][x]>>24) < (uint8_t)(color >>24))) - { - color = line[4][x]; - top = 0x10; - } - - if(color & 0x00010000) { - // semi-transparent OBJ - uint32_t back = backdrop; - uint8_t top2 = 0x20; - - if((mask & 4) && line[2][x] < back) { - back = line[2][x]; - top2 = 0x04; - } - - alpha_blend_brightness_switch(); - } else if(mask & 32) { - switch((BLDMOD >> 6) & 3) { - case 0: - break; - case 1: - if(top & BLDMOD) - { - uint32_t back = backdrop; - uint8_t top2 = 0x20; - - if((mask & 4) && (top != 0x04) && (line[2][x] < backdrop)) { - back = line[2][x]; - top2 = 0x04; - } - - if((mask & 16) && (top != 0x10) && (uint8_t)(line[4][x]>>24) < (uint8_t)(back >> 24)) { - back = line[4][x]; - top2 = 0x10; - } - - if(top2 & (BLDMOD>>8) && color < 0x80000000) - { - GFX_ALPHA_BLEND(color, back, coeff[COLEV & 0x1F], coeff[(COLEV >> 8) & 0x1F]); - } - } - break; - case 2: - if(BLDMOD & top) - color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); - break; - case 3: - if(BLDMOD & top) - color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); - break; - } - } - - lineMix[x] = CONVERT_COLOR(color); - } - gfxBG2Changed = 0; - //gfxLastVCOUNT = io_registers[REG_VCOUNT]; -} - -/* -Mode 5 is a low resolution (160x128) 15-bit colour bitmap graphics mode -with 2 swappable pages! -It has a single layer, background layer 2, lower resolution than the screen. -It doesn't support scrolling, flipping, rotation or tiles. - -These routines only render a single line at a time, because of the way the GBA does events. -*/ - -void mode5RenderLine (void) -{ -#ifdef REPORT_VIDEO_MODES - fprintf(stderr, "MODE 5: Render Line\n"); -#endif - INIT_COLOR_DEPTH_LINE_MIX(); - uint16_t *palette = (uint16_t *)graphics.paletteRAM; - - if(graphics.layerEnable & 0x0400) { - int changed = gfxBG2Changed; - -#if 0 - if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) - changed = 3; -#endif - - gfxDrawRotScreen16Bit160(gfxBG2X, gfxBG2Y, changed); - } - - uint32_t background; - background = (READ16LE(&palette[0]) | 0x30000000); - - for(int x = 0; x < 240; ++x) { - uint32_t color = background; - uint8_t top = 0x20; - - if(line[2][x] < background) { - color = line[2][x]; - top = 0x04; - } - - if((uint8_t)(line[4][x]>>24) < (uint8_t)(color >>24)) { - color = line[4][x]; - top = 0x10; - - if(color & 0x00010000) { - // semi-transparent OBJ - uint32_t back = background; - uint8_t top2 = 0x20; - - if(line[2][x] < back) { - back = line[2][x]; - top2 = 0x04; - } - - alpha_blend_brightness_switch(); - } - } - - - lineMix[x] = CONVERT_COLOR(color); - } - gfxBG2Changed = 0; - //gfxLastVCOUNT = io_registers[REG_VCOUNT]; -} - -void mode5RenderLineNoWindow (void) -{ -#ifdef REPORT_VIDEO_MODES - fprintf(stderr, "MODE 5: Render Line No Window\n"); -#endif - INIT_COLOR_DEPTH_LINE_MIX(); - - uint16_t *palette = (uint16_t *)graphics.paletteRAM; - - if(graphics.layerEnable & 0x0400) { - int changed = gfxBG2Changed; - -#if 0 - if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) - changed = 3; -#endif - - gfxDrawRotScreen16Bit160(gfxBG2X, gfxBG2Y, changed); - } - - uint32_t background; - background = (READ16LE(&palette[0]) | 0x30000000); - - for(int x = 0; x < 240; ++x) { - uint32_t color = background; - uint8_t top = 0x20; - - if(line[2][x] < background) { - color = line[2][x]; - top = 0x04; - } - - if((uint8_t)(line[4][x]>>24) < (uint8_t)(color >>24)) { - color = line[4][x]; - top = 0x10; - } - - if(!(color & 0x00010000)) { - switch((BLDMOD >> 6) & 3) { - case 0: - break; - case 1: - if(top & BLDMOD) - { - uint32_t back = background; - uint8_t top2 = 0x20; - - if((top != 0x04) && line[2][x] < background) { - back = line[2][x]; - top2 = 0x04; - } - - if((top != 0x10) && (uint8_t)(line[4][x]>>24) < (uint8_t)(back >> 24)) { - back = line[4][x]; - top2 = 0x10; - } - - if(top2 & (BLDMOD>>8) && color < 0x80000000) - { - GFX_ALPHA_BLEND(color, back, coeff[COLEV & 0x1F], coeff[(COLEV >> 8) & 0x1F]); - } - - } - break; - case 2: - if(BLDMOD & top) - color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); - break; - case 3: - if(BLDMOD & top) - color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); - break; - } - } else { - // semi-transparent OBJ - uint32_t back = background; - uint8_t top2 = 0x20; - - if(line[2][x] < back) { - back = line[2][x]; - top2 = 0x04; - } - - alpha_blend_brightness_switch(); - } - - lineMix[x] = CONVERT_COLOR(color); - } - gfxBG2Changed = 0; - //gfxLastVCOUNT = io_registers[REG_VCOUNT]; -} - -void mode5RenderLineAll (void) -{ -#ifdef REPORT_VIDEO_MODES - fprintf(stderr, "MODE 5: Render Line All\n"); -#endif - INIT_COLOR_DEPTH_LINE_MIX(); - - uint16_t *palette = (uint16_t *)graphics.paletteRAM; - - if(graphics.layerEnable & 0x0400) - { - int changed = gfxBG2Changed; - -#if 0 - if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) - changed = 3; -#endif - - gfxDrawRotScreen16Bit160(gfxBG2X, gfxBG2Y, changed); - } - - - - bool inWindow0 = false; - bool inWindow1 = false; - - if(graphics.layerEnable & 0x2000) - { - uint8_t v0 = io_registers[REG_WIN0V] >> 8; - uint8_t v1 = io_registers[REG_WIN0V] & 255; - inWindow0 = ((v0 == v1) && (v0 >= 0xe8)); -#ifndef ORIGINAL_BRANCHES - uint32_t condition = v1 >= v0; - int32_t condition_mask = ((condition) | -(condition)) >> 31; - inWindow0 = (((inWindow0 | (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1)) & condition_mask) | (((inWindow0 | (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1)) & ~(condition_mask)))); -#else - if(v1 >= v0) - inWindow0 |= (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1); - else - inWindow0 |= (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1); -#endif - } - - if(graphics.layerEnable & 0x4000) - { - uint8_t v0 = io_registers[REG_WIN1V] >> 8; - uint8_t v1 = io_registers[REG_WIN1V] & 255; - inWindow1 = ((v0 == v1) && (v0 >= 0xe8)); -#ifndef ORIGINAL_BRANCHES - uint32_t condition = v1 >= v0; - int32_t condition_mask = ((condition) | -(condition)) >> 31; - inWindow1 = (((inWindow1 | (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1)) & condition_mask) | (((inWindow1 | (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1)) & ~(condition_mask)))); -#else - if(v1 >= v0) - inWindow1 |= (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1); - else - inWindow1 |= (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1); -#endif - } - - uint8_t inWin0Mask = io_registers[REG_WININ] & 0xFF; - uint8_t inWin1Mask = io_registers[REG_WININ] >> 8; - uint8_t outMask = io_registers[REG_WINOUT] & 0xFF; - - uint32_t background; - background = (READ16LE(&palette[0]) | 0x30000000); - - for(int x = 0; x < 240; ++x) { - uint32_t color = background; - uint8_t top = 0x20; - uint8_t mask = outMask; - - if(!(line[5][x] & 0x80000000)) { - mask = io_registers[REG_WINOUT] >> 8; - } - - int32_t window1_mask = ((inWindow1 & gfxInWin[1][x]) | -(inWindow1 & gfxInWin[1][x])) >> 31; - int32_t window0_mask = ((inWindow0 & gfxInWin[0][x]) | -(inWindow0 & gfxInWin[0][x])) >> 31; - mask = (inWin1Mask & window1_mask) | (mask & ~window1_mask); - mask = (inWin0Mask & window0_mask) | (mask & ~window0_mask); - - if((mask & 4) && (line[2][x] < background)) { - color = line[2][x]; - top = 0x04; - } - - if((mask & 16) && ((uint8_t)(line[4][x]>>24) < (uint8_t)(color >>24))) { - color = line[4][x]; - top = 0x10; - } - - if(color & 0x00010000) { - // semi-transparent OBJ - uint32_t back = background; - uint8_t top2 = 0x20; - - if((mask & 4) && line[2][x] < back) { - back = line[2][x]; - top2 = 0x04; - } - - alpha_blend_brightness_switch(); - } else if(mask & 32) { - switch((BLDMOD >> 6) & 3) { - case 0: - break; - case 1: - if(top & BLDMOD) - { - uint32_t back = background; - uint8_t top2 = 0x20; - - if((mask & 4) && (top != 0x04) && (line[2][x] < background)) { - back = line[2][x]; - top2 = 0x04; - } - - if((mask & 16) && (top != 0x10) && (uint8_t)(line[4][x]>>24) < (uint8_t)(back >> 24)) { - back = line[4][x]; - top2 = 0x10; - } - - if(top2 & (BLDMOD>>8) && color < 0x80000000) - { - GFX_ALPHA_BLEND(color, back, coeff[COLEV & 0x1F], coeff[(COLEV >> 8) & 0x1F]); - } - } - break; - case 2: - if(BLDMOD & top) - color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); - break; - case 3: - if(BLDMOD & top) - color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); - break; - } - } - - lineMix[x] = CONVERT_COLOR(color); - } - gfxBG2Changed = 0; - //gfxLastVCOUNT = io_registers[REG_VCOUNT]; -} - -void (Gigazoid::*renderLine)(void); -bool render_line_all_enabled; - -#define CPUUpdateRender() \ - render_line_all_enabled = false; \ - switch(io_registers[REG_DISPCNT] & 7) { \ - case 0: \ - if((!fxOn && !windowOn && !(graphics.layerEnable & 0x8000))) \ - renderLine = &Gigazoid::mode0RenderLine; \ - else if(fxOn && !windowOn && !(graphics.layerEnable & 0x8000)) \ - renderLine = &Gigazoid::mode0RenderLineNoWindow; \ - else { \ - renderLine = &Gigazoid::mode0RenderLineAll; \ - render_line_all_enabled = true; \ - } \ - break; \ - case 1: \ - if((!fxOn && !windowOn && !(graphics.layerEnable & 0x8000))) \ - renderLine = &Gigazoid::mode1RenderLine; \ - else if(fxOn && !windowOn && !(graphics.layerEnable & 0x8000)) \ - renderLine = &Gigazoid::mode1RenderLineNoWindow; \ - else { \ - renderLine = &Gigazoid::mode1RenderLineAll; \ - render_line_all_enabled = true; \ - } \ - break; \ - case 2: \ - if((!fxOn && !windowOn && !(graphics.layerEnable & 0x8000))) \ - renderLine = &Gigazoid::mode2RenderLine; \ - else if(fxOn && !windowOn && !(graphics.layerEnable & 0x8000)) \ - renderLine = &Gigazoid::mode2RenderLineNoWindow; \ - else { \ - renderLine = &Gigazoid::mode2RenderLineAll; \ - render_line_all_enabled = true; \ - } \ - break; \ - case 3: \ - if((!fxOn && !windowOn && !(graphics.layerEnable & 0x8000))) \ - renderLine = &Gigazoid::mode3RenderLine; \ - else if(fxOn && !windowOn && !(graphics.layerEnable & 0x8000)) \ - renderLine = &Gigazoid::mode3RenderLineNoWindow; \ - else { \ - renderLine = &Gigazoid::mode3RenderLineAll; \ - render_line_all_enabled = true; \ - } \ - break; \ - case 4: \ - if((!fxOn && !windowOn && !(graphics.layerEnable & 0x8000))) \ - renderLine = &Gigazoid::mode4RenderLine; \ - else if(fxOn && !windowOn && !(graphics.layerEnable & 0x8000)) \ - renderLine = &Gigazoid::mode4RenderLineNoWindow; \ - else { \ - renderLine = &Gigazoid::mode4RenderLineAll; \ - render_line_all_enabled = true; \ - } \ - break; \ - case 5: \ - if((!fxOn && !windowOn && !(graphics.layerEnable & 0x8000))) \ - renderLine = &Gigazoid::mode5RenderLine; \ - else if(fxOn && !windowOn && !(graphics.layerEnable & 0x8000)) \ - renderLine = &Gigazoid::mode5RenderLineNoWindow; \ - else { \ - renderLine = &Gigazoid::mode5RenderLineAll; \ - render_line_all_enabled = true; \ - } \ - } - -#define CPUSwap(a, b) \ -a ^= b; \ -b ^= a; \ -a ^= b; - -void CPUSwitchMode(int mode, bool saveState, bool breakLoop) -{ - CPU_UPDATE_CPSR(); - - switch(armMode) { - case 0x10: - case 0x1F: - bus.reg[R13_USR].I = bus.reg[13].I; - bus.reg[R14_USR].I = bus.reg[14].I; - bus.reg[17].I = bus.reg[16].I; - break; - case 0x11: - CPUSwap(bus.reg[R8_FIQ].I, bus.reg[8].I); - CPUSwap(bus.reg[R9_FIQ].I, bus.reg[9].I); - CPUSwap(bus.reg[R10_FIQ].I, bus.reg[10].I); - CPUSwap(bus.reg[R11_FIQ].I, bus.reg[11].I); - CPUSwap(bus.reg[R12_FIQ].I, bus.reg[12].I); - bus.reg[R13_FIQ].I = bus.reg[13].I; - bus.reg[R14_FIQ].I = bus.reg[14].I; - bus.reg[SPSR_FIQ].I = bus.reg[17].I; - break; - case 0x12: - bus.reg[R13_IRQ].I = bus.reg[13].I; - bus.reg[R14_IRQ].I = bus.reg[14].I; - bus.reg[SPSR_IRQ].I = bus.reg[17].I; - break; - case 0x13: - bus.reg[R13_SVC].I = bus.reg[13].I; - bus.reg[R14_SVC].I = bus.reg[14].I; - bus.reg[SPSR_SVC].I = bus.reg[17].I; - break; - case 0x17: - bus.reg[R13_ABT].I = bus.reg[13].I; - bus.reg[R14_ABT].I = bus.reg[14].I; - bus.reg[SPSR_ABT].I = bus.reg[17].I; - break; - case 0x1b: - bus.reg[R13_UND].I = bus.reg[13].I; - bus.reg[R14_UND].I = bus.reg[14].I; - bus.reg[SPSR_UND].I = bus.reg[17].I; - break; - } - - uint32_t CPSR = bus.reg[16].I; - uint32_t SPSR = bus.reg[17].I; - - switch(mode) { - case 0x10: - case 0x1F: - bus.reg[13].I = bus.reg[R13_USR].I; - bus.reg[14].I = bus.reg[R14_USR].I; - bus.reg[16].I = SPSR; - break; - case 0x11: - CPUSwap(bus.reg[8].I, bus.reg[R8_FIQ].I); - CPUSwap(bus.reg[9].I, bus.reg[R9_FIQ].I); - CPUSwap(bus.reg[10].I, bus.reg[R10_FIQ].I); - CPUSwap(bus.reg[11].I, bus.reg[R11_FIQ].I); - CPUSwap(bus.reg[12].I, bus.reg[R12_FIQ].I); - bus.reg[13].I = bus.reg[R13_FIQ].I; - bus.reg[14].I = bus.reg[R14_FIQ].I; - if(saveState) - bus.reg[17].I = CPSR; else - bus.reg[17].I = bus.reg[SPSR_FIQ].I; - break; - case 0x12: - bus.reg[13].I = bus.reg[R13_IRQ].I; - bus.reg[14].I = bus.reg[R14_IRQ].I; - bus.reg[16].I = SPSR; - if(saveState) - bus.reg[17].I = CPSR; - else - bus.reg[17].I = bus.reg[SPSR_IRQ].I; - break; - case 0x13: - bus.reg[13].I = bus.reg[R13_SVC].I; - bus.reg[14].I = bus.reg[R14_SVC].I; - bus.reg[16].I = SPSR; - if(saveState) - bus.reg[17].I = CPSR; - else - bus.reg[17].I = bus.reg[SPSR_SVC].I; - break; - case 0x17: - bus.reg[13].I = bus.reg[R13_ABT].I; - bus.reg[14].I = bus.reg[R14_ABT].I; - bus.reg[16].I = SPSR; - if(saveState) - bus.reg[17].I = CPSR; - else - bus.reg[17].I = bus.reg[SPSR_ABT].I; - break; - case 0x1b: - bus.reg[13].I = bus.reg[R13_UND].I; - bus.reg[14].I = bus.reg[R14_UND].I; - bus.reg[16].I = SPSR; - if(saveState) - bus.reg[17].I = CPSR; - else - bus.reg[17].I = bus.reg[SPSR_UND].I; - break; - default: - break; - } - armMode = mode; - CPUUpdateFlags(breakLoop); - CPU_UPDATE_CPSR(); -} - - - -void doDMA(uint32_t &s, uint32_t &d, uint32_t si, uint32_t di, uint32_t c, int transfer32) -{ - int sm = s >> 24; - int dm = d >> 24; - int sw = 0; - int dw = 0; - int sc = c; - - cpuDmaCount = c; - // This is done to get the correct waitstates. - int32_t sm_gt_15_mask = ((sm>15) | -(sm>15)) >> 31; - int32_t dm_gt_15_mask = ((dm>15) | -(dm>15)) >> 31; - sm = ((((15) & sm_gt_15_mask) | ((((sm) & ~(sm_gt_15_mask)))))); - dm = ((((15) & dm_gt_15_mask) | ((((dm) & ~(dm_gt_15_mask)))))); - - //if ((sm>=0x05) && (sm<=0x07) || (dm>=0x05) && (dm <=0x07)) - // blank = (((io_registers[REG_DISPSTAT] | ((io_registers[REG_DISPSTAT] >> 1)&1))==1) ? true : false); - - if(transfer32) - { - s &= 0xFFFFFFFC; - if(s < 0x02000000 && (bus.reg[15].I >> 24)) - { - do - { - CPUWriteMemory(d, 0); - d += di; - c--; - }while(c != 0); - } - else - { - do { - CPUWriteMemory(d, CPUReadMemory(s)); - d += di; - s += si; - c--; - }while(c != 0); - } - } - else - { - s &= 0xFFFFFFFE; - si = (int)si >> 1; - di = (int)di >> 1; - if(s < 0x02000000 && (bus.reg[15].I >> 24)) - { - do { - CPUWriteHalfWord(d, 0); - d += di; - c--; - }while(c != 0); - } - else - { - do{ - CPUWriteHalfWord(d, CPUReadHalfWord(s)); - d += di; - s += si; - c--; - }while(c != 0); - } - } - - cpuDmaCount = 0; - - if(transfer32) - { - sw = 1+memoryWaitSeq32[sm & 15]; - dw = 1+memoryWaitSeq32[dm & 15]; - cpuDmaTicksToUpdate += (sw+dw)*(sc-1) + 6 + memoryWait32[sm & 15] + memoryWaitSeq32[dm & 15]; - } - else - { - sw = 1+memoryWaitSeq[sm & 15]; - dw = 1+memoryWaitSeq[dm & 15]; - cpuDmaTicksToUpdate += (sw+dw)*(sc-1) + 6 + memoryWait[sm & 15] + memoryWaitSeq[dm & 15]; - } -} - - -void CPUCheckDMA(int reason, int dmamask) -{ - uint32_t arrayval[] = {4, (uint32_t)-4, 0, 4}; - // DMA 0 - if((DM0CNT_H & 0x8000) && (dmamask & 1)) - { - if(((DM0CNT_H >> 12) & 3) == reason) - { - uint32_t sourceIncrement, destIncrement; - uint32_t condition1 = ((DM0CNT_H >> 7) & 3); - uint32_t condition2 = ((DM0CNT_H >> 5) & 3); - sourceIncrement = arrayval[condition1]; - destIncrement = arrayval[condition2]; - doDMA(dma0Source, dma0Dest, sourceIncrement, destIncrement, - DM0CNT_L ? DM0CNT_L : 0x4000, - DM0CNT_H & 0x0400); - - if(DM0CNT_H & 0x4000) - { - io_registers[REG_IF] |= 0x0100; - UPDATE_REG(0x202, io_registers[REG_IF]); - cpuNextEvent = cpuTotalTicks; - } - - if(((DM0CNT_H >> 5) & 3) == 3) { - dma0Dest = DM0DAD_L | (DM0DAD_H << 16); - } - - if(!(DM0CNT_H & 0x0200) || (reason == 0)) { - DM0CNT_H &= 0x7FFF; - UPDATE_REG(0xBA, DM0CNT_H); - } - } - } - - // DMA 1 - if((DM1CNT_H & 0x8000) && (dmamask & 2)) { - if(((DM1CNT_H >> 12) & 3) == reason) { - uint32_t sourceIncrement, destIncrement; - uint32_t condition1 = ((DM1CNT_H >> 7) & 3); - uint32_t condition2 = ((DM1CNT_H >> 5) & 3); - sourceIncrement = arrayval[condition1]; - destIncrement = arrayval[condition2]; - uint32_t di_value, c_value, transfer_value; - if(reason == 3) - { - di_value = 0; - c_value = 4; - transfer_value = 0x0400; - } - else - { - di_value = destIncrement; - c_value = DM1CNT_L ? DM1CNT_L : 0x4000; - transfer_value = DM1CNT_H & 0x0400; - } - doDMA(dma1Source, dma1Dest, sourceIncrement, di_value, c_value, transfer_value); - - if(DM1CNT_H & 0x4000) { - io_registers[REG_IF] |= 0x0200; - UPDATE_REG(0x202, io_registers[REG_IF]); - cpuNextEvent = cpuTotalTicks; - } - - if(((DM1CNT_H >> 5) & 3) == 3) { - dma1Dest = DM1DAD_L | (DM1DAD_H << 16); - } - - if(!(DM1CNT_H & 0x0200) || (reason == 0)) { - DM1CNT_H &= 0x7FFF; - UPDATE_REG(0xC6, DM1CNT_H); - } - } - } - - // DMA 2 - if((DM2CNT_H & 0x8000) && (dmamask & 4)) { - if(((DM2CNT_H >> 12) & 3) == reason) { - uint32_t sourceIncrement, destIncrement; - uint32_t condition1 = ((DM2CNT_H >> 7) & 3); - uint32_t condition2 = ((DM2CNT_H >> 5) & 3); - sourceIncrement = arrayval[condition1]; - destIncrement = arrayval[condition2]; - uint32_t di_value, c_value, transfer_value; - if(reason == 3) - { - di_value = 0; - c_value = 4; - transfer_value = 0x0400; - } - else - { - di_value = destIncrement; - c_value = DM2CNT_L ? DM2CNT_L : 0x4000; - transfer_value = DM2CNT_H & 0x0400; - } - doDMA(dma2Source, dma2Dest, sourceIncrement, di_value, c_value, transfer_value); - - if(DM2CNT_H & 0x4000) { - io_registers[REG_IF] |= 0x0400; - UPDATE_REG(0x202, io_registers[REG_IF]); - cpuNextEvent = cpuTotalTicks; - } - - if(((DM2CNT_H >> 5) & 3) == 3) { - dma2Dest = DM2DAD_L | (DM2DAD_H << 16); - } - - if(!(DM2CNT_H & 0x0200) || (reason == 0)) { - DM2CNT_H &= 0x7FFF; - UPDATE_REG(0xD2, DM2CNT_H); - } - } - } - - // DMA 3 - if((DM3CNT_H & 0x8000) && (dmamask & 8)) - { - if(((DM3CNT_H >> 12) & 3) == reason) - { - uint32_t sourceIncrement, destIncrement; - uint32_t condition1 = ((DM3CNT_H >> 7) & 3); - uint32_t condition2 = ((DM3CNT_H >> 5) & 3); - sourceIncrement = arrayval[condition1]; - destIncrement = arrayval[condition2]; - doDMA(dma3Source, dma3Dest, sourceIncrement, destIncrement, - DM3CNT_L ? DM3CNT_L : 0x10000, - DM3CNT_H & 0x0400); - if(DM3CNT_H & 0x4000) { - io_registers[REG_IF] |= 0x0800; - UPDATE_REG(0x202, io_registers[REG_IF]); - cpuNextEvent = cpuTotalTicks; - } - - if(((DM3CNT_H >> 5) & 3) == 3) { - dma3Dest = DM3DAD_L | (DM3DAD_H << 16); - } - - if(!(DM3CNT_H & 0x0200) || (reason == 0)) { - DM3CNT_H &= 0x7FFF; - UPDATE_REG(0xDE, DM3CNT_H); - } - } - } -} - -uint16_t *address_lut[0x300]; - -void CPUUpdateRegister(uint32_t address, uint16_t value) -{ - switch(address) - { - case 0x00: - { - if((value & 7) > 5) // display modes above 0-5 are prohibited - io_registers[REG_DISPCNT] = (value & 7); - - bool change = (0 != ((io_registers[REG_DISPCNT] ^ value) & 0x80)); - bool changeBG = (0 != ((io_registers[REG_DISPCNT] ^ value) & 0x0F00)); - uint16_t changeBGon = ((~io_registers[REG_DISPCNT]) & value) & 0x0F00; // these layers are being activated - - io_registers[REG_DISPCNT] = (value & 0xFFF7); // bit 3 can only be accessed by the BIOS to enable GBC mode - UPDATE_REG(0x00, io_registers[REG_DISPCNT]); - - graphics.layerEnable = value; - - if(changeBGon) - { - graphics.layerEnableDelay = 4; - graphics.layerEnable &= ~changeBGon; - } - - windowOn = (graphics.layerEnable & 0x6000) ? true : false; - if(change && !((value & 0x80))) - { - if(!(io_registers[REG_DISPSTAT] & 1)) - { - graphics.lcdTicks = 1008; - io_registers[REG_DISPSTAT] &= 0xFFFC; - UPDATE_REG(0x04, io_registers[REG_DISPSTAT]); - CPUCompareVCOUNT(); - } - } - CPUUpdateRender(); - // we only care about changes in BG0-BG3 - if(changeBG) - { - if(!(graphics.layerEnable & 0x0100)) - memset(line[0], -1, 240 * sizeof(u32)); - if(!(graphics.layerEnable & 0x0200)) - memset(line[1], -1, 240 * sizeof(u32)); - if(!(graphics.layerEnable & 0x0400)) - memset(line[2], -1, 240 * sizeof(u32)); - if(!(graphics.layerEnable & 0x0800)) - memset(line[3], -1, 240 * sizeof(u32)); - } - break; - } - case 0x04: - io_registers[REG_DISPSTAT] = (value & 0xFF38) | (io_registers[REG_DISPSTAT] & 7); - UPDATE_REG(0x04, io_registers[REG_DISPSTAT]); - break; - case 0x06: - // not writable - break; - case 0x08: /* BG0CNT */ - case 0x0A: /* BG1CNT */ - *address_lut[address] = (value & 0xDFCF); - UPDATE_REG(address, *address_lut[address]); - break; - case 0x0C: /* BG2CNT */ - case 0x0E: /* BG3CNT */ - *address_lut[address] = (value & 0xFFCF); - UPDATE_REG(address, *address_lut[address]); - break; - case 0x10: /* BG0HOFS */ - case 0x12: /* BG0VOFS */ - case 0x14: /* BG1HOFS */ - case 0x16: /* BG1VOFS */ - case 0x18: /* BG2HOFS */ - case 0x1A: /* BG2VOFS */ - case 0x1C: /* BG3HOFS */ - case 0x1E: /* BG3VOFS */ - *address_lut[address] = value & 511; - UPDATE_REG(address, *address_lut[address]); - break; - case 0x20: /* BG2PA */ - case 0x22: /* BG2PB */ - case 0x24: /* BG2PC */ - case 0x26: /* BG2PD */ - *address_lut[address] = value; - UPDATE_REG(address, *address_lut[address]); - break; - case 0x28: - BG2X_L = value; - UPDATE_REG(0x28, BG2X_L); - gfxBG2Changed |= 1; - break; - case 0x2A: - BG2X_H = (value & 0xFFF); - UPDATE_REG(0x2A, BG2X_H); - gfxBG2Changed |= 1; - break; - case 0x2C: - BG2Y_L = value; - UPDATE_REG(0x2C, BG2Y_L); - gfxBG2Changed |= 2; - break; - case 0x2E: - BG2Y_H = value & 0xFFF; - UPDATE_REG(0x2E, BG2Y_H); - gfxBG2Changed |= 2; - break; - case 0x30: /* BG3PA */ - case 0x32: /* BG3PB */ - case 0x34: /* BG3PC */ - case 0x36: /* BG3PD */ - *address_lut[address] = value; - UPDATE_REG(address, *address_lut[address]); - break; - case 0x38: - BG3X_L = value; - UPDATE_REG(0x38, BG3X_L); - gfxBG3Changed |= 1; - break; - case 0x3A: - BG3X_H = value & 0xFFF; - UPDATE_REG(0x3A, BG3X_H); - gfxBG3Changed |= 1; - break; - case 0x3C: - BG3Y_L = value; - UPDATE_REG(0x3C, BG3Y_L); - gfxBG3Changed |= 2; - break; - case 0x3E: - BG3Y_H = value & 0xFFF; - UPDATE_REG(0x3E, BG3Y_H); - gfxBG3Changed |= 2; - break; - case 0x40: - io_registers[REG_WIN0H] = value; - UPDATE_REG(0x40, io_registers[REG_WIN0H]); - CPUUpdateWindow0(); - break; - case 0x42: - io_registers[REG_WIN1H] = value; - UPDATE_REG(0x42, io_registers[REG_WIN1H]); - CPUUpdateWindow1(); - break; - case 0x44: - case 0x46: - *address_lut[address] = value; - UPDATE_REG(address, *address_lut[address]); - break; - case 0x48: /* WININ */ - case 0x4A: /* WINOUT */ - *address_lut[address] = value & 0x3F3F; - UPDATE_REG(address, *address_lut[address]); - break; - case 0x4C: - MOSAIC = value; - UPDATE_REG(0x4C, MOSAIC); - break; - case 0x50: - BLDMOD = value & 0x3FFF; - UPDATE_REG(0x50, BLDMOD); - fxOn = ((BLDMOD>>6)&3) != 0; - CPUUpdateRender(); - break; - case 0x52: - COLEV = value & 0x1F1F; - UPDATE_REG(0x52, COLEV); - break; - case 0x54: - COLY = value & 0x1F; - UPDATE_REG(0x54, COLY); - break; - case 0x60: - case 0x62: - case 0x64: - case 0x68: - case 0x6c: - case 0x70: - case 0x72: - case 0x74: - case 0x78: - case 0x7c: - case 0x80: - case 0x84: - { - int gb_addr[2] = {address & 0xFF, (address & 0xFF) + 1}; - uint32_t address_array[2] = {address & 0xFF, (address&0xFF)+1}; - uint8_t data_array[2] = {(uint8_t)(value & 0xFF), (uint8_t)(value>>8)}; - gb_addr[0] = table[gb_addr[0] - 0x60]; - gb_addr[1] = table[gb_addr[1] - 0x60]; - soundEvent_u8_parallel(gb_addr, address_array, data_array); - break; - } - case 0x82: - case 0x88: - case 0xa0: - case 0xa2: - case 0xa4: - case 0xa6: - case 0x90: - case 0x92: - case 0x94: - case 0x96: - case 0x98: - case 0x9a: - case 0x9c: - case 0x9e: - soundEvent_u16(address&0xFF, value); - break; - case 0xB0: - DM0SAD_L = value; - UPDATE_REG(0xB0, DM0SAD_L); - break; - case 0xB2: - DM0SAD_H = value & 0x07FF; - UPDATE_REG(0xB2, DM0SAD_H); - break; - case 0xB4: - DM0DAD_L = value; - UPDATE_REG(0xB4, DM0DAD_L); - break; - case 0xB6: - DM0DAD_H = value & 0x07FF; - UPDATE_REG(0xB6, DM0DAD_H); - break; - case 0xB8: - DM0CNT_L = value & 0x3FFF; - UPDATE_REG(0xB8, 0); - break; - case 0xBA: - { - bool start = ((DM0CNT_H ^ value) & 0x8000) ? true : false; - value &= 0xF7E0; - - DM0CNT_H = value; - UPDATE_REG(0xBA, DM0CNT_H); - - if(start && (value & 0x8000)) - { - dma0Source = DM0SAD_L | (DM0SAD_H << 16); - dma0Dest = DM0DAD_L | (DM0DAD_H << 16); - CPUCheckDMA(0, 1); - } - } - break; - case 0xBC: - DM1SAD_L = value; - UPDATE_REG(0xBC, DM1SAD_L); - break; - case 0xBE: - DM1SAD_H = value & 0x0FFF; - UPDATE_REG(0xBE, DM1SAD_H); - break; - case 0xC0: - DM1DAD_L = value; - UPDATE_REG(0xC0, DM1DAD_L); - break; - case 0xC2: - DM1DAD_H = value & 0x07FF; - UPDATE_REG(0xC2, DM1DAD_H); - break; - case 0xC4: - DM1CNT_L = value & 0x3FFF; - UPDATE_REG(0xC4, 0); - break; - case 0xC6: - { - bool start = ((DM1CNT_H ^ value) & 0x8000) ? true : false; - value &= 0xF7E0; - - DM1CNT_H = value; - UPDATE_REG(0xC6, DM1CNT_H); - - if(start && (value & 0x8000)) - { - dma1Source = DM1SAD_L | (DM1SAD_H << 16); - dma1Dest = DM1DAD_L | (DM1DAD_H << 16); - CPUCheckDMA(0, 2); - } - } - break; - case 0xC8: - DM2SAD_L = value; - UPDATE_REG(0xC8, DM2SAD_L); - break; - case 0xCA: - DM2SAD_H = value & 0x0FFF; - UPDATE_REG(0xCA, DM2SAD_H); - break; - case 0xCC: - DM2DAD_L = value; - UPDATE_REG(0xCC, DM2DAD_L); - break; - case 0xCE: - DM2DAD_H = value & 0x07FF; - UPDATE_REG(0xCE, DM2DAD_H); - break; - case 0xD0: - DM2CNT_L = value & 0x3FFF; - UPDATE_REG(0xD0, 0); - break; - case 0xD2: - { - bool start = ((DM2CNT_H ^ value) & 0x8000) ? true : false; - - value &= 0xF7E0; - - DM2CNT_H = value; - UPDATE_REG(0xD2, DM2CNT_H); - - if(start && (value & 0x8000)) { - dma2Source = DM2SAD_L | (DM2SAD_H << 16); - dma2Dest = DM2DAD_L | (DM2DAD_H << 16); - - CPUCheckDMA(0, 4); - } - } - break; - case 0xD4: - DM3SAD_L = value; - UPDATE_REG(0xD4, DM3SAD_L); - break; - case 0xD6: - DM3SAD_H = value & 0x0FFF; - UPDATE_REG(0xD6, DM3SAD_H); - break; - case 0xD8: - DM3DAD_L = value; - UPDATE_REG(0xD8, DM3DAD_L); - break; - case 0xDA: - DM3DAD_H = value & 0x0FFF; - UPDATE_REG(0xDA, DM3DAD_H); - break; - case 0xDC: - DM3CNT_L = value; - UPDATE_REG(0xDC, 0); - break; - case 0xDE: - { - bool start = ((DM3CNT_H ^ value) & 0x8000) ? true : false; - - value &= 0xFFE0; - - DM3CNT_H = value; - UPDATE_REG(0xDE, DM3CNT_H); - - if(start && (value & 0x8000)) { - dma3Source = DM3SAD_L | (DM3SAD_H << 16); - dma3Dest = DM3DAD_L | (DM3DAD_H << 16); - CPUCheckDMA(0,8); - } - } - break; - case 0x100: - timer0Reload = value; - break; - case 0x102: - timer0Value = value; - timerOnOffDelay|=1; - cpuNextEvent = cpuTotalTicks; - break; - case 0x104: - timer1Reload = value; - break; - case 0x106: - timer1Value = value; - timerOnOffDelay|=2; - cpuNextEvent = cpuTotalTicks; - break; - case 0x108: - timer2Reload = value; - break; - case 0x10A: - timer2Value = value; - timerOnOffDelay|=4; - cpuNextEvent = cpuTotalTicks; - break; - case 0x10C: - timer3Reload = value; - break; - case 0x10E: - timer3Value = value; - timerOnOffDelay|=8; - cpuNextEvent = cpuTotalTicks; - break; - case 0x130: - io_registers[REG_P1] |= (value & 0x3FF); - UPDATE_REG(0x130, io_registers[REG_P1]); - break; - case 0x132: - UPDATE_REG(0x132, value & 0xC3FF); - break; - - - case 0x200: - io_registers[REG_IE] = value & 0x3FFF; - UPDATE_REG(0x200, io_registers[REG_IE]); - if ((io_registers[REG_IME] & 1) && (io_registers[REG_IF] & io_registers[REG_IE]) && armIrqEnable) - cpuNextEvent = cpuTotalTicks; - break; - case 0x202: - io_registers[REG_IF] ^= (value & io_registers[REG_IF]); - UPDATE_REG(0x202, io_registers[REG_IF]); - break; - case 0x204: - { - memoryWait[0x0e] = memoryWaitSeq[0x0e] = gamepakRamWaitState[value & 3]; - - memoryWait[0x08] = memoryWait[0x09] = 3; - memoryWaitSeq[0x08] = memoryWaitSeq[0x09] = 1; - - memoryWait[0x0a] = memoryWait[0x0b] = 3; - memoryWaitSeq[0x0a] = memoryWaitSeq[0x0b] = 1; - - memoryWait[0x0c] = memoryWait[0x0d] = 3; - memoryWaitSeq[0x0c] = memoryWaitSeq[0x0d] = 1; - - memoryWait32[8] = memoryWait[8] + memoryWaitSeq[8] + 1; - memoryWaitSeq32[8] = memoryWaitSeq[8]*2 + 1; - - memoryWait32[9] = memoryWait[9] + memoryWaitSeq[9] + 1; - memoryWaitSeq32[9] = memoryWaitSeq[9]*2 + 1; - - memoryWait32[10] = memoryWait[10] + memoryWaitSeq[10] + 1; - memoryWaitSeq32[10] = memoryWaitSeq[10]*2 + 1; - - memoryWait32[11] = memoryWait[11] + memoryWaitSeq[11] + 1; - memoryWaitSeq32[11] = memoryWaitSeq[11]*2 + 1; - - memoryWait32[12] = memoryWait[12] + memoryWaitSeq[12] + 1; - memoryWaitSeq32[12] = memoryWaitSeq[12]*2 + 1; - - memoryWait32[13] = memoryWait[13] + memoryWaitSeq[13] + 1; - memoryWaitSeq32[13] = memoryWaitSeq[13]*2 + 1; - - memoryWait32[14] = memoryWait[14] + memoryWaitSeq[14] + 1; - memoryWaitSeq32[14] = memoryWaitSeq[14]*2 + 1; - - if((value & 0x4000) == 0x4000) - bus.busPrefetchEnable = true; - else - bus.busPrefetchEnable = false; - - bus.busPrefetch = false; - bus.busPrefetchCount = 0; - - UPDATE_REG(0x204, value & 0x7FFF); - - } - break; - case 0x208: - io_registers[REG_IME] = value & 1; - UPDATE_REG(0x208, io_registers[REG_IME]); - if ((io_registers[REG_IME] & 1) && (io_registers[REG_IF] & io_registers[REG_IE]) && armIrqEnable) - cpuNextEvent = cpuTotalTicks; - break; - case 0x300: - if(value != 0) - value &= 0xFFFE; - UPDATE_REG(0x300, value); - break; - default: - UPDATE_REG(address&0x3FE, value); - break; - } -} - - -void CPUInit(const u8 *biosfile, const u32 biosfilelen) -{ - eepromInUse = false; - switch(cpuSaveType) - { - case 0: // automatic - default: - cpuEEPROMEnabled = true; - cpuEEPROMSensorEnabled = false; - cpuSaveGameFunc = &Gigazoid::flashSaveDecide; // EEPROM usage is automatically detected - break; - case 1: // EEPROM - cpuEEPROMEnabled = true; - cpuEEPROMSensorEnabled = false; - cpuSaveGameFunc = &Gigazoid::dummyWrite; // EEPROM usage is automatically detected - break; - case 2: // SRAM - cpuEEPROMEnabled = false; - cpuEEPROMSensorEnabled = false; - cpuSaveGameFunc = &Gigazoid::sramWrite; - break; - case 3: // FLASH - cpuEEPROMEnabled = false; - cpuEEPROMSensorEnabled = false; - cpuSaveGameFunc = &Gigazoid::flashWrite; - break; - case 4: // EEPROM+Sensor - cpuEEPROMEnabled = true; - cpuEEPROMSensorEnabled = true; - cpuSaveGameFunc = &Gigazoid::dummyWrite; // EEPROM usage is automatically detected - break; - case 5: // NONE - cpuEEPROMEnabled = false; - cpuEEPROMSensorEnabled = false; - cpuSaveGameFunc = &Gigazoid::dummyWrite; - break; - } - - memcpy(bios, biosfile, 16384); - - int i = 0; - - biosProtected[0] = 0x00; - biosProtected[1] = 0xf0; - biosProtected[2] = 0x29; - biosProtected[3] = 0xe1; - - for(i = 0; i < 256; i++) - { - int count = 0; - int j; - for(j = 0; j < 8; j++) - if(i & (1 << j)) - count++; - cpuBitsSet[i] = count; - - for(j = 0; j < 8; j++) - if(i & (1 << j)) - break; - } - - for(i = 0; i < 0x400; i++) - ioReadable[i] = true; - for(i = 0x10; i < 0x48; i++) - ioReadable[i] = false; - for(i = 0x4c; i < 0x50; i++) - ioReadable[i] = false; - for(i = 0x54; i < 0x60; i++) - ioReadable[i] = false; - for(i = 0x8c; i < 0x90; i++) - ioReadable[i] = false; - for(i = 0xa0; i < 0xb8; i++) - ioReadable[i] = false; - for(i = 0xbc; i < 0xc4; i++) - ioReadable[i] = false; - for(i = 0xc8; i < 0xd0; i++) - ioReadable[i] = false; - for(i = 0xd4; i < 0xdc; i++) - ioReadable[i] = false; - for(i = 0xe0; i < 0x100; i++) - ioReadable[i] = false; - for(i = 0x110; i < 0x120; i++) - ioReadable[i] = false; - for(i = 0x12c; i < 0x130; i++) - ioReadable[i] = false; - for(i = 0x138; i < 0x140; i++) - ioReadable[i] = false; - for(i = 0x144; i < 0x150; i++) - ioReadable[i] = false; - for(i = 0x15c; i < 0x200; i++) - ioReadable[i] = false; - for(i = 0x20c; i < 0x300; i++) - ioReadable[i] = false; - for(i = 0x304; i < 0x400; i++) - ioReadable[i] = false; - - // what is this? - if(romSize < 0x1fe2000) { - *((uint16_t *)&rom[0x1fe209c]) = 0xdffa; // SWI 0xFA - *((uint16_t *)&rom[0x1fe209e]) = 0x4770; // BX LR - } - - graphics.layerEnable = 0xff00; - graphics.layerEnableDelay = 1; - io_registers[REG_DISPCNT] = 0x0080; - io_registers[REG_DISPSTAT] = 0; - graphics.lcdTicks = (useBios && !skipBios) ? 1008 : 208; - - /* address lut for use in CPUUpdateRegister */ - address_lut[0x08] = &io_registers[REG_BG0CNT]; - address_lut[0x0A] = &io_registers[REG_BG1CNT]; - address_lut[0x0C] = &io_registers[REG_BG2CNT]; - address_lut[0x0E] = &io_registers[REG_BG3CNT]; - address_lut[0x10] = &io_registers[REG_BG0HOFS]; - address_lut[0x12] = &io_registers[REG_BG0VOFS]; - address_lut[0x14] = &io_registers[REG_BG1HOFS]; - address_lut[0x16] = &io_registers[REG_BG1VOFS]; - address_lut[0x18] = &io_registers[REG_BG2HOFS]; - address_lut[0x1A] = &io_registers[REG_BG2VOFS]; - address_lut[0x1C] = &io_registers[REG_BG3HOFS]; - address_lut[0x1E] = &io_registers[REG_BG3VOFS]; - address_lut[0x20] = &io_registers[REG_BG2PA]; - address_lut[0x22] = &io_registers[REG_BG2PB]; - address_lut[0x24] = &io_registers[REG_BG2PC]; - address_lut[0x26] = &io_registers[REG_BG2PD]; - address_lut[0x48] = &io_registers[REG_WININ]; - address_lut[0x4A] = &io_registers[REG_WINOUT]; - address_lut[0x30] = &io_registers[REG_BG3PA]; - address_lut[0x32] = &io_registers[REG_BG3PB]; - address_lut[0x34] = &io_registers[REG_BG3PC]; - address_lut[0x36] = &io_registers[REG_BG3PD]; - address_lut[0x40] = &io_registers[REG_WIN0H]; - address_lut[0x42] = &io_registers[REG_WIN1H]; - address_lut[0x44] = &io_registers[REG_WIN0V]; - address_lut[0x46] = &io_registers[REG_WIN1V]; -} - -void CPUReset (void) -{ - rtcReset(); - memset(&bus.reg[0], 0, sizeof(bus.reg)); // clean registers - memset(oam, 0, 0x400); // clean OAM - memset(graphics.paletteRAM, 0, 0x400); // clean palette - memset(pix, 0, 4 * 160 * 240); // clean picture - memset(vram, 0, 0x20000); // clean vram - memset(ioMem, 0, 0x400); // clean io memory - - io_registers[REG_DISPCNT] = 0x0080; - io_registers[REG_DISPSTAT] = 0x0000; - io_registers[REG_VCOUNT] = (useBios && !skipBios) ? 0 :0x007E; - io_registers[REG_BG0CNT] = 0x0000; - io_registers[REG_BG1CNT] = 0x0000; - io_registers[REG_BG2CNT] = 0x0000; - io_registers[REG_BG3CNT] = 0x0000; - io_registers[REG_BG0HOFS] = 0x0000; - io_registers[REG_BG0VOFS] = 0x0000; - io_registers[REG_BG1HOFS] = 0x0000; - io_registers[REG_BG1VOFS] = 0x0000; - io_registers[REG_BG2HOFS] = 0x0000; - io_registers[REG_BG2VOFS] = 0x0000; - io_registers[REG_BG3HOFS] = 0x0000; - io_registers[REG_BG3VOFS] = 0x0000; - io_registers[REG_BG2PA] = 0x0100; - io_registers[REG_BG2PB] = 0x0000; - io_registers[REG_BG2PC] = 0x0000; - io_registers[REG_BG2PD] = 0x0100; - BG2X_L = 0x0000; - BG2X_H = 0x0000; - BG2Y_L = 0x0000; - BG2Y_H = 0x0000; - io_registers[REG_BG3PA] = 0x0100; - io_registers[REG_BG3PB] = 0x0000; - io_registers[REG_BG3PC] = 0x0000; - io_registers[REG_BG3PD] = 0x0100; - BG3X_L = 0x0000; - BG3X_H = 0x0000; - BG3Y_L = 0x0000; - BG3Y_H = 0x0000; - io_registers[REG_WIN0H] = 0x0000; - io_registers[REG_WIN1H] = 0x0000; - io_registers[REG_WIN0V] = 0x0000; - io_registers[REG_WIN1V] = 0x0000; - io_registers[REG_WININ] = 0x0000; - io_registers[REG_WINOUT] = 0x0000; - MOSAIC = 0x0000; - BLDMOD = 0x0000; - COLEV = 0x0000; - COLY = 0x0000; - DM0SAD_L = 0x0000; - DM0SAD_H = 0x0000; - DM0DAD_L = 0x0000; - DM0DAD_H = 0x0000; - DM0CNT_L = 0x0000; - DM0CNT_H = 0x0000; - DM1SAD_L = 0x0000; - DM1SAD_H = 0x0000; - DM1DAD_L = 0x0000; - DM1DAD_H = 0x0000; - DM1CNT_L = 0x0000; - DM1CNT_H = 0x0000; - DM2SAD_L = 0x0000; - DM2SAD_H = 0x0000; - DM2DAD_L = 0x0000; - DM2DAD_H = 0x0000; - DM2CNT_L = 0x0000; - DM2CNT_H = 0x0000; - DM3SAD_L = 0x0000; - DM3SAD_H = 0x0000; - DM3DAD_L = 0x0000; - DM3DAD_H = 0x0000; - DM3CNT_L = 0x0000; - DM3CNT_H = 0x0000; - io_registers[REG_TM0D] = 0x0000; - io_registers[REG_TM0CNT] = 0x0000; - io_registers[REG_TM1D] = 0x0000; - io_registers[REG_TM1CNT] = 0x0000; - io_registers[REG_TM2D] = 0x0000; - io_registers[REG_TM2CNT] = 0x0000; - io_registers[REG_TM3D] = 0x0000; - io_registers[REG_TM3CNT] = 0x0000; - io_registers[REG_P1] = 0x03FF; - io_registers[REG_IE] = 0x0000; - io_registers[REG_IF] = 0x0000; - io_registers[REG_IME] = 0x0000; - - armMode = 0x1F; - - if(cpuIsMultiBoot) { - bus.reg[13].I = 0x03007F00; - bus.reg[15].I = 0x02000000; - bus.reg[16].I = 0x00000000; - bus.reg[R13_IRQ].I = 0x03007FA0; - bus.reg[R13_SVC].I = 0x03007FE0; - armIrqEnable = true; - } else { -#ifdef HAVE_HLE_BIOS - if(useBios && !skipBios) - { - bus.reg[15].I = 0x00000000; - armMode = 0x13; - armIrqEnable = false; - } - else - { -#endif - bus.reg[13].I = 0x03007F00; - bus.reg[15].I = 0x08000000; - bus.reg[16].I = 0x00000000; - bus.reg[R13_IRQ].I = 0x03007FA0; - bus.reg[R13_SVC].I = 0x03007FE0; - armIrqEnable = true; -#ifdef HAVE_HLE_BIOS - } -#endif - } - armState = true; - C_FLAG = V_FLAG = N_FLAG = Z_FLAG = false; - UPDATE_REG(0x00, io_registers[REG_DISPCNT]); - UPDATE_REG(0x06, io_registers[REG_VCOUNT]); - UPDATE_REG(0x20, io_registers[REG_BG2PA]); - UPDATE_REG(0x26, io_registers[REG_BG2PD]); - UPDATE_REG(0x30, io_registers[REG_BG3PA]); - UPDATE_REG(0x36, io_registers[REG_BG3PD]); - UPDATE_REG(0x130, io_registers[REG_P1]); - UPDATE_REG(0x88, 0x200); - - // disable FIQ - bus.reg[16].I |= 0x40; - - CPU_UPDATE_CPSR(); - - bus.armNextPC = bus.reg[15].I; - bus.reg[15].I += 4; - - // reset internal state - holdState = false; - - biosProtected[0] = 0x00; - biosProtected[1] = 0xf0; - biosProtected[2] = 0x29; - biosProtected[3] = 0xe1; - - graphics.lcdTicks = (useBios && !skipBios) ? 1008 : 208; - timer0On = false; - timer0Ticks = 0; - timer0Reload = 0; - timer0ClockReload = 0; - timer1On = false; - timer1Ticks = 0; - timer1Reload = 0; - timer1ClockReload = 0; - timer2On = false; - timer2Ticks = 0; - timer2Reload = 0; - timer2ClockReload = 0; - timer3On = false; - timer3Ticks = 0; - timer3Reload = 0; - timer3ClockReload = 0; - dma0Source = 0; - dma0Dest = 0; - dma1Source = 0; - dma1Dest = 0; - dma2Source = 0; - dma2Dest = 0; - dma3Source = 0; - dma3Dest = 0; - renderLine = &Gigazoid::mode0RenderLine; - fxOn = false; - windowOn = false; - graphics.layerEnable = io_registers[REG_DISPCNT]; - - memset(line[0], -1, 240 * sizeof(u32)); - memset(line[1], -1, 240 * sizeof(u32)); - memset(line[2], -1, 240 * sizeof(u32)); - memset(line[3], -1, 240 * sizeof(u32)); - - for(int i = 0; i < 256; i++) { - map[i].address = 0; - map[i].mask = 0; - } - - map[0].address = bios; - map[0].mask = 0x3FFF; - map[2].address = workRAM; - map[2].mask = 0x3FFFF; - map[3].address = internalRAM; - map[3].mask = 0x7FFF; - map[4].address = ioMem; - map[4].mask = 0x3FF; - map[5].address = graphics.paletteRAM; - map[5].mask = 0x3FF; - map[6].address = vram; - map[6].mask = 0x1FFFF; - map[7].address = oam; - map[7].mask = 0x3FF; - map[8].address = rom; - map[8].mask = 0x1FFFFFF; - map[9].address = rom; - map[9].mask = 0x1FFFFFF; - map[10].address = rom; - map[10].mask = 0x1FFFFFF; - map[12].address = rom; - map[12].mask = 0x1FFFFFF; - map[14].address = flashSaveMemory; - map[14].mask = 0xFFFF; - - eepromReset(); - flashReset(); - - soundReset(); - - CPUUpdateWindow0(); - CPUUpdateWindow1(); - - // make sure registers are correctly initialized if not using BIOS - if(cpuIsMultiBoot) - BIOS_RegisterRamReset(0xfe); - else if(!useBios && !cpuIsMultiBoot) - BIOS_RegisterRamReset(0xff); - else if (skipBios) - BIOS_RegisterRamReset(0xff); // ?? - - ARM_PREFETCH; -} - -void CPUInterrupt(void) -{ - uint32_t PC = bus.reg[15].I; - bool savedState = armState; - - if(armMode != 0x12 ) - CPUSwitchMode(0x12, true, false); - - bus.reg[14].I = PC; - if(!savedState) - bus.reg[14].I += 2; - bus.reg[15].I = 0x18; - armState = true; - armIrqEnable = false; - - bus.armNextPC = bus.reg[15].I; - bus.reg[15].I += 4; - ARM_PREFETCH; - - // if(!holdState) - biosProtected[0] = 0x02; - biosProtected[1] = 0xc0; - biosProtected[2] = 0x5e; - biosProtected[3] = 0xe5; -} - -void CPULoop (void) -{ - bus.busPrefetchCount = 0; - int ticks = 300000; - int timerOverflow = 0; - // variable used by the CPU core - cpuTotalTicks = 0; - - cpuNextEvent = CPUUpdateTicks(); - if(cpuNextEvent > ticks) - cpuNextEvent = ticks; - - bool framedone = false; - - do - { - if(!holdState) - { - if(armState) - { - if (!armExecute()) - return; - } - else - { - if (!thumbExecute()) - return; - } - clockTicks = 0; - } - else - clockTicks = CPUUpdateTicks(); - - cpuTotalTicks += clockTicks; - - - if(cpuTotalTicks >= cpuNextEvent) { - int remainingTicks = cpuTotalTicks - cpuNextEvent; - - clockTicks = cpuNextEvent; - cpuTotalTicks = 0; - -updateLoop: - - if (IRQTicks) - { - IRQTicks -= clockTicks; - if (IRQTicks<0) - IRQTicks = 0; - } - - graphics.lcdTicks -= clockTicks; - - soundTicksUp += clockTicks; - - AdvanceRTC(clockTicks); - - if(graphics.lcdTicks <= 0) - { - if(io_registers[REG_DISPSTAT] & 1) - { // V-BLANK - // if in V-Blank mode, keep computing... - if(io_registers[REG_DISPSTAT] & 2) - { - graphics.lcdTicks += 1008; - io_registers[REG_VCOUNT] += 1; - UPDATE_REG(0x06, io_registers[REG_VCOUNT]); - io_registers[REG_DISPSTAT] &= 0xFFFD; - UPDATE_REG(0x04, io_registers[REG_DISPSTAT]); - CPUCompareVCOUNT(); - } - else - { - graphics.lcdTicks += 224; - io_registers[REG_DISPSTAT] |= 2; - UPDATE_REG(0x04, io_registers[REG_DISPSTAT]); - if(io_registers[REG_DISPSTAT] & 16) - { - io_registers[REG_IF] |= 2; - UPDATE_REG(0x202, io_registers[REG_IF]); - } - if (scanlineCallback && scanlineCallbackLine == io_registers[REG_VCOUNT]) - scanlineCallback(); - } - - if(io_registers[REG_VCOUNT] >= 228) - { - //Reaching last line - io_registers[REG_DISPSTAT] &= 0xFFFC; - UPDATE_REG(0x04, io_registers[REG_DISPSTAT]); - io_registers[REG_VCOUNT] = 0; - UPDATE_REG(0x06, io_registers[REG_VCOUNT]); - CPUCompareVCOUNT(); - } - } - else if(io_registers[REG_DISPSTAT] & 2) - { - // if in H-Blank, leave it and move to drawing mode - io_registers[REG_VCOUNT] += 1; - UPDATE_REG(0x06, io_registers[REG_VCOUNT]); - graphics.lcdTicks += 1008; - io_registers[REG_DISPSTAT] &= 0xFFFD; - if(io_registers[REG_VCOUNT] == 160) - { - // moved to start of emulated frame - //UpdateJoypad(); - - io_registers[REG_DISPSTAT] |= 1; - io_registers[REG_DISPSTAT] &= 0xFFFD; - UPDATE_REG(0x04, io_registers[REG_DISPSTAT]); - if(io_registers[REG_DISPSTAT] & 0x0008) - { - io_registers[REG_IF] |= 1; - UPDATE_REG(0x202, io_registers[REG_IF]); - } - CPUCheckDMA(1, 0x0f); - systemDrawScreen(); - - process_sound_tick_fn(); - framedone = true; - } - - UPDATE_REG(0x04, io_registers[REG_DISPSTAT]); - CPUCompareVCOUNT(); - } - else - { - bool draw_objwin = (graphics.layerEnable & 0x9000) == 0x9000; - bool draw_sprites = graphics.layerEnable & 0x1000; - memset(line[4], -1, 240 * sizeof(u32)); // erase all sprites - - if(draw_sprites) - gfxDrawSprites(); - - if(render_line_all_enabled) - { - memset(line[5], -1, 240 * sizeof(u32)); // erase all OBJ Win - if(draw_objwin) - gfxDrawOBJWin(); - } - - (this->*renderLine)(); - - // entering H-Blank - io_registers[REG_DISPSTAT] |= 2; - UPDATE_REG(0x04, io_registers[REG_DISPSTAT]); - graphics.lcdTicks += 224; - CPUCheckDMA(2, 0x0f); - if(io_registers[REG_DISPSTAT] & 16) - { - io_registers[REG_IF] |= 2; - UPDATE_REG(0x202, io_registers[REG_IF]); - } - if (scanlineCallback && scanlineCallbackLine == io_registers[REG_VCOUNT]) - scanlineCallback(); - } - } - - // we shouldn't be doing sound in stop state, but we lose synchronization - // if sound is disabled, so in stop state, soundTick will just produce - // mute sound - - // moving this may have consequences; we'll see - //soundTicksUp += clockTicks; - - if(!stopState) { - if(timer0On) { - timer0Ticks -= clockTicks; - if(timer0Ticks <= 0) { - timer0Ticks += (0x10000 - timer0Reload) << timer0ClockReload; - timerOverflow |= 1; - soundTimerOverflow(0); - if(io_registers[REG_TM0CNT] & 0x40) { - io_registers[REG_IF] |= 0x08; - UPDATE_REG(0x202, io_registers[REG_IF]); - } - } - io_registers[REG_TM0D] = 0xFFFF - (timer0Ticks >> timer0ClockReload); - UPDATE_REG(0x100, io_registers[REG_TM0D]); - } - - if(timer1On) { - if(io_registers[REG_TM1CNT] & 4) { - if(timerOverflow & 1) { - io_registers[REG_TM1D]++; - if(io_registers[REG_TM1D] == 0) { - io_registers[REG_TM1D] += timer1Reload; - timerOverflow |= 2; - soundTimerOverflow(1); - if(io_registers[REG_TM1CNT] & 0x40) { - io_registers[REG_IF] |= 0x10; - UPDATE_REG(0x202, io_registers[REG_IF]); - } - } - UPDATE_REG(0x104, io_registers[REG_TM1D]); - } - } else { - timer1Ticks -= clockTicks; - if(timer1Ticks <= 0) { - timer1Ticks += (0x10000 - timer1Reload) << timer1ClockReload; - timerOverflow |= 2; - soundTimerOverflow(1); - if(io_registers[REG_TM1CNT] & 0x40) { - io_registers[REG_IF] |= 0x10; - UPDATE_REG(0x202, io_registers[REG_IF]); - } - } - io_registers[REG_TM1D] = 0xFFFF - (timer1Ticks >> timer1ClockReload); - UPDATE_REG(0x104, io_registers[REG_TM1D]); - } - } - - if(timer2On) { - if(io_registers[REG_TM2CNT] & 4) { - if(timerOverflow & 2) { - io_registers[REG_TM2D]++; - if(io_registers[REG_TM2D] == 0) { - io_registers[REG_TM2D] += timer2Reload; - timerOverflow |= 4; - if(io_registers[REG_TM2CNT] & 0x40) { - io_registers[REG_IF] |= 0x20; - UPDATE_REG(0x202, io_registers[REG_IF]); - } - } - UPDATE_REG(0x108, io_registers[REG_TM2D]); - } - } else { - timer2Ticks -= clockTicks; - if(timer2Ticks <= 0) { - timer2Ticks += (0x10000 - timer2Reload) << timer2ClockReload; - timerOverflow |= 4; - if(io_registers[REG_TM2CNT] & 0x40) { - io_registers[REG_IF] |= 0x20; - UPDATE_REG(0x202, io_registers[REG_IF]); - } - } - io_registers[REG_TM2D] = 0xFFFF - (timer2Ticks >> timer2ClockReload); - UPDATE_REG(0x108, io_registers[REG_TM2D]); - } - } - - if(timer3On) { - if(io_registers[REG_TM3CNT] & 4) { - if(timerOverflow & 4) { - io_registers[REG_TM3D]++; - if(io_registers[REG_TM3D] == 0) { - io_registers[REG_TM3D] += timer3Reload; - if(io_registers[REG_TM3CNT] & 0x40) { - io_registers[REG_IF] |= 0x40; - UPDATE_REG(0x202, io_registers[REG_IF]); - } - } - UPDATE_REG(0x10C, io_registers[REG_TM3D]); - } - } else { - timer3Ticks -= clockTicks; - if(timer3Ticks <= 0) { - timer3Ticks += (0x10000 - timer3Reload) << timer3ClockReload; - if(io_registers[REG_TM3CNT] & 0x40) { - io_registers[REG_IF] |= 0x40; - UPDATE_REG(0x202, io_registers[REG_IF]); - } - } - io_registers[REG_TM3D] = 0xFFFF - (timer3Ticks >> timer3ClockReload); - UPDATE_REG(0x10C, io_registers[REG_TM3D]); - } - } - } - - timerOverflow = 0; - ticks -= clockTicks; - cpuNextEvent = CPUUpdateTicks(); - - if(cpuDmaTicksToUpdate > 0) - { - if(cpuDmaTicksToUpdate > cpuNextEvent) - clockTicks = cpuNextEvent; - else - clockTicks = cpuDmaTicksToUpdate; - cpuDmaTicksToUpdate -= clockTicks; - if(cpuDmaTicksToUpdate < 0) - cpuDmaTicksToUpdate = 0; - goto skipIRQ; - } - - if(io_registers[REG_IF] && (io_registers[REG_IME] & 1) && armIrqEnable) - { - int res = io_registers[REG_IF] & io_registers[REG_IE]; - if(stopState) - res &= 0x3080; - if(res) - { - if (intState) - { - if (!IRQTicks) - { - CPUInterrupt(); - intState = false; - holdState = false; - stopState = false; - } - } - else - { - if (!holdState) - { - intState = true; - IRQTicks=7; - if (cpuNextEvent> IRQTicks) - cpuNextEvent = IRQTicks; - } - else - { - CPUInterrupt(); - holdState = false; - stopState = false; - } - } - } - } - - skipIRQ: - - if(remainingTicks > 0) { - if(remainingTicks > cpuNextEvent) - clockTicks = cpuNextEvent; - else - clockTicks = remainingTicks; - remainingTicks -= clockTicks; - if(remainingTicks < 0) - remainingTicks = 0; - goto updateLoop; - } - - if (timerOnOffDelay) - { - // Apply Timer - if (timerOnOffDelay & 1) - { - timer0ClockReload = TIMER_TICKS[timer0Value & 3]; - if(!timer0On && (timer0Value & 0x80)) { - // reload the counter - io_registers[REG_TM0D] = timer0Reload; - timer0Ticks = (0x10000 - io_registers[REG_TM0D]) << timer0ClockReload; - UPDATE_REG(0x100, io_registers[REG_TM0D]); - } - timer0On = timer0Value & 0x80 ? true : false; - io_registers[REG_TM0CNT] = timer0Value & 0xC7; - UPDATE_REG(0x102, io_registers[REG_TM0CNT]); - } - if (timerOnOffDelay & 2) - { - timer1ClockReload = TIMER_TICKS[timer1Value & 3]; - if(!timer1On && (timer1Value & 0x80)) { - // reload the counter - io_registers[REG_TM1D] = timer1Reload; - timer1Ticks = (0x10000 - io_registers[REG_TM1D]) << timer1ClockReload; - UPDATE_REG(0x104, io_registers[REG_TM1D]); - } - timer1On = timer1Value & 0x80 ? true : false; - io_registers[REG_TM1CNT] = timer1Value & 0xC7; - UPDATE_REG(0x106, io_registers[REG_TM1CNT]); - } - if (timerOnOffDelay & 4) - { - timer2ClockReload = TIMER_TICKS[timer2Value & 3]; - if(!timer2On && (timer2Value & 0x80)) { - // reload the counter - io_registers[REG_TM2D] = timer2Reload; - timer2Ticks = (0x10000 - io_registers[REG_TM2D]) << timer2ClockReload; - UPDATE_REG(0x108, io_registers[REG_TM2D]); - } - timer2On = timer2Value & 0x80 ? true : false; - io_registers[REG_TM2CNT] = timer2Value & 0xC7; - UPDATE_REG(0x10A, io_registers[REG_TM2CNT]); - } - if (timerOnOffDelay & 8) - { - timer3ClockReload = TIMER_TICKS[timer3Value & 3]; - if(!timer3On && (timer3Value & 0x80)) { - // reload the counter - io_registers[REG_TM3D] = timer3Reload; - timer3Ticks = (0x10000 - io_registers[REG_TM3D]) << timer3ClockReload; - UPDATE_REG(0x10C, io_registers[REG_TM3D]); - } - timer3On = timer3Value & 0x80 ? true : false; - io_registers[REG_TM3CNT] = timer3Value & 0xC7; - UPDATE_REG(0x10E, io_registers[REG_TM3CNT]); - } - cpuNextEvent = CPUUpdateTicks(); - timerOnOffDelay = 0; - // End of Apply Timer - } - - if(cpuNextEvent > ticks) - cpuNextEvent = ticks; - - if(ticks <= 0 || framedone) - break; - - } - }while(1); -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// END GBA.CPP -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void Gigazoid_Init() -{ - // one time constructor stuff - - flashState = FLASH_READ_ARRAY; - flashReadState = FLASH_READ_ARRAY; - flashSize = 0x10000; - flashDeviceID = 0x1b; - flashManufacturerID = 0x32; - flashBank = 0; - - eepromMode = EEPROM_IDLE; - - eepromInUse = false; - eepromSize = 512; - - // this is constant now - // soundSampleRate = 22050; - - soundEnableFlag = 0x3ff; /* emulator channels enabled*/ - - armState = true; - armIrqEnable = true; - armMode = 0x1f; - - renderLine = &Gigazoid::mode0RenderLine; - - #define ARRAYINIT(n) memcpy((n), (n##_init), sizeof(n)) - ARRAYINIT(memoryWait); - ARRAYINIT(memoryWaitSeq); - ARRAYINIT(memoryWait32); - ARRAYINIT(memoryWaitSeq32); - #undef ARRAYINIT -} - -u32 *systemVideoFrameDest; -u32 *systemVideoFramePalette; -s16 *systemAudioFrameDest; -int *systemAudioFrameSamp; -bool lagged; - -void (*scanlineCallback)(); -int scanlineCallbackLine; - -void (*fetchCallback)(u32 addr); -void (*writeCallback)(u32 addr); -void (*readCallback)(u32 addr); -void (*traceCallback)(u32 addr, u32 opcode); - -void (*padCallback)(); - -void systemDrawScreen (void) -{ - // upconvert 555->888 (TODO: BETTER) - for (int i = 0; i < 240 * 160; i++) - { - u32 input = pix[i]; - /* - u32 output = 0xff000000 | - input << 9 & 0xf80000 | - input << 6 & 0xf800 | - input << 3 & 0xf8; - */ - u32 output = systemVideoFramePalette[input]; - systemVideoFrameDest[i] = output; - } - systemVideoFrameDest = nullptr; - systemVideoFramePalette = nullptr; -} - -// called at regular intervals on sound clock -void systemOnWriteDataToSoundBuffer(int16_t * finalWave, int length) -{ - memcpy(systemAudioFrameDest, finalWave, length * 2); - systemAudioFrameDest = nullptr; - *systemAudioFrameSamp = length / 2; - systemAudioFrameSamp = nullptr; -} - -void UpdateJoypad() -{ - /* update joystick information */ - io_registers[REG_P1] = 0x03FF ^ (joy & 0x3FF); -#if 0 - if(cpuEEPROMSensorEnabled) - systemUpdateMotionSensor(); -#endif - UPDATE_REG(0x130, io_registers[REG_P1]); - io_registers[REG_P1CNT] = READ16LE(((uint16_t *)&ioMem[0x132])); - - // this seems wrong, but there are cases where the game - // can enter the stop state without requesting an IRQ from - // the joypad. - if((io_registers[REG_P1CNT] & 0x4000) || stopState) { - uint16_t p1 = (0x3FF ^ io_registers[REG_P1CNT]) & 0x3FF; - if(io_registers[REG_P1CNT] & 0x8000) { - if(p1 == (io_registers[REG_P1CNT] & 0x3FF)) { - io_registers[REG_IF] |= 0x1000; - UPDATE_REG(0x202, io_registers[REG_IF]); - } - } else { - if(p1 & io_registers[REG_P1CNT]) { - io_registers[REG_IF] |= 0x1000; - UPDATE_REG(0x202, io_registers[REG_IF]); - } - } - } -} - -public: - -templatevoid SyncState(NewState *ns) -{ - NSS(flashSaveMemory); - NSS(flashState); - NSS(flashReadState); - NSS(flashSize); - NSS(flashDeviceID); - NSS(flashManufacturerID); - NSS(flashBank); - - NSS(eepromMode); - NSS(eepromByte); - NSS(eepromBits); - NSS(eepromAddress); - NSS(eepromData); - NSS(eepromBuffer); - NSS(eepromInUse); - NSS(eepromSize); - - NSS(rtcClockData); - NSS(rtcEnabled); - SSS(rtcInternalTime); - NSS(RTCTicks); - NSS(RTCUseRealTime); - - NSS(soundTicksUp); - NSS(soundEnableFlag); - SSS_HACKY(pcm[0], this); - SSS_HACKY(pcm[1], this); - SSS(pcm_synth); - +*/ + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// END SOUND.CPP +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// BEGIN GBA.CPP +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/*============================================================ + GBA INLINE +============================================================ */ + +#define UPDATE_REG(address, value) WRITE16LE(((u16 *)&ioMem[address]),value); +#define ARM_PREFETCH_NEXT cpuPrefetch[1] = CPUReadMemoryQuick(bus.armNextPC+4); +#define THUMB_PREFETCH_NEXT cpuPrefetch[1] = CPUReadHalfWordQuick(bus.armNextPC+2); + +#define ARM_PREFETCH \ + {\ + cpuPrefetch[0] = CPUReadMemoryQuick(bus.armNextPC);\ + cpuPrefetch[1] = CPUReadMemoryQuick(bus.armNextPC+4);\ + } + +#define THUMB_PREFETCH \ + {\ + cpuPrefetch[0] = CPUReadHalfWordQuick(bus.armNextPC);\ + cpuPrefetch[1] = CPUReadHalfWordQuick(bus.armNextPC+2);\ + } + +int cpuNextEvent; // = 0; +bool holdState; // = false; +uint32_t cpuPrefetch[2]; +int cpuTotalTicks; // = 0; +uint8_t memoryWait[16]; +uint8_t memoryWaitSeq[16]; +uint8_t memoryWait32[16]; +uint8_t memoryWaitSeq32[16]; + +uint8_t biosProtected[4]; +uint8_t cpuBitsSet[256]; + +bool N_FLAG; // = 0; +bool C_FLAG; // = 0; +bool Z_FLAG; // = 0; +bool V_FLAG; // = 0; +bool armState; // = true; +bool armIrqEnable; // = true; +int armMode; // = 0x1f; + +typedef enum +{ + REG_DISPCNT = 0x000, + REG_DISPSTAT = 0x002, + REG_VCOUNT = 0x003, + REG_BG0CNT = 0x004, + REG_BG1CNT = 0x005, + REG_BG2CNT = 0x006, + REG_BG3CNT = 0x007, + REG_BG0HOFS = 0x08, + REG_BG0VOFS = 0x09, + REG_BG1HOFS = 0x0A, + REG_BG1VOFS = 0x0B, + REG_BG2HOFS = 0x0C, + REG_BG2VOFS = 0x0D, + REG_BG3HOFS = 0x0E, + REG_BG3VOFS = 0x0F, + REG_BG2PA = 0x10, + REG_BG2PB = 0x11, + REG_BG2PC = 0x12, + REG_BG2PD = 0x13, + REG_BG2X_L = 0x14, + REG_BG2X_H = 0x15, + REG_BG2Y_L = 0x16, + REG_BG2Y_H = 0x17, + REG_BG3PA = 0x18, + REG_BG3PB = 0x19, + REG_BG3PC = 0x1A, + REG_BG3PD = 0x1B, + REG_BG3X_L = 0x1C, + REG_BG3X_H = 0x1D, + REG_BG3Y_L = 0x1E, + REG_BG3Y_H = 0x1F, + REG_WIN0H = 0x20, + REG_WIN1H = 0x21, + REG_WIN0V = 0x22, + REG_WIN1V = 0x23, + REG_WININ = 0x24, + REG_WINOUT = 0x25, + REG_BLDCNT = 0x28, + REG_BLDALPHA = 0x29, + REG_BLDY = 0x2A, + REG_TM0D = 0x80, + REG_TM0CNT = 0x81, + REG_TM1D = 0x82, + REG_TM1CNT = 0x83, + REG_TM2D = 0x84, + REG_TM2CNT = 0x85, + REG_TM3D = 0x86, + REG_TM3CNT = 0x87, + REG_P1 = 0x098, + REG_P1CNT = 0x099, + REG_RCNT = 0x9A, + REG_IE = 0x100, + REG_IF = 0x101, + REG_IME = 0x104, + REG_HALTCNT = 0x180 +} hardware_register; + +uint16_t io_registers[1024 * 16]; + +u16 MOSAIC; + +uint16_t BG2X_L ; +uint16_t BG2X_H ; +uint16_t BG2Y_L ; +uint16_t BG2Y_H ; +uint16_t BG3X_L ; +uint16_t BG3X_H ; +uint16_t BG3Y_L ; +uint16_t BG3Y_H ; +uint16_t BLDMOD ; +uint16_t COLEV ; +uint16_t COLY ; +uint16_t DM0SAD_L ; +uint16_t DM0SAD_H ; +uint16_t DM0DAD_L ; +uint16_t DM0DAD_H ; +uint16_t DM0CNT_L ; +uint16_t DM0CNT_H ; +uint16_t DM1SAD_L ; +uint16_t DM1SAD_H ; +uint16_t DM1DAD_L ; +uint16_t DM1DAD_H ; +uint16_t DM1CNT_L ; +uint16_t DM1CNT_H ; +uint16_t DM2SAD_L ; +uint16_t DM2SAD_H ; +uint16_t DM2DAD_L ; +uint16_t DM2DAD_H ; +uint16_t DM2CNT_L ; +uint16_t DM2CNT_H ; +uint16_t DM3SAD_L ; +uint16_t DM3SAD_H ; +uint16_t DM3DAD_L ; +uint16_t DM3DAD_H ; +uint16_t DM3CNT_L ; +uint16_t DM3CNT_H ; + +uint8_t timerOnOffDelay ; +uint16_t timer0Value ; +uint32_t dma0Source ; +uint32_t dma0Dest ; +uint32_t dma1Source ; +uint32_t dma1Dest ; +uint32_t dma2Source ; +uint32_t dma2Dest ; +uint32_t dma3Source ; +uint32_t dma3Dest ; +void (Gigazoid::*cpuSaveGameFunc)(uint32_t,uint8_t); +bool fxOn ; +bool windowOn ; + +int cpuDmaTicksToUpdate; + +int IRQTicks; +bool intState; + +bus_t bus; +graphics_t graphics; + +memoryMap map[256]; +int clockTicks; + +int romSize; // = 0x2000000; +uint32_t line[6][240]; +bool gfxInWin[2][240]; +int lineOBJpixleft[128]; +int joy; + +int gfxBG2Changed; +int gfxBG3Changed; + +int gfxBG2X; +int gfxBG2Y; +int gfxBG3X; +int gfxBG3Y; + +bool ioReadable[0x400]; + +//int gfxLastVCOUNT = 0; + +// Waitstates when accessing data + +#define DATATICKS_ACCESS_BUS_PREFETCH(address, value) \ + int addr = (address >> 24) & 15; \ + if ((addr>=0x08) || (addr < 0x02)) \ + { \ + bus.busPrefetchCount=0; \ + bus.busPrefetch=false; \ + } \ + else if (bus.busPrefetch) \ + { \ + int waitState = value; \ + waitState = (1 & ~waitState) | (waitState & waitState); \ + bus.busPrefetchCount = ((bus.busPrefetchCount+1)<> 24) & 15]) +#define DATATICKS_ACCESS_32BIT_SEQ(address) (memoryWaitSeq32[(address >> 24) & 15]) +#define DATATICKS_ACCESS_16BIT(address) (memoryWait[(address >> 24) & 15]) +#define DATATICKS_ACCESS_16BIT_SEQ(address) (memoryWaitSeq[(address >> 24) & 15]) + +// Waitstates when executing opcode +INLINE int codeTicksAccess(u32 address, u8 bit32) // THUMB NON SEQ +{ + int addr, ret; + + addr = (address>>24) & 15; + + if (unsigned(addr - 0x08) <= 5) + { + if (bus.busPrefetchCount&0x1) + { + if (bus.busPrefetchCount&0x2) + { + bus.busPrefetchCount = ((bus.busPrefetchCount&0xFF)>>2) | (bus.busPrefetchCount&0xFFFFFF00); + return 0; + } + bus.busPrefetchCount = ((bus.busPrefetchCount&0xFF)>>1) | (bus.busPrefetchCount&0xFFFFFF00); + return memoryWaitSeq[addr]-1; + } + } + bus.busPrefetchCount = 0; + + if(bit32) /* ARM NON SEQ */ + ret = memoryWait32[addr]; + else /* THUMB NON SEQ */ + ret = memoryWait[addr]; + + return ret; +} + +INLINE int codeTicksAccessSeq16(u32 address) // THUMB SEQ +{ + int addr = (address>>24) & 15; + + if (unsigned(addr - 0x08) <= 5) + { + if (bus.busPrefetchCount&0x1) + { + bus.busPrefetchCount = ((bus.busPrefetchCount&0xFF)>>1) | (bus.busPrefetchCount&0xFFFFFF00); + return 0; + } + else if (bus.busPrefetchCount>0xFF) + { + bus.busPrefetchCount=0; + return memoryWait[addr]; + } + } + else + bus.busPrefetchCount = 0; + + return memoryWaitSeq[addr]; +} + +INLINE int codeTicksAccessSeq32(u32 address) // ARM SEQ +{ + int addr = (address>>24)&15; + + if (unsigned(addr - 0x08) <= 5) + { + if (bus.busPrefetchCount&0x1) + { + if (bus.busPrefetchCount&0x2) + { + bus.busPrefetchCount = ((bus.busPrefetchCount&0xFF)>>2) | (bus.busPrefetchCount&0xFFFFFF00); + return 0; + } + bus.busPrefetchCount = ((bus.busPrefetchCount&0xFF)>>1) | (bus.busPrefetchCount&0xFFFFFF00); + return memoryWaitSeq[addr]; + } + else if (bus.busPrefetchCount > 0xFF) + { + bus.busPrefetchCount=0; + return memoryWait32[addr]; + } + } + return memoryWaitSeq32[addr]; +} + +#define CPUReadByteQuick(addr) map[(addr)>>24].address[(addr) & map[(addr)>>24].mask] +#define CPUReadHalfWordQuick(addr) READ16LE(((u16*)&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask])) +#define CPUReadMemoryQuick(addr) READ32LE(((u32*)&map[(addr)>>24].address[(addr) & map[(addr)>>24].mask])) + +bool stopState; +#ifdef USE_MOTION_SENSOR +extern bool cpuEEPROMSensorEnabled; +#endif +bool timer0On ; +int timer0Ticks ; +int timer0Reload ; +int timer0ClockReload ; +uint16_t timer1Value ; +bool timer1On ; +int timer1Ticks ; +int timer1Reload ; +int timer1ClockReload ; +uint16_t timer2Value ; +bool timer2On ; +int timer2Ticks ; +int timer2Reload ; +int timer2ClockReload ; +uint16_t timer3Value ; +bool timer3On ; +int timer3Ticks ; +int timer3Reload ; +int timer3ClockReload ; + +INLINE u32 CPUReadMemory(u32 address) +{ + if (readCallback) + readCallback(address); + + u32 value; + switch(address >> 24) + { + case 0: + /* BIOS */ + if(bus.reg[15].I >> 24) + { + if(address < 0x4000) + value = READ32LE(((u32 *)&biosProtected)); + else goto unreadable; + } + else + value = READ32LE(((u32 *)&bios[address & 0x3FFC])); + break; + case 0x02: + /* external work RAM */ + value = READ32LE(((u32 *)&workRAM[address & 0x3FFFC])); + break; + case 0x03: + /* internal work RAM */ + value = READ32LE(((u32 *)&internalRAM[address & 0x7ffC])); + break; + case 0x04: + /* I/O registers */ + if (address == 0x4000130) + { + if (padCallback) + padCallback(); + lagged = false; + } + if((address < 0x4000400) && ioReadable[address & 0x3fc]) + { + if(ioReadable[(address & 0x3fc) + 2]) + value = READ32LE(((u32 *)&ioMem[address & 0x3fC])); + else + value = READ16LE(((u16 *)&ioMem[address & 0x3fc])); + } + else + goto unreadable; + break; + case 0x05: + /* palette RAM */ + value = READ32LE(((u32 *)&graphics.paletteRAM[address & 0x3fC])); + break; + case 0x06: + /* VRAM */ + address = (address & 0x1fffc); + if (((io_registers[REG_DISPCNT] & 7) >2) && ((address & 0x1C000) == 0x18000)) + { + value = 0; + break; + } + if ((address & 0x18000) == 0x18000) + address &= 0x17fff; + value = READ32LE(((u32 *)&vram[address])); + break; + case 0x07: + /* OAM RAM */ + value = READ32LE(((u32 *)&oam[address & 0x3FC])); + break; + case 0x08: + case 0x09: + case 0x0A: + case 0x0B: + case 0x0C: + /* gamepak ROM */ + value = READ32LE(((u32 *)&rom[address&0x1FFFFFC])); + break; + case 0x0D: + value = eepromRead(); + break; + case 14: + case 15: + value = flashRead(address) * 0x01010101; + break; + default: +unreadable: + if(armState) + value = CPUReadHalfWordQuick(bus.reg[15].I + (address & 2)); + else + value = CPUReadHalfWordQuick(bus.reg[15].I); + } + + if(address & 3) { + int shift = (address & 3) << 3; + value = (value >> shift) | (value << (32 - shift)); + } + return value; +} + +INLINE u32 CPUReadHalfWord(u32 address) +{ + if (readCallback) + readCallback(address); + + u32 value; + switch(address >> 24) + { + case 0: + if (bus.reg[15].I >> 24) + { + if(address < 0x4000) + value = READ16LE(((u16 *)&biosProtected[address&2])); + else + goto unreadable; + } + else + value = READ16LE(((u16 *)&bios[address & 0x3FFE])); + break; + case 2: + value = READ16LE(((u16 *)&workRAM[address & 0x3FFFE])); + break; + case 3: + value = READ16LE(((u16 *)&internalRAM[address & 0x7ffe])); + break; + case 4: + if (address == 0x4000130) + { + if (padCallback) + padCallback(); + lagged = false; + } + + if((address < 0x4000400) && ioReadable[address & 0x3fe]) + { + value = READ16LE(((u16 *)&ioMem[address & 0x3fe])); + if (((address & 0x3fe)>0xFF) && ((address & 0x3fe)<0x10E)) + { + if (((address & 0x3fe) == 0x100) && timer0On) + value = 0xFFFF - ((timer0Ticks-cpuTotalTicks) >> timer0ClockReload); + else + if (((address & 0x3fe) == 0x104) && timer1On && !(io_registers[REG_TM1CNT] & 4)) + value = 0xFFFF - ((timer1Ticks-cpuTotalTicks) >> timer1ClockReload); + else + if (((address & 0x3fe) == 0x108) && timer2On && !(io_registers[REG_TM2CNT] & 4)) + value = 0xFFFF - ((timer2Ticks-cpuTotalTicks) >> timer2ClockReload); + else + if (((address & 0x3fe) == 0x10C) && timer3On && !(io_registers[REG_TM3CNT] & 4)) + value = 0xFFFF - ((timer3Ticks-cpuTotalTicks) >> timer3ClockReload); + } + } + else goto unreadable; + break; + case 5: + value = READ16LE(((u16 *)&graphics.paletteRAM[address & 0x3fe])); + break; + case 6: + address = (address & 0x1fffe); + if (((io_registers[REG_DISPCNT] & 7) >2) && ((address & 0x1C000) == 0x18000)) + { + value = 0; + break; + } + if ((address & 0x18000) == 0x18000) + address &= 0x17fff; + value = READ16LE(((u16 *)&vram[address])); + break; + case 7: + value = READ16LE(((u16 *)&oam[address & 0x3fe])); + break; + case 8: + case 9: + case 10: + case 11: + case 12: + if(rtcEnabled && (address == 0x80000c4 || address == 0x80000c6 || address == 0x80000c8)) + value = rtcRead(address); + else + value = READ16LE(((u16 *)&rom[address & 0x1FFFFFE])); + break; + case 13: + value = eepromRead(); + break; + case 14: + value = flashRead(address) * 0x0101; + break; + default: +unreadable: + { + int param = bus.reg[15].I; + if(armState) + param += (address & 2); + value = CPUReadHalfWordQuick(param); + } + break; + } + + if(address & 1) + value = (value >> 8) | (value << 24); + + return value; +} + +INLINE u16 CPUReadHalfWordSigned(u32 address) +{ + u16 value = CPUReadHalfWord(address); + if((address & 1)) + value = (s8)value; + return value; +} + +INLINE u8 CPUReadByte(u32 address) +{ + if (readCallback) + readCallback(address); + + switch(address >> 24) + { + case 0: + if (bus.reg[15].I >> 24) + { + if(address < 0x4000) + return biosProtected[address & 3]; + else + goto unreadable; + } + return bios[address & 0x3FFF]; + case 2: + return workRAM[address & 0x3FFFF]; + case 3: + return internalRAM[address & 0x7fff]; + case 4: + if (address == 0x4000130 || address == 0x4000131) + { + if (padCallback) + padCallback(); + lagged = false; + } + + if((address < 0x4000400) && ioReadable[address & 0x3ff]) + return ioMem[address & 0x3ff]; + else goto unreadable; + case 5: + return graphics.paletteRAM[address & 0x3ff]; + case 6: + address = (address & 0x1ffff); + if (((io_registers[REG_DISPCNT] & 7) >2) && ((address & 0x1C000) == 0x18000)) + return 0; + if ((address & 0x18000) == 0x18000) + address &= 0x17fff; + return vram[address]; + case 7: + return oam[address & 0x3ff]; + case 8: + case 9: + case 10: + case 11: + case 12: + return rom[address & 0x1FFFFFF]; + case 13: + return eepromRead(); + case 14: +#ifdef USE_MOTION_SENSOR + if(cpuEEPROMSensorEnabled) + { + switch(address & 0x00008f00) + { + case 0x8200: + return systemGetSensorX() & 255; + case 0x8300: + return (systemGetSensorX() >> 8)|0x80; + case 0x8400: + return systemGetSensorY() & 255; + case 0x8500: + return systemGetSensorY() >> 8; + } + } +#endif + return flashRead(address); + default: +unreadable: + if(armState) + return CPUReadByteQuick(bus.reg[15].I+(address & 3)); + else + return CPUReadByteQuick(bus.reg[15].I+(address & 1)); + } +} + +INLINE void CPUWriteMemory(u32 address, u32 value) +{ + if (writeCallback) + writeCallback(address); + + switch(address >> 24) + { + case 0x02: + WRITE32LE(((u32 *)&workRAM[address & 0x3FFFC]), value); + break; + case 0x03: + WRITE32LE(((u32 *)&internalRAM[address & 0x7ffC]), value); + break; + case 0x04: + if(address < 0x4000400) + { + CPUUpdateRegister((address & 0x3FC), value & 0xFFFF); + CPUUpdateRegister((address & 0x3FC) + 2, (value >> 16)); + } + break; + case 0x05: + WRITE32LE(((u32 *)&graphics.paletteRAM[address & 0x3FC]), value); + break; + case 0x06: + address = (address & 0x1fffc); + if (((io_registers[REG_DISPCNT] & 7) >2) && ((address & 0x1C000) == 0x18000)) + return; + if ((address & 0x18000) == 0x18000) + address &= 0x17fff; + + + WRITE32LE(((u32 *)&vram[address]), value); + break; + case 0x07: + WRITE32LE(((u32 *)&oam[address & 0x3fc]), value); + break; + case 0x0D: + if (cpuEEPROMEnabled) + eepromWrite(value); + break; + case 0x0E: + (this->*cpuSaveGameFunc)(address, (u8)value); + break; + default: + break; + } +} + +INLINE void CPUWriteHalfWord(u32 address, u16 value) +{ + if (writeCallback) + writeCallback(address); + + switch(address >> 24) + { + case 2: + WRITE16LE(((u16 *)&workRAM[address & 0x3FFFE]),value); + break; + case 3: + WRITE16LE(((u16 *)&internalRAM[address & 0x7ffe]), value); + break; + case 4: + if(address < 0x4000400) + CPUUpdateRegister(address & 0x3fe, value); + break; + case 5: + WRITE16LE(((u16 *)&graphics.paletteRAM[address & 0x3fe]), value); + break; + case 6: + address = (address & 0x1fffe); + if (((io_registers[REG_DISPCNT] & 7) >2) && ((address & 0x1C000) == 0x18000)) + return; + if ((address & 0x18000) == 0x18000) + address &= 0x17fff; + WRITE16LE(((u16 *)&vram[address]), value); + break; + case 7: + WRITE16LE(((u16 *)&oam[address & 0x3fe]), value); + break; + case 8: + case 9: + if(address == 0x80000c4 || address == 0x80000c6 || address == 0x80000c8) + if(!rtcWrite(address, value)) + break; + break; + case 13: + if(cpuEEPROMEnabled) + eepromWrite((u8)value); + break; + case 14: + (this->*cpuSaveGameFunc)(address, (u8)value); + break; + default: + break; + } +} + +INLINE void CPUWriteByte(u32 address, u8 b) +{ + if (writeCallback) + writeCallback(address); + + switch(address >> 24) + { + case 2: + workRAM[address & 0x3FFFF] = b; + break; + case 3: + internalRAM[address & 0x7fff] = b; + break; + case 4: + if(address < 0x4000400) + { + switch(address & 0x3FF) + { + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x68: + case 0x69: + case 0x6c: + case 0x6d: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x78: + case 0x79: + case 0x7c: + case 0x7d: + case 0x80: + case 0x81: + case 0x84: + case 0x85: + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + case 0x98: + case 0x99: + case 0x9a: + case 0x9b: + case 0x9c: + case 0x9d: + case 0x9e: + case 0x9f: + { + int gb_addr = table[(address & 0xFF) - 0x60]; + soundEvent_u8(gb_addr, address&0xFF, b); + } + break; + case 0x301: // HALTCNT, undocumented + if(b == 0x80) + stopState = true; + holdState = 1; + cpuNextEvent = cpuTotalTicks; + break; + default: // every other register + { + u32 lowerBits = address & 0x3fe; + uint16_t param; + if(address & 1) + param = (READ16LE(&ioMem[lowerBits]) & 0x00FF) | (b << 8); + else + param = (READ16LE(&ioMem[lowerBits]) & 0xFF00) | b; + + CPUUpdateRegister(lowerBits, param); + } + break; + } + } + break; + case 5: + // no need to switch + *((u16 *)&graphics.paletteRAM[address & 0x3FE]) = (b << 8) | b; + break; + case 6: + address = (address & 0x1fffe); + if (((io_registers[REG_DISPCNT] & 7) >2) && ((address & 0x1C000) == 0x18000)) + return; + if ((address & 0x18000) == 0x18000) + address &= 0x17fff; + + // no need to switch + // byte writes to OBJ VRAM are ignored + if ((address) < objTilesAddress[((io_registers[REG_DISPCNT] & 7)+1)>>2]) + *((u16 *)&vram[address]) = (b << 8) | b; + break; + case 7: + // no need to switch + // byte writes to OAM are ignored + // *((u16 *)&oam[address & 0x3FE]) = (b << 8) | b; + break; + case 13: + if(cpuEEPROMEnabled) + eepromWrite(b); + break; + case 14: + (this->*cpuSaveGameFunc)(address, b); + break; + default: + break; + } +} + + +/*============================================================ + BIOS +============================================================ */ + +void BIOS_RegisterRamReset(u32 flags) +{ + // no need to trace here. this is only called directly from GBA.cpp + // to emulate bios initialization + + CPUUpdateRegister(0x0, 0x80); + + if(flags) + { + if(flags & 0x01) + memset(workRAM, 0, 0x40000); // clear work RAM + + if(flags & 0x02) + memset(internalRAM, 0, 0x7e00); // don't clear 0x7e00-0x7fff, clear internal RAM + + if(flags & 0x04) + memset(graphics.paletteRAM, 0, 0x400); // clear palette RAM + + if(flags & 0x08) + memset(vram, 0, 0x18000); // clear VRAM + + if(flags & 0x10) + memset(oam, 0, 0x400); // clean OAM + + if(flags & 0x80) { + int i; + for(i = 0; i < 0x10; i++) + CPUUpdateRegister(0x200+i*2, 0); + + for(i = 0; i < 0xF; i++) + CPUUpdateRegister(0x4+i*2, 0); + + for(i = 0; i < 0x20; i++) + CPUUpdateRegister(0x20+i*2, 0); + + for(i = 0; i < 0x18; i++) + CPUUpdateRegister(0xb0+i*2, 0); + + CPUUpdateRegister(0x130, 0); + CPUUpdateRegister(0x20, 0x100); + CPUUpdateRegister(0x30, 0x100); + CPUUpdateRegister(0x26, 0x100); + CPUUpdateRegister(0x36, 0x100); + } + + if(flags & 0x20) { + int i; + for(i = 0; i < 8; i++) + CPUUpdateRegister(0x110+i*2, 0); + CPUUpdateRegister(0x134, 0x8000); + for(i = 0; i < 7; i++) + CPUUpdateRegister(0x140+i*2, 0); + } + + if(flags & 0x40) { + int i; + CPUWriteByte(0x4000084, 0); + CPUWriteByte(0x4000084, 0x80); + CPUWriteMemory(0x4000080, 0x880e0000); + CPUUpdateRegister(0x88, CPUReadHalfWord(0x4000088)&0x3ff); + CPUWriteByte(0x4000070, 0x70); + for(i = 0; i < 8; i++) + CPUUpdateRegister(0x90+i*2, 0); + CPUWriteByte(0x4000070, 0); + for(i = 0; i < 8; i++) + CPUUpdateRegister(0x90+i*2, 0); + CPUWriteByte(0x4000084, 0); + } + } +} + +void BIOS_SoftReset (void) +{ + armState = true; + armMode = 0x1F; + armIrqEnable = false; + C_FLAG = V_FLAG = N_FLAG = Z_FLAG = false; + bus.reg[13].I = 0x03007F00; + bus.reg[14].I = 0x00000000; + bus.reg[16].I = 0x00000000; + bus.reg[R13_IRQ].I = 0x03007FA0; + bus.reg[R14_IRQ].I = 0x00000000; + bus.reg[SPSR_IRQ].I = 0x00000000; + bus.reg[R13_SVC].I = 0x03007FE0; + bus.reg[R14_SVC].I = 0x00000000; + bus.reg[SPSR_SVC].I = 0x00000000; + u8 b = internalRAM[0x7ffa]; + + memset(&internalRAM[0x7e00], 0, 0x200); + + if(b) { + bus.armNextPC = 0x02000000; + bus.reg[15].I = 0x02000004; + } else { + bus.armNextPC = 0x08000000; + bus.reg[15].I = 0x08000004; + } +} + +#define BIOS_REGISTER_RAM_RESET() BIOS_RegisterRamReset(bus.reg[0].I); + +#define CPU_UPDATE_CPSR() \ +{ \ + uint32_t CPSR; \ + CPSR = bus.reg[16].I & 0x40; \ + if(N_FLAG) \ + CPSR |= 0x80000000; \ + if(Z_FLAG) \ + CPSR |= 0x40000000; \ + if(C_FLAG) \ + CPSR |= 0x20000000; \ + if(V_FLAG) \ + CPSR |= 0x10000000; \ + if(!armState) \ + CPSR |= 0x00000020; \ + if(!armIrqEnable) \ + CPSR |= 0x80; \ + CPSR |= (armMode & 0x1F); \ + bus.reg[16].I = CPSR; \ +} + +#define CPU_SOFTWARE_INTERRUPT() \ +{ \ + uint32_t PC = bus.reg[15].I; \ + bool savedArmState = armState; \ + if(armMode != 0x13) \ + CPUSwitchMode(0x13, true, false); \ + bus.reg[14].I = PC - (savedArmState ? 4 : 2); \ + bus.reg[15].I = 0x08; \ + armState = true; \ + armIrqEnable = false; \ + bus.armNextPC = 0x08; \ + ARM_PREFETCH; \ + bus.reg[15].I += 4; \ +} + +void CPUUpdateFlags(bool breakLoop) +{ + uint32_t CPSR = bus.reg[16].I; + + N_FLAG = (CPSR & 0x80000000) ? true: false; + Z_FLAG = (CPSR & 0x40000000) ? true: false; + C_FLAG = (CPSR & 0x20000000) ? true: false; + V_FLAG = (CPSR & 0x10000000) ? true: false; + armState = (CPSR & 0x20) ? false : true; + armIrqEnable = (CPSR & 0x80) ? false : true; + if (breakLoop && armIrqEnable && (io_registers[REG_IF] & io_registers[REG_IE]) && (io_registers[REG_IME] & 1)) + cpuNextEvent = cpuTotalTicks; +} + +void CPUSoftwareInterrupt(int comment) +{ + if(armState) + comment >>= 16; + + CPU_SOFTWARE_INTERRUPT(); +} + + +/*============================================================ + GBA ARM CORE +============================================================ */ + +#ifdef _MSC_VER + // Disable "empty statement" warnings + #pragma warning(disable: 4390) + // Visual C's inline assembler treats "offset" as a reserved word, so we + // tell it otherwise. If you want to use it, write "OFFSET" in capitals. + #define offset offset_ +#endif + +void armUnknownInsn(u32 opcode) +{ + u32 PC = bus.reg[15].I; + bool savedArmState = armState; + if(armMode != 0x1b ) + CPUSwitchMode(0x1b, true, false); + bus.reg[14].I = PC - (savedArmState ? 4 : 2); + bus.reg[15].I = 0x04; + armState = true; + armIrqEnable = false; + bus.armNextPC = 0x04; + ARM_PREFETCH; + bus.reg[15].I += 4; +} + +// Common macros ////////////////////////////////////////////////////////// + +#define NEG(i) ((i) >> 31) +#define POS(i) ((~(i)) >> 31) + +// The following macros are used for optimization; any not defined for a +// particular compiler/CPU combination default to the C core versions. +// +// ALU_INIT_C: Used at the beginning of ALU instructions (AND/EOR/...). +// (ALU_INIT_NC) Can consist of variable declarations, like the C core, +// or the start of a continued assembly block, like the +// x86-optimized version. The _C version is used when the +// carry flag from the shift operation is needed (logical +// operations that set condition codes, like ANDS); the +// _NC version is used when the carry result is ignored. +// VALUE_XXX: Retrieve the second operand's value for an ALU instruction. +// The _C and _NC versions are used the same way as ALU_INIT. +// OP_XXX: ALU operations. XXX is the instruction name. +// SETCOND_NONE: Used in multiply instructions in place of SETCOND_MUL +// when the condition codes are not set. Usually empty. +// SETCOND_MUL: Used in multiply instructions to set the condition codes. +// ROR_IMM_MSR: Used to rotate the immediate operand for MSR. +// ROR_OFFSET: Used to rotate the `offset' parameter for LDR and STR +// instructions. +// RRX_OFFSET: Used to rotate (RRX) the `offset' parameter for LDR and +// STR instructions. + +// C core + +#define C_SETCOND_LOGICAL \ + N_FLAG = ((s32)res < 0) ? true : false; \ + Z_FLAG = (res == 0) ? true : false; \ + C_FLAG = C_OUT; +#define C_SETCOND_ADD \ + N_FLAG = ((s32)res < 0) ? true : false; \ + Z_FLAG = (res == 0) ? true : false; \ + V_FLAG = ((NEG(lhs) & NEG(rhs) & POS(res)) | \ + (POS(lhs) & POS(rhs) & NEG(res))) ? true : false;\ + C_FLAG = ((NEG(lhs) & NEG(rhs)) | \ + (NEG(lhs) & POS(res)) | \ + (NEG(rhs) & POS(res))) ? true : false; +#define C_SETCOND_SUB \ + N_FLAG = ((s32)res < 0) ? true : false; \ + Z_FLAG = (res == 0) ? true : false; \ + V_FLAG = ((NEG(lhs) & POS(rhs) & POS(res)) | \ + (POS(lhs) & NEG(rhs) & NEG(res))) ? true : false;\ + C_FLAG = ((NEG(lhs) & POS(rhs)) | \ + (NEG(lhs) & POS(res)) | \ + (POS(rhs) & POS(res))) ? true : false; + +#ifndef ALU_INIT_C + #define ALU_INIT_C \ + int dest = (opcode>>12) & 15; \ + bool C_OUT = C_FLAG; \ + u32 value; +#endif +// OP Rd,Rb,Rm LSL # +#ifndef VALUE_LSL_IMM_C + #define VALUE_LSL_IMM_C \ + unsigned int shift = (opcode >> 7) & 0x1F; \ + if (!shift) { /* LSL #0 most common? */ \ + value = bus.reg[opcode & 0x0F].I; \ + } else { \ + u32 v = bus.reg[opcode & 0x0F].I; \ + C_OUT = (v >> (32 - shift)) & 1 ? true : false; \ + value = v << shift; \ + } +#endif +// OP Rd,Rb,Rm LSL Rs +#ifndef VALUE_LSL_REG_C + #define VALUE_LSL_REG_C \ + unsigned int shift = bus.reg[(opcode >> 8)&15].B.B0; \ + if (shift) { \ + if (shift == 32) { \ + value = 0; \ + C_OUT = (bus.reg[opcode & 0x0F].I & 1 ? true : false);\ + } else if (shift < 32) { \ + u32 v = bus.reg[opcode & 0x0F].I; \ + C_OUT = (v >> (32 - shift)) & 1 ? true : false;\ + value = v << shift; \ + } else { \ + value = 0; \ + C_OUT = false; \ + } \ + } else { \ + value = bus.reg[opcode & 0x0F].I; \ + } +#endif +// OP Rd,Rb,Rm LSR # +#ifndef VALUE_LSR_IMM_C + #define VALUE_LSR_IMM_C \ + unsigned int shift = (opcode >> 7) & 0x1F; \ + if (shift) { \ + u32 v = bus.reg[opcode & 0x0F].I; \ + C_OUT = (v >> (shift - 1)) & 1 ? true : false; \ + value = v >> shift; \ + } else { \ + value = 0; \ + C_OUT = (bus.reg[opcode & 0x0F].I & 0x80000000) ? true : false;\ + } +#endif +// OP Rd,Rb,Rm LSR Rs +#ifndef VALUE_LSR_REG_C + #define VALUE_LSR_REG_C \ + unsigned int shift = bus.reg[(opcode >> 8)&15].B.B0; \ + if (shift) { \ + if (shift == 32) { \ + value = 0; \ + C_OUT = (bus.reg[opcode & 0x0F].I & 0x80000000 ? true : false);\ + } else if (shift < 32) { \ + u32 v = bus.reg[opcode & 0x0F].I; \ + C_OUT = (v >> (shift - 1)) & 1 ? true : false;\ + value = v >> shift; \ + } else { \ + value = 0; \ + C_OUT = false; \ + } \ + } else { \ + value = bus.reg[opcode & 0x0F].I; \ + } +#endif +// OP Rd,Rb,Rm ASR # +#ifndef VALUE_ASR_IMM_C + #define VALUE_ASR_IMM_C \ + unsigned int shift = (opcode >> 7) & 0x1F; \ + if (shift) { \ + s32 v = bus.reg[opcode & 0x0F].I; \ + C_OUT = (v >> (int)(shift - 1)) & 1 ? true : false;\ + value = v >> (int)shift; \ + } else { \ + if (bus.reg[opcode & 0x0F].I & 0x80000000) { \ + value = 0xFFFFFFFF; \ + C_OUT = true; \ + } else { \ + value = 0; \ + C_OUT = false; \ + } \ + } +#endif +// OP Rd,Rb,Rm ASR Rs +#ifndef VALUE_ASR_REG_C + #define VALUE_ASR_REG_C \ + unsigned int shift = bus.reg[(opcode >> 8)&15].B.B0; \ + if (shift < 32) { \ + if (shift) { \ + s32 v = bus.reg[opcode & 0x0F].I; \ + C_OUT = (v >> (int)(shift - 1)) & 1 ? true : false;\ + value = v >> (int)shift; \ + } else { \ + value = bus.reg[opcode & 0x0F].I; \ + } \ + } else { \ + if (bus.reg[opcode & 0x0F].I & 0x80000000) { \ + value = 0xFFFFFFFF; \ + C_OUT = true; \ + } else { \ + value = 0; \ + C_OUT = false; \ + } \ + } +#endif +// OP Rd,Rb,Rm ROR # +#ifndef VALUE_ROR_IMM_C + #define VALUE_ROR_IMM_C \ + unsigned int shift = (opcode >> 7) & 0x1F; \ + if (shift) { \ + u32 v = bus.reg[opcode & 0x0F].I; \ + C_OUT = (v >> (shift - 1)) & 1 ? true : false; \ + value = ((v << (32 - shift)) | \ + (v >> shift)); \ + } else { \ + u32 v = bus.reg[opcode & 0x0F].I; \ + C_OUT = (v & 1) ? true : false; \ + value = ((v >> 1) | \ + (C_FLAG << 31)); \ + } +#endif +// OP Rd,Rb,Rm ROR Rs +#ifndef VALUE_ROR_REG_C + #define VALUE_ROR_REG_C \ + unsigned int shift = bus.reg[(opcode >> 8)&15].B.B0; \ + if (shift & 0x1F) { \ + u32 v = bus.reg[opcode & 0x0F].I; \ + C_OUT = (v >> (shift - 1)) & 1 ? true : false; \ + value = ((v << (32 - shift)) | \ + (v >> shift)); \ + } else { \ + value = bus.reg[opcode & 0x0F].I; \ + if (shift) \ + C_OUT = (value & 0x80000000 ? true : false);\ + } +#endif +// OP Rd,Rb,# ROR # +#ifndef VALUE_IMM_C + #define VALUE_IMM_C \ + int shift = (opcode & 0xF00) >> 7; \ + if (shift) { \ + u32 v = opcode & 0xFF; \ + C_OUT = (v >> (shift - 1)) & 1 ? true : false; \ + value = ((v << (32 - shift)) | \ + (v >> shift)); \ + } else { \ + value = opcode & 0xFF; \ + } +#endif + +// Make the non-carry versions default to the carry versions +// (this is fine for C--the compiler will optimize the dead code out) +#ifndef ALU_INIT_NC + #define ALU_INIT_NC ALU_INIT_C +#endif +#ifndef VALUE_LSL_IMM_NC + #define VALUE_LSL_IMM_NC VALUE_LSL_IMM_C +#endif +#ifndef VALUE_LSL_REG_NC + #define VALUE_LSL_REG_NC VALUE_LSL_REG_C +#endif +#ifndef VALUE_LSR_IMM_NC + #define VALUE_LSR_IMM_NC VALUE_LSR_IMM_C +#endif +#ifndef VALUE_LSR_REG_NC + #define VALUE_LSR_REG_NC VALUE_LSR_REG_C +#endif +#ifndef VALUE_ASR_IMM_NC + #define VALUE_ASR_IMM_NC VALUE_ASR_IMM_C +#endif +#ifndef VALUE_ASR_REG_NC + #define VALUE_ASR_REG_NC VALUE_ASR_REG_C +#endif +#ifndef VALUE_ROR_IMM_NC + #define VALUE_ROR_IMM_NC VALUE_ROR_IMM_C +#endif +#ifndef VALUE_ROR_REG_NC + #define VALUE_ROR_REG_NC VALUE_ROR_REG_C +#endif +#ifndef VALUE_IMM_NC + #define VALUE_IMM_NC VALUE_IMM_C +#endif + +#define C_CHECK_PC(SETCOND) if (dest != 15) { SETCOND } +#ifndef OP_AND + #define OP_AND \ + u32 res = bus.reg[(opcode>>16)&15].I & value; \ + bus.reg[dest].I = res; +#endif +#ifndef OP_ANDS + #define OP_ANDS OP_AND C_CHECK_PC(C_SETCOND_LOGICAL) +#endif +#ifndef OP_EOR + #define OP_EOR \ + u32 res = bus.reg[(opcode>>16)&15].I ^ value; \ + bus.reg[dest].I = res; +#endif +#ifndef OP_EORS + #define OP_EORS OP_EOR C_CHECK_PC(C_SETCOND_LOGICAL) +#endif +#ifndef OP_SUB + #define OP_SUB \ + u32 lhs = bus.reg[(opcode>>16)&15].I; \ + u32 rhs = value; \ + u32 res = lhs - rhs; \ + bus.reg[dest].I = res; +#endif +#ifndef OP_SUBS + #define OP_SUBS OP_SUB C_CHECK_PC(C_SETCOND_SUB) +#endif +#ifndef OP_RSB + #define OP_RSB \ + u32 lhs = bus.reg[(opcode>>16)&15].I; \ + u32 rhs = value; \ + u32 res = rhs - lhs; \ + bus.reg[dest].I = res; +#endif +#ifndef OP_RSBS + #define OP_RSBS OP_RSB C_CHECK_PC(C_SETCOND_SUB) +#endif +#ifndef OP_ADD + #define OP_ADD \ + u32 lhs = bus.reg[(opcode>>16)&15].I; \ + u32 rhs = value; \ + u32 res = lhs + rhs; \ + bus.reg[dest].I = res; +#endif +#ifndef OP_ADDS + #define OP_ADDS OP_ADD C_CHECK_PC(C_SETCOND_ADD) +#endif +#ifndef OP_ADC + #define OP_ADC \ + u32 lhs = bus.reg[(opcode>>16)&15].I; \ + u32 rhs = value; \ + u32 res = lhs + rhs + (u32)C_FLAG; \ + bus.reg[dest].I = res; +#endif +#ifndef OP_ADCS + #define OP_ADCS OP_ADC C_CHECK_PC(C_SETCOND_ADD) +#endif +#ifndef OP_SBC + #define OP_SBC \ + u32 lhs = bus.reg[(opcode>>16)&15].I; \ + u32 rhs = value; \ + u32 res = lhs - rhs - !((u32)C_FLAG); \ + bus.reg[dest].I = res; +#endif +#ifndef OP_SBCS + #define OP_SBCS OP_SBC C_CHECK_PC(C_SETCOND_SUB) +#endif +#ifndef OP_RSC + #define OP_RSC \ + u32 lhs = bus.reg[(opcode>>16)&15].I; \ + u32 rhs = value; \ + u32 res = rhs - lhs - !((u32)C_FLAG); \ + bus.reg[dest].I = res; +#endif +#ifndef OP_RSCS + #define OP_RSCS OP_RSC C_CHECK_PC(C_SETCOND_SUB) +#endif +#ifndef OP_TST + #define OP_TST \ + u32 res = bus.reg[(opcode >> 16) & 0x0F].I & value; \ + C_SETCOND_LOGICAL; +#endif +#ifndef OP_TEQ + #define OP_TEQ \ + u32 res = bus.reg[(opcode >> 16) & 0x0F].I ^ value; \ + C_SETCOND_LOGICAL; +#endif +#ifndef OP_CMP + #define OP_CMP \ + u32 lhs = bus.reg[(opcode>>16)&15].I; \ + u32 rhs = value; \ + u32 res = lhs - rhs; \ + C_SETCOND_SUB; +#endif +#ifndef OP_CMN + #define OP_CMN \ + u32 lhs = bus.reg[(opcode>>16)&15].I; \ + u32 rhs = value; \ + u32 res = lhs + rhs; \ + C_SETCOND_ADD; +#endif +#ifndef OP_ORR + #define OP_ORR \ + u32 res = bus.reg[(opcode >> 16) & 0x0F].I | value; \ + bus.reg[dest].I = res; +#endif +#ifndef OP_ORRS + #define OP_ORRS OP_ORR C_CHECK_PC(C_SETCOND_LOGICAL) +#endif +#ifndef OP_MOV + #define OP_MOV \ + u32 res = value; \ + bus.reg[dest].I = res; +#endif +#ifndef OP_MOVS + #define OP_MOVS OP_MOV C_CHECK_PC(C_SETCOND_LOGICAL) +#endif +#ifndef OP_BIC + #define OP_BIC \ + u32 res = bus.reg[(opcode >> 16) & 0x0F].I & (~value); \ + bus.reg[dest].I = res; +#endif +#ifndef OP_BICS + #define OP_BICS OP_BIC C_CHECK_PC(C_SETCOND_LOGICAL) +#endif +#ifndef OP_MVN + #define OP_MVN \ + u32 res = ~value; \ + bus.reg[dest].I = res; +#endif +#ifndef OP_MVNS + #define OP_MVNS OP_MVN C_CHECK_PC(C_SETCOND_LOGICAL) +#endif + +#ifndef SETCOND_NONE + #define SETCOND_NONE /*nothing*/ +#endif +#ifndef SETCOND_MUL + #define SETCOND_MUL \ + N_FLAG = ((s32)bus.reg[dest].I < 0) ? true : false; \ + Z_FLAG = bus.reg[dest].I ? false : true; +#endif +#ifndef SETCOND_MULL + #define SETCOND_MULL \ + N_FLAG = (bus.reg[dest].I & 0x80000000) ? true : false;\ + Z_FLAG = bus.reg[dest].I || bus.reg[acc].I ? false : true; +#endif + +#ifndef ROR_IMM_MSR + #define ROR_IMM_MSR \ + u32 v = opcode & 0xff; \ + value = ((v << (32 - shift)) | (v >> shift)); +#endif +#ifndef ROR_OFFSET + #define ROR_OFFSET \ + offset = ((offset << (32 - shift)) | (offset >> shift)); +#endif +#ifndef RRX_OFFSET + #define RRX_OFFSET \ + offset = ((offset >> 1) | ((int)C_FLAG << 31)); +#endif + +// ALU ops (except multiply) ////////////////////////////////////////////// + +// ALU_INIT: init code (ALU_INIT_C or ALU_INIT_NC) +// GETVALUE: load value and shift/rotate (VALUE_XXX) +// OP: ALU operation (OP_XXX) +// MODECHANGE: MODECHANGE_NO or MODECHANGE_YES +// ISREGSHIFT: 1 for insns of the form ...,Rn LSL/etc Rs; 0 otherwise +// ALU_INIT, GETVALUE and OP are concatenated in order. +#define ALU_INSN(ALU_INIT, GETVALUE, OP, MODECHANGE, ISREGSHIFT) \ + ALU_INIT GETVALUE OP; \ + if ((opcode & 0x0000F000) != 0x0000F000) { \ + clockTicks = 1 + ISREGSHIFT \ + + codeTicksAccessSeq32(bus.armNextPC); \ + } else { \ + MODECHANGE; \ + if (armState) { \ + bus.reg[15].I &= 0xFFFFFFFC; \ + bus.armNextPC = bus.reg[15].I; \ + bus.reg[15].I += 4; \ + ARM_PREFETCH; \ + } else { \ + bus.reg[15].I &= 0xFFFFFFFE; \ + bus.armNextPC = bus.reg[15].I; \ + bus.reg[15].I += 2; \ + THUMB_PREFETCH; \ + } \ + clockTicks = 3 + ISREGSHIFT \ + + codeTicksAccess(bus.armNextPC, BITS_32) \ + + ((codeTicksAccessSeq32(bus.armNextPC)) << 1); \ + } + +#define MODECHANGE_NO /*nothing*/ +#define MODECHANGE_YES if(armMode != (bus.reg[17].I & 0x1f)) CPUSwitchMode(bus.reg[17].I & 0x1f, false, true); + +#define DEFINE_ALU_INSN_C(CODE1, CODE2, OP, MODECHANGE) \ + void arm##CODE1##0(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_LSL_IMM_C, OP_##OP, MODECHANGE_##MODECHANGE, 0); }\ + void arm##CODE1##1(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_LSL_REG_C, OP_##OP, MODECHANGE_##MODECHANGE, 1); }\ + void arm##CODE1##2(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_LSR_IMM_C, OP_##OP, MODECHANGE_##MODECHANGE, 0); }\ + void arm##CODE1##3(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_LSR_REG_C, OP_##OP, MODECHANGE_##MODECHANGE, 1); }\ + void arm##CODE1##4(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_ASR_IMM_C, OP_##OP, MODECHANGE_##MODECHANGE, 0); }\ + void arm##CODE1##5(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_ASR_REG_C, OP_##OP, MODECHANGE_##MODECHANGE, 1); }\ + void arm##CODE1##6(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_ROR_IMM_C, OP_##OP, MODECHANGE_##MODECHANGE, 0); }\ + void arm##CODE1##7(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_ROR_REG_C, OP_##OP, MODECHANGE_##MODECHANGE, 1); }\ + void arm##CODE2##0(u32 opcode) { ALU_INSN(ALU_INIT_C, VALUE_IMM_C, OP_##OP, MODECHANGE_##MODECHANGE, 0); } +#define DEFINE_ALU_INSN_NC(CODE1, CODE2, OP, MODECHANGE) \ + void arm##CODE1##0(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_LSL_IMM_NC, OP_##OP, MODECHANGE_##MODECHANGE, 0); }\ + void arm##CODE1##1(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_LSL_REG_NC, OP_##OP, MODECHANGE_##MODECHANGE, 1); }\ + void arm##CODE1##2(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_LSR_IMM_NC, OP_##OP, MODECHANGE_##MODECHANGE, 0); }\ + void arm##CODE1##3(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_LSR_REG_NC, OP_##OP, MODECHANGE_##MODECHANGE, 1); }\ + void arm##CODE1##4(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_ASR_IMM_NC, OP_##OP, MODECHANGE_##MODECHANGE, 0); }\ + void arm##CODE1##5(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_ASR_REG_NC, OP_##OP, MODECHANGE_##MODECHANGE, 1); }\ + void arm##CODE1##6(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_ROR_IMM_NC, OP_##OP, MODECHANGE_##MODECHANGE, 0); }\ + void arm##CODE1##7(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_ROR_REG_NC, OP_##OP, MODECHANGE_##MODECHANGE, 1); }\ + void arm##CODE2##0(u32 opcode) { ALU_INSN(ALU_INIT_NC, VALUE_IMM_NC, OP_##OP, MODECHANGE_##MODECHANGE, 0); } + +// AND +DEFINE_ALU_INSN_NC(00, 20, AND, NO) +// ANDS +DEFINE_ALU_INSN_C (01, 21, ANDS, YES) + +// EOR +DEFINE_ALU_INSN_NC(02, 22, EOR, NO) +// EORS +DEFINE_ALU_INSN_C (03, 23, EORS, YES) + +// SUB +DEFINE_ALU_INSN_NC(04, 24, SUB, NO) +// SUBS +DEFINE_ALU_INSN_NC(05, 25, SUBS, YES) + +// RSB +DEFINE_ALU_INSN_NC(06, 26, RSB, NO) +// RSBS +DEFINE_ALU_INSN_NC(07, 27, RSBS, YES) + +// ADD +DEFINE_ALU_INSN_NC(08, 28, ADD, NO) +// ADDS +DEFINE_ALU_INSN_NC(09, 29, ADDS, YES) + +// ADC +DEFINE_ALU_INSN_NC(0A, 2A, ADC, NO) +// ADCS +DEFINE_ALU_INSN_NC(0B, 2B, ADCS, YES) + +// SBC +DEFINE_ALU_INSN_NC(0C, 2C, SBC, NO) +// SBCS +DEFINE_ALU_INSN_NC(0D, 2D, SBCS, YES) + +// RSC +DEFINE_ALU_INSN_NC(0E, 2E, RSC, NO) +// RSCS +DEFINE_ALU_INSN_NC(0F, 2F, RSCS, YES) + +// TST +DEFINE_ALU_INSN_C (11, 31, TST, NO) + +// TEQ +DEFINE_ALU_INSN_C (13, 33, TEQ, NO) + +// CMP +DEFINE_ALU_INSN_NC(15, 35, CMP, NO) + +// CMN +DEFINE_ALU_INSN_NC(17, 37, CMN, NO) + +// ORR +DEFINE_ALU_INSN_NC(18, 38, ORR, NO) +// ORRS +DEFINE_ALU_INSN_C (19, 39, ORRS, YES) + +// MOV +DEFINE_ALU_INSN_NC(1A, 3A, MOV, NO) +// MOVS +DEFINE_ALU_INSN_C (1B, 3B, MOVS, YES) + +// BIC +DEFINE_ALU_INSN_NC(1C, 3C, BIC, NO) +// BICS +DEFINE_ALU_INSN_C (1D, 3D, BICS, YES) + +// MVN +DEFINE_ALU_INSN_NC(1E, 3E, MVN, NO) +// MVNS +DEFINE_ALU_INSN_C (1F, 3F, MVNS, YES) + +// Multiply instructions ////////////////////////////////////////////////// + +// OP: OP_MUL, OP_MLA etc. +// SETCOND: SETCOND_NONE, SETCOND_MUL, or SETCOND_MULL +// CYCLES: base cycle count (1, 2, or 3) +#define MUL_INSN(OP, SETCOND, CYCLES) \ + int mult = (opcode & 0x0F); \ + u32 rs = bus.reg[(opcode >> 8) & 0x0F].I; \ + int acc = (opcode >> 12) & 0x0F; /* or destLo */ \ + int dest = (opcode >> 16) & 0x0F; /* or destHi */ \ + OP; \ + SETCOND; \ + if ((s32)rs < 0) \ + rs = ~rs; \ + if ((rs & 0xFFFF0000) == 0) \ + clockTicks += 1; \ + else if ((rs & 0xFF000000) == 0) \ + clockTicks += 2; \ + else \ + clockTicks += 3; \ + if (bus.busPrefetchCount == 0) \ + bus.busPrefetchCount = ((bus.busPrefetchCount+1)<> 32); +#define OP_MLAL(SIGN) \ + SIGN##64 res = ((SIGN##64)bus.reg[dest].I<<32 | bus.reg[acc].I)\ + + ((SIGN##64)(SIGN##32)bus.reg[mult].I \ + * (SIGN##64)(SIGN##32)rs); \ + bus.reg[acc].I = (u32)res; \ + bus.reg[dest].I = (u32)(res >> 32); +#define OP_UMULL OP_MULL(u) +#define OP_UMLAL OP_MLAL(u) +#define OP_SMULL OP_MULL(s) +#define OP_SMLAL OP_MLAL(s) + +// MUL Rd, Rm, Rs + void arm009(u32 opcode) { MUL_INSN(OP_MUL, SETCOND_NONE, 1); } +// MULS Rd, Rm, Rs + void arm019(u32 opcode) { MUL_INSN(OP_MUL, SETCOND_MUL, 1); } + +// MLA Rd, Rm, Rs, Rn + void arm029(u32 opcode) { MUL_INSN(OP_MLA, SETCOND_NONE, 2); } +// MLAS Rd, Rm, Rs, Rn + void arm039(u32 opcode) { MUL_INSN(OP_MLA, SETCOND_MUL, 2); } + +// UMULL RdLo, RdHi, Rn, Rs + void arm089(u32 opcode) { MUL_INSN(OP_UMULL, SETCOND_NONE, 2); } +// UMULLS RdLo, RdHi, Rn, Rs + void arm099(u32 opcode) { MUL_INSN(OP_UMULL, SETCOND_MULL, 2); } + +// UMLAL RdLo, RdHi, Rn, Rs + void arm0A9(u32 opcode) { MUL_INSN(OP_UMLAL, SETCOND_NONE, 3); } +// UMLALS RdLo, RdHi, Rn, Rs + void arm0B9(u32 opcode) { MUL_INSN(OP_UMLAL, SETCOND_MULL, 3); } + +// SMULL RdLo, RdHi, Rm, Rs + void arm0C9(u32 opcode) { MUL_INSN(OP_SMULL, SETCOND_NONE, 2); } +// SMULLS RdLo, RdHi, Rm, Rs + void arm0D9(u32 opcode) { MUL_INSN(OP_SMULL, SETCOND_MULL, 2); } + +// SMLAL RdLo, RdHi, Rm, Rs + void arm0E9(u32 opcode) { MUL_INSN(OP_SMLAL, SETCOND_NONE, 3); } +// SMLALS RdLo, RdHi, Rm, Rs + void arm0F9(u32 opcode) { MUL_INSN(OP_SMLAL, SETCOND_MULL, 3); } + +// Misc instructions ////////////////////////////////////////////////////// + +// SWP Rd, Rm, [Rn] + void arm109(u32 opcode) +{ + u32 address = bus.reg[(opcode >> 16) & 15].I; + u32 temp = CPUReadMemory(address); + CPUWriteMemory(address, bus.reg[opcode&15].I); + bus.reg[(opcode >> 12) & 15].I = temp; + int dataticks_value = DATATICKS_ACCESS_32BIT(address); + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); + clockTicks = 4 + (dataticks_value << 1) + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// SWPB Rd, Rm, [Rn] + void arm149(u32 opcode) +{ + u32 address = bus.reg[(opcode >> 16) & 15].I; + u32 temp = CPUReadByte(address); + CPUWriteByte(address, bus.reg[opcode&15].B.B0); + bus.reg[(opcode>>12)&15].I = temp; + u32 dataticks_value = DATATICKS_ACCESS_32BIT(address); + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); + clockTicks = 4 + (dataticks_value << 1) + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// MRS Rd, CPSR + void arm100(u32 opcode) +{ + if ((opcode & 0x0FFF0FFF) == 0x010F0000) + { + CPU_UPDATE_CPSR(); + bus.reg[(opcode >> 12) & 0x0F].I = bus.reg[16].I; + } + else + armUnknownInsn(opcode); +} + +// MRS Rd, SPSR + void arm140(u32 opcode) +{ + if ((opcode & 0x0FFF0FFF) == 0x014F0000) + bus.reg[(opcode >> 12) & 0x0F].I = bus.reg[17].I; + else + armUnknownInsn(opcode); +} + +// MSR CPSR_fields, Rm + void arm120(u32 opcode) +{ + if ((opcode & 0x0FF0FFF0) == 0x0120F000) + { + CPU_UPDATE_CPSR(); + u32 value = bus.reg[opcode & 15].I; + u32 newValue = bus.reg[16].I; + if (armMode > 0x10) { + if (opcode & 0x00010000) + newValue = (newValue & 0xFFFFFF00) | (value & 0x000000FF); + if (opcode & 0x00020000) + newValue = (newValue & 0xFFFF00FF) | (value & 0x0000FF00); + if (opcode & 0x00040000) + newValue = (newValue & 0xFF00FFFF) | (value & 0x00FF0000); + } + if (opcode & 0x00080000) + newValue = (newValue & 0x00FFFFFF) | (value & 0xFF000000); + newValue |= 0x10; + if(armMode != (newValue & 0x1F)) + CPUSwitchMode(newValue & 0x1F, false, true); + bus.reg[16].I = newValue; + CPUUpdateFlags(1); + if (!armState) { // this should not be allowed, but it seems to work + THUMB_PREFETCH; + bus.reg[15].I = bus.armNextPC + 2; + } + } + else + armUnknownInsn(opcode); +} + +// MSR SPSR_fields, Rm + void arm160(u32 opcode) +{ + if ((opcode & 0x0FF0FFF0) == 0x0160F000) + { + u32 value = bus.reg[opcode & 15].I; + if (armMode > 0x10 && armMode < 0x1F) + { + if (opcode & 0x00010000) + bus.reg[17].I = (bus.reg[17].I & 0xFFFFFF00) | (value & 0x000000FF); + if (opcode & 0x00020000) + bus.reg[17].I = (bus.reg[17].I & 0xFFFF00FF) | (value & 0x0000FF00); + if (opcode & 0x00040000) + bus.reg[17].I = (bus.reg[17].I & 0xFF00FFFF) | (value & 0x00FF0000); + if (opcode & 0x00080000) + bus.reg[17].I = (bus.reg[17].I & 0x00FFFFFF) | (value & 0xFF000000); + } + } + else + armUnknownInsn(opcode); +} + +// MSR CPSR_fields, # + void arm320(u32 opcode) +{ + if ((opcode & 0x0FF0F000) == 0x0320F000) + { + CPU_UPDATE_CPSR(); + u32 value = opcode & 0xFF; + int shift = (opcode & 0xF00) >> 7; + if (shift) { + ROR_IMM_MSR; + } + u32 newValue = bus.reg[16].I; + if (armMode > 0x10) { + if (opcode & 0x00010000) + newValue = (newValue & 0xFFFFFF00) | (value & 0x000000FF); + if (opcode & 0x00020000) + newValue = (newValue & 0xFFFF00FF) | (value & 0x0000FF00); + if (opcode & 0x00040000) + newValue = (newValue & 0xFF00FFFF) | (value & 0x00FF0000); + } + if (opcode & 0x00080000) + newValue = (newValue & 0x00FFFFFF) | (value & 0xFF000000); + + newValue |= 0x10; + + if(armMode != (newValue & 0x1F)) + CPUSwitchMode(newValue & 0x1F, false, true); + bus.reg[16].I = newValue; + CPUUpdateFlags(1); + if (!armState) { // this should not be allowed, but it seems to work + THUMB_PREFETCH; + bus.reg[15].I = bus.armNextPC + 2; + } + } + else + armUnknownInsn(opcode); +} + +// MSR SPSR_fields, # + void arm360(u32 opcode) +{ + if ((opcode & 0x0FF0F000) == 0x0360F000) { + if (armMode > 0x10 && armMode < 0x1F) { + u32 value = opcode & 0xFF; + int shift = (opcode & 0xF00) >> 7; + if (shift) { + ROR_IMM_MSR; + } + if (opcode & 0x00010000) + bus.reg[17].I = (bus.reg[17].I & 0xFFFFFF00) | (value & 0x000000FF); + if (opcode & 0x00020000) + bus.reg[17].I = (bus.reg[17].I & 0xFFFF00FF) | (value & 0x0000FF00); + if (opcode & 0x00040000) + bus.reg[17].I = (bus.reg[17].I & 0xFF00FFFF) | (value & 0x00FF0000); + if (opcode & 0x00080000) + bus.reg[17].I = (bus.reg[17].I & 0x00FFFFFF) | (value & 0xFF000000); + } + } + else + armUnknownInsn(opcode); +} + +// BX Rm + void arm121(u32 opcode) +{ + if ((opcode & 0x0FFFFFF0) == 0x012FFF10) { + int base = opcode & 0x0F; + bus.busPrefetchCount = 0; + armState = bus.reg[base].I & 1 ? false : true; + if (armState) { + bus.reg[15].I = bus.reg[base].I & 0xFFFFFFFC; + bus.armNextPC = bus.reg[15].I; + bus.reg[15].I += 4; + ARM_PREFETCH; + clockTicks = 3 + (codeTicksAccessSeq32(bus.armNextPC)<<1) + + codeTicksAccess(bus.armNextPC, BITS_32); + } else { + bus.reg[15].I = bus.reg[base].I & 0xFFFFFFFE; + bus.armNextPC = bus.reg[15].I; + bus.reg[15].I += 2; + THUMB_PREFETCH; + clockTicks = 3 + (codeTicksAccessSeq16(bus.armNextPC)<<1) + + codeTicksAccess(bus.armNextPC, BITS_16); + } + } + else + armUnknownInsn(opcode); +} + +// Load/store ///////////////////////////////////////////////////////////// + +#define OFFSET_IMM \ + int offset = opcode & 0xFFF; +#define OFFSET_IMM8 \ + int offset = ((opcode & 0x0F) | ((opcode>>4) & 0xF0)); +#define OFFSET_REG \ + int offset = bus.reg[opcode & 15].I; +#define OFFSET_LSL \ + int offset = bus.reg[opcode & 15].I << ((opcode>>7) & 31); +#define OFFSET_LSR \ + int shift = (opcode >> 7) & 31; \ + int offset = shift ? bus.reg[opcode & 15].I >> shift : 0; +#define OFFSET_ASR \ + int shift = (opcode >> 7) & 31; \ + int offset; \ + if (shift) \ + offset = (int)((s32)bus.reg[opcode & 15].I >> shift);\ + else if (bus.reg[opcode & 15].I & 0x80000000) \ + offset = 0xFFFFFFFF; \ + else \ + offset = 0; +#define OFFSET_ROR \ + int shift = (opcode >> 7) & 31; \ + u32 offset = bus.reg[opcode & 15].I; \ + if (shift) { \ + ROR_OFFSET; \ + } else { \ + RRX_OFFSET; \ + } + +#define ADDRESS_POST (bus.reg[base].I) +#define ADDRESS_PREDEC (bus.reg[base].I - offset) +#define ADDRESS_PREINC (bus.reg[base].I + offset) + +#define OP_STR CPUWriteMemory(address, bus.reg[dest].I) +#define OP_STRH CPUWriteHalfWord(address, bus.reg[dest].W.W0) +#define OP_STRB CPUWriteByte(address, bus.reg[dest].B.B0) +#define OP_LDR bus.reg[dest].I = CPUReadMemory(address) +#define OP_LDRH bus.reg[dest].I = CPUReadHalfWord(address) +#define OP_LDRB bus.reg[dest].I = CPUReadByte(address) +#define OP_LDRSH bus.reg[dest].I = (s16)CPUReadHalfWordSigned(address) +#define OP_LDRSB bus.reg[dest].I = (s8)CPUReadByte(address) + +#define WRITEBACK_NONE /*nothing*/ +#define WRITEBACK_PRE bus.reg[base].I = address +#define WRITEBACK_POSTDEC bus.reg[base].I = address - offset +#define WRITEBACK_POSTINC bus.reg[base].I = address + offset + +#define LDRSTR_INIT(CALC_OFFSET, CALC_ADDRESS) \ + if (bus.busPrefetchCount == 0) \ + bus.busPrefetch = bus.busPrefetchEnable; \ + int dest = (opcode >> 12) & 15; \ + int base = (opcode >> 16) & 15; \ + CALC_OFFSET; \ + u32 address = CALC_ADDRESS; + +#define STR(CALC_OFFSET, CALC_ADDRESS, STORE_DATA, WRITEBACK1, WRITEBACK2, SIZE) \ + LDRSTR_INIT(CALC_OFFSET, CALC_ADDRESS); \ + WRITEBACK1; \ + STORE_DATA; \ + WRITEBACK2; \ + int dataticks_val; \ + if(SIZE == 32) \ + dataticks_val = DATATICKS_ACCESS_32BIT(address); \ + else \ + dataticks_val = DATATICKS_ACCESS_16BIT(address); \ + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_val); \ + clockTicks = 2 + dataticks_val + codeTicksAccess(bus.armNextPC, BITS_32); + +#define LDR(CALC_OFFSET, CALC_ADDRESS, LOAD_DATA, WRITEBACK, SIZE) \ + LDRSTR_INIT(CALC_OFFSET, CALC_ADDRESS); \ + LOAD_DATA; \ + if (dest != base) \ + { \ + WRITEBACK; \ + } \ + clockTicks = 0; \ + int dataticks_value; \ + if (dest == 15) { \ + bus.reg[15].I &= 0xFFFFFFFC; \ + bus.armNextPC = bus.reg[15].I; \ + bus.reg[15].I += 4; \ + ARM_PREFETCH; \ + dataticks_value = DATATICKS_ACCESS_32BIT_SEQ(address); \ + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); \ + clockTicks += 2 + (dataticks_value << 1);\ + } \ + if(SIZE == 32) \ + dataticks_value = DATATICKS_ACCESS_32BIT(address); \ + else \ + dataticks_value = DATATICKS_ACCESS_16BIT(address); \ + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); \ + clockTicks += 3 + dataticks_value + codeTicksAccess(bus.armNextPC, BITS_32); +#define STR_POSTDEC(CALC_OFFSET, STORE_DATA, SIZE) \ + STR(CALC_OFFSET, ADDRESS_POST, STORE_DATA, WRITEBACK_NONE, WRITEBACK_POSTDEC, SIZE) +#define STR_POSTINC(CALC_OFFSET, STORE_DATA, SIZE) \ + STR(CALC_OFFSET, ADDRESS_POST, STORE_DATA, WRITEBACK_NONE, WRITEBACK_POSTINC, SIZE) +#define STR_PREDEC(CALC_OFFSET, STORE_DATA, SIZE) \ + STR(CALC_OFFSET, ADDRESS_PREDEC, STORE_DATA, WRITEBACK_NONE, WRITEBACK_NONE, SIZE) +#define STR_PREDEC_WB(CALC_OFFSET, STORE_DATA, SIZE) \ + STR(CALC_OFFSET, ADDRESS_PREDEC, STORE_DATA, WRITEBACK_PRE, WRITEBACK_NONE, SIZE) +#define STR_PREINC(CALC_OFFSET, STORE_DATA, SIZE) \ + STR(CALC_OFFSET, ADDRESS_PREINC, STORE_DATA, WRITEBACK_NONE, WRITEBACK_NONE, SIZE) +#define STR_PREINC_WB(CALC_OFFSET, STORE_DATA, SIZE) \ + STR(CALC_OFFSET, ADDRESS_PREINC, STORE_DATA, WRITEBACK_PRE, WRITEBACK_NONE, SIZE) +#define LDR_POSTDEC(CALC_OFFSET, LOAD_DATA, SIZE) \ + LDR(CALC_OFFSET, ADDRESS_POST, LOAD_DATA, WRITEBACK_POSTDEC, SIZE) +#define LDR_POSTINC(CALC_OFFSET, LOAD_DATA, SIZE) \ + LDR(CALC_OFFSET, ADDRESS_POST, LOAD_DATA, WRITEBACK_POSTINC, SIZE) +#define LDR_PREDEC(CALC_OFFSET, LOAD_DATA, SIZE) \ + LDR(CALC_OFFSET, ADDRESS_PREDEC, LOAD_DATA, WRITEBACK_NONE, SIZE) +#define LDR_PREDEC_WB(CALC_OFFSET, LOAD_DATA, SIZE) \ + LDR(CALC_OFFSET, ADDRESS_PREDEC, LOAD_DATA, WRITEBACK_PRE, SIZE) +#define LDR_PREINC(CALC_OFFSET, LOAD_DATA, SIZE) \ + LDR(CALC_OFFSET, ADDRESS_PREINC, LOAD_DATA, WRITEBACK_NONE, SIZE) +#define LDR_PREINC_WB(CALC_OFFSET, LOAD_DATA, SIZE) \ + LDR(CALC_OFFSET, ADDRESS_PREINC, LOAD_DATA, WRITEBACK_PRE, SIZE) + +// STRH Rd, [Rn], -Rm + void arm00B(u32 opcode) { STR_POSTDEC(OFFSET_REG, OP_STRH, 16); } +// STRH Rd, [Rn], #-offset + void arm04B(u32 opcode) { STR_POSTDEC(OFFSET_IMM8, OP_STRH, 16); } +// STRH Rd, [Rn], Rm + void arm08B(u32 opcode) { STR_POSTINC(OFFSET_REG, OP_STRH, 16); } +// STRH Rd, [Rn], #offset + void arm0CB(u32 opcode) { STR_POSTINC(OFFSET_IMM8, OP_STRH, 16); } +// STRH Rd, [Rn, -Rm] + void arm10B(u32 opcode) { STR_PREDEC(OFFSET_REG, OP_STRH, 16); } +// STRH Rd, [Rn, -Rm]! + void arm12B(u32 opcode) { STR_PREDEC_WB(OFFSET_REG, OP_STRH, 16); } +// STRH Rd, [Rn, -#offset] + void arm14B(u32 opcode) { STR_PREDEC(OFFSET_IMM8, OP_STRH, 16); } +// STRH Rd, [Rn, -#offset]! + void arm16B(u32 opcode) { STR_PREDEC_WB(OFFSET_IMM8, OP_STRH, 16); } +// STRH Rd, [Rn, Rm] + void arm18B(u32 opcode) { STR_PREINC(OFFSET_REG, OP_STRH, 16); } +// STRH Rd, [Rn, Rm]! + void arm1AB(u32 opcode) { STR_PREINC_WB(OFFSET_REG, OP_STRH, 16); } +// STRH Rd, [Rn, #offset] + void arm1CB(u32 opcode) { STR_PREINC(OFFSET_IMM8, OP_STRH, 16); } +// STRH Rd, [Rn, #offset]! + void arm1EB(u32 opcode) { STR_PREINC_WB(OFFSET_IMM8, OP_STRH, 16); } + +// LDRH Rd, [Rn], -Rm + void arm01B(u32 opcode) { LDR_POSTDEC(OFFSET_REG, OP_LDRH, 16); } +// LDRH Rd, [Rn], #-offset + void arm05B(u32 opcode) { LDR_POSTDEC(OFFSET_IMM8, OP_LDRH, 16); } +// LDRH Rd, [Rn], Rm + void arm09B(u32 opcode) { LDR_POSTINC(OFFSET_REG, OP_LDRH, 16); } +// LDRH Rd, [Rn], #offset + void arm0DB(u32 opcode) { LDR_POSTINC(OFFSET_IMM8, OP_LDRH, 16); } +// LDRH Rd, [Rn, -Rm] + void arm11B(u32 opcode) { LDR_PREDEC(OFFSET_REG, OP_LDRH, 16); } +// LDRH Rd, [Rn, -Rm]! + void arm13B(u32 opcode) { LDR_PREDEC_WB(OFFSET_REG, OP_LDRH, 16); } +// LDRH Rd, [Rn, -#offset] + void arm15B(u32 opcode) { LDR_PREDEC(OFFSET_IMM8, OP_LDRH, 16); } +// LDRH Rd, [Rn, -#offset]! + void arm17B(u32 opcode) { LDR_PREDEC_WB(OFFSET_IMM8, OP_LDRH, 16); } +// LDRH Rd, [Rn, Rm] + void arm19B(u32 opcode) { LDR_PREINC(OFFSET_REG, OP_LDRH, 16); } +// LDRH Rd, [Rn, Rm]! + void arm1BB(u32 opcode) { LDR_PREINC_WB(OFFSET_REG, OP_LDRH, 16); } +// LDRH Rd, [Rn, #offset] + void arm1DB(u32 opcode) { LDR_PREINC(OFFSET_IMM8, OP_LDRH, 16); } +// LDRH Rd, [Rn, #offset]! + void arm1FB(u32 opcode) { LDR_PREINC_WB(OFFSET_IMM8, OP_LDRH, 16); } + +// LDRSB Rd, [Rn], -Rm + void arm01D(u32 opcode) { LDR_POSTDEC(OFFSET_REG, OP_LDRSB, 16); } +// LDRSB Rd, [Rn], #-offset + void arm05D(u32 opcode) { LDR_POSTDEC(OFFSET_IMM8, OP_LDRSB, 16); } +// LDRSB Rd, [Rn], Rm + void arm09D(u32 opcode) { LDR_POSTINC(OFFSET_REG, OP_LDRSB, 16); } +// LDRSB Rd, [Rn], #offset + void arm0DD(u32 opcode) { LDR_POSTINC(OFFSET_IMM8, OP_LDRSB, 16); } +// LDRSB Rd, [Rn, -Rm] + void arm11D(u32 opcode) { LDR_PREDEC(OFFSET_REG, OP_LDRSB, 16); } +// LDRSB Rd, [Rn, -Rm]! + void arm13D(u32 opcode) { LDR_PREDEC_WB(OFFSET_REG, OP_LDRSB, 16); } +// LDRSB Rd, [Rn, -#offset] + void arm15D(u32 opcode) { LDR_PREDEC(OFFSET_IMM8, OP_LDRSB, 16); } +// LDRSB Rd, [Rn, -#offset]! + void arm17D(u32 opcode) { LDR_PREDEC_WB(OFFSET_IMM8, OP_LDRSB, 16); } +// LDRSB Rd, [Rn, Rm] + void arm19D(u32 opcode) { LDR_PREINC(OFFSET_REG, OP_LDRSB, 16); } +// LDRSB Rd, [Rn, Rm]! + void arm1BD(u32 opcode) { LDR_PREINC_WB(OFFSET_REG, OP_LDRSB, 16); } +// LDRSB Rd, [Rn, #offset] + void arm1DD(u32 opcode) { LDR_PREINC(OFFSET_IMM8, OP_LDRSB, 16); } +// LDRSB Rd, [Rn, #offset]! + void arm1FD(u32 opcode) { LDR_PREINC_WB(OFFSET_IMM8, OP_LDRSB, 16); } + +// LDRSH Rd, [Rn], -Rm + void arm01F(u32 opcode) { LDR_POSTDEC(OFFSET_REG, OP_LDRSH, 16); } +// LDRSH Rd, [Rn], #-offset + void arm05F(u32 opcode) { LDR_POSTDEC(OFFSET_IMM8, OP_LDRSH, 16); } +// LDRSH Rd, [Rn], Rm + void arm09F(u32 opcode) { LDR_POSTINC(OFFSET_REG, OP_LDRSH, 16); } +// LDRSH Rd, [Rn], #offset + void arm0DF(u32 opcode) { LDR_POSTINC(OFFSET_IMM8, OP_LDRSH, 16); } +// LDRSH Rd, [Rn, -Rm] + void arm11F(u32 opcode) { LDR_PREDEC(OFFSET_REG, OP_LDRSH, 16); } +// LDRSH Rd, [Rn, -Rm]! + void arm13F(u32 opcode) { LDR_PREDEC_WB(OFFSET_REG, OP_LDRSH, 16); } +// LDRSH Rd, [Rn, -#offset] + void arm15F(u32 opcode) { LDR_PREDEC(OFFSET_IMM8, OP_LDRSH, 16); } +// LDRSH Rd, [Rn, -#offset]! + void arm17F(u32 opcode) { LDR_PREDEC_WB(OFFSET_IMM8, OP_LDRSH, 16); } +// LDRSH Rd, [Rn, Rm] + void arm19F(u32 opcode) { LDR_PREINC(OFFSET_REG, OP_LDRSH, 16); } +// LDRSH Rd, [Rn, Rm]! + void arm1BF(u32 opcode) { LDR_PREINC_WB(OFFSET_REG, OP_LDRSH, 16); } +// LDRSH Rd, [Rn, #offset] + void arm1DF(u32 opcode) { LDR_PREINC(OFFSET_IMM8, OP_LDRSH, 16); } +// LDRSH Rd, [Rn, #offset]! + void arm1FF(u32 opcode) { LDR_PREINC_WB(OFFSET_IMM8, OP_LDRSH, 16); } + +// STR[T] Rd, [Rn], -# +// Note: STR and STRT do the same thing on the GBA (likewise for LDR/LDRT etc) + void arm400(u32 opcode) { STR_POSTDEC(OFFSET_IMM, OP_STR, 32); } +// LDR[T] Rd, [Rn], -# + void arm410(u32 opcode) { LDR_POSTDEC(OFFSET_IMM, OP_LDR, 32); } +// STRB[T] Rd, [Rn], -# + void arm440(u32 opcode) { STR_POSTDEC(OFFSET_IMM, OP_STRB, 16); } +// LDRB[T] Rd, [Rn], -# + void arm450(u32 opcode) { LDR_POSTDEC(OFFSET_IMM, OP_LDRB, 16); } +// STR[T] Rd, [Rn], # + void arm480(u32 opcode) { STR_POSTINC(OFFSET_IMM, OP_STR, 32); } +// LDR Rd, [Rn], # + void arm490(u32 opcode) { LDR_POSTINC(OFFSET_IMM, OP_LDR, 32); } +// STRB[T] Rd, [Rn], # + void arm4C0(u32 opcode) { STR_POSTINC(OFFSET_IMM, OP_STRB, 16); } +// LDRB[T] Rd, [Rn], # + void arm4D0(u32 opcode) { LDR_POSTINC(OFFSET_IMM, OP_LDRB, 16); } +// STR Rd, [Rn, -#] + void arm500(u32 opcode) { STR_PREDEC(OFFSET_IMM, OP_STR, 32); } +// LDR Rd, [Rn, -#] + void arm510(u32 opcode) { LDR_PREDEC(OFFSET_IMM, OP_LDR, 32); } +// STR Rd, [Rn, -#]! + void arm520(u32 opcode) { STR_PREDEC_WB(OFFSET_IMM, OP_STR, 32); } +// LDR Rd, [Rn, -#]! + void arm530(u32 opcode) { LDR_PREDEC_WB(OFFSET_IMM, OP_LDR, 32); } +// STRB Rd, [Rn, -#] + void arm540(u32 opcode) { STR_PREDEC(OFFSET_IMM, OP_STRB, 16); } +// LDRB Rd, [Rn, -#] + void arm550(u32 opcode) { LDR_PREDEC(OFFSET_IMM, OP_LDRB, 16); } +// STRB Rd, [Rn, -#]! + void arm560(u32 opcode) { STR_PREDEC_WB(OFFSET_IMM, OP_STRB, 16); } +// LDRB Rd, [Rn, -#]! + void arm570(u32 opcode) { LDR_PREDEC_WB(OFFSET_IMM, OP_LDRB, 16); } +// STR Rd, [Rn, #] + void arm580(u32 opcode) { STR_PREINC(OFFSET_IMM, OP_STR, 32); } +// LDR Rd, [Rn, #] + void arm590(u32 opcode) { LDR_PREINC(OFFSET_IMM, OP_LDR, 32); } +// STR Rd, [Rn, #]! + void arm5A0(u32 opcode) { STR_PREINC_WB(OFFSET_IMM, OP_STR, 32); } +// LDR Rd, [Rn, #]! + void arm5B0(u32 opcode) { LDR_PREINC_WB(OFFSET_IMM, OP_LDR, 32); } +// STRB Rd, [Rn, #] + void arm5C0(u32 opcode) { STR_PREINC(OFFSET_IMM, OP_STRB, 16); } +// LDRB Rd, [Rn, #] + void arm5D0(u32 opcode) { LDR_PREINC(OFFSET_IMM, OP_LDRB, 16); } +// STRB Rd, [Rn, #]! + void arm5E0(u32 opcode) { STR_PREINC_WB(OFFSET_IMM, OP_STRB, 16); } +// LDRB Rd, [Rn, #]! + void arm5F0(u32 opcode) { LDR_PREINC_WB(OFFSET_IMM, OP_LDRB, 16); } + +// STR[T] Rd, [Rn], -Rm, LSL # + void arm600(u32 opcode) { STR_POSTDEC(OFFSET_LSL, OP_STR, 32); } +// STR[T] Rd, [Rn], -Rm, LSR # + void arm602(u32 opcode) { STR_POSTDEC(OFFSET_LSR, OP_STR, 32); } +// STR[T] Rd, [Rn], -Rm, ASR # + void arm604(u32 opcode) { STR_POSTDEC(OFFSET_ASR, OP_STR, 32); } +// STR[T] Rd, [Rn], -Rm, ROR # + void arm606(u32 opcode) { STR_POSTDEC(OFFSET_ROR, OP_STR, 32); } +// LDR[T] Rd, [Rn], -Rm, LSL # + void arm610(u32 opcode) { LDR_POSTDEC(OFFSET_LSL, OP_LDR, 32); } +// LDR[T] Rd, [Rn], -Rm, LSR # + void arm612(u32 opcode) { LDR_POSTDEC(OFFSET_LSR, OP_LDR, 32); } +// LDR[T] Rd, [Rn], -Rm, ASR # + void arm614(u32 opcode) { LDR_POSTDEC(OFFSET_ASR, OP_LDR, 32); } +// LDR[T] Rd, [Rn], -Rm, ROR # + void arm616(u32 opcode) { LDR_POSTDEC(OFFSET_ROR, OP_LDR, 32); } +// STRB[T] Rd, [Rn], -Rm, LSL # + void arm640(u32 opcode) { STR_POSTDEC(OFFSET_LSL, OP_STRB, 16); } +// STRB[T] Rd, [Rn], -Rm, LSR # + void arm642(u32 opcode) { STR_POSTDEC(OFFSET_LSR, OP_STRB, 16); } +// STRB[T] Rd, [Rn], -Rm, ASR # + void arm644(u32 opcode) { STR_POSTDEC(OFFSET_ASR, OP_STRB, 16); } +// STRB[T] Rd, [Rn], -Rm, ROR # + void arm646(u32 opcode) { STR_POSTDEC(OFFSET_ROR, OP_STRB, 16); } +// LDRB[T] Rd, [Rn], -Rm, LSL # + void arm650(u32 opcode) { LDR_POSTDEC(OFFSET_LSL, OP_LDRB, 16); } +// LDRB[T] Rd, [Rn], -Rm, LSR # + void arm652(u32 opcode) { LDR_POSTDEC(OFFSET_LSR, OP_LDRB, 16); } +// LDRB[T] Rd, [Rn], -Rm, ASR # + void arm654(u32 opcode) { LDR_POSTDEC(OFFSET_ASR, OP_LDRB, 16); } +// LDRB Rd, [Rn], -Rm, ROR # + void arm656(u32 opcode) { LDR_POSTDEC(OFFSET_ROR, OP_LDRB, 16); } +// STR[T] Rd, [Rn], Rm, LSL # + void arm680(u32 opcode) { STR_POSTINC(OFFSET_LSL, OP_STR, 32); } +// STR[T] Rd, [Rn], Rm, LSR # + void arm682(u32 opcode) { STR_POSTINC(OFFSET_LSR, OP_STR, 32); } +// STR[T] Rd, [Rn], Rm, ASR # + void arm684(u32 opcode) { STR_POSTINC(OFFSET_ASR, OP_STR, 32); } +// STR[T] Rd, [Rn], Rm, ROR # + void arm686(u32 opcode) { STR_POSTINC(OFFSET_ROR, OP_STR, 32); } +// LDR[T] Rd, [Rn], Rm, LSL # + void arm690(u32 opcode) { LDR_POSTINC(OFFSET_LSL, OP_LDR, 32); } +// LDR[T] Rd, [Rn], Rm, LSR # + void arm692(u32 opcode) { LDR_POSTINC(OFFSET_LSR, OP_LDR, 32); } +// LDR[T] Rd, [Rn], Rm, ASR # + void arm694(u32 opcode) { LDR_POSTINC(OFFSET_ASR, OP_LDR, 32); } +// LDR[T] Rd, [Rn], Rm, ROR # + void arm696(u32 opcode) { LDR_POSTINC(OFFSET_ROR, OP_LDR, 32); } +// STRB[T] Rd, [Rn], Rm, LSL # + void arm6C0(u32 opcode) { STR_POSTINC(OFFSET_LSL, OP_STRB, 16); } +// STRB[T] Rd, [Rn], Rm, LSR # + void arm6C2(u32 opcode) { STR_POSTINC(OFFSET_LSR, OP_STRB, 16); } +// STRB[T] Rd, [Rn], Rm, ASR # + void arm6C4(u32 opcode) { STR_POSTINC(OFFSET_ASR, OP_STRB, 16); } +// STRB[T] Rd, [Rn], Rm, ROR # + void arm6C6(u32 opcode) { STR_POSTINC(OFFSET_ROR, OP_STRB, 16); } +// LDRB[T] Rd, [Rn], Rm, LSL # + void arm6D0(u32 opcode) { LDR_POSTINC(OFFSET_LSL, OP_LDRB, 16); } +// LDRB[T] Rd, [Rn], Rm, LSR # + void arm6D2(u32 opcode) { LDR_POSTINC(OFFSET_LSR, OP_LDRB, 16); } +// LDRB[T] Rd, [Rn], Rm, ASR # + void arm6D4(u32 opcode) { LDR_POSTINC(OFFSET_ASR, OP_LDRB, 16); } +// LDRB[T] Rd, [Rn], Rm, ROR # + void arm6D6(u32 opcode) { LDR_POSTINC(OFFSET_ROR, OP_LDRB, 16); } +// STR Rd, [Rn, -Rm, LSL #] + void arm700(u32 opcode) { STR_PREDEC(OFFSET_LSL, OP_STR, 32); } +// STR Rd, [Rn, -Rm, LSR #] + void arm702(u32 opcode) { STR_PREDEC(OFFSET_LSR, OP_STR, 32); } +// STR Rd, [Rn, -Rm, ASR #] + void arm704(u32 opcode) { STR_PREDEC(OFFSET_ASR, OP_STR, 32); } +// STR Rd, [Rn, -Rm, ROR #] + void arm706(u32 opcode) { STR_PREDEC(OFFSET_ROR, OP_STR, 32); } +// LDR Rd, [Rn, -Rm, LSL #] + void arm710(u32 opcode) { LDR_PREDEC(OFFSET_LSL, OP_LDR, 32); } +// LDR Rd, [Rn, -Rm, LSR #] + void arm712(u32 opcode) { LDR_PREDEC(OFFSET_LSR, OP_LDR, 32); } +// LDR Rd, [Rn, -Rm, ASR #] + void arm714(u32 opcode) { LDR_PREDEC(OFFSET_ASR, OP_LDR, 32); } +// LDR Rd, [Rn, -Rm, ROR #] + void arm716(u32 opcode) { LDR_PREDEC(OFFSET_ROR, OP_LDR, 32); } +// STR Rd, [Rn, -Rm, LSL #]! + void arm720(u32 opcode) { STR_PREDEC_WB(OFFSET_LSL, OP_STR, 32); } +// STR Rd, [Rn, -Rm, LSR #]! + void arm722(u32 opcode) { STR_PREDEC_WB(OFFSET_LSR, OP_STR, 32); } +// STR Rd, [Rn, -Rm, ASR #]! + void arm724(u32 opcode) { STR_PREDEC_WB(OFFSET_ASR, OP_STR, 32); } +// STR Rd, [Rn, -Rm, ROR #]! + void arm726(u32 opcode) { STR_PREDEC_WB(OFFSET_ROR, OP_STR, 32); } +// LDR Rd, [Rn, -Rm, LSL #]! + void arm730(u32 opcode) { LDR_PREDEC_WB(OFFSET_LSL, OP_LDR, 32); } +// LDR Rd, [Rn, -Rm, LSR #]! + void arm732(u32 opcode) { LDR_PREDEC_WB(OFFSET_LSR, OP_LDR, 32); } +// LDR Rd, [Rn, -Rm, ASR #]! + void arm734(u32 opcode) { LDR_PREDEC_WB(OFFSET_ASR, OP_LDR, 32); } +// LDR Rd, [Rn, -Rm, ROR #]! + void arm736(u32 opcode) { LDR_PREDEC_WB(OFFSET_ROR, OP_LDR, 32); } +// STRB Rd, [Rn, -Rm, LSL #] + void arm740(u32 opcode) { STR_PREDEC(OFFSET_LSL, OP_STRB, 16); } +// STRB Rd, [Rn, -Rm, LSR #] + void arm742(u32 opcode) { STR_PREDEC(OFFSET_LSR, OP_STRB, 16); } +// STRB Rd, [Rn, -Rm, ASR #] + void arm744(u32 opcode) { STR_PREDEC(OFFSET_ASR, OP_STRB, 16); } +// STRB Rd, [Rn, -Rm, ROR #] + void arm746(u32 opcode) { STR_PREDEC(OFFSET_ROR, OP_STRB, 16); } +// LDRB Rd, [Rn, -Rm, LSL #] + void arm750(u32 opcode) { LDR_PREDEC(OFFSET_LSL, OP_LDRB, 16); } +// LDRB Rd, [Rn, -Rm, LSR #] + void arm752(u32 opcode) { LDR_PREDEC(OFFSET_LSR, OP_LDRB, 16); } +// LDRB Rd, [Rn, -Rm, ASR #] + void arm754(u32 opcode) { LDR_PREDEC(OFFSET_ASR, OP_LDRB, 16); } +// LDRB Rd, [Rn, -Rm, ROR #] + void arm756(u32 opcode) { LDR_PREDEC(OFFSET_ROR, OP_LDRB, 16); } +// STRB Rd, [Rn, -Rm, LSL #]! + void arm760(u32 opcode) { STR_PREDEC_WB(OFFSET_LSL, OP_STRB, 16); } +// STRB Rd, [Rn, -Rm, LSR #]! + void arm762(u32 opcode) { STR_PREDEC_WB(OFFSET_LSR, OP_STRB, 16); } +// STRB Rd, [Rn, -Rm, ASR #]! + void arm764(u32 opcode) { STR_PREDEC_WB(OFFSET_ASR, OP_STRB, 16); } +// STRB Rd, [Rn, -Rm, ROR #]! + void arm766(u32 opcode) { STR_PREDEC_WB(OFFSET_ROR, OP_STRB, 16); } +// LDRB Rd, [Rn, -Rm, LSL #]! + void arm770(u32 opcode) { LDR_PREDEC_WB(OFFSET_LSL, OP_LDRB, 16); } +// LDRB Rd, [Rn, -Rm, LSR #]! + void arm772(u32 opcode) { LDR_PREDEC_WB(OFFSET_LSR, OP_LDRB, 16); } +// LDRB Rd, [Rn, -Rm, ASR #]! + void arm774(u32 opcode) { LDR_PREDEC_WB(OFFSET_ASR, OP_LDRB, 16); } +// LDRB Rd, [Rn, -Rm, ROR #]! + void arm776(u32 opcode) { LDR_PREDEC_WB(OFFSET_ROR, OP_LDRB, 16); } +// STR Rd, [Rn, Rm, LSL #] + void arm780(u32 opcode) { STR_PREINC(OFFSET_LSL, OP_STR, 32); } +// STR Rd, [Rn, Rm, LSR #] + void arm782(u32 opcode) { STR_PREINC(OFFSET_LSR, OP_STR, 32); } +// STR Rd, [Rn, Rm, ASR #] + void arm784(u32 opcode) { STR_PREINC(OFFSET_ASR, OP_STR, 32); } +// STR Rd, [Rn, Rm, ROR #] + void arm786(u32 opcode) { STR_PREINC(OFFSET_ROR, OP_STR, 32); } +// LDR Rd, [Rn, Rm, LSL #] + void arm790(u32 opcode) { LDR_PREINC(OFFSET_LSL, OP_LDR, 32); } +// LDR Rd, [Rn, Rm, LSR #] + void arm792(u32 opcode) { LDR_PREINC(OFFSET_LSR, OP_LDR, 32); } +// LDR Rd, [Rn, Rm, ASR #] + void arm794(u32 opcode) { LDR_PREINC(OFFSET_ASR, OP_LDR, 32); } +// LDR Rd, [Rn, Rm, ROR #] + void arm796(u32 opcode) { LDR_PREINC(OFFSET_ROR, OP_LDR, 32); } +// STR Rd, [Rn, Rm, LSL #]! + void arm7A0(u32 opcode) { STR_PREINC_WB(OFFSET_LSL, OP_STR, 32); } +// STR Rd, [Rn, Rm, LSR #]! + void arm7A2(u32 opcode) { STR_PREINC_WB(OFFSET_LSR, OP_STR, 32); } +// STR Rd, [Rn, Rm, ASR #]! + void arm7A4(u32 opcode) { STR_PREINC_WB(OFFSET_ASR, OP_STR, 32); } +// STR Rd, [Rn, Rm, ROR #]! + void arm7A6(u32 opcode) { STR_PREINC_WB(OFFSET_ROR, OP_STR, 32); } +// LDR Rd, [Rn, Rm, LSL #]! + void arm7B0(u32 opcode) { LDR_PREINC_WB(OFFSET_LSL, OP_LDR, 32); } +// LDR Rd, [Rn, Rm, LSR #]! + void arm7B2(u32 opcode) { LDR_PREINC_WB(OFFSET_LSR, OP_LDR, 32); } +// LDR Rd, [Rn, Rm, ASR #]! + void arm7B4(u32 opcode) { LDR_PREINC_WB(OFFSET_ASR, OP_LDR, 32); } +// LDR Rd, [Rn, Rm, ROR #]! + void arm7B6(u32 opcode) { LDR_PREINC_WB(OFFSET_ROR, OP_LDR, 32); } +// STRB Rd, [Rn, Rm, LSL #] + void arm7C0(u32 opcode) { STR_PREINC(OFFSET_LSL, OP_STRB, 16); } +// STRB Rd, [Rn, Rm, LSR #] + void arm7C2(u32 opcode) { STR_PREINC(OFFSET_LSR, OP_STRB, 16); } +// STRB Rd, [Rn, Rm, ASR #] + void arm7C4(u32 opcode) { STR_PREINC(OFFSET_ASR, OP_STRB, 16); } +// STRB Rd, [Rn, Rm, ROR #] + void arm7C6(u32 opcode) { STR_PREINC(OFFSET_ROR, OP_STRB, 16); } +// LDRB Rd, [Rn, Rm, LSL #] + void arm7D0(u32 opcode) { LDR_PREINC(OFFSET_LSL, OP_LDRB, 16); } +// LDRB Rd, [Rn, Rm, LSR #] + void arm7D2(u32 opcode) { LDR_PREINC(OFFSET_LSR, OP_LDRB, 16); } +// LDRB Rd, [Rn, Rm, ASR #] + void arm7D4(u32 opcode) { LDR_PREINC(OFFSET_ASR, OP_LDRB, 16); } +// LDRB Rd, [Rn, Rm, ROR #] + void arm7D6(u32 opcode) { LDR_PREINC(OFFSET_ROR, OP_LDRB, 16); } +// STRB Rd, [Rn, Rm, LSL #]! + void arm7E0(u32 opcode) { STR_PREINC_WB(OFFSET_LSL, OP_STRB, 16); } +// STRB Rd, [Rn, Rm, LSR #]! + void arm7E2(u32 opcode) { STR_PREINC_WB(OFFSET_LSR, OP_STRB, 16); } +// STRB Rd, [Rn, Rm, ASR #]! + void arm7E4(u32 opcode) { STR_PREINC_WB(OFFSET_ASR, OP_STRB, 16); } +// STRB Rd, [Rn, Rm, ROR #]! + void arm7E6(u32 opcode) { STR_PREINC_WB(OFFSET_ROR, OP_STRB, 16); } +// LDRB Rd, [Rn, Rm, LSL #]! + void arm7F0(u32 opcode) { LDR_PREINC_WB(OFFSET_LSL, OP_LDRB, 16); } +// LDRB Rd, [Rn, Rm, LSR #]! + void arm7F2(u32 opcode) { LDR_PREINC_WB(OFFSET_LSR, OP_LDRB, 16); } +// LDRB Rd, [Rn, Rm, ASR #]! + void arm7F4(u32 opcode) { LDR_PREINC_WB(OFFSET_ASR, OP_LDRB, 16); } +// LDRB Rd, [Rn, Rm, ROR #]! + void arm7F6(u32 opcode) { LDR_PREINC_WB(OFFSET_ROR, OP_LDRB, 16); } + +// STM/LDM //////////////////////////////////////////////////////////////// + +#define STM_REG(bit,num) \ + if (opcode & (1U<<(bit))) { \ + CPUWriteMemory(address, bus.reg[(num)].I); \ + int dataticks_value = count ? DATATICKS_ACCESS_32BIT_SEQ(address) : DATATICKS_ACCESS_32BIT(address); \ + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); \ + clockTicks += 1 + dataticks_value; \ + count++; \ + address += 4; \ + } +#define STMW_REG(bit,num) \ + if (opcode & (1U<<(bit))) { \ + CPUWriteMemory(address, bus.reg[(num)].I); \ + int dataticks_value = count ? DATATICKS_ACCESS_32BIT_SEQ(address) : DATATICKS_ACCESS_32BIT(address); \ + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); \ + clockTicks += 1 + dataticks_value; \ + bus.reg[base].I = temp; \ + count++; \ + address += 4; \ + } +#define LDM_REG(bit,num) \ + if (opcode & (1U<<(bit))) { \ + int dataticks_value; \ + bus.reg[(num)].I = CPUReadMemory(address); \ + dataticks_value = count ? DATATICKS_ACCESS_32BIT_SEQ(address) : DATATICKS_ACCESS_32BIT(address); \ + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); \ + clockTicks += 1 + dataticks_value; \ + count++; \ + address += 4; \ + } +#define STM_LOW(STORE_REG) \ + STORE_REG(0, 0); \ + STORE_REG(1, 1); \ + STORE_REG(2, 2); \ + STORE_REG(3, 3); \ + STORE_REG(4, 4); \ + STORE_REG(5, 5); \ + STORE_REG(6, 6); \ + STORE_REG(7, 7); +#define STM_HIGH(STORE_REG) \ + STORE_REG(8, 8); \ + STORE_REG(9, 9); \ + STORE_REG(10, 10); \ + STORE_REG(11, 11); \ + STORE_REG(12, 12); \ + STORE_REG(13, 13); \ + STORE_REG(14, 14); +#define STM_HIGH_2(STORE_REG) \ + if (armMode == 0x11) { \ + STORE_REG(8, R8_FIQ); \ + STORE_REG(9, R9_FIQ); \ + STORE_REG(10, R10_FIQ); \ + STORE_REG(11, R11_FIQ); \ + STORE_REG(12, R12_FIQ); \ + } else { \ + STORE_REG(8, 8); \ + STORE_REG(9, 9); \ + STORE_REG(10, 10); \ + STORE_REG(11, 11); \ + STORE_REG(12, 12); \ + } \ + if (armMode != 0x10 && armMode != 0x1F) { \ + STORE_REG(13, R13_USR); \ + STORE_REG(14, R14_USR); \ + } else { \ + STORE_REG(13, 13); \ + STORE_REG(14, 14); \ + } +#define STM_PC \ + if (opcode & (1U<<15)) { \ + CPUWriteMemory(address, bus.reg[15].I+4); \ + int dataticks_value = count ? DATATICKS_ACCESS_32BIT_SEQ(address) : DATATICKS_ACCESS_32BIT(address); \ + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); \ + clockTicks += 1 + dataticks_value; \ + count++; \ + } +#define STMW_PC \ + if (opcode & (1U<<15)) { \ + CPUWriteMemory(address, bus.reg[15].I+4); \ + int dataticks_value = count ? DATATICKS_ACCESS_32BIT_SEQ(address) : DATATICKS_ACCESS_32BIT(address); \ + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); \ + clockTicks += 1 + dataticks_value; \ + bus.reg[base].I = temp; \ + count++; \ + } +#define LDM_LOW \ + LDM_REG(0, 0); \ + LDM_REG(1, 1); \ + LDM_REG(2, 2); \ + LDM_REG(3, 3); \ + LDM_REG(4, 4); \ + LDM_REG(5, 5); \ + LDM_REG(6, 6); \ + LDM_REG(7, 7); +#define LDM_HIGH \ + LDM_REG(8, 8); \ + LDM_REG(9, 9); \ + LDM_REG(10, 10); \ + LDM_REG(11, 11); \ + LDM_REG(12, 12); \ + LDM_REG(13, 13); \ + LDM_REG(14, 14); +#define LDM_HIGH_2 \ + if (armMode == 0x11) { \ + LDM_REG(8, R8_FIQ); \ + LDM_REG(9, R9_FIQ); \ + LDM_REG(10, R10_FIQ); \ + LDM_REG(11, R11_FIQ); \ + LDM_REG(12, R12_FIQ); \ + } else { \ + LDM_REG(8, 8); \ + LDM_REG(9, 9); \ + LDM_REG(10, 10); \ + LDM_REG(11, 11); \ + LDM_REG(12, 12); \ + } \ + if (armMode != 0x10 && armMode != 0x1F) { \ + LDM_REG(13, R13_USR); \ + LDM_REG(14, R14_USR); \ + } else { \ + LDM_REG(13, 13); \ + LDM_REG(14, 14); \ + } +#define STM_ALL \ + STM_LOW(STM_REG); \ + STM_HIGH(STM_REG); \ + STM_PC; +#define STMW_ALL \ + STM_LOW(STMW_REG); \ + STM_HIGH(STMW_REG); \ + STMW_PC; +#define LDM_ALL \ + LDM_LOW; \ + LDM_HIGH; \ + if (opcode & (1U<<15)) { \ + bus.reg[15].I = CPUReadMemory(address); \ + int dataticks_value = count ? DATATICKS_ACCESS_32BIT_SEQ(address) : DATATICKS_ACCESS_32BIT(address); \ + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); \ + clockTicks += 1 + dataticks_value; \ + count++; \ + } \ + if (opcode & (1U<<15)) { \ + bus.armNextPC = bus.reg[15].I; \ + bus.reg[15].I += 4; \ + ARM_PREFETCH; \ + clockTicks += 1 + codeTicksAccessSeq32(bus.armNextPC);\ + } +#define STM_ALL_2 \ + STM_LOW(STM_REG); \ + STM_HIGH_2(STM_REG); \ + STM_PC; +#define STMW_ALL_2 \ + STM_LOW(STMW_REG); \ + STM_HIGH_2(STMW_REG); \ + STMW_PC; +#define LDM_ALL_2 \ + LDM_LOW; \ + if (opcode & (1U<<15)) { \ + LDM_HIGH; \ + bus.reg[15].I = CPUReadMemory(address); \ + int dataticks_value = count ? DATATICKS_ACCESS_32BIT_SEQ(address) : DATATICKS_ACCESS_32BIT(address); \ + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); \ + clockTicks += 1 + dataticks_value; \ + count++; \ + } else { \ + LDM_HIGH_2; \ + } +#define LDM_ALL_2B \ + if (opcode & (1U<<15)) { \ + if(armMode != (bus.reg[17].I & 0x1F)) \ + CPUSwitchMode(bus.reg[17].I & 0x1F, false, true); \ + if (armState) { \ + bus.armNextPC = bus.reg[15].I & 0xFFFFFFFC; \ + bus.reg[15].I = bus.armNextPC + 4; \ + ARM_PREFETCH; \ + } else { \ + bus.armNextPC = bus.reg[15].I & 0xFFFFFFFE; \ + bus.reg[15].I = bus.armNextPC + 2; \ + THUMB_PREFETCH; \ + } \ + clockTicks += 1 + codeTicksAccessSeq32(bus.armNextPC);\ + } + + +// STMDA Rn, {Rlist} + void arm800(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = bus.reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (temp + 4) & 0xFFFFFFFC; + int count = 0; + STM_ALL; + clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// LDMDA Rn, {Rlist} + void arm810(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = bus.reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (temp + 4) & 0xFFFFFFFC; + int count = 0; + LDM_ALL; + clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// STMDA Rn!, {Rlist} + void arm820(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = bus.reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (temp+4) & 0xFFFFFFFC; + int count = 0; + STMW_ALL; + clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// LDMDA Rn!, {Rlist} + void arm830(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = bus.reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (temp + 4) & 0xFFFFFFFC; + int count = 0; + LDM_ALL; + clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32); + if (!(opcode & (1U << base))) + bus.reg[base].I = temp; +} + +// STMDA Rn, {Rlist}^ + void arm840(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = bus.reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (temp+4) & 0xFFFFFFFC; + int count = 0; + STM_ALL_2; + clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// LDMDA Rn, {Rlist}^ + void arm850(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = bus.reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (temp + 4) & 0xFFFFFFFC; + int count = 0; + LDM_ALL_2; + LDM_ALL_2B; + clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// STMDA Rn!, {Rlist}^ + void arm860(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = bus.reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (temp+4) & 0xFFFFFFFC; + int count = 0; + STMW_ALL_2; + clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// LDMDA Rn!, {Rlist}^ + void arm870(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = bus.reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (temp + 4) & 0xFFFFFFFC; + int count = 0; + LDM_ALL_2; + if (!(opcode & (1U << base))) + bus.reg[base].I = temp; + LDM_ALL_2B; + clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// STMIA Rn, {Rlist} + void arm880(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 address = bus.reg[base].I & 0xFFFFFFFC; + int count = 0; + STM_ALL; + clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// LDMIA Rn, {Rlist} + void arm890(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 address = bus.reg[base].I & 0xFFFFFFFC; + int count = 0; + LDM_ALL; + clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// STMIA Rn!, {Rlist} + void arm8A0(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 address = bus.reg[base].I & 0xFFFFFFFC; + int count = 0; + u32 temp = bus.reg[base].I + + 4 * (cpuBitsSet[opcode & 0xFF] + cpuBitsSet[(opcode >> 8) & 255]); + STMW_ALL; + clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// LDMIA Rn!, {Rlist} + void arm8B0(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = bus.reg[base].I + + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = bus.reg[base].I & 0xFFFFFFFC; + int count = 0; + LDM_ALL; + clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32); + if (!(opcode & (1U << base))) + bus.reg[base].I = temp; +} + +// STMIA Rn, {Rlist}^ + void arm8C0(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 address = bus.reg[base].I & 0xFFFFFFFC; + int count = 0; + STM_ALL_2; + clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// LDMIA Rn, {Rlist}^ + void arm8D0(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 address = bus.reg[base].I & 0xFFFFFFFC; + int count = 0; + LDM_ALL_2; + LDM_ALL_2B; + clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// STMIA Rn!, {Rlist}^ + void arm8E0(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 address = bus.reg[base].I & 0xFFFFFFFC; + int count = 0; + u32 temp = bus.reg[base].I + + 4 * (cpuBitsSet[opcode & 0xFF] + cpuBitsSet[(opcode >> 8) & 255]); + STMW_ALL_2; + clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// LDMIA Rn!, {Rlist}^ + void arm8F0(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = bus.reg[base].I + + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = bus.reg[base].I & 0xFFFFFFFC; + int count = 0; + LDM_ALL_2; + if (!(opcode & (1U << base))) + bus.reg[base].I = temp; + LDM_ALL_2B; + clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// STMDB Rn, {Rlist} + void arm900(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = bus.reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = temp & 0xFFFFFFFC; + int count = 0; + STM_ALL; + clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// LDMDB Rn, {Rlist} + void arm910(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = bus.reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = temp & 0xFFFFFFFC; + int count = 0; + LDM_ALL; + clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// STMDB Rn!, {Rlist} + void arm920(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = bus.reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = temp & 0xFFFFFFFC; + int count = 0; + STMW_ALL; + clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// LDMDB Rn!, {Rlist} + void arm930(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = bus.reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = temp & 0xFFFFFFFC; + int count = 0; + LDM_ALL; + clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32); + if (!(opcode & (1U << base))) + bus.reg[base].I = temp; +} + +// STMDB Rn, {Rlist}^ + void arm940(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = bus.reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = temp & 0xFFFFFFFC; + int count = 0; + STM_ALL_2; + clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// LDMDB Rn, {Rlist}^ + void arm950(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = bus.reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = temp & 0xFFFFFFFC; + int count = 0; + LDM_ALL_2; + LDM_ALL_2B; + clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// STMDB Rn!, {Rlist}^ + void arm960(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = bus.reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = temp & 0xFFFFFFFC; + int count = 0; + STMW_ALL_2; + clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// LDMDB Rn!, {Rlist}^ + void arm970(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = bus.reg[base].I - + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = temp & 0xFFFFFFFC; + int count = 0; + LDM_ALL_2; + if (!(opcode & (1U << base))) + bus.reg[base].I = temp; + LDM_ALL_2B; + clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// STMIB Rn, {Rlist} + void arm980(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 address = (bus.reg[base].I+4) & 0xFFFFFFFC; + int count = 0; + STM_ALL; + clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// LDMIB Rn, {Rlist} + void arm990(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 address = (bus.reg[base].I+4) & 0xFFFFFFFC; + int count = 0; + LDM_ALL; + clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// STMIB Rn!, {Rlist} + void arm9A0(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 address = (bus.reg[base].I+4) & 0xFFFFFFFC; + int count = 0; + u32 temp = bus.reg[base].I + + 4 * (cpuBitsSet[opcode & 0xFF] + cpuBitsSet[(opcode >> 8) & 255]); + STMW_ALL; + clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// LDMIB Rn!, {Rlist} + void arm9B0(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = bus.reg[base].I + + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (bus.reg[base].I+4) & 0xFFFFFFFC; + int count = 0; + LDM_ALL; + clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32); + if (!(opcode & (1U << base))) + bus.reg[base].I = temp; +} + +// STMIB Rn, {Rlist}^ + void arm9C0(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 address = (bus.reg[base].I+4) & 0xFFFFFFFC; + int count = 0; + STM_ALL_2; + clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// LDMIB Rn, {Rlist}^ + void arm9D0(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 address = (bus.reg[base].I+4) & 0xFFFFFFFC; + int count = 0; + LDM_ALL_2; + LDM_ALL_2B; + clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// STMIB Rn!, {Rlist}^ + void arm9E0(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 address = (bus.reg[base].I+4) & 0xFFFFFFFC; + int count = 0; + u32 temp = bus.reg[base].I + + 4 * (cpuBitsSet[opcode & 0xFF] + cpuBitsSet[(opcode >> 8) & 255]); + STMW_ALL_2; + clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// LDMIB Rn!, {Rlist}^ + void arm9F0(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int base = (opcode & 0x000F0000) >> 16; + u32 temp = bus.reg[base].I + + 4 * (cpuBitsSet[opcode & 255] + cpuBitsSet[(opcode >> 8) & 255]); + u32 address = (bus.reg[base].I+4) & 0xFFFFFFFC; + int count = 0; + LDM_ALL_2; + if (!(opcode & (1U << base))) + bus.reg[base].I = temp; + LDM_ALL_2B; + clockTicks += 2 + codeTicksAccess(bus.armNextPC, BITS_32); +} + +// B/BL/SWI and (unimplemented) coproc support //////////////////////////// + +// B + void armA00(u32 opcode) +{ + int codeTicksVal = 0; + int ct = 0; + int offset = opcode & 0x00FFFFFF; + if (offset & 0x00800000) + offset |= 0xFF000000; // negative offset + bus.reg[15].I += offset<<2; + bus.armNextPC = bus.reg[15].I; + bus.reg[15].I += 4; + ARM_PREFETCH; + + codeTicksVal = codeTicksAccessSeq32(bus.armNextPC); + ct = codeTicksVal + 3; + ct += 2 + codeTicksAccess(bus.armNextPC, BITS_32) + codeTicksVal; + + bus.busPrefetchCount = 0; + clockTicks = ct; +} + +// BL + void armB00(u32 opcode) +{ + int codeTicksVal = 0; + int ct = 0; + + int offset = opcode & 0x00FFFFFF; + if (offset & 0x00800000) + offset |= 0xFF000000; // negative offset + bus.reg[14].I = bus.reg[15].I - 4; + bus.reg[15].I += offset<<2; + bus.armNextPC = bus.reg[15].I; + bus.reg[15].I += 4; + ARM_PREFETCH; + + codeTicksVal = codeTicksAccessSeq32(bus.armNextPC); + ct = codeTicksVal + 3; + ct += 2 + codeTicksAccess(bus.armNextPC, BITS_32) + codeTicksVal; + + bus.busPrefetchCount = 0; + clockTicks = ct; +} + +#define armE01 armUnknownInsn + +// SWI + void armF00(u32 opcode) +{ + int codeTicksVal = 0; + int ct = 0; + + codeTicksVal = codeTicksAccessSeq32(bus.armNextPC); + ct = codeTicksVal + 3; + ct += 2 + codeTicksAccess(bus.armNextPC, BITS_32) + codeTicksVal; + + bus.busPrefetchCount = 0; + + clockTicks = ct; + CPUSoftwareInterrupt(opcode & 0x00FFFFFF); + +} + +// Instruction table ////////////////////////////////////////////////////// + +static void (Gigazoid::*const armInsnTable[4096])(u32 opcode); + +// Wrapper routine (execution loop) /////////////////////////////////////// +int armExecute (void) +{ + CACHE_PREFETCH(clockTicks); + + u32 cond1; + u32 cond2; + + int ct = 0; + + do + { + + clockTicks = 0; + + if ((bus.armNextPC & 0x0803FFFF) == 0x08020000) + bus.busPrefetchCount = 0x100; + + u32 opcode = cpuPrefetch[0]; + cpuPrefetch[0] = cpuPrefetch[1]; + + bus.busPrefetch = false; + int32_t busprefetch_mask = ((bus.busPrefetchCount & 0xFFFFFE00) | -(bus.busPrefetchCount & 0xFFFFFE00)) >> 31; + bus.busPrefetchCount = (0x100 | (bus.busPrefetchCount & 0xFF) & busprefetch_mask) | (bus.busPrefetchCount & ~busprefetch_mask); +#if 0 + if (bus.busPrefetchCount & 0xFFFFFE00) + bus.busPrefetchCount = 0x100 | (bus.busPrefetchCount & 0xFF); +#endif + + + int oldArmNextPC = bus.armNextPC; + + bus.armNextPC = bus.reg[15].I; + if (traceCallback) + traceCallback(bus.armNextPC, opcode); + if (fetchCallback) + fetchCallback(bus.armNextPC); + bus.reg[15].I += 4; + ARM_PREFETCH_NEXT; + + int cond = opcode >> 28; + bool cond_res = true; + if (cond != 0x0E) { // most opcodes are AL (always) + switch(cond) { + case 0x00: // EQ + cond_res = Z_FLAG; + break; + case 0x01: // NE + cond_res = !Z_FLAG; + break; + case 0x02: // CS + cond_res = C_FLAG; + break; + case 0x03: // CC + cond_res = !C_FLAG; + break; + case 0x04: // MI + cond_res = N_FLAG; + break; + case 0x05: // PL + cond_res = !N_FLAG; + break; + case 0x06: // VS + cond_res = V_FLAG; + break; + case 0x07: // VC + cond_res = !V_FLAG; + break; + case 0x08: // HI + cond_res = C_FLAG && !Z_FLAG; + break; + case 0x09: // LS + cond_res = !C_FLAG || Z_FLAG; + break; + case 0x0A: // GE + cond_res = N_FLAG == V_FLAG; + break; + case 0x0B: // LT + cond_res = N_FLAG != V_FLAG; + break; + case 0x0C: // GT + cond_res = !Z_FLAG &&(N_FLAG == V_FLAG); + break; + case 0x0D: // LE + cond_res = Z_FLAG || (N_FLAG != V_FLAG); + break; + case 0x0E: // AL (impossible, checked above) + cond_res = true; + break; + case 0x0F: + default: + // ??? + cond_res = false; + break; + } + } + + if (cond_res) + { + cond1 = (opcode>>16)&0xFF0; + cond2 = (opcode>>4)&0x0F; + + (this->*armInsnTable[(cond1| cond2)])(opcode); + + } + ct = clockTicks; + + if (ct < 0) + return 0; + + /// better pipelining + + if (ct == 0) + clockTicks = 1 + codeTicksAccessSeq32(oldArmNextPC); + + cpuTotalTicks += clockTicks; + +} while ((cpuTotalTicks < cpuNextEvent) & armState & ~holdState); + return 1; +} + + +/*============================================================ + GBA THUMB CORE +============================================================ */ + + void thumbUnknownInsn(u32 opcode) +{ + u32 PC = bus.reg[15].I; + bool savedArmState = armState; + if(armMode != 0x1b) + CPUSwitchMode(0x1b, true, false); + bus.reg[14].I = PC - (savedArmState ? 4 : 2); + bus.reg[15].I = 0x04; + armState = true; + armIrqEnable = false; + bus.armNextPC = 0x04; + ARM_PREFETCH; + bus.reg[15].I += 4; +} + +#define NEG(i) ((i) >> 31) +#define POS(i) ((~(i)) >> 31) + +// C core +#ifndef ADDCARRY + #define ADDCARRY(a, b, c) \ + C_FLAG = ((NEG(a) & NEG(b)) |\ + (NEG(a) & POS(c)) |\ + (NEG(b) & POS(c))) ? true : false; +#endif + +#ifndef ADDOVERFLOW + #define ADDOVERFLOW(a, b, c) \ + V_FLAG = ((NEG(a) & NEG(b) & POS(c)) |\ + (POS(a) & POS(b) & NEG(c))) ? true : false; +#endif + +#ifndef SUBCARRY + #define SUBCARRY(a, b, c) \ + C_FLAG = ((NEG(a) & POS(b)) |\ + (NEG(a) & POS(c)) |\ + (POS(b) & POS(c))) ? true : false; +#endif + +#ifndef SUBOVERFLOW + #define SUBOVERFLOW(a, b, c)\ + V_FLAG = ((NEG(a) & POS(b) & POS(c)) |\ + (POS(a) & NEG(b) & NEG(c))) ? true : false; +#endif + +#ifndef ADD_RD_RS_RN + #define ADD_RD_RS_RN(N) \ + {\ + u32 lhs = bus.reg[source].I;\ + u32 rhs = bus.reg[N].I;\ + u32 res = lhs + rhs;\ + bus.reg[dest].I = res;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + ADDCARRY(lhs, rhs, res);\ + ADDOVERFLOW(lhs, rhs, res);\ + } +#endif + +#ifndef ADD_RD_RS_O3 + #define ADD_RD_RS_O3(N) \ + {\ + u32 lhs = bus.reg[source].I;\ + u32 rhs = N;\ + u32 res = lhs + rhs;\ + bus.reg[dest].I = res;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + ADDCARRY(lhs, rhs, res);\ + ADDOVERFLOW(lhs, rhs, res);\ + } +#endif + +#ifndef ADD_RD_RS_O3_0 +# define ADD_RD_RS_O3_0 ADD_RD_RS_O3 +#endif + +#ifndef ADD_RN_O8 + #define ADD_RN_O8(d) \ + {\ + u32 lhs = bus.reg[(d)].I;\ + u32 rhs = (opcode & 255);\ + u32 res = lhs + rhs;\ + bus.reg[(d)].I = res;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + ADDCARRY(lhs, rhs, res);\ + ADDOVERFLOW(lhs, rhs, res);\ + } +#endif + +#ifndef CMN_RD_RS + #define CMN_RD_RS \ + {\ + u32 lhs = bus.reg[dest].I;\ + u32 rhs = value;\ + u32 res = lhs + rhs;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + ADDCARRY(lhs, rhs, res);\ + ADDOVERFLOW(lhs, rhs, res);\ + } +#endif + +#ifndef ADC_RD_RS + #define ADC_RD_RS \ + {\ + u32 lhs = bus.reg[dest].I;\ + u32 rhs = value;\ + u32 res = lhs + rhs + (u32)C_FLAG;\ + bus.reg[dest].I = res;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + ADDCARRY(lhs, rhs, res);\ + ADDOVERFLOW(lhs, rhs, res);\ + } +#endif + +#ifndef SUB_RD_RS_RN + #define SUB_RD_RS_RN(N) \ + {\ + u32 lhs = bus.reg[source].I;\ + u32 rhs = bus.reg[N].I;\ + u32 res = lhs - rhs;\ + bus.reg[dest].I = res;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + SUBCARRY(lhs, rhs, res);\ + SUBOVERFLOW(lhs, rhs, res);\ + } +#endif + +#ifndef SUB_RD_RS_O3 + #define SUB_RD_RS_O3(N) \ + {\ + u32 lhs = bus.reg[source].I;\ + u32 rhs = N;\ + u32 res = lhs - rhs;\ + bus.reg[dest].I = res;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + SUBCARRY(lhs, rhs, res);\ + SUBOVERFLOW(lhs, rhs, res);\ + } +#endif + +#ifndef SUB_RD_RS_O3_0 +# define SUB_RD_RS_O3_0 SUB_RD_RS_O3 +#endif +#ifndef SUB_RN_O8 + #define SUB_RN_O8(d) \ + {\ + u32 lhs = bus.reg[(d)].I;\ + u32 rhs = (opcode & 255);\ + u32 res = lhs - rhs;\ + bus.reg[(d)].I = res;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + SUBCARRY(lhs, rhs, res);\ + SUBOVERFLOW(lhs, rhs, res);\ + } +#endif +#ifndef MOV_RN_O8 + #define MOV_RN_O8(d) \ + {\ + u32 val;\ + val = (opcode & 255);\ + bus.reg[d].I = val;\ + N_FLAG = false;\ + Z_FLAG = (val ? false : true);\ + } +#endif +#ifndef CMP_RN_O8 + #define CMP_RN_O8(d) \ + {\ + u32 lhs = bus.reg[(d)].I;\ + u32 rhs = (opcode & 255);\ + u32 res = lhs - rhs;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + SUBCARRY(lhs, rhs, res);\ + SUBOVERFLOW(lhs, rhs, res);\ + } +#endif +#ifndef SBC_RD_RS + #define SBC_RD_RS \ + {\ + u32 lhs = bus.reg[dest].I;\ + u32 rhs = value;\ + u32 res = lhs - rhs - !((u32)C_FLAG);\ + bus.reg[dest].I = res;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + SUBCARRY(lhs, rhs, res);\ + SUBOVERFLOW(lhs, rhs, res);\ + } +#endif +#ifndef LSL_RD_RM_I5 + #define LSL_RD_RM_I5 \ + {\ + C_FLAG = (bus.reg[source].I >> (32 - shift)) & 1 ? true : false;\ + value = bus.reg[source].I << shift;\ + } +#endif +#ifndef LSL_RD_RS + #define LSL_RD_RS \ + {\ + C_FLAG = (bus.reg[dest].I >> (32 - value)) & 1 ? true : false;\ + value = bus.reg[dest].I << value;\ + } +#endif +#ifndef LSR_RD_RM_I5 + #define LSR_RD_RM_I5 \ + {\ + C_FLAG = (bus.reg[source].I >> (shift - 1)) & 1 ? true : false;\ + value = bus.reg[source].I >> shift;\ + } +#endif +#ifndef LSR_RD_RS + #define LSR_RD_RS \ + {\ + C_FLAG = (bus.reg[dest].I >> (value - 1)) & 1 ? true : false;\ + value = bus.reg[dest].I >> value;\ + } +#endif +#ifndef ASR_RD_RM_I5 + #define ASR_RD_RM_I5 \ + {\ + C_FLAG = ((s32)bus.reg[source].I >> (int)(shift - 1)) & 1 ? true : false;\ + value = (s32)bus.reg[source].I >> (int)shift;\ + } +#endif +#ifndef ASR_RD_RS + #define ASR_RD_RS \ + {\ + C_FLAG = ((s32)bus.reg[dest].I >> (int)(value - 1)) & 1 ? true : false;\ + value = (s32)bus.reg[dest].I >> (int)value;\ + } +#endif +#ifndef ROR_RD_RS + #define ROR_RD_RS \ + {\ + C_FLAG = (bus.reg[dest].I >> (value - 1)) & 1 ? true : false;\ + value = ((bus.reg[dest].I << (32 - value)) |\ + (bus.reg[dest].I >> value));\ + } +#endif +#ifndef NEG_RD_RS + #define NEG_RD_RS \ + {\ + u32 lhs = bus.reg[source].I;\ + u32 rhs = 0;\ + u32 res = rhs - lhs;\ + bus.reg[dest].I = res;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + SUBCARRY(rhs, lhs, res);\ + SUBOVERFLOW(rhs, lhs, res);\ + } +#endif +#ifndef CMP_RD_RS + #define CMP_RD_RS \ + {\ + u32 lhs = bus.reg[dest].I;\ + u32 rhs = value;\ + u32 res = lhs - rhs;\ + Z_FLAG = (res == 0) ? true : false;\ + N_FLAG = NEG(res) ? true : false;\ + SUBCARRY(lhs, rhs, res);\ + SUBOVERFLOW(lhs, rhs, res);\ + } +#endif +#ifndef IMM5_INSN + #define IMM5_INSN(OP,N) \ + int dest = opcode & 0x07;\ + int source = (opcode >> 3) & 0x07;\ + u32 value;\ + OP(N);\ + bus.reg[dest].I = value;\ + N_FLAG = (value & 0x80000000 ? true : false);\ + Z_FLAG = (value ? false : true); + #define IMM5_INSN_0(OP) \ + int dest = opcode & 0x07;\ + int source = (opcode >> 3) & 0x07;\ + u32 value;\ + OP;\ + bus.reg[dest].I = value;\ + N_FLAG = (value & 0x80000000 ? true : false);\ + Z_FLAG = (value ? false : true); + #define IMM5_LSL(N) \ + int shift = N;\ + LSL_RD_RM_I5; + #define IMM5_LSL_0 \ + value = bus.reg[source].I; + #define IMM5_LSR(N) \ + int shift = N;\ + LSR_RD_RM_I5; + #define IMM5_LSR_0 \ + C_FLAG = bus.reg[source].I & 0x80000000 ? true : false;\ + value = 0; + #define IMM5_ASR(N) \ + int shift = N;\ + ASR_RD_RM_I5; + #define IMM5_ASR_0 \ + if(bus.reg[source].I & 0x80000000) {\ + value = 0xFFFFFFFF;\ + C_FLAG = true;\ + } else {\ + value = 0;\ + C_FLAG = false;\ + } +#endif +#ifndef THREEARG_INSN + #define THREEARG_INSN(OP,N) \ + int dest = opcode & 0x07; \ + int source = (opcode >> 3) & 0x07; \ + OP(N); +#endif + +// Shift instructions ///////////////////////////////////////////////////// + +#define DEFINE_IMM5_INSN(OP,BASE) \ + void thumb##BASE##_00(u32 opcode) { IMM5_INSN_0(OP##_0); } \ + void thumb##BASE##_01(u32 opcode) { IMM5_INSN(OP, 1); } \ + void thumb##BASE##_02(u32 opcode) { IMM5_INSN(OP, 2); } \ + void thumb##BASE##_03(u32 opcode) { IMM5_INSN(OP, 3); } \ + void thumb##BASE##_04(u32 opcode) { IMM5_INSN(OP, 4); } \ + void thumb##BASE##_05(u32 opcode) { IMM5_INSN(OP, 5); } \ + void thumb##BASE##_06(u32 opcode) { IMM5_INSN(OP, 6); } \ + void thumb##BASE##_07(u32 opcode) { IMM5_INSN(OP, 7); } \ + void thumb##BASE##_08(u32 opcode) { IMM5_INSN(OP, 8); } \ + void thumb##BASE##_09(u32 opcode) { IMM5_INSN(OP, 9); } \ + void thumb##BASE##_0A(u32 opcode) { IMM5_INSN(OP,10); } \ + void thumb##BASE##_0B(u32 opcode) { IMM5_INSN(OP,11); } \ + void thumb##BASE##_0C(u32 opcode) { IMM5_INSN(OP,12); } \ + void thumb##BASE##_0D(u32 opcode) { IMM5_INSN(OP,13); } \ + void thumb##BASE##_0E(u32 opcode) { IMM5_INSN(OP,14); } \ + void thumb##BASE##_0F(u32 opcode) { IMM5_INSN(OP,15); } \ + void thumb##BASE##_10(u32 opcode) { IMM5_INSN(OP,16); } \ + void thumb##BASE##_11(u32 opcode) { IMM5_INSN(OP,17); } \ + void thumb##BASE##_12(u32 opcode) { IMM5_INSN(OP,18); } \ + void thumb##BASE##_13(u32 opcode) { IMM5_INSN(OP,19); } \ + void thumb##BASE##_14(u32 opcode) { IMM5_INSN(OP,20); } \ + void thumb##BASE##_15(u32 opcode) { IMM5_INSN(OP,21); } \ + void thumb##BASE##_16(u32 opcode) { IMM5_INSN(OP,22); } \ + void thumb##BASE##_17(u32 opcode) { IMM5_INSN(OP,23); } \ + void thumb##BASE##_18(u32 opcode) { IMM5_INSN(OP,24); } \ + void thumb##BASE##_19(u32 opcode) { IMM5_INSN(OP,25); } \ + void thumb##BASE##_1A(u32 opcode) { IMM5_INSN(OP,26); } \ + void thumb##BASE##_1B(u32 opcode) { IMM5_INSN(OP,27); } \ + void thumb##BASE##_1C(u32 opcode) { IMM5_INSN(OP,28); } \ + void thumb##BASE##_1D(u32 opcode) { IMM5_INSN(OP,29); } \ + void thumb##BASE##_1E(u32 opcode) { IMM5_INSN(OP,30); } \ + void thumb##BASE##_1F(u32 opcode) { IMM5_INSN(OP,31); } + +// LSL Rd, Rm, #Imm 5 +DEFINE_IMM5_INSN(IMM5_LSL,00) +// LSR Rd, Rm, #Imm 5 +DEFINE_IMM5_INSN(IMM5_LSR,08) +// ASR Rd, Rm, #Imm 5 +DEFINE_IMM5_INSN(IMM5_ASR,10) + +// 3-argument ADD/SUB ///////////////////////////////////////////////////// + +#define DEFINE_REG3_INSN(OP,BASE) \ + void thumb##BASE##_0(u32 opcode) { THREEARG_INSN(OP,0); } \ + void thumb##BASE##_1(u32 opcode) { THREEARG_INSN(OP,1); } \ + void thumb##BASE##_2(u32 opcode) { THREEARG_INSN(OP,2); } \ + void thumb##BASE##_3(u32 opcode) { THREEARG_INSN(OP,3); } \ + void thumb##BASE##_4(u32 opcode) { THREEARG_INSN(OP,4); } \ + void thumb##BASE##_5(u32 opcode) { THREEARG_INSN(OP,5); } \ + void thumb##BASE##_6(u32 opcode) { THREEARG_INSN(OP,6); } \ + void thumb##BASE##_7(u32 opcode) { THREEARG_INSN(OP,7); } + +#define DEFINE_IMM3_INSN(OP,BASE) \ + void thumb##BASE##_0(u32 opcode) { THREEARG_INSN(OP##_0,0); } \ + void thumb##BASE##_1(u32 opcode) { THREEARG_INSN(OP,1); } \ + void thumb##BASE##_2(u32 opcode) { THREEARG_INSN(OP,2); } \ + void thumb##BASE##_3(u32 opcode) { THREEARG_INSN(OP,3); } \ + void thumb##BASE##_4(u32 opcode) { THREEARG_INSN(OP,4); } \ + void thumb##BASE##_5(u32 opcode) { THREEARG_INSN(OP,5); } \ + void thumb##BASE##_6(u32 opcode) { THREEARG_INSN(OP,6); } \ + void thumb##BASE##_7(u32 opcode) { THREEARG_INSN(OP,7); } + +// ADD Rd, Rs, Rn +DEFINE_REG3_INSN(ADD_RD_RS_RN,18) +// SUB Rd, Rs, Rn +DEFINE_REG3_INSN(SUB_RD_RS_RN,1A) +// ADD Rd, Rs, #Offset3 +DEFINE_IMM3_INSN(ADD_RD_RS_O3,1C) +// SUB Rd, Rs, #Offset3 +DEFINE_IMM3_INSN(SUB_RD_RS_O3,1E) + +// MOV/CMP/ADD/SUB immediate ////////////////////////////////////////////// + +// MOV R0, #Offset8 + void thumb20(u32 opcode) { MOV_RN_O8(0); } +// MOV R1, #Offset8 + void thumb21(u32 opcode) { MOV_RN_O8(1); } +// MOV R2, #Offset8 + void thumb22(u32 opcode) { MOV_RN_O8(2); } +// MOV R3, #Offset8 + void thumb23(u32 opcode) { MOV_RN_O8(3); } +// MOV R4, #Offset8 + void thumb24(u32 opcode) { MOV_RN_O8(4); } +// MOV R5, #Offset8 + void thumb25(u32 opcode) { MOV_RN_O8(5); } +// MOV R6, #Offset8 + void thumb26(u32 opcode) { MOV_RN_O8(6); } +// MOV R7, #Offset8 + void thumb27(u32 opcode) { MOV_RN_O8(7); } + +// CMP R0, #Offset8 + void thumb28(u32 opcode) { CMP_RN_O8(0); } +// CMP R1, #Offset8 + void thumb29(u32 opcode) { CMP_RN_O8(1); } +// CMP R2, #Offset8 + void thumb2A(u32 opcode) { CMP_RN_O8(2); } +// CMP R3, #Offset8 + void thumb2B(u32 opcode) { CMP_RN_O8(3); } +// CMP R4, #Offset8 + void thumb2C(u32 opcode) { CMP_RN_O8(4); } +// CMP R5, #Offset8 + void thumb2D(u32 opcode) { CMP_RN_O8(5); } +// CMP R6, #Offset8 + void thumb2E(u32 opcode) { CMP_RN_O8(6); } +// CMP R7, #Offset8 + void thumb2F(u32 opcode) { CMP_RN_O8(7); } + +// ADD R0,#Offset8 + void thumb30(u32 opcode) { ADD_RN_O8(0); } +// ADD R1,#Offset8 + void thumb31(u32 opcode) { ADD_RN_O8(1); } +// ADD R2,#Offset8 + void thumb32(u32 opcode) { ADD_RN_O8(2); } +// ADD R3,#Offset8 + void thumb33(u32 opcode) { ADD_RN_O8(3); } +// ADD R4,#Offset8 + void thumb34(u32 opcode) { ADD_RN_O8(4); } +// ADD R5,#Offset8 + void thumb35(u32 opcode) { ADD_RN_O8(5); } +// ADD R6,#Offset8 + void thumb36(u32 opcode) { ADD_RN_O8(6); } +// ADD R7,#Offset8 + void thumb37(u32 opcode) { ADD_RN_O8(7); } + +// SUB R0,#Offset8 + void thumb38(u32 opcode) { SUB_RN_O8(0); } +// SUB R1,#Offset8 + void thumb39(u32 opcode) { SUB_RN_O8(1); } +// SUB R2,#Offset8 + void thumb3A(u32 opcode) { SUB_RN_O8(2); } +// SUB R3,#Offset8 + void thumb3B(u32 opcode) { SUB_RN_O8(3); } +// SUB R4,#Offset8 + void thumb3C(u32 opcode) { SUB_RN_O8(4); } +// SUB R5,#Offset8 + void thumb3D(u32 opcode) { SUB_RN_O8(5); } +// SUB R6,#Offset8 + void thumb3E(u32 opcode) { SUB_RN_O8(6); } +// SUB R7,#Offset8 + void thumb3F(u32 opcode) { SUB_RN_O8(7); } + +// ALU operations ///////////////////////////////////////////////////////// + +// AND Rd, Rs + void thumb40_0(u32 opcode) +{ + int dest = opcode & 7; + u32 val = (bus.reg[dest].I & bus.reg[(opcode >> 3)&7].I); + + //bus.reg[dest].I &= bus.reg[(opcode >> 3)&7].I; + N_FLAG = val & 0x80000000 ? true : false; + Z_FLAG = val ? false : true; + + bus.reg[dest].I = val; + +} + +// EOR Rd, Rs + void thumb40_1(u32 opcode) +{ + int dest = opcode & 7; + bus.reg[dest].I ^= bus.reg[(opcode >> 3)&7].I; + N_FLAG = bus.reg[dest].I & 0x80000000 ? true : false; + Z_FLAG = bus.reg[dest].I ? false : true; +} + +// LSL Rd, Rs + void thumb40_2(u32 opcode) +{ + int dest = opcode & 7; + u32 value = bus.reg[(opcode >> 3)&7].B.B0; + u32 val = value; + if(val) { + if(val == 32) { + value = 0; + C_FLAG = (bus.reg[dest].I & 1 ? true : false); + } else if(val < 32) { + LSL_RD_RS; + } else { + value = 0; + C_FLAG = false; + } + bus.reg[dest].I = value; + } + N_FLAG = bus.reg[dest].I & 0x80000000 ? true : false; + Z_FLAG = bus.reg[dest].I ? false : true; + clockTicks = codeTicksAccess(bus.armNextPC, BITS_16)+2; +} + +// LSR Rd, Rs + void thumb40_3(u32 opcode) +{ + int dest = opcode & 7; + u32 value = bus.reg[(opcode >> 3)&7].B.B0; + u32 val = value; + if(val) { + if(val == 32) { + value = 0; + C_FLAG = (bus.reg[dest].I & 0x80000000 ? true : false); + } else if(val < 32) { + LSR_RD_RS; + } else { + value = 0; + C_FLAG = false; + } + bus.reg[dest].I = value; + } + N_FLAG = bus.reg[dest].I & 0x80000000 ? true : false; + Z_FLAG = bus.reg[dest].I ? false : true; + clockTicks = codeTicksAccess(bus.armNextPC, BITS_16)+2; +} + +// ASR Rd, Rs + void thumb41_0(u32 opcode) +{ + int dest = opcode & 7; + u32 value = bus.reg[(opcode >> 3)&7].B.B0; + + if(value) { + if(value < 32) { + ASR_RD_RS; + bus.reg[dest].I = value; + } else { + if(bus.reg[dest].I & 0x80000000){ + bus.reg[dest].I = 0xFFFFFFFF; + C_FLAG = true; + } else { + bus.reg[dest].I = 0x00000000; + C_FLAG = false; + } + } + } + N_FLAG = bus.reg[dest].I & 0x80000000 ? true : false; + Z_FLAG = bus.reg[dest].I ? false : true; + clockTicks = codeTicksAccess(bus.armNextPC, BITS_16)+2; +} + +// ADC Rd, Rs + void thumb41_1(u32 opcode) +{ + int dest = opcode & 0x07; + u32 value = bus.reg[(opcode >> 3)&7].I; + ADC_RD_RS; +} + +// SBC Rd, Rs + void thumb41_2(u32 opcode) +{ + int dest = opcode & 0x07; + u32 value = bus.reg[(opcode >> 3)&7].I; + SBC_RD_RS; +} + +// ROR Rd, Rs + void thumb41_3(u32 opcode) +{ + int dest = opcode & 7; + u32 value = bus.reg[(opcode >> 3)&7].B.B0; + u32 val = value; + if(val) { + value = value & 0x1f; + if(val == 0) { + C_FLAG = (bus.reg[dest].I & 0x80000000 ? true : false); + } else { + ROR_RD_RS; + bus.reg[dest].I = value; + } + } + clockTicks = codeTicksAccess(bus.armNextPC, BITS_16)+2; + N_FLAG = bus.reg[dest].I & 0x80000000 ? true : false; + Z_FLAG = bus.reg[dest].I ? false : true; +} + +// TST Rd, Rs + void thumb42_0(u32 opcode) +{ + u32 value = bus.reg[opcode & 7].I & bus.reg[(opcode >> 3) & 7].I; + N_FLAG = value & 0x80000000 ? true : false; + Z_FLAG = value ? false : true; +} + +// NEG Rd, Rs + void thumb42_1(u32 opcode) +{ + int dest = opcode & 7; + int source = (opcode >> 3) & 7; + NEG_RD_RS; +} + +// CMP Rd, Rs + void thumb42_2(u32 opcode) +{ + int dest = opcode & 7; + u32 value = bus.reg[(opcode >> 3)&7].I; + CMP_RD_RS; +} + +// CMN Rd, Rs + void thumb42_3(u32 opcode) +{ + int dest = opcode & 7; + u32 value = bus.reg[(opcode >> 3)&7].I; + CMN_RD_RS; +} + +// ORR Rd, Rs + void thumb43_0(u32 opcode) +{ + int dest = opcode & 7; + bus.reg[dest].I |= bus.reg[(opcode >> 3) & 7].I; + Z_FLAG = bus.reg[dest].I ? false : true; + N_FLAG = bus.reg[dest].I & 0x80000000 ? true : false; +} + +// MUL Rd, Rs + void thumb43_1(u32 opcode) +{ + clockTicks = 1; + int dest = opcode & 7; + u32 rm = bus.reg[dest].I; + bus.reg[dest].I = bus.reg[(opcode >> 3) & 7].I * rm; + if (((s32)rm) < 0) + rm = ~rm; + if ((rm & 0xFFFF0000) == 0) + clockTicks += 1; + else if ((rm & 0xFF000000) == 0) + clockTicks += 2; + else + clockTicks += 3; + bus.busPrefetchCount = (bus.busPrefetchCount<>(8-clockTicks)); + clockTicks += codeTicksAccess(bus.armNextPC, BITS_16) + 1; + Z_FLAG = bus.reg[dest].I ? false : true; + N_FLAG = bus.reg[dest].I & 0x80000000 ? true : false; +} + +// BIC Rd, Rs + void thumb43_2(u32 opcode) +{ + int dest = opcode & 7; + bus.reg[dest].I &= (~bus.reg[(opcode >> 3) & 7].I); + Z_FLAG = bus.reg[dest].I ? false : true; + N_FLAG = bus.reg[dest].I & 0x80000000 ? true : false; +} + +// MVN Rd, Rs + void thumb43_3(u32 opcode) +{ + int dest = opcode & 7; + bus.reg[dest].I = ~bus.reg[(opcode >> 3) & 7].I; + Z_FLAG = bus.reg[dest].I ? false : true; + N_FLAG = bus.reg[dest].I & 0x80000000 ? true : false; +} + +// High-register instructions and BX ////////////////////////////////////// + +// ADD Rd, Hs + void thumb44_1(u32 opcode) +{ + bus.reg[opcode&7].I += bus.reg[((opcode>>3)&7)+8].I; +} + +// ADD Hd, Rs + void thumb44_2(u32 opcode) +{ + bus.reg[(opcode&7)+8].I += bus.reg[(opcode>>3)&7].I; + if((opcode&7) == 7) { + bus.reg[15].I &= 0xFFFFFFFE; + bus.armNextPC = bus.reg[15].I; + bus.reg[15].I += 2; + THUMB_PREFETCH; + clockTicks = codeTicksAccessSeq16(bus.armNextPC)<<1 + + codeTicksAccess(bus.armNextPC, BITS_16) + 3; + } +} + +// ADD Hd, Hs + void thumb44_3(u32 opcode) +{ + bus.reg[(opcode&7)+8].I += bus.reg[((opcode>>3)&7)+8].I; + if((opcode&7) == 7) { + bus.reg[15].I &= 0xFFFFFFFE; + bus.armNextPC = bus.reg[15].I; + bus.reg[15].I += 2; + THUMB_PREFETCH; + clockTicks = codeTicksAccessSeq16(bus.armNextPC)<<1 + + codeTicksAccess(bus.armNextPC, BITS_16) + 3; + } +} + +// CMP Rd, Hs + void thumb45_1(u32 opcode) +{ + int dest = opcode & 7; + u32 value = bus.reg[((opcode>>3)&7)+8].I; + CMP_RD_RS; +} + +// CMP Hd, Rs + void thumb45_2(u32 opcode) +{ + int dest = (opcode & 7) + 8; + u32 value = bus.reg[(opcode>>3)&7].I; + CMP_RD_RS; +} + +// CMP Hd, Hs + void thumb45_3(u32 opcode) +{ + int dest = (opcode & 7) + 8; + u32 value = bus.reg[((opcode>>3)&7)+8].I; + CMP_RD_RS; +} + +// MOV Rd, Hs + void thumb46_1(u32 opcode) +{ + bus.reg[opcode&7].I = bus.reg[((opcode>>3)&7)+8].I; +} + +// MOV Hd, Rs + void thumb46_2(u32 opcode) +{ + bus.reg[(opcode&7)+8].I = bus.reg[(opcode>>3)&7].I; + if((opcode&7) == 7) { + bus.reg[15].I &= 0xFFFFFFFE; + bus.armNextPC = bus.reg[15].I; + bus.reg[15].I += 2; + THUMB_PREFETCH; + clockTicks = codeTicksAccessSeq16(bus.armNextPC)<<1 + + codeTicksAccess(bus.armNextPC, BITS_16) + 3; + } +} + +// MOV Hd, Hs + void thumb46_3(u32 opcode) +{ + bus.reg[(opcode&7)+8].I = bus.reg[((opcode>>3)&7)+8].I; + if((opcode&7) == 7) { + bus.reg[15].I &= 0xFFFFFFFE; + bus.armNextPC = bus.reg[15].I; + bus.reg[15].I += 2; + THUMB_PREFETCH; + clockTicks = codeTicksAccessSeq16(bus.armNextPC)<<1 + + codeTicksAccess(bus.armNextPC, BITS_16) + 3; + } +} + + +// BX Rs + void thumb47(u32 opcode) +{ + int base = (opcode >> 3) & 15; + bus.busPrefetchCount=0; + bus.reg[15].I = bus.reg[base].I; + if(bus.reg[base].I & 1) { + armState = false; + bus.reg[15].I &= 0xFFFFFFFE; + bus.armNextPC = bus.reg[15].I; + bus.reg[15].I += 2; + THUMB_PREFETCH; + clockTicks = ((codeTicksAccessSeq16(bus.armNextPC)) << 1) + + codeTicksAccess(bus.armNextPC, BITS_16) + 3; + } else { + armState = true; + bus.reg[15].I &= 0xFFFFFFFC; + bus.armNextPC = bus.reg[15].I; + bus.reg[15].I += 4; + ARM_PREFETCH; + clockTicks = ((codeTicksAccessSeq32(bus.armNextPC)) << 1) + + codeTicksAccess(bus.armNextPC, BITS_32) + 3; + } +} + +// Load/store instructions //////////////////////////////////////////////// + +// LDR R0~R7,[PC, #Imm] + void thumb48(u32 opcode) +{ + u8 regist = (opcode >> 8) & 7; + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + u32 address = (bus.reg[15].I & 0xFFFFFFFC) + ((opcode & 0xFF) << 2); + // why quick? + // bus.reg[regist].I = CPUReadMemoryQuick(address); + bus.reg[regist].I = CPUReadMemory(address); + bus.busPrefetchCount=0; + int dataticks_value = DATATICKS_ACCESS_32BIT(address); + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); + clockTicks = 3 + dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16); +} + +// STR Rd, [Rs, Rn] + void thumb50(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + u32 address = bus.reg[(opcode>>3)&7].I + bus.reg[(opcode>>6)&7].I; + CPUWriteMemory(address, bus.reg[opcode & 7].I); + int dataticks_value = DATATICKS_ACCESS_32BIT(address); + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); + clockTicks = dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16) + 2; +} + +// STRH Rd, [Rs, Rn] + void thumb52(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + u32 address = bus.reg[(opcode>>3)&7].I + bus.reg[(opcode>>6)&7].I; + CPUWriteHalfWord(address, bus.reg[opcode&7].W.W0); + int dataticks_value = DATATICKS_ACCESS_16BIT(address); + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); + clockTicks = dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16) + 2; +} + +// STRB Rd, [Rs, Rn] + void thumb54(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + u32 address = bus.reg[(opcode>>3)&7].I + bus.reg[(opcode >>6)&7].I; + CPUWriteByte(address, bus.reg[opcode & 7].B.B0); + int dataticks_value = DATATICKS_ACCESS_16BIT(address); + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); + clockTicks = dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16) + 2; +} + +// LDSB Rd, [Rs, Rn] + void thumb56(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + u32 address = bus.reg[(opcode>>3)&7].I + bus.reg[(opcode>>6)&7].I; + bus.reg[opcode&7].I = (s8)CPUReadByte(address); + int dataticks_value = DATATICKS_ACCESS_16BIT(address); + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); + clockTicks = 3 + dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16); +} + +// LDR Rd, [Rs, Rn] + void thumb58(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + u32 address = bus.reg[(opcode>>3)&7].I + bus.reg[(opcode>>6)&7].I; + bus.reg[opcode&7].I = CPUReadMemory(address); + int dataticks_value = DATATICKS_ACCESS_32BIT(address); + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); + clockTicks = 3 + dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16); +} + +// LDRH Rd, [Rs, Rn] + void thumb5A(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + u32 address = bus.reg[(opcode>>3)&7].I + bus.reg[(opcode>>6)&7].I; + bus.reg[opcode&7].I = CPUReadHalfWord(address); + int dataticks_value = DATATICKS_ACCESS_32BIT(address); + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); + clockTicks = 3 + dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16); +} + +// LDRB Rd, [Rs, Rn] + void thumb5C(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + u32 address = bus.reg[(opcode>>3)&7].I + bus.reg[(opcode>>6)&7].I; + bus.reg[opcode&7].I = CPUReadByte(address); + int dataticks_value = DATATICKS_ACCESS_16BIT(address); + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); + clockTicks = 3 + dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16); +} + +// LDSH Rd, [Rs, Rn] + void thumb5E(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + u32 address = bus.reg[(opcode>>3)&7].I + bus.reg[(opcode>>6)&7].I; + bus.reg[opcode&7].I = (s16)CPUReadHalfWordSigned(address); + int dataticks_value = DATATICKS_ACCESS_16BIT(address); + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); + clockTicks = 3 + dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16); +} + +// STR Rd, [Rs, #Imm] + void thumb60(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + u32 address = bus.reg[(opcode>>3)&7].I + (((opcode>>6)&31)<<2); + CPUWriteMemory(address, bus.reg[opcode&7].I); + int dataticks_value = DATATICKS_ACCESS_32BIT(address); + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); + clockTicks = dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16) + 2; +} + +// LDR Rd, [Rs, #Imm] + void thumb68(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + u32 address = bus.reg[(opcode>>3)&7].I + (((opcode>>6)&31)<<2); + bus.reg[opcode&7].I = CPUReadMemory(address); + int dataticks_value = DATATICKS_ACCESS_32BIT(address); + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); + clockTicks = 3 + dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16); +} + +// STRB Rd, [Rs, #Imm] + void thumb70(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + u32 address = bus.reg[(opcode>>3)&7].I + (((opcode>>6)&31)); + CPUWriteByte(address, bus.reg[opcode&7].B.B0); + int dataticks_value = DATATICKS_ACCESS_16BIT(address); + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); + clockTicks = dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16) + 2; +} + +// LDRB Rd, [Rs, #Imm] + void thumb78(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + u32 address = bus.reg[(opcode>>3)&7].I + (((opcode>>6)&31)); + bus.reg[opcode&7].I = CPUReadByte(address); + int dataticks_value = DATATICKS_ACCESS_16BIT(address); + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); + clockTicks = 3 + dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16); +} + +// STRH Rd, [Rs, #Imm] + void thumb80(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + u32 address = bus.reg[(opcode>>3)&7].I + (((opcode>>6)&31)<<1); + CPUWriteHalfWord(address, bus.reg[opcode&7].W.W0); + int dataticks_value = DATATICKS_ACCESS_16BIT(address); + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); + clockTicks = dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16) + 2; +} + +// LDRH Rd, [Rs, #Imm] + void thumb88(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + u32 address = bus.reg[(opcode>>3)&7].I + (((opcode>>6)&31)<<1); + bus.reg[opcode&7].I = CPUReadHalfWord(address); + int dataticks_value = DATATICKS_ACCESS_16BIT(address); + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); + clockTicks = 3 + dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16); +} + +// STR R0~R7, [SP, #Imm] + void thumb90(u32 opcode) +{ + u8 regist = (opcode >> 8) & 7; + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + u32 address = bus.reg[13].I + ((opcode&255)<<2); + CPUWriteMemory(address, bus.reg[regist].I); + int dataticks_value = DATATICKS_ACCESS_32BIT(address); + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); + clockTicks = dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16) + 2; +} + +// LDR R0~R7, [SP, #Imm] + void thumb98(u32 opcode) +{ + u8 regist = (opcode >> 8) & 7; + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + u32 address = bus.reg[13].I + ((opcode&255)<<2); + // why quick? + // bus.reg[regist].I = CPUReadMemoryQuick(address); + bus.reg[regist].I = CPUReadMemory(address); + int dataticks_value = DATATICKS_ACCESS_32BIT(address); + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); + clockTicks = 3 + dataticks_value + codeTicksAccess(bus.armNextPC, BITS_16); +} + +// PC/stack-related /////////////////////////////////////////////////////// + +// ADD R0~R7, PC, Imm + void thumbA0(u32 opcode) +{ + u8 regist = (opcode >> 8) & 7; + bus.reg[regist].I = (bus.reg[15].I & 0xFFFFFFFC) + ((opcode&255)<<2); +} + +// ADD R0~R7, SP, Imm + void thumbA8(u32 opcode) +{ + u8 regist = (opcode >> 8) & 7; + bus.reg[regist].I = bus.reg[13].I + ((opcode&255)<<2); +} + +// ADD SP, Imm + void thumbB0(u32 opcode) +{ + int offset = (opcode & 127) << 2; + if(opcode & 0x80) + offset = -offset; + bus.reg[13].I += offset; +} + +// Push and pop /////////////////////////////////////////////////////////// + +#define PUSH_REG(val, r) \ + if (opcode & (val)) { \ + CPUWriteMemory(address, bus.reg[(r)].I); \ + int dataticks_value = count ? DATATICKS_ACCESS_32BIT_SEQ(address) : DATATICKS_ACCESS_32BIT(address); \ + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); \ + clockTicks += 1 + dataticks_value; \ + count++; \ + address += 4; \ + } + +#define POP_REG(val, r) \ + if (opcode & (val)) { \ + bus.reg[(r)].I = CPUReadMemory(address); \ +int dataticks_value = count ? DATATICKS_ACCESS_32BIT_SEQ(address) : DATATICKS_ACCESS_32BIT(address); \ + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); \ + clockTicks += 1 + dataticks_value; \ + count++; \ + address += 4; \ + } + +// PUSH {Rlist} + void thumbB4(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int count = 0; + u32 temp = bus.reg[13].I - 4 * cpuBitsSet[opcode & 0xff]; + u32 address = temp & 0xFFFFFFFC; + PUSH_REG(1, 0); + PUSH_REG(2, 1); + PUSH_REG(4, 2); + PUSH_REG(8, 3); + PUSH_REG(16, 4); + PUSH_REG(32, 5); + PUSH_REG(64, 6); + PUSH_REG(128, 7); + clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_16); + bus.reg[13].I = temp; +} + +// PUSH {Rlist, LR} + void thumbB5(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int count = 0; + u32 temp = bus.reg[13].I - 4 - 4 * cpuBitsSet[opcode & 0xff]; + u32 address = temp & 0xFFFFFFFC; + PUSH_REG(1, 0); + PUSH_REG(2, 1); + PUSH_REG(4, 2); + PUSH_REG(8, 3); + PUSH_REG(16, 4); + PUSH_REG(32, 5); + PUSH_REG(64, 6); + PUSH_REG(128, 7); + PUSH_REG(256, 14); + clockTicks += 1 + codeTicksAccess(bus.armNextPC, BITS_16); + bus.reg[13].I = temp; +} + +// POP {Rlist} + void thumbBC(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int count = 0; + u32 address = bus.reg[13].I & 0xFFFFFFFC; + u32 temp = bus.reg[13].I + 4*cpuBitsSet[opcode & 0xFF]; + POP_REG(1, 0); + POP_REG(2, 1); + POP_REG(4, 2); + POP_REG(8, 3); + POP_REG(16, 4); + POP_REG(32, 5); + POP_REG(64, 6); + POP_REG(128, 7); + bus.reg[13].I = temp; + clockTicks = 2 + codeTicksAccess(bus.armNextPC, BITS_16); +} + +// POP {Rlist, PC} + void thumbBD(u32 opcode) +{ + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + int count = 0; + u32 address = bus.reg[13].I & 0xFFFFFFFC; + u32 temp = bus.reg[13].I + 4 + 4*cpuBitsSet[opcode & 0xFF]; + POP_REG(1, 0); + POP_REG(2, 1); + POP_REG(4, 2); + POP_REG(8, 3); + POP_REG(16, 4); + POP_REG(32, 5); + POP_REG(64, 6); + POP_REG(128, 7); + bus.reg[15].I = (CPUReadMemory(address) & 0xFFFFFFFE); + int dataticks_value = count ? DATATICKS_ACCESS_32BIT_SEQ(address) : DATATICKS_ACCESS_32BIT(address); + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_value); + clockTicks += 1 + dataticks_value; + count++; + bus.armNextPC = bus.reg[15].I; + bus.reg[15].I += 2; + bus.reg[13].I = temp; + THUMB_PREFETCH; + bus.busPrefetchCount = 0; + clockTicks += 3 + ((codeTicksAccess(bus.armNextPC, BITS_16)) << 1); +} + +// Load/store multiple //////////////////////////////////////////////////// + +#define THUMB_STM_REG(val,r,b) \ + if(opcode & (val)) { \ + CPUWriteMemory(address, bus.reg[(r)].I); \ + bus.reg[(b)].I = temp; \ + int dataticks_val = count ? DATATICKS_ACCESS_32BIT_SEQ(address) : DATATICKS_ACCESS_32BIT(address); \ + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_val); \ + clockTicks += 1 + dataticks_val; \ + count++; \ + address += 4; \ + } + +#define THUMB_LDM_REG(val,r) \ + if(opcode & (val)) { \ + bus.reg[(r)].I = CPUReadMemory(address); \ + int dataticks_val = count ? DATATICKS_ACCESS_32BIT_SEQ(address) : DATATICKS_ACCESS_32BIT(address); \ + DATATICKS_ACCESS_BUS_PREFETCH(address, dataticks_val); \ + clockTicks += 1 + dataticks_val; \ + count++; \ + address += 4; \ + } + +// STM R0~7!, {Rlist} + void thumbC0(u32 opcode) +{ + u8 regist = (opcode >> 8) & 7; + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + u32 address = bus.reg[regist].I & 0xFFFFFFFC; + u32 temp = bus.reg[regist].I + 4*cpuBitsSet[opcode & 0xff]; + int count = 0; + // store + THUMB_STM_REG(1, 0, regist); + THUMB_STM_REG(2, 1, regist); + THUMB_STM_REG(4, 2, regist); + THUMB_STM_REG(8, 3, regist); + THUMB_STM_REG(16, 4, regist); + THUMB_STM_REG(32, 5, regist); + THUMB_STM_REG(64, 6, regist); + THUMB_STM_REG(128, 7, regist); + clockTicks = 1 + codeTicksAccess(bus.armNextPC, BITS_16); +} + +// LDM R0~R7!, {Rlist} + void thumbC8(u32 opcode) +{ + u8 regist = (opcode >> 8) & 7; + if (bus.busPrefetchCount == 0) + bus.busPrefetch = bus.busPrefetchEnable; + u32 address = bus.reg[regist].I & 0xFFFFFFFC; + u32 temp = bus.reg[regist].I + 4*cpuBitsSet[opcode & 0xFF]; + int count = 0; + // load + THUMB_LDM_REG(1, 0); + THUMB_LDM_REG(2, 1); + THUMB_LDM_REG(4, 2); + THUMB_LDM_REG(8, 3); + THUMB_LDM_REG(16, 4); + THUMB_LDM_REG(32, 5); + THUMB_LDM_REG(64, 6); + THUMB_LDM_REG(128, 7); + clockTicks = 2 + codeTicksAccess(bus.armNextPC, BITS_16); + if(!(opcode & (1<*thumbInsnTable[opcode>>6])(opcode); + + ct = clockTicks; + + if (ct < 0) + return 0; + + /// better pipelining + if (ct==0) + clockTicks = codeTicksAccessSeq16(oldArmNextPC) + 1; + + cpuTotalTicks += clockTicks; + +} while ((cpuTotalTicks < cpuNextEvent) & ~armState & ~holdState); + return 1; +} + + +/*============================================================ + GBA GFX +============================================================ */ + +#ifdef TILED_RENDERING +#ifdef _MSC_VER +union u8h +{ + __pragma( pack(push, 1)); + struct +#ifdef LSB_FIRST + { + /* 0*/ unsigned char lo:4; + /* 4*/ unsigned char hi:4; +#else + { + /* 4*/ unsigned char hi:4; + /* 0*/ unsigned char lo:4; +#endif + } + __pragma(pack(pop)); + u8 val; +}; +#else +union u8h +{ + struct +#ifdef LSB_FIRST + { + /* 0*/ unsigned char lo:4; + /* 4*/ unsigned char hi:4; +#else + { + /* 4*/ unsigned char hi:4; + /* 0*/ unsigned char lo:4; +#endif + } __attribute__ ((packed)); + u8 val; +}; +#endif + +union TileEntry +{ +#ifdef LSB_FIRST + struct + { + /* 0*/ unsigned tileNum:10; + /*12*/ unsigned hFlip:1; + /*13*/ unsigned vFlip:1; + /*14*/ unsigned palette:4; + }; +#else + struct + { + /*14*/ unsigned palette:4; + /*13*/ unsigned vFlip:1; + /*12*/ unsigned hFlip:1; + /* 0*/ unsigned tileNum:10; + }; +#endif + u16 val; +}; + +struct TileLine +{ + u32 pixels[8]; +}; + +typedef const TileLine (*TileReader) (const u16 *, const int, const u8 *, u16 *, const u32); + +static inline void gfxDrawPixel(u32 *dest, const u8 color, const u16 *palette, const u32 prio) +{ + *dest = color ? (READ16LE(&palette[color]) | prio): 0x80000000; +} + +static inline const TileLine gfxReadTile(const u16 *screenSource, const int yyy, const u8 *charBase, u16 *palette, const u32 prio) +{ + TileEntry tile; + tile.val = READ16LE(screenSource); + + int tileY = yyy & 7; + if (tile.vFlip) tileY = 7 - tileY; + TileLine tileLine; + + const u8 *tileBase = &charBase[tile.tileNum * 64 + tileY * 8]; + + if (!tile.hFlip) + { + gfxDrawPixel(&tileLine.pixels[0], tileBase[0], palette, prio); + gfxDrawPixel(&tileLine.pixels[1], tileBase[1], palette, prio); + gfxDrawPixel(&tileLine.pixels[2], tileBase[2], palette, prio); + gfxDrawPixel(&tileLine.pixels[3], tileBase[3], palette, prio); + gfxDrawPixel(&tileLine.pixels[4], tileBase[4], palette, prio); + gfxDrawPixel(&tileLine.pixels[5], tileBase[5], palette, prio); + gfxDrawPixel(&tileLine.pixels[6], tileBase[6], palette, prio); + gfxDrawPixel(&tileLine.pixels[7], tileBase[7], palette, prio); + } + else + { + gfxDrawPixel(&tileLine.pixels[0], tileBase[7], palette, prio); + gfxDrawPixel(&tileLine.pixels[1], tileBase[6], palette, prio); + gfxDrawPixel(&tileLine.pixels[2], tileBase[5], palette, prio); + gfxDrawPixel(&tileLine.pixels[3], tileBase[4], palette, prio); + gfxDrawPixel(&tileLine.pixels[4], tileBase[3], palette, prio); + gfxDrawPixel(&tileLine.pixels[5], tileBase[2], palette, prio); + gfxDrawPixel(&tileLine.pixels[6], tileBase[1], palette, prio); + gfxDrawPixel(&tileLine.pixels[7], tileBase[0], palette, prio); + } + + return tileLine; +} + +static inline const TileLine gfxReadTilePal(const u16 *screenSource, const int yyy, const u8 *charBase, u16 *palette, const u32 prio) +{ + TileEntry tile; + tile.val = READ16LE(screenSource); + + int tileY = yyy & 7; + if (tile.vFlip) tileY = 7 - tileY; + palette += tile.palette * 16; + TileLine tileLine; + + const u8h *tileBase = (u8h*) &charBase[tile.tileNum * 32 + tileY * 4]; + + if (!tile.hFlip) + { + gfxDrawPixel(&tileLine.pixels[0], tileBase[0].lo, palette, prio); + gfxDrawPixel(&tileLine.pixels[1], tileBase[0].hi, palette, prio); + gfxDrawPixel(&tileLine.pixels[2], tileBase[1].lo, palette, prio); + gfxDrawPixel(&tileLine.pixels[3], tileBase[1].hi, palette, prio); + gfxDrawPixel(&tileLine.pixels[4], tileBase[2].lo, palette, prio); + gfxDrawPixel(&tileLine.pixels[5], tileBase[2].hi, palette, prio); + gfxDrawPixel(&tileLine.pixels[6], tileBase[3].lo, palette, prio); + gfxDrawPixel(&tileLine.pixels[7], tileBase[3].hi, palette, prio); + } + else + { + gfxDrawPixel(&tileLine.pixels[0], tileBase[3].hi, palette, prio); + gfxDrawPixel(&tileLine.pixels[1], tileBase[3].lo, palette, prio); + gfxDrawPixel(&tileLine.pixels[2], tileBase[2].hi, palette, prio); + gfxDrawPixel(&tileLine.pixels[3], tileBase[2].lo, palette, prio); + gfxDrawPixel(&tileLine.pixels[4], tileBase[1].hi, palette, prio); + gfxDrawPixel(&tileLine.pixels[5], tileBase[1].lo, palette, prio); + gfxDrawPixel(&tileLine.pixels[6], tileBase[0].hi, palette, prio); + gfxDrawPixel(&tileLine.pixels[7], tileBase[0].lo, palette, prio); + } + + return tileLine; +} + +static inline void gfxDrawTile(const TileLine &tileLine, u32 *line) +{ + memcpy(line, tileLine.pixels, sizeof(tileLine.pixels)); +} + +static inline void gfxDrawTileClipped(const TileLine &tileLine, u32 *line, const int start, int w) +{ + memcpy(line, tileLine.pixels + start, w * sizeof(u32)); +} + +template +void gfxDrawTextScreen(u16 control, u16 hofs, u16 vofs, + u32 *line) +{ + u16 *palette = (u16 *)graphics.paletteRAM; + u8 *charBase = &vram[((control >> 2) & 0x03) * 0x4000]; + u16 *screenBase = (u16 *)&vram[((control >> 8) & 0x1f) * 0x800]; + u32 prio = ((control & 3)<<25) + 0x1000000; + int sizeX = 256; + int sizeY = 256; + switch ((control >> 14) & 3) + { + case 0: + break; + case 1: + sizeX = 512; + break; + case 2: + sizeY = 512; + break; + case 3: + sizeX = 512; + sizeY = 512; + break; + } + + int maskX = sizeX-1; + int maskY = sizeY-1; + + bool mosaicOn = (control & 0x40) ? true : false; + + int xxx = hofs & maskX; + int yyy = (vofs + io_registers[REG_VCOUNT]) & maskY; + int mosaicX = (MOSAIC & 0x000F)+1; + int mosaicY = ((MOSAIC & 0x00F0)>>4)+1; + + if (mosaicOn) + { + if ((io_registers[REG_VCOUNT] % mosaicY) != 0) + { + mosaicY = io_registers[REG_VCOUNT] - (io_registers[REG_VCOUNT] % mosaicY); + yyy = (vofs + mosaicY) & maskY; + } + } + + if (yyy > 255 && sizeY > 256) + { + yyy &= 255; + screenBase += 0x400; + if (sizeX > 256) + screenBase += 0x400; + } + + int yshift = ((yyy>>3)<<5); + + u16 *screenSource = screenBase + 0x400 * (xxx>>8) + ((xxx & 255)>>3) + yshift; + int x = 0; + const int firstTileX = xxx & 7; + + // First tile, if clipped + if (firstTileX) + { + gfxDrawTileClipped(readTile(screenSource, yyy, charBase, palette, prio), &line[x], firstTileX, 8 - firstTileX); + screenSource++; + x += 8 - firstTileX; + xxx += 8 - firstTileX; + + if (xxx == 256 && sizeX > 256) + { + screenSource = screenBase + 0x400 + yshift; + } + else if (xxx >= sizeX) + { + xxx = 0; + screenSource = screenBase + yshift; + } + } + + // Middle tiles, full + while (x < 240 - firstTileX) + { + gfxDrawTile(readTile(screenSource, yyy, charBase, palette, prio), &line[x]); + screenSource++; + xxx += 8; + x += 8; + + if (xxx == 256 && sizeX > 256) + { + screenSource = screenBase + 0x400 + yshift; + } + else if (xxx >= sizeX) + { + xxx = 0; + screenSource = screenBase + yshift; + } + } + + // Last tile, if clipped + if (firstTileX) + { + gfxDrawTileClipped(readTile(screenSource, yyy, charBase, palette, prio), &line[x], 0, firstTileX); + } + + if (mosaicOn) + { + if (mosaicX > 1) + { + int m = 1; + for (int i = 0; i < 239; i++) + { + line[i+1] = line[i]; + m++; + if (m == mosaicX) + { + m = 1; + i++; + } + } + } + } +} + +void gfxDrawTextScreen(u16 control, u16 hofs, u16 vofs, u32 *line) +{ + if (control & 0x80) // 1 pal / 256 col + gfxDrawTextScreen(control, hofs, vofs, line); + else // 16 pal / 16 col + gfxDrawTextScreen(control, hofs, vofs, line); +} +#else +inline void gfxDrawTextScreen(u16 control, u16 hofs, u16 vofs, + u32 *line) +{ + u16 *palette = (u16 *)graphics.paletteRAM; + u8 *charBase = &vram[((control >> 2) & 0x03) * 0x4000]; + u16 *screenBase = (u16 *)&vram[((control >> 8) & 0x1f) * 0x800]; + u32 prio = ((control & 3)<<25) + 0x1000000; + int sizeX = 256; + int sizeY = 256; + switch((control >> 14) & 3) { + case 0: + break; + case 1: + sizeX = 512; + break; + case 2: + sizeY = 512; + break; + case 3: + sizeX = 512; + sizeY = 512; + break; + } + + int maskX = sizeX-1; + int maskY = sizeY-1; + + bool mosaicOn = (control & 0x40) ? true : false; + + int xxx = hofs & maskX; + int yyy = (vofs + io_registers[REG_VCOUNT]) & maskY; + int mosaicX = (MOSAIC & 0x000F)+1; + int mosaicY = ((MOSAIC & 0x00F0)>>4)+1; + + if(mosaicOn) { + if((io_registers[REG_VCOUNT] % mosaicY) != 0) { + mosaicY = io_registers[REG_VCOUNT] - (io_registers[REG_VCOUNT] % mosaicY); + yyy = (vofs + mosaicY) & maskY; + } + } + + if(yyy > 255 && sizeY > 256) { + yyy &= 255; + screenBase += 0x400; + if(sizeX > 256) + screenBase += 0x400; + } + + int yshift = ((yyy>>3)<<5); + if((control) & 0x80) { + u16 *screenSource = screenBase + 0x400 * (xxx>>8) + ((xxx & 255)>>3) + yshift; + for(int x = 0; x < 240; x++) { + u16 data = READ16LE(screenSource); + + int tile = data & 0x3FF; + int tileX = (xxx & 7); + int tileY = yyy & 7; + + if(tileX == 7) + screenSource++; + + if(data & 0x0400) + tileX = 7 - tileX; + if(data & 0x0800) + tileY = 7 - tileY; + + u8 color = charBase[tile * 64 + tileY * 8 + tileX]; + + line[x] = color ? (READ16LE(&palette[color]) | prio): 0x80000000; + + xxx++; + if(xxx == 256) { + if(sizeX > 256) + screenSource = screenBase + 0x400 + yshift; + else { + screenSource = screenBase + yshift; + xxx = 0; + } + } else if(xxx >= sizeX) { + xxx = 0; + screenSource = screenBase + yshift; + } + } + } else { + u16 *screenSource = screenBase + 0x400*(xxx>>8)+((xxx&255)>>3) + + yshift; + for(int x = 0; x < 240; x++) { + u16 data = READ16LE(screenSource); + + int tile = data & 0x3FF; + int tileX = (xxx & 7); + int tileY = yyy & 7; + + if(tileX == 7) + screenSource++; + + if(data & 0x0400) + tileX = 7 - tileX; + if(data & 0x0800) + tileY = 7 - tileY; + + u8 color = charBase[(tile<<5) + (tileY<<2) + (tileX>>1)]; + + if(tileX & 1) { + color = (color >> 4); + } else { + color &= 0x0F; + } + + int pal = (data>>8) & 0xF0; + line[x] = color ? (READ16LE(&palette[pal + color])|prio): 0x80000000; + + xxx++; + if(xxx == 256) { + if(sizeX > 256) + screenSource = screenBase + 0x400 + yshift; + else { + screenSource = screenBase + yshift; + xxx = 0; + } + } else if(xxx >= sizeX) { + xxx = 0; + screenSource = screenBase + yshift; + } + } + } + if(mosaicOn) { + if(mosaicX > 1) { + int m = 1; + for(int i = 0; i < 239; i++) { + line[i+1] = line[i]; + m++; + if(m == mosaicX) { + m = 1; + i++; + } + } + } + } +} +#endif + +INLINE void gfxDrawRotScreen(u16 control, u16 x_l, u16 x_h, u16 y_l, u16 y_h, +u16 pa, u16 pb, u16 pc, u16 pd, int& currentX, int& currentY, int changed, u32 *line) +{ + u16 *palette = (u16 *)graphics.paletteRAM; + u8 *charBase = &vram[((control >> 2) & 0x03) << 14]; + u8 *screenBase = (u8 *)&vram[((control >> 8) & 0x1f) << 11]; + int prio = ((control & 3) << 25) + 0x1000000; + + u32 map_size = (control >> 14) & 3; + u32 sizeX = map_sizes_rot[map_size]; + u32 sizeY = map_sizes_rot[map_size]; + + int maskX = sizeX-1; + int maskY = sizeY-1; + + int yshift = ((control >> 14) & 3)+4; + +#ifdef BRANCHLESS_GBA_GFX + int dx = pa & 0x7FFF; + int dmx = pb & 0x7FFF; + int dy = pc & 0x7FFF; + int dmy = pd & 0x7FFF; + + dx |= isel(-(pa & 0x8000), 0, 0xFFFF8000); + + dmx |= isel(-(pb & 0x8000), 0, 0xFFFF8000); + + dy |= isel(-(pc & 0x8000), 0, 0xFFFF8000); + + dmy |= isel(-(pd & 0x8000), 0, 0xFFFF8000); +#else + int dx = pa & 0x7FFF; + if(pa & 0x8000) + dx |= 0xFFFF8000; + int dmx = pb & 0x7FFF; + if(pb & 0x8000) + dmx |= 0xFFFF8000; + int dy = pc & 0x7FFF; + if(pc & 0x8000) + dy |= 0xFFFF8000; + int dmy = pd & 0x7FFF; + if(pd & 0x8000) + dmy |= 0xFFFF8000; +#endif + + if(io_registers[REG_VCOUNT] == 0) + changed = 3; + + currentX += dmx; + currentY += dmy; + + if(changed & 1) + { + currentX = (x_l) | ((x_h & 0x07FF)<<16); + if(x_h & 0x0800) + currentX |= 0xF8000000; + } + + if(changed & 2) + { + currentY = (y_l) | ((y_h & 0x07FF)<<16); + if(y_h & 0x0800) + currentY |= 0xF8000000; + } + + int realX = currentX; + int realY = currentY; + + if(control & 0x40) + { + int mosaicY = ((MOSAIC & 0xF0)>>4) + 1; + int y = (io_registers[REG_VCOUNT] % mosaicY); + realX -= y*dmx; + realY -= y*dmy; + } + + memset(line, -1, 240 * sizeof(u32)); + if(control & 0x2000) + { + for(u32 x = 0; x < 240u; ++x) + { + int xxx = (realX >> 8) & maskX; + int yyy = (realY >> 8) & maskY; + + int tile = screenBase[(xxx>>3) + ((yyy>>3)<> 8); + unsigned yyy = (realY >> 8); + + if(xxx < sizeX && yyy < sizeY) + { + int tile = screenBase[(xxx>>3) + ((yyy>>3)< 1) + { + int m = 1; + for(u32 i = 0; i < 239u; ++i) + { + line[i+1] = line[i]; + if(++m == mosaicX) + { + m = 1; + ++i; + } + } + } + } +} + +INLINE void gfxDrawRotScreen16Bit( int& currentX, int& currentY, int changed) +{ + u16 *screenBase = (u16 *)&vram[0]; + int prio = ((io_registers[REG_BG2CNT] & 3) << 25) + 0x1000000; + + u32 sizeX = 240; + u32 sizeY = 160; + + int startX = (BG2X_L) | ((BG2X_H & 0x07FF)<<16); + if(BG2X_H & 0x0800) + startX |= 0xF8000000; + int startY = (BG2Y_L) | ((BG2Y_H & 0x07FF)<<16); + if(BG2Y_H & 0x0800) + startY |= 0xF8000000; + +#ifdef BRANCHLESS_GBA_GFX + int dx = io_registers[REG_BG2PA] & 0x7FFF; + dx |= isel(-(io_registers[REG_BG2PA] & 0x8000), 0, 0xFFFF8000); + + int dmx = io_registers[REG_BG2PB] & 0x7FFF; + dmx |= isel(-(io_registers[REG_BG2PB] & 0x8000), 0, 0xFFFF8000); + + int dy = io_registers[REG_BG2PC] & 0x7FFF; + dy |= isel(-(io_registers[REG_BG2PC] & 0x8000), 0, 0xFFFF8000); + + int dmy = io_registers[REG_BG2PD] & 0x7FFF; + dmy |= isel(-(io_registers[REG_BG2PD] & 0x8000), 0, 0xFFFF8000); +#else + int dx = io_registers[REG_BG2PA] & 0x7FFF; + if(io_registers[REG_BG2PA] & 0x8000) + dx |= 0xFFFF8000; + int dmx = io_registers[REG_BG2PB] & 0x7FFF; + if(io_registers[REG_BG2PB] & 0x8000) + dmx |= 0xFFFF8000; + int dy = io_registers[REG_BG2PC] & 0x7FFF; + if(io_registers[REG_BG2PC] & 0x8000) + dy |= 0xFFFF8000; + int dmy = io_registers[REG_BG2PD] & 0x7FFF; + if(io_registers[REG_BG2PD] & 0x8000) + dmy |= 0xFFFF8000; +#endif + + if(io_registers[REG_VCOUNT] == 0) + changed = 3; + + currentX += dmx; + currentY += dmy; + + if(changed & 1) + { + currentX = (BG2X_L) | ((BG2X_H & 0x07FF)<<16); + if(BG2X_H & 0x0800) + currentX |= 0xF8000000; + } + + if(changed & 2) + { + currentY = (BG2Y_L) | ((BG2Y_H & 0x07FF)<<16); + if(BG2Y_H & 0x0800) + currentY |= 0xF8000000; + } + + int realX = currentX; + int realY = currentY; + + if(io_registers[REG_BG2CNT] & 0x40) { + int mosaicY = ((MOSAIC & 0xF0)>>4) + 1; + int y = (io_registers[REG_VCOUNT] % mosaicY); + realX -= y*dmx; + realY -= y*dmy; + } + + unsigned xxx = (realX >> 8); + unsigned yyy = (realY >> 8); + + memset(line[2], -1, 240 * sizeof(u32)); + for(u32 x = 0; x < 240u; ++x) + { + if(xxx < sizeX && yyy < sizeY) + line[2][x] = (READ16LE(&screenBase[yyy * sizeX + xxx]) | prio); + + realX += dx; + realY += dy; + + xxx = (realX >> 8); + yyy = (realY >> 8); + } + + if(io_registers[REG_BG2CNT] & 0x40) { + int mosaicX = (MOSAIC & 0xF) + 1; + if(mosaicX > 1) { + int m = 1; + for(u32 i = 0; i < 239u; ++i) + { + line[2][i+1] = line[2][i]; + if(++m == mosaicX) + { + m = 1; + ++i; + } + } + } + } +} + +INLINE void gfxDrawRotScreen256(int ¤tX, int& currentY, int changed) +{ + u16 *palette = (u16 *)graphics.paletteRAM; + u8 *screenBase = (io_registers[REG_DISPCNT] & 0x0010) ? &vram[0xA000] : &vram[0x0000]; + int prio = ((io_registers[REG_BG2CNT] & 3) << 25) + 0x1000000; + u32 sizeX = 240; + u32 sizeY = 160; + + int startX = (BG2X_L) | ((BG2X_H & 0x07FF)<<16); + if(BG2X_H & 0x0800) + startX |= 0xF8000000; + int startY = (BG2Y_L) | ((BG2Y_H & 0x07FF)<<16); + if(BG2Y_H & 0x0800) + startY |= 0xF8000000; + +#ifdef BRANCHLESS_GBA_GFX + int dx = io_registers[REG_BG2PA] & 0x7FFF; + dx |= isel(-(io_registers[REG_BG2PA] & 0x8000), 0, 0xFFFF8000); + + int dmx = io_registers[REG_BG2PB] & 0x7FFF; + dmx |= isel(-(io_registers[REG_BG2PB] & 0x8000), 0, 0xFFFF8000); + + int dy = io_registers[REG_BG2PC] & 0x7FFF; + dy |= isel(-(io_registers[REG_BG2PC] & 0x8000), 0, 0xFFFF8000); + + int dmy = io_registers[REG_BG2PD] & 0x7FFF; + dmy |= isel(-(io_registers[REG_BG2PD] & 0x8000), 0, 0xFFFF8000); +#else + int dx = io_registers[REG_BG2PA] & 0x7FFF; + if(io_registers[REG_BG2PA] & 0x8000) + dx |= 0xFFFF8000; + int dmx = io_registers[REG_BG2PB] & 0x7FFF; + if(io_registers[REG_BG2PB] & 0x8000) + dmx |= 0xFFFF8000; + int dy = io_registers[REG_BG2PC] & 0x7FFF; + if(io_registers[REG_BG2PC] & 0x8000) + dy |= 0xFFFF8000; + int dmy = io_registers[REG_BG2PD] & 0x7FFF; + if(io_registers[REG_BG2PD] & 0x8000) + dmy |= 0xFFFF8000; +#endif + + if(io_registers[REG_VCOUNT] == 0) + changed = 3; + + currentX += dmx; + currentY += dmy; + + if(changed & 1) + { + currentX = (BG2X_L) | ((BG2X_H & 0x07FF)<<16); + if(BG2X_H & 0x0800) + currentX |= 0xF8000000; + } + + if(changed & 2) + { + currentY = (BG2Y_L) | ((BG2Y_H & 0x07FF)<<16); + if(BG2Y_H & 0x0800) + currentY |= 0xF8000000; + } + + int realX = currentX; + int realY = currentY; + + if(io_registers[REG_BG2CNT] & 0x40) { + int mosaicY = ((MOSAIC & 0xF0)>>4) + 1; + int y = io_registers[REG_VCOUNT] - (io_registers[REG_VCOUNT] % mosaicY); + realX = startX + y*dmx; + realY = startY + y*dmy; + } + + int xxx = (realX >> 8); + int yyy = (realY >> 8); + + memset(line[2], -1, 240 * sizeof(u32)); + for(u32 x = 0; x < 240; ++x) + { + u8 color = screenBase[yyy * 240 + xxx]; + if(unsigned(xxx) < sizeX && unsigned(yyy) < sizeY && color) + line[2][x] = (READ16LE(&palette[color])|prio); + realX += dx; + realY += dy; + + xxx = (realX >> 8); + yyy = (realY >> 8); + } + + if(io_registers[REG_BG2CNT] & 0x40) + { + int mosaicX = (MOSAIC & 0xF) + 1; + if(mosaicX > 1) + { + int m = 1; + for(u32 i = 0; i < 239u; ++i) + { + line[2][i+1] = line[2][i]; + if(++m == mosaicX) + { + m = 1; + ++i; + } + } + } + } +} + +INLINE void gfxDrawRotScreen16Bit160(int& currentX, int& currentY, int changed) +{ + u16 *screenBase = (io_registers[REG_DISPCNT] & 0x0010) ? (u16 *)&vram[0xa000] : + (u16 *)&vram[0]; + int prio = ((io_registers[REG_BG2CNT] & 3) << 25) + 0x1000000; + u32 sizeX = 160; + u32 sizeY = 128; + + int startX = (BG2X_L) | ((BG2X_H & 0x07FF)<<16); + if(BG2X_H & 0x0800) + startX |= 0xF8000000; + int startY = (BG2Y_L) | ((BG2Y_H & 0x07FF)<<16); + if(BG2Y_H & 0x0800) + startY |= 0xF8000000; + +#ifdef BRANCHLESS_GBA_GFX + int dx = io_registers[REG_BG2PA] & 0x7FFF; + dx |= isel(-(io_registers[REG_BG2PA] & 0x8000), 0, 0xFFFF8000); + + int dmx = io_registers[REG_BG2PB] & 0x7FFF; + dmx |= isel(-(io_registers[REG_BG2PB] & 0x8000), 0, 0xFFFF8000); + + int dy = io_registers[REG_BG2PC] & 0x7FFF; + dy |= isel(-(io_registers[REG_BG2PC] & 0x8000), 0, 0xFFFF8000); + + int dmy = io_registers[REG_BG2PD] & 0x7FFF; + dmy |= isel(-(io_registers[REG_BG2PD] & 0x8000), 0, 0xFFFF8000); +#else + int dx = io_registers[REG_BG2PA] & 0x7FFF; + if(io_registers[REG_BG2PA] & 0x8000) + dx |= 0xFFFF8000; + int dmx = io_registers[REG_BG2PB] & 0x7FFF; + if(io_registers[REG_BG2PB] & 0x8000) + dmx |= 0xFFFF8000; + int dy = io_registers[REG_BG2PC] & 0x7FFF; + if(io_registers[REG_BG2PC] & 0x8000) + dy |= 0xFFFF8000; + int dmy = io_registers[REG_BG2PD] & 0x7FFF; + if(io_registers[REG_BG2PD] & 0x8000) + dmy |= 0xFFFF8000; +#endif + + if(io_registers[REG_VCOUNT] == 0) + changed = 3; + + currentX += dmx; + currentY += dmy; + + if(changed & 1) + { + currentX = (BG2X_L) | ((BG2X_H & 0x07FF)<<16); + if(BG2X_H & 0x0800) + currentX |= 0xF8000000; + } + + if(changed & 2) + { + currentY = (BG2Y_L) | ((BG2Y_H & 0x07FF)<<16); + if(BG2Y_H & 0x0800) + currentY |= 0xF8000000; + } + + int realX = currentX; + int realY = currentY; + + if(io_registers[REG_BG2CNT] & 0x40) { + int mosaicY = ((MOSAIC & 0xF0)>>4) + 1; + int y = io_registers[REG_VCOUNT] - (io_registers[REG_VCOUNT] % mosaicY); + realX = startX + y*dmx; + realY = startY + y*dmy; + } + + int xxx = (realX >> 8); + int yyy = (realY >> 8); + + memset(line[2], -1, 240 * sizeof(u32)); + for(u32 x = 0; x < 240u; ++x) + { + if(unsigned(xxx) < sizeX && unsigned(yyy) < sizeY) + line[2][x] = (READ16LE(&screenBase[yyy * sizeX + xxx]) | prio); + + realX += dx; + realY += dy; + + xxx = (realX >> 8); + yyy = (realY >> 8); + } + + + int mosaicX = (MOSAIC & 0xF) + 1; + if(io_registers[REG_BG2CNT] & 0x40 && (mosaicX > 1)) + { + int m = 1; + for(u32 i = 0; i < 239u; ++i) + { + line[2][i+1] = line[2][i]; + if(++m == mosaicX) + { + m = 1; + ++i; + } + } + } +} + +/* lineOBJpix is used to keep track of the drawn OBJs + and to stop drawing them if the 'maximum number of OBJ per line' + has been reached. */ + +INLINE void gfxDrawSprites (void) +{ + unsigned lineOBJpix, m; + + lineOBJpix = (io_registers[REG_DISPCNT] & 0x20) ? 954 : 1226; + m = 0; + + u16 *sprites = (u16 *)oam; + u16 *spritePalette = &((u16 *)graphics.paletteRAM)[256]; + int mosaicY = ((MOSAIC & 0xF000)>>12) + 1; + int mosaicX = ((MOSAIC & 0xF00)>>8) + 1; + for(u32 x = 0; x < 128; x++) + { + u16 a0 = READ16LE(sprites++); + u16 a1 = READ16LE(sprites++); + u16 a2 = READ16LE(sprites++); + ++sprites; + + lineOBJpixleft[x]=lineOBJpix; + + lineOBJpix-=2; + if (lineOBJpix<=0) + return; + + if ((a0 & 0x0c00) == 0x0c00) + a0 &=0xF3FF; + + u16 a0val = a0>>14; + + if (a0val == 3) + { + a0 &= 0x3FFF; + a1 &= 0x3FFF; + } + + u32 sizeX = 8<<(a1>>14); + u32 sizeY = sizeX; + + + if (a0val & 1) + { +#ifdef BRANCHLESS_GBA_GFX + sizeX <<= isel(-(sizeX & (~31u)), 1, 0); + sizeY >>= isel(-(sizeY>8), 0, 1); +#else + if (sizeX<32) + sizeX<<=1; + if (sizeY>8) + sizeY>>=1; +#endif + } + else if (a0val & 2) + { +#ifdef BRANCHLESS_GBA_GFX + sizeX >>= isel(-(sizeX>8), 0, 1); + sizeY <<= isel(-(sizeY & (~31u)), 1, 0); +#else + if (sizeX>8) + sizeX>>=1; + if (sizeY<32) + sizeY<<=1; +#endif + + } + + + int sy = (a0 & 255); + int sx = (a1 & 0x1FF); + + // computes ticks used by OBJ-WIN if OBJWIN is enabled + if (((a0 & 0x0c00) == 0x0800) && (graphics.layerEnable & 0x8000)) + { + if ((a0 & 0x0300) == 0x0300) + { + sizeX<<=1; + sizeY<<=1; + } + +#ifdef BRANCHLESS_GBA_GFX + sy -= isel(256 - sy - sizeY, 0, 256); + sx -= isel(512 - sx - sizeX, 0, 512); +#else + if((sy+sizeY) > 256) + sy -= 256; + if ((sx+sizeX)> 512) + sx -= 512; +#endif + + if (sx < 0) + { + sizeX+=sx; + sx = 0; + } + else if ((sx+sizeX)>240) + sizeX=240-sx; + + if ((io_registers[REG_VCOUNT]>=sy) && (io_registers[REG_VCOUNT] 256) + sy -= 256; + int t = io_registers[REG_VCOUNT] - sy; + if(unsigned(t) < fieldY) + { + u32 startpix = 0; + if ((sx+fieldX)> 512) + startpix=512-sx; + + if (lineOBJpix && ((sx < 240) || startpix)) + { + lineOBJpix-=8; + int rot = (((a1 >> 9) & 0x1F) << 4); + u16 *OAM = (u16 *)oam; + int dx = READ16LE(&OAM[3 + rot]); + if(dx & 0x8000) + dx |= 0xFFFF8000; + int dmx = READ16LE(&OAM[7 + rot]); + if(dmx & 0x8000) + dmx |= 0xFFFF8000; + int dy = READ16LE(&OAM[11 + rot]); + if(dy & 0x8000) + dy |= 0xFFFF8000; + int dmy = READ16LE(&OAM[15 + rot]); + if(dmy & 0x8000) + dmy |= 0xFFFF8000; + + if(a0 & 0x1000) + t -= (t % mosaicY); + + int realX = ((sizeX) << 7) - (fieldX >> 1)*dx + ((t - (fieldY>>1))* dmx); + int realY = ((sizeY) << 7) - (fieldX >> 1)*dy + ((t - (fieldY>>1))* dmy); + + u32 prio = (((a2 >> 10) & 3) << 25) | ((a0 & 0x0c00)<<6); + + int c = (a2 & 0x3FF); + if((io_registers[REG_DISPCNT] & 7) > 2 && (c < 512)) + continue; + + if(a0 & 0x2000) + { + int inc = 32; + if(io_registers[REG_DISPCNT] & 0x40) + inc = sizeX >> 2; + else + c &= 0x3FE; + for(u32 x = 0; x < fieldX; x++) + { + if (x >= startpix) + lineOBJpix-=2; + unsigned xxx = realX >> 8; + unsigned yyy = realY >> 8; + if(xxx < sizeX && yyy < sizeY && sx < 240) + { + + u32 color = vram[0x10000 + ((((c + (yyy>>3) * inc)<<5) + + ((yyy & 7)<<3) + ((xxx >> 3)<<6) + (xxx & 7))&0x7FFF)]; + + if ((color==0) && (((prio >> 25)&3) < ((line[4][sx]>>25)&3))) + { + line[4][sx] = (line[4][sx] & 0xF9FFFFFF) | prio; + if((a0 & 0x1000) && m) + line[4][sx]=(line[4][sx-1] & 0xF9FFFFFF) | prio; + } + else if((color) && (prio < (line[4][sx]&0xFF000000))) + { + line[4][sx] = READ16LE(&spritePalette[color]) | prio; + if((a0 & 0x1000) && m) + line[4][sx]=(line[4][sx-1] & 0xF9FFFFFF) | prio; + } + + if ((a0 & 0x1000) && ((m+1) == mosaicX)) + m = 0; + } + sx = (sx+1)&511; + realX += dx; + realY += dy; + } + } + else + { + int inc = 32; + if(io_registers[REG_DISPCNT] & 0x40) + inc = sizeX >> 3; + int palette = (a2 >> 8) & 0xF0; + for(u32 x = 0; x < fieldX; ++x) + { + if (x >= startpix) + lineOBJpix-=2; + unsigned xxx = realX >> 8; + unsigned yyy = realY >> 8; + if(xxx < sizeX && yyy < sizeY && sx < 240) + { + + u32 color = vram[0x10000 + ((((c + (yyy>>3) * inc)<<5) + + ((yyy & 7)<<2) + ((xxx >> 3)<<5) + + ((xxx & 7)>>1))&0x7FFF)]; + if(xxx & 1) + color >>= 4; + else + color &= 0x0F; + + if ((color==0) && (((prio >> 25)&3) < + ((line[4][sx]>>25)&3))) + { + line[4][sx] = (line[4][sx] & 0xF9FFFFFF) | prio; + if((a0 & 0x1000) && m) + line[4][sx]=(line[4][sx-1] & 0xF9FFFFFF) | prio; + } + else if((color) && (prio < (line[4][sx]&0xFF000000))) + { + line[4][sx] = READ16LE(&spritePalette[palette+color]) | prio; + if((a0 & 0x1000) && m) + line[4][sx]=(line[4][sx-1] & 0xF9FFFFFF) | prio; + } + } + if((a0 & 0x1000) && m) + { + if (++m==mosaicX) + m=0; + } + + sx = (sx+1)&511; + realX += dx; + realY += dy; + + } + } + } + } + } + else + { + if(sy+sizeY > 256) + sy -= 256; + int t = io_registers[REG_VCOUNT] - sy; + if(unsigned(t) < sizeY) + { + u32 startpix = 0; + if ((sx+sizeX)> 512) + startpix=512-sx; + + if((sx < 240) || startpix) + { + lineOBJpix+=2; + + if(a1 & 0x2000) + t = sizeY - t - 1; + + int c = (a2 & 0x3FF); + if((io_registers[REG_DISPCNT] & 7) > 2 && (c < 512)) + continue; + + int inc = 32; + int xxx = 0; + if(a1 & 0x1000) + xxx = sizeX-1; + + if(a0 & 0x1000) + t -= (t % mosaicY); + + if(a0 & 0x2000) + { + if(io_registers[REG_DISPCNT] & 0x40) + inc = sizeX >> 2; + else + c &= 0x3FE; + + int address = 0x10000 + ((((c+ (t>>3) * inc) << 5) + + ((t & 7) << 3) + ((xxx>>3)<<6) + (xxx & 7)) & 0x7FFF); + + if(a1 & 0x1000) + xxx = 7; + u32 prio = (((a2 >> 10) & 3) << 25) | ((a0 & 0x0c00)<<6); + + for(u32 xx = 0; xx < sizeX; xx++) + { + if (xx >= startpix) + --lineOBJpix; + if(sx < 240) + { + u8 color = vram[address]; + if ((color==0) && (((prio >> 25)&3) < + ((line[4][sx]>>25)&3))) + { + line[4][sx] = (line[4][sx] & 0xF9FFFFFF) | prio; + if((a0 & 0x1000) && m) + line[4][sx]=(line[4][sx-1] & 0xF9FFFFFF) | prio; + } + else if((color) && (prio < (line[4][sx]&0xFF000000))) + { + line[4][sx] = READ16LE(&spritePalette[color]) | prio; + if((a0 & 0x1000) && m) + line[4][sx]=(line[4][sx-1] & 0xF9FFFFFF) | prio; + } + + if ((a0 & 0x1000) && ((m+1) == mosaicX)) + m = 0; + } + + sx = (sx+1) & 511; + if(a1 & 0x1000) + { + --address; + if(--xxx == -1) + { + address -= 56; + xxx = 7; + } + if(address < 0x10000) + address += 0x8000; + } + else + { + ++address; + if(++xxx == 8) + { + address += 56; + xxx = 0; + } + if(address > 0x17fff) + address -= 0x8000; + } + } + } + else + { + if(io_registers[REG_DISPCNT] & 0x40) + inc = sizeX >> 3; + + int address = 0x10000 + ((((c + (t>>3) * inc)<<5) + + ((t & 7)<<2) + ((xxx>>3)<<5) + ((xxx & 7) >> 1))&0x7FFF); + + u32 prio = (((a2 >> 10) & 3) << 25) | ((a0 & 0x0c00)<<6); + int palette = (a2 >> 8) & 0xF0; + if(a1 & 0x1000) + { + xxx = 7; + int xx = sizeX - 1; + do + { + if (xx >= (int)(startpix)) + --lineOBJpix; + //if (lineOBJpix<0) + // continue; + if(sx < 240) + { + u8 color = vram[address]; + if(xx & 1) + color >>= 4; + else + color &= 0x0F; + + if ((color==0) && (((prio >> 25)&3) < + ((line[4][sx]>>25)&3))) + { + line[4][sx] = (line[4][sx] & 0xF9FFFFFF) | prio; + if((a0 & 0x1000) && m) + line[4][sx]=(line[4][sx-1] & 0xF9FFFFFF) | prio; + } + else if((color) && (prio < (line[4][sx]&0xFF000000))) + { + line[4][sx] = READ16LE(&spritePalette[palette + color]) | prio; + if((a0 & 0x1000) && m) + line[4][sx]=(line[4][sx-1] & 0xF9FFFFFF) | prio; + } + } + + if ((a0 & 0x1000) && ((m+1) == mosaicX)) + m=0; + + sx = (sx+1) & 511; + if(!(xx & 1)) + --address; + if(--xxx == -1) + { + xxx = 7; + address -= 28; + } + if(address < 0x10000) + address += 0x8000; + }while(--xx >= 0); + } + else + { + for(u32 xx = 0; xx < sizeX; ++xx) + { + if (xx >= startpix) + --lineOBJpix; + //if (lineOBJpix<0) + // continue; + if(sx < 240) + { + u8 color = vram[address]; + if(xx & 1) + color >>= 4; + else + color &= 0x0F; + + if ((color==0) && (((prio >> 25)&3) < + ((line[4][sx]>>25)&3))) + { + line[4][sx] = (line[4][sx] & 0xF9FFFFFF) | prio; + if((a0 & 0x1000) && m) + line[4][sx]=(line[4][sx-1] & 0xF9FFFFFF) | prio; + } + else if((color) && (prio < (line[4][sx]&0xFF000000))) + { + line[4][sx] = READ16LE(&spritePalette[palette + color]) | prio; + if((a0 & 0x1000) && m) + line[4][sx]=(line[4][sx-1] & 0xF9FFFFFF) | prio; + + } + } + if ((a0 & 0x1000) && ((m+1) == mosaicX)) + m=0; + + sx = (sx+1) & 511; + if(xx & 1) + ++address; + if(++xxx == 8) + { + address += 28; + xxx = 0; + } + if(address > 0x17fff) + address -= 0x8000; + } + } + } + } + } + } + } +} + +INLINE void gfxDrawOBJWin (void) +{ + u16 *sprites = (u16 *)oam; + for(int x = 0; x < 128 ; x++) + { + int lineOBJpix = lineOBJpixleft[x]; + u16 a0 = READ16LE(sprites++); + u16 a1 = READ16LE(sprites++); + u16 a2 = READ16LE(sprites++); + sprites++; + + if (lineOBJpix<=0) + return; + + // ignores non OBJ-WIN and disabled OBJ-WIN + if(((a0 & 0x0c00) != 0x0800) || ((a0 & 0x0300) == 0x0200)) + continue; + + u16 a0val = a0>>14; + + if ((a0 & 0x0c00) == 0x0c00) + a0 &=0xF3FF; + + if (a0val == 3) + { + a0 &= 0x3FFF; + a1 &= 0x3FFF; + } + + int sizeX = 8<<(a1>>14); + int sizeY = sizeX; + + if (a0val & 1) + { +#ifdef BRANCHLESS_GBA_GFX + sizeX <<= isel(-(sizeX & (~31u)), 1, 0); + sizeY >>= isel(-(sizeY>8), 0, 1); +#else + if (sizeX<32) + sizeX<<=1; + if (sizeY>8) + sizeY>>=1; +#endif + } + else if (a0val & 2) + { +#ifdef BRANCHLESS_GBA_GFX + sizeX >>= isel(-(sizeX>8), 0, 1); + sizeY <<= isel(-(sizeY & (~31u)), 1, 0); +#else + if (sizeX>8) + sizeX>>=1; + if (sizeY<32) + sizeY<<=1; +#endif + + } + + int sy = (a0 & 255); + + if(a0 & 0x0100) + { + int fieldX = sizeX; + int fieldY = sizeY; + if(a0 & 0x0200) + { + fieldX <<= 1; + fieldY <<= 1; + } + if((sy+fieldY) > 256) + sy -= 256; + int t = io_registers[REG_VCOUNT] - sy; + if((t >= 0) && (t < fieldY)) + { + int sx = (a1 & 0x1FF); + int startpix = 0; + if ((sx+fieldX)> 512) + startpix=512-sx; + + if((sx < 240) || startpix) + { + lineOBJpix-=8; + // int t2 = t - (fieldY >> 1); + int rot = (a1 >> 9) & 0x1F; + u16 *OAM = (u16 *)oam; + int dx = READ16LE(&OAM[3 + (rot << 4)]); + if(dx & 0x8000) + dx |= 0xFFFF8000; + int dmx = READ16LE(&OAM[7 + (rot << 4)]); + if(dmx & 0x8000) + dmx |= 0xFFFF8000; + int dy = READ16LE(&OAM[11 + (rot << 4)]); + if(dy & 0x8000) + dy |= 0xFFFF8000; + int dmy = READ16LE(&OAM[15 + (rot << 4)]); + if(dmy & 0x8000) + dmy |= 0xFFFF8000; + + int realX = ((sizeX) << 7) - (fieldX >> 1)*dx - (fieldY>>1)*dmx + + t * dmx; + int realY = ((sizeY) << 7) - (fieldX >> 1)*dy - (fieldY>>1)*dmy + + t * dmy; + + int c = (a2 & 0x3FF); + if((io_registers[REG_DISPCNT] & 7) > 2 && (c < 512)) + continue; + + int inc = 32; + bool condition1 = a0 & 0x2000; + + if(io_registers[REG_DISPCNT] & 0x40) + inc = sizeX >> 3; + + for(int x = 0; x < fieldX; x++) + { + bool cont = true; + if (x >= startpix) + lineOBJpix-=2; + if (lineOBJpix<0) + continue; + int xxx = realX >> 8; + int yyy = realY >> 8; + + if(xxx < 0 || xxx >= sizeX || yyy < 0 || yyy >= sizeY || sx >= 240) + cont = false; + + if(cont) + { + u32 color; + if(condition1) + color = vram[0x10000 + ((((c + (yyy>>3) * inc)<<5) + + ((yyy & 7)<<3) + ((xxx >> 3)<<6) + + (xxx & 7))&0x7fff)]; + else + { + color = vram[0x10000 + ((((c + (yyy>>3) * inc)<<5) + + ((yyy & 7)<<2) + ((xxx >> 3)<<5) + + ((xxx & 7)>>1))&0x7fff)]; + if(xxx & 1) + color >>= 4; + else + color &= 0x0F; + } + + if(color) + line[5][sx] = 1; + } + sx = (sx+1)&511; + realX += dx; + realY += dy; + } + } + } + } + else + { + if((sy+sizeY) > 256) + sy -= 256; + int t = io_registers[REG_VCOUNT] - sy; + if((t >= 0) && (t < sizeY)) + { + int sx = (a1 & 0x1FF); + int startpix = 0; + if ((sx+sizeX)> 512) + startpix=512-sx; + + if((sx < 240) || startpix) + { + lineOBJpix+=2; + if(a1 & 0x2000) + t = sizeY - t - 1; + int c = (a2 & 0x3FF); + if((io_registers[REG_DISPCNT] & 7) > 2 && (c < 512)) + continue; + if(a0 & 0x2000) + { + + int inc = 32; + if(io_registers[REG_DISPCNT] & 0x40) + inc = sizeX >> 2; + else + c &= 0x3FE; + + int xxx = 0; + if(a1 & 0x1000) + xxx = sizeX-1; + int address = 0x10000 + ((((c+ (t>>3) * inc) << 5) + + ((t & 7) << 3) + ((xxx>>3)<<6) + (xxx & 7))&0x7fff); + if(a1 & 0x1000) + xxx = 7; + for(int xx = 0; xx < sizeX; xx++) + { + if (xx >= startpix) + lineOBJpix--; + if (lineOBJpix<0) + continue; + if(sx < 240) + { + u8 color = vram[address]; + if(color) + line[5][sx] = 1; + } + + sx = (sx+1) & 511; + if(a1 & 0x1000) { + xxx--; + address--; + if(xxx == -1) { + address -= 56; + xxx = 7; + } + if(address < 0x10000) + address += 0x8000; + } else { + xxx++; + address++; + if(xxx == 8) { + address += 56; + xxx = 0; + } + if(address > 0x17fff) + address -= 0x8000; + } + } + } + else + { + int inc = 32; + if(io_registers[REG_DISPCNT] & 0x40) + inc = sizeX >> 3; + int xxx = 0; + if(a1 & 0x1000) + xxx = sizeX - 1; + int address = 0x10000 + ((((c + (t>>3) * inc)<<5) + + ((t & 7)<<2) + ((xxx>>3)<<5) + ((xxx & 7) >> 1))&0x7fff); + // u32 prio = (((a2 >> 10) & 3) << 25) | ((a0 & 0x0c00)<<6); + // int palette = (a2 >> 8) & 0xF0; + if(a1 & 0x1000) + { + xxx = 7; + for(int xx = sizeX - 1; xx >= 0; xx--) + { + if (xx >= startpix) + lineOBJpix--; + if (lineOBJpix<0) + continue; + if(sx < 240) + { + u8 color = vram[address]; + if(xx & 1) + color = (color >> 4); + else + color &= 0x0F; + + if(color) + line[5][sx] = 1; + } + sx = (sx+1) & 511; + xxx--; + if(!(xx & 1)) + address--; + if(xxx == -1) { + xxx = 7; + address -= 28; + } + if(address < 0x10000) + address += 0x8000; + } + } + else + { + for(int xx = 0; xx < sizeX; xx++) + { + if (xx >= startpix) + lineOBJpix--; + if (lineOBJpix<0) + continue; + if(sx < 240) + { + u8 color = vram[address]; + if(xx & 1) + color = (color >> 4); + else + color &= 0x0F; + + if(color) + line[5][sx] = 1; + } + sx = (sx+1) & 511; + xxx++; + if(xx & 1) + address++; + if(xxx == 8) { + address += 28; + xxx = 0; + } + if(address > 0x17fff) + address -= 0x8000; + } + } + } + } + } + } + } +} + +INLINE u32 gfxIncreaseBrightness(u32 color, int coeff) +{ + color = (((color & 0xffff) << 16) | (color & 0xffff)) & 0x3E07C1F; + + color += ((((0x3E07C1F - color) * coeff) >> 4) & 0x3E07C1F); + + return (color >> 16) | color; +} + +INLINE u32 gfxDecreaseBrightness(u32 color, int coeff) +{ + color = (((color & 0xffff) << 16) | (color & 0xffff)) & 0x3E07C1F; + + color -= (((color * coeff) >> 4) & 0x3E07C1F); + + return (color >> 16) | color; +} + +#define GFX_ALPHA_BLEND(color, color2, ca, cb) \ + int r = AlphaClampLUT[(((color & 0x1F) * ca) >> 4) + (((color2 & 0x1F) * cb) >> 4)]; \ + int g = AlphaClampLUT[((((color >> 5) & 0x1F) * ca) >> 4) + ((((color2 >> 5) & 0x1F) * cb) >> 4)]; \ + int b = AlphaClampLUT[((((color >> 10) & 0x1F) * ca) >> 4) + ((((color2 >> 10) & 0x1F) * cb) >> 4)]; \ + color = (color & 0xFFFF0000) | (b << 10) | (g << 5) | r; + +/*============================================================ + GBA.CPP +============================================================ */ +static const bool useBios = true; +bool skipBios; +// it's a few bytes in the linkscript to make a multiboot image work in normal boot as well, +// and most of the ones i've seen have done that, so this is not terribly useful +static const bool cpuIsMultiBoot = false; +int cpuSaveType; // used only in init() to set up function pointers and for save file determination +bool mirroringEnable; + +int cpuDmaCount; + +uint8_t bios[0x4000]; +uint8_t rom[0x2000000]; +uint8_t internalRAM[0x8000]; +uint8_t workRAM[0x40000]; +uint8_t vram[0x20000]; +u16 pix[2 * PIX_BUFFER_SCREEN_WIDTH * 160]; +uint8_t oam[0x400]; +uint8_t ioMem[0x400]; + +bool cpuEEPROMEnabled; // true to process writes to EEPROM at 0dxxxxxx +bool cpuEEPROMSensorEnabled; // eeprom motion sensor? code is mostly disabled + +#ifndef LSB_FIRST +bool cpuBiosSwapped = false; +#endif + +INLINE int CPUUpdateTicks (void) +{ + int cpuLoopTicks = graphics.lcdTicks; + + if(timer0On && (timer0Ticks < cpuLoopTicks)) + cpuLoopTicks = timer0Ticks; + + if(timer1On && !(io_registers[REG_TM1CNT] & 4) && (timer1Ticks < cpuLoopTicks)) + cpuLoopTicks = timer1Ticks; + + if(timer2On && !(io_registers[REG_TM2CNT] & 4) && (timer2Ticks < cpuLoopTicks)) + cpuLoopTicks = timer2Ticks; + + if(timer3On && !(io_registers[REG_TM3CNT] & 4) && (timer3Ticks < cpuLoopTicks)) + cpuLoopTicks = timer3Ticks; + + if (IRQTicks) + { + if (IRQTicks < cpuLoopTicks) + cpuLoopTicks = IRQTicks; + } + + return cpuLoopTicks; +} + +#define CPUUpdateWindow0() \ +{ \ + int x00_window0 = io_registers[REG_WIN0H] >>8; \ + int x01_window0 = io_registers[REG_WIN0H] & 255; \ + int x00_lte_x01 = x00_window0 <= x01_window0; \ + for(int i = 0; i < 240; i++) \ + gfxInWin[0][i] = ((i >= x00_window0 && i < x01_window0) & x00_lte_x01) | ((i >= x00_window0 || i < x01_window0) & ~x00_lte_x01); \ +} + +#define CPUUpdateWindow1() \ +{ \ + int x00_window1 = io_registers[REG_WIN1H]>>8; \ + int x01_window1 = io_registers[REG_WIN1H] & 255; \ + int x00_lte_x01 = x00_window1 <= x01_window1; \ + for(int i = 0; i < 240; i++) \ + gfxInWin[1][i] = ((i >= x00_window1 && i < x01_window1) & x00_lte_x01) | ((i >= x00_window1 || i < x01_window1) & ~x00_lte_x01); \ +} + +#define CPUCompareVCOUNT() \ + if(io_registers[REG_VCOUNT] == (io_registers[REG_DISPSTAT] >> 8)) \ + { \ + io_registers[REG_DISPSTAT] |= 4; \ + UPDATE_REG(0x04, io_registers[REG_DISPSTAT]); \ + if(io_registers[REG_DISPSTAT] & 0x20) \ + { \ + io_registers[REG_IF] |= 4; \ + UPDATE_REG(0x202, io_registers[REG_IF]); \ + } \ + } \ + else \ + { \ + io_registers[REG_DISPSTAT] &= 0xFFFB; \ + UPDATE_REG(0x4, io_registers[REG_DISPSTAT]); \ + } \ + if (graphics.layerEnableDelay > 0) \ + { \ + graphics.layerEnableDelay--; \ + if (graphics.layerEnableDelay == 1) \ + graphics.layerEnable = io_registers[REG_DISPCNT]; \ + } + +int CPULoadRom(const u8 *romfile, const u32 romfilelen) +{ + if (cpuIsMultiBoot) + { + if (romfilelen > 0x40000) + return 0; + } + else + { + if (romfilelen > 0x2000000) + return 0; + } + + uint8_t *whereToLoad = cpuIsMultiBoot ? workRAM : rom; + + memcpy(whereToLoad, romfile, romfilelen); + romSize = romfilelen; + + uint16_t *temp = (uint16_t *)(rom+((romSize+1)&~1)); + int i; + for(i = (romSize+1)&~1; i < 0x2000000; i+=2) { + WRITE16LE(temp, (i >> 1) & 0xFFFF); + temp++; + } + + + flashInit(); + eepromInit(); + + memset(line[0], -1, 240 * sizeof(u32)); + memset(line[1], -1, 240 * sizeof(u32)); + memset(line[2], -1, 240 * sizeof(u32)); + memset(line[3], -1, 240 * sizeof(u32)); + + return romSize; +} + +void doMirroring (bool b) +{ + uint32_t mirroredRomSize = (((romSize)>>20) & 0x3F)<<20; + uint32_t mirroredRomAddress = romSize; + if ((mirroredRomSize <=0x800000) && (b)) + { + mirroredRomAddress = mirroredRomSize; + if (mirroredRomSize==0) + mirroredRomSize=0x100000; + while (mirroredRomAddress<0x01000000) + { + memcpy((uint16_t *)(rom+mirroredRomAddress), (uint16_t *)(rom), mirroredRomSize); + mirroredRomAddress+=mirroredRomSize; + } + } +} + +#define brightness_switch() \ + switch((BLDMOD >> 6) & 3) \ + { \ + case 2: \ + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); \ + break; \ + case 3: \ + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); \ + break; \ + } + +#define alpha_blend_brightness_switch() \ + if(top2 & (BLDMOD>>8)) \ + if(color < 0x80000000) \ + { \ + GFX_ALPHA_BLEND(color, back, coeff[COLEV & 0x1F], coeff[(COLEV >> 8) & 0x1F]); \ + } \ + else if(BLDMOD & top) \ + { \ + brightness_switch(); \ + } + +/* we only use 16bit color depth */ +#define INIT_COLOR_DEPTH_LINE_MIX() uint16_t * lineMix = (pix + PIX_BUFFER_SCREEN_WIDTH * io_registers[REG_VCOUNT]) + +void mode0RenderLine (void) +{ +#ifdef REPORT_VIDEO_MODES + fprintf(stderr, "MODE 0: Render Line\n"); +#endif + INIT_COLOR_DEPTH_LINE_MIX(); + + uint16_t *palette = (uint16_t *)graphics.paletteRAM; + + if(graphics.layerEnable & 0x0100) { + gfxDrawTextScreen(io_registers[REG_BG0CNT], io_registers[REG_BG0HOFS], io_registers[REG_BG0VOFS], line[0]); + } + + if(graphics.layerEnable & 0x0200) { + gfxDrawTextScreen(io_registers[REG_BG1CNT], io_registers[REG_BG1HOFS], io_registers[REG_BG1VOFS], line[1]); + } + + if(graphics.layerEnable & 0x0400) { + gfxDrawTextScreen(io_registers[REG_BG2CNT], io_registers[REG_BG2HOFS], io_registers[REG_BG2VOFS], line[2]); + } + + if(graphics.layerEnable & 0x0800) { + gfxDrawTextScreen(io_registers[REG_BG3CNT], io_registers[REG_BG3HOFS], io_registers[REG_BG3VOFS], line[3]); + } + + + uint32_t backdrop = (READ16LE(&palette[0]) | 0x30000000); + + for(int x = 0; x < 240; x++) + { + uint32_t color = backdrop; + uint8_t top = 0x20; + + if(line[0][x] < color) { + color = line[0][x]; + top = 0x01; + } + + if((uint8_t)(line[1][x]>>24) < (uint8_t)(color >> 24)) { + color = line[1][x]; + top = 0x02; + } + + if((uint8_t)(line[2][x]>>24) < (uint8_t)(color >> 24)) { + color = line[2][x]; + top = 0x04; + } + + if((uint8_t)(line[3][x]>>24) < (uint8_t)(color >> 24)) { + color = line[3][x]; + top = 0x08; + } + + if((uint8_t)(line[4][x]>>24) < (uint8_t)(color >> 24)) { + color = line[4][x]; + top = 0x10; + + if(color & 0x00010000) { + // semi-transparent OBJ + uint32_t back = backdrop; + uint8_t top2 = 0x20; + + if((uint8_t)(line[0][x]>>24) < (uint8_t)(back >> 24)) { + back = line[0][x]; + top2 = 0x01; + } + + if((uint8_t)(line[1][x]>>24) < (uint8_t)(back >> 24)) { + back = line[1][x]; + top2 = 0x02; + } + + if((uint8_t)(line[2][x]>>24) < (uint8_t)(back >> 24)) { + back = line[2][x]; + top2 = 0x04; + } + + if((uint8_t)(line[3][x]>>24) < (uint8_t)(back >> 24)) { + back = line[3][x]; + top2 = 0x08; + } + + alpha_blend_brightness_switch(); + } + } + + + lineMix[x] = CONVERT_COLOR(color); + } +} + +void mode0RenderLineNoWindow (void) +{ +#ifdef REPORT_VIDEO_MODES + fprintf(stderr, "MODE 0: Render Line No Window\n"); +#endif + INIT_COLOR_DEPTH_LINE_MIX(); + + uint16_t *palette = (uint16_t *)graphics.paletteRAM; + uint32_t backdrop = (READ16LE(&palette[0]) | 0x30000000); + + if(graphics.layerEnable & 0x0100) { + gfxDrawTextScreen(io_registers[REG_BG0CNT], io_registers[REG_BG0HOFS], io_registers[REG_BG0VOFS], line[0]); + } + + if(graphics.layerEnable & 0x0200) { + gfxDrawTextScreen(io_registers[REG_BG1CNT], io_registers[REG_BG1HOFS], io_registers[REG_BG1VOFS], line[1]); + } + + if(graphics.layerEnable & 0x0400) { + gfxDrawTextScreen(io_registers[REG_BG2CNT], io_registers[REG_BG2HOFS], io_registers[REG_BG2VOFS], line[2]); + } + + if(graphics.layerEnable & 0x0800) { + gfxDrawTextScreen(io_registers[REG_BG3CNT], io_registers[REG_BG3HOFS], io_registers[REG_BG3VOFS], line[3]); + } + + int effect = (BLDMOD >> 6) & 3; + + for(int x = 0; x < 240; x++) { + uint32_t color = backdrop; + uint8_t top = 0x20; + + if(line[0][x] < color) { + color = line[0][x]; + top = 0x01; + } + + if(line[1][x] < (color & 0xFF000000)) { + color = line[1][x]; + top = 0x02; + } + + if(line[2][x] < (color & 0xFF000000)) { + color = line[2][x]; + top = 0x04; + } + + if(line[3][x] < (color & 0xFF000000)) { + color = line[3][x]; + top = 0x08; + } + + if(line[4][x] < (color & 0xFF000000)) { + color = line[4][x]; + top = 0x10; + } + + if(!(color & 0x00010000)) { + switch(effect) { + case 0: + break; + case 1: + if(top & BLDMOD) + { + uint32_t back = backdrop; + uint8_t top2 = 0x20; + if((line[0][x] < back) && (top != 0x01)) + { + back = line[0][x]; + top2 = 0x01; + } + + if((line[1][x] < (back & 0xFF000000)) && (top != 0x02)) + { + back = line[1][x]; + top2 = 0x02; + } + + if((line[2][x] < (back & 0xFF000000)) && (top != 0x04)) + { + back = line[2][x]; + top2 = 0x04; + } + + if((line[3][x] < (back & 0xFF000000)) && (top != 0x08)) + { + back = line[3][x]; + top2 = 0x08; + } + + if((line[4][x] < (back & 0xFF000000)) && (top != 0x10)) + { + back = line[4][x]; + top2 = 0x10; + } + + if(top2 & (BLDMOD>>8) && color < 0x80000000) + { + GFX_ALPHA_BLEND(color, back, coeff[COLEV & 0x1F], coeff[(COLEV >> 8) & 0x1F]); + } + + } + break; + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } else { + // semi-transparent OBJ + uint32_t back = backdrop; + uint8_t top2 = 0x20; + + if(line[0][x] < back) { + back = line[0][x]; + top2 = 0x01; + } + + if(line[1][x] < (back & 0xFF000000)) { + back = line[1][x]; + top2 = 0x02; + } + + if(line[2][x] < (back & 0xFF000000)) { + back = line[2][x]; + top2 = 0x04; + } + + if(line[3][x] < (back & 0xFF000000)) { + back = line[3][x]; + top2 = 0x08; + } + + alpha_blend_brightness_switch(); + } + + lineMix[x] = CONVERT_COLOR(color); + } +} + +void mode0RenderLineAll (void) +{ +#ifdef REPORT_VIDEO_MODES + fprintf(stderr, "MODE 0: Render Line All\n"); +#endif + INIT_COLOR_DEPTH_LINE_MIX(); + + uint16_t *palette = (uint16_t *)graphics.paletteRAM; + + bool inWindow0 = false; + bool inWindow1 = false; + + if(graphics.layerEnable & 0x2000) { + uint8_t v0 = io_registers[REG_WIN0V] >> 8; + uint8_t v1 = io_registers[REG_WIN0V] & 255; + inWindow0 = ((v0 == v1) && (v0 >= 0xe8)); + if(v1 >= v0) + inWindow0 |= (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1); + else + inWindow0 |= (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1); + } + if(graphics.layerEnable & 0x4000) { + uint8_t v0 = io_registers[REG_WIN1V] >> 8; + uint8_t v1 = io_registers[REG_WIN1V] & 255; + inWindow1 = ((v0 == v1) && (v0 >= 0xe8)); + if(v1 >= v0) + inWindow1 |= (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1); + else + inWindow1 |= (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1); + } + + if((graphics.layerEnable & 0x0100)) { + gfxDrawTextScreen(io_registers[REG_BG0CNT], io_registers[REG_BG0HOFS], io_registers[REG_BG0VOFS], line[0]); + } + + if((graphics.layerEnable & 0x0200)) { + gfxDrawTextScreen(io_registers[REG_BG1CNT], io_registers[REG_BG1HOFS], io_registers[REG_BG1VOFS], line[1]); + } + + if((graphics.layerEnable & 0x0400)) { + gfxDrawTextScreen(io_registers[REG_BG2CNT], io_registers[REG_BG2HOFS], io_registers[REG_BG2VOFS], line[2]); + } + + if((graphics.layerEnable & 0x0800)) { + gfxDrawTextScreen(io_registers[REG_BG3CNT], io_registers[REG_BG3HOFS], io_registers[REG_BG3VOFS], line[3]); + } + + uint32_t backdrop = (READ16LE(&palette[0]) | 0x30000000); + + uint8_t inWin0Mask = io_registers[REG_WININ] & 0xFF; + uint8_t inWin1Mask = io_registers[REG_WININ] >> 8; + uint8_t outMask = io_registers[REG_WINOUT] & 0xFF; + + for(int x = 0; x < 240; x++) { + uint32_t color = backdrop; + uint8_t top = 0x20; + uint8_t mask = outMask; + + if(!(line[5][x] & 0x80000000)) { + mask = io_registers[REG_WINOUT] >> 8; + } + + int32_t window1_mask = ((inWindow1 & gfxInWin[1][x]) | -(inWindow1 & gfxInWin[1][x])) >> 31; + int32_t window0_mask = ((inWindow0 & gfxInWin[0][x]) | -(inWindow0 & gfxInWin[0][x])) >> 31; + mask = (inWin1Mask & window1_mask) | (mask & ~window1_mask); + mask = (inWin0Mask & window0_mask) | (mask & ~window0_mask); + + if((mask & 1) && (line[0][x] < color)) { + color = line[0][x]; + top = 0x01; + } + + if((mask & 2) && ((uint8_t)(line[1][x]>>24) < (uint8_t)(color >> 24))) { + color = line[1][x]; + top = 0x02; + } + + if((mask & 4) && ((uint8_t)(line[2][x]>>24) < (uint8_t)(color >> 24))) { + color = line[2][x]; + top = 0x04; + } + + if((mask & 8) && ((uint8_t)(line[3][x]>>24) < (uint8_t)(color >> 24))) { + color = line[3][x]; + top = 0x08; + } + + if((mask & 16) && ((uint8_t)(line[4][x]>>24) < (uint8_t)(color >> 24))) { + color = line[4][x]; + top = 0x10; + } + + if(color & 0x00010000) + { + // semi-transparent OBJ + uint32_t back = backdrop; + uint8_t top2 = 0x20; + + if((mask & 1) && ((uint8_t)(line[0][x]>>24) < (uint8_t)(back >> 24))) { + back = line[0][x]; + top2 = 0x01; + } + + if((mask & 2) && ((uint8_t)(line[1][x]>>24) < (uint8_t)(back >> 24))) { + back = line[1][x]; + top2 = 0x02; + } + + if((mask & 4) && ((uint8_t)(line[2][x]>>24) < (uint8_t)(back >> 24))) { + back = line[2][x]; + top2 = 0x04; + } + + if((mask & 8) && ((uint8_t)(line[3][x]>>24) < (uint8_t)(back >> 24))) { + back = line[3][x]; + top2 = 0x08; + } + + alpha_blend_brightness_switch(); + } + else if((mask & 32) && (top & BLDMOD)) + { + // special FX on in the window + switch((BLDMOD >> 6) & 3) + { + case 0: + break; + case 1: + { + uint32_t back = backdrop; + uint8_t top2 = 0x20; + if(((mask & 1) && (uint8_t)(line[0][x]>>24) < (uint8_t)(back >> 24)) && top != 0x01) + { + back = line[0][x]; + top2 = 0x01; + } + + if(((mask & 2) && (uint8_t)(line[1][x]>>24) < (uint8_t)(back >> 24)) && top != 0x02) + { + back = line[1][x]; + top2 = 0x02; + } + + if(((mask & 4) && (uint8_t)(line[2][x]>>24) < (uint8_t)(back >> 24)) && top != 0x04) + { + back = line[2][x]; + top2 = 0x04; + } + + if(((mask & 8) && (uint8_t)(line[3][x]>>24) < (uint8_t)(back >> 24)) && top != 0x08) + { + back = line[3][x]; + top2 = 0x08; + } + + if(((mask & 16) && (uint8_t)(line[4][x]>>24) < (uint8_t)(back >> 24)) && top != 0x10) { + back = line[4][x]; + top2 = 0x10; + } + + if(top2 & (BLDMOD>>8) && color < 0x80000000) + { + GFX_ALPHA_BLEND(color, back, coeff[COLEV & 0x1F], coeff[(COLEV >> 8) & 0x1F]); + } + } + break; + case 2: + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + + lineMix[x] = CONVERT_COLOR(color); + } +} + +/* +Mode 1 is a tiled graphics mode, but with background layer 2 supporting scaling and rotation. +There is no layer 3 in this mode. +Layers 0 and 1 can be either 16 colours (with 16 different palettes) or 256 colours. +There are 1024 tiles available. +Layer 2 is 256 colours and allows only 256 tiles. + +These routines only render a single line at a time, because of the way the GBA does events. +*/ + +void mode1RenderLine (void) +{ +#ifdef REPORT_VIDEO_MODES + fprintf(stderr, "MODE 1: Render Line\n"); +#endif + INIT_COLOR_DEPTH_LINE_MIX(); + + uint16_t *palette = (uint16_t *)graphics.paletteRAM; + + if(graphics.layerEnable & 0x0100) { + gfxDrawTextScreen(io_registers[REG_BG0CNT], io_registers[REG_BG0HOFS], io_registers[REG_BG0VOFS], line[0]); + } + + if(graphics.layerEnable & 0x0200) { + gfxDrawTextScreen(io_registers[REG_BG1CNT], io_registers[REG_BG1HOFS], io_registers[REG_BG1VOFS], line[1]); + } + + if(graphics.layerEnable & 0x0400) { + int changed = gfxBG2Changed; +#if 0 + if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) + changed = 3; +#endif + gfxDrawRotScreen(io_registers[REG_BG2CNT], BG2X_L, BG2X_H, BG2Y_L, BG2Y_H, + io_registers[REG_BG2PA], io_registers[REG_BG2PB], io_registers[REG_BG2PC], io_registers[REG_BG2PD], + gfxBG2X, gfxBG2Y, changed, line[2]); + } + + uint32_t backdrop = (READ16LE(&palette[0]) | 0x30000000); + + for(uint32_t x = 0; x < 240u; ++x) { + uint32_t color = backdrop; + uint8_t top = 0x20; + + uint8_t li1 = (uint8_t)(line[1][x]>>24); + uint8_t li2 = (uint8_t)(line[2][x]>>24); + uint8_t li4 = (uint8_t)(line[4][x]>>24); + + uint8_t r = (li2 < li1) ? (li2) : (li1); + + if(li4 < r){ + r = (li4); + } + + if(line[0][x] < backdrop) { + color = line[0][x]; + top = 0x01; + } + + if(r < (uint8_t)(color >> 24)) { + if(r == li1){ + color = line[1][x]; + top = 0x02; + }else if(r == li2){ + color = line[2][x]; + top = 0x04; + }else if(r == li4){ + color = line[4][x]; + top = 0x10; + if((color & 0x00010000)) + { + // semi-transparent OBJ + uint32_t back = backdrop; + uint8_t top2 = 0x20; + + uint8_t li0 = (uint8_t)(line[0][x]>>24); + uint8_t li1 = (uint8_t)(line[1][x]>>24); + uint8_t li2 = (uint8_t)(line[2][x]>>24); + uint8_t r = (li1 < li0) ? (li1) : (li0); + + if(li2 < r) { + r = (li2); + } + + if(r < (uint8_t)(back >> 24)) { + if(r == li0){ + back = line[0][x]; + top2 = 0x01; + }else if(r == li1){ + back = line[1][x]; + top2 = 0x02; + }else if(r == li2){ + back = line[2][x]; + top2 = 0x04; + } + } + + alpha_blend_brightness_switch(); + } + } + } + + + lineMix[x] = CONVERT_COLOR(color); + } + gfxBG2Changed = 0; + //gfxLastVCOUNT = io_registers[REG_VCOUNT]; +} + +void mode1RenderLineNoWindow (void) +{ +#ifdef REPORT_VIDEO_MODES + fprintf(stderr, "MODE 1: Render Line No Window\n"); +#endif + INIT_COLOR_DEPTH_LINE_MIX(); + + uint16_t *palette = (uint16_t *)graphics.paletteRAM; + + if(graphics.layerEnable & 0x0100) { + gfxDrawTextScreen(io_registers[REG_BG0CNT], io_registers[REG_BG0HOFS], io_registers[REG_BG0VOFS], line[0]); + } + + + if(graphics.layerEnable & 0x0200) { + gfxDrawTextScreen(io_registers[REG_BG1CNT], io_registers[REG_BG1HOFS], io_registers[REG_BG1VOFS], line[1]); + } + + if(graphics.layerEnable & 0x0400) { + int changed = gfxBG2Changed; +#if 0 + if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) + changed = 3; +#endif + gfxDrawRotScreen(io_registers[REG_BG2CNT], BG2X_L, BG2X_H, BG2Y_L, BG2Y_H, + io_registers[REG_BG2PA], io_registers[REG_BG2PB], io_registers[REG_BG2PC], io_registers[REG_BG2PD], + gfxBG2X, gfxBG2Y, changed, line[2]); + } + + uint32_t backdrop = (READ16LE(&palette[0]) | 0x30000000); + + for(int x = 0; x < 240; ++x) { + uint32_t color = backdrop; + uint8_t top = 0x20; + + uint8_t li1 = (uint8_t)(line[1][x]>>24); + uint8_t li2 = (uint8_t)(line[2][x]>>24); + uint8_t li4 = (uint8_t)(line[4][x]>>24); + + uint8_t r = (li2 < li1) ? (li2) : (li1); + + if(li4 < r){ + r = (li4); + } + + if(line[0][x] < backdrop) { + color = line[0][x]; + top = 0x01; + } + + if(r < (uint8_t)(color >> 24)) { + if(r == li1){ + color = line[1][x]; + top = 0x02; + }else if(r == li2){ + color = line[2][x]; + top = 0x04; + }else if(r == li4){ + color = line[4][x]; + top = 0x10; + } + } + + if(!(color & 0x00010000)) { + switch((BLDMOD >> 6) & 3) { + case 0: + break; + case 1: + if(top & BLDMOD) + { + uint32_t back = backdrop; + uint8_t top2 = 0x20; + + if((top != 0x01) && (uint8_t)(line[0][x]>>24) < (uint8_t)(back >> 24)) { + back = line[0][x]; + top2 = 0x01; + } + + if((top != 0x02) && (uint8_t)(line[1][x]>>24) < (uint8_t)(back >> 24)) { + back = line[1][x]; + top2 = 0x02; + } + + if((top != 0x04) && (uint8_t)(line[2][x]>>24) < (uint8_t)(back >> 24)) { + back = line[2][x]; + top2 = 0x04; + } + + if((top != 0x10) && (uint8_t)(line[4][x]>>24) < (uint8_t)(back >> 24)) { + back = line[4][x]; + top2 = 0x10; + } + + if(top2 & (BLDMOD>>8) && color < 0x80000000) + { + GFX_ALPHA_BLEND(color, back, coeff[COLEV & 0x1F], coeff[(COLEV >> 8) & 0x1F]); + } + } + break; + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } else { + // semi-transparent OBJ + uint32_t back = backdrop; + uint8_t top2 = 0x20; + + uint8_t li0 = (uint8_t)(line[0][x]>>24); + uint8_t li1 = (uint8_t)(line[1][x]>>24); + uint8_t li2 = (uint8_t)(line[2][x]>>24); + + uint8_t r = (li1 < li0) ? (li1) : (li0); + + if(li2 < r) { + r = (li2); + } + + if(r < (uint8_t)(back >> 24)) + { + if(r == li0) + { + back = line[0][x]; + top2 = 0x01; + } + else if(r == li1) + { + back = line[1][x]; + top2 = 0x02; + } + else if(r == li2) + { + back = line[2][x]; + top2 = 0x04; + } + } + + alpha_blend_brightness_switch(); + } + + lineMix[x] = CONVERT_COLOR(color); + } + gfxBG2Changed = 0; + //gfxLastVCOUNT = io_registers[REG_VCOUNT]; +} + +void mode1RenderLineAll (void) +{ +#ifdef REPORT_VIDEO_MODES + fprintf(stderr, "MODE 1: Render Line All\n"); +#endif + INIT_COLOR_DEPTH_LINE_MIX(); + + uint16_t *palette = (uint16_t *)graphics.paletteRAM; + + bool inWindow0 = false; + bool inWindow1 = false; + + if(graphics.layerEnable & 0x2000) + { + uint8_t v0 = io_registers[REG_WIN0V] >> 8; + uint8_t v1 = io_registers[REG_WIN0V] & 255; + inWindow0 = ((v0 == v1) && (v0 >= 0xe8)); +#ifndef ORIGINAL_BRANCHES + uint32_t condition = v1 >= v0; + int32_t condition_mask = ((condition) | -(condition)) >> 31; + inWindow0 = (((inWindow0 | (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1)) & condition_mask) | (((inWindow0 | (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1)) & ~(condition_mask)))); +#else + if(v1 >= v0) + inWindow0 |= (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1); + else + inWindow0 |= (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1); +#endif + } + if(graphics.layerEnable & 0x4000) + { + uint8_t v0 = io_registers[REG_WIN1V] >> 8; + uint8_t v1 = io_registers[REG_WIN1V] & 255; + inWindow1 = ((v0 == v1) && (v0 >= 0xe8)); +#ifndef ORIGINAL_BRANCHES + uint32_t condition = v1 >= v0; + int32_t condition_mask = ((condition) | -(condition)) >> 31; + inWindow1 = (((inWindow1 | (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1)) & condition_mask) | (((inWindow1 | (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1)) & ~(condition_mask)))); +#else + if(v1 >= v0) + inWindow1 |= (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1); + else + inWindow1 |= (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1); +#endif + } + + if(graphics.layerEnable & 0x0100) { + gfxDrawTextScreen(io_registers[REG_BG0CNT], io_registers[REG_BG0HOFS], io_registers[REG_BG0VOFS], line[0]); + } + + if(graphics.layerEnable & 0x0200) { + gfxDrawTextScreen(io_registers[REG_BG1CNT], io_registers[REG_BG1HOFS], io_registers[REG_BG1VOFS], line[1]); + } + + if(graphics.layerEnable & 0x0400) { + int changed = gfxBG2Changed; +#if 0 + if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) + changed = 3; +#endif + gfxDrawRotScreen(io_registers[REG_BG2CNT], BG2X_L, BG2X_H, BG2Y_L, BG2Y_H, + io_registers[REG_BG2PA], io_registers[REG_BG2PB], io_registers[REG_BG2PC], io_registers[REG_BG2PD], + gfxBG2X, gfxBG2Y, changed, line[2]); + } + + uint32_t backdrop = (READ16LE(&palette[0]) | 0x30000000); + + uint8_t inWin0Mask = io_registers[REG_WININ] & 0xFF; + uint8_t inWin1Mask = io_registers[REG_WININ] >> 8; + uint8_t outMask = io_registers[REG_WINOUT] & 0xFF; + + for(int x = 0; x < 240; ++x) { + uint32_t color = backdrop; + uint8_t top = 0x20; + uint8_t mask = outMask; + + if(!(line[5][x] & 0x80000000)) { + mask = io_registers[REG_WINOUT] >> 8; + } + + int32_t window1_mask = ((inWindow1 & gfxInWin[1][x]) | -(inWindow1 & gfxInWin[1][x])) >> 31; + int32_t window0_mask = ((inWindow0 & gfxInWin[0][x]) | -(inWindow0 & gfxInWin[0][x])) >> 31; + mask = (inWin1Mask & window1_mask) | (mask & ~window1_mask); + mask = (inWin0Mask & window0_mask) | (mask & ~window0_mask); + + // At the very least, move the inexpensive 'mask' operation up front + if((mask & 1) && line[0][x] < backdrop) { + color = line[0][x]; + top = 0x01; + } + + if((mask & 2) && (uint8_t)(line[1][x]>>24) < (uint8_t)(color >> 24)) { + color = line[1][x]; + top = 0x02; + } + + if((mask & 4) && (uint8_t)(line[2][x]>>24) < (uint8_t)(color >> 24)) { + color = line[2][x]; + top = 0x04; + } + + if((mask & 16) && (uint8_t)(line[4][x]>>24) < (uint8_t)(color >> 24)) { + color = line[4][x]; + top = 0x10; + } + + if(color & 0x00010000) { + // semi-transparent OBJ + uint32_t back = backdrop; + uint8_t top2 = 0x20; + + if((mask & 1) && (uint8_t)(line[0][x]>>24) < (uint8_t)(backdrop >> 24)) { + back = line[0][x]; + top2 = 0x01; + } + + if((mask & 2) && (uint8_t)(line[1][x]>>24) < (uint8_t)(back >> 24)) { + back = line[1][x]; + top2 = 0x02; + } + + if((mask & 4) && (uint8_t)(line[2][x]>>24) < (uint8_t)(back >> 24)) { + back = line[2][x]; + top2 = 0x04; + } + + alpha_blend_brightness_switch(); + } else if(mask & 32) { + // special FX on the window + switch((BLDMOD >> 6) & 3) { + case 0: + break; + case 1: + if(top & BLDMOD) + { + uint32_t back = backdrop; + uint8_t top2 = 0x20; + + if((mask & 1) && (top != 0x01) && (uint8_t)(line[0][x]>>24) < (uint8_t)(backdrop >> 24)) { + back = line[0][x]; + top2 = 0x01; + } + + if((mask & 2) && (top != 0x02) && (uint8_t)(line[1][x]>>24) < (uint8_t)(back >> 24)) { + back = line[1][x]; + top2 = 0x02; + } + + if((mask & 4) && (top != 0x04) && (uint8_t)(line[2][x]>>24) < (uint8_t)(back >> 24)) { + back = line[2][x]; + top2 = 0x04; + } + + if((mask & 16) && (top != 0x10) && (uint8_t)(line[4][x]>>24) < (uint8_t)(back >> 24)) { + back = line[4][x]; + top2 = 0x10; + } + + if(top2 & (BLDMOD>>8) && color < 0x80000000) + { + GFX_ALPHA_BLEND(color, back, coeff[COLEV & 0x1F], coeff[(COLEV >> 8) & 0x1F]); + } + } + break; + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + + lineMix[x] = CONVERT_COLOR(color); + } + gfxBG2Changed = 0; + //gfxLastVCOUNT = io_registers[REG_VCOUNT]; +} + +/* +Mode 2 is a 256 colour tiled graphics mode which supports scaling and rotation. +There is no background layer 0 or 1 in this mode. Only background layers 2 and 3. +There are 256 tiles available. +It does not support flipping. + +These routines only render a single line at a time, because of the way the GBA does events. +*/ + +void mode2RenderLine (void) +{ +#ifdef REPORT_VIDEO_MODES + fprintf(stderr, "MODE 2: Render Line\n"); +#endif + INIT_COLOR_DEPTH_LINE_MIX(); + + uint16_t *palette = (uint16_t *)graphics.paletteRAM; + + if(graphics.layerEnable & 0x0400) { + int changed = gfxBG2Changed; +#if 0 + if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) + changed = 3; +#endif + + gfxDrawRotScreen(io_registers[REG_BG2CNT], BG2X_L, BG2X_H, BG2Y_L, BG2Y_H, + io_registers[REG_BG2PA], io_registers[REG_BG2PB], io_registers[REG_BG2PC], io_registers[REG_BG2PD], gfxBG2X, gfxBG2Y, + changed, line[2]); + } + + if(graphics.layerEnable & 0x0800) { + int changed = gfxBG3Changed; +#if 0 + if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) + changed = 3; +#endif + + gfxDrawRotScreen(io_registers[REG_BG3CNT], BG3X_L, BG3X_H, BG3Y_L, BG3Y_H, + io_registers[REG_BG3PA], io_registers[REG_BG3PB], io_registers[REG_BG3PC], io_registers[REG_BG3PD], gfxBG3X, gfxBG3Y, + changed, line[3]); + } + + uint32_t backdrop = (READ16LE(&palette[0]) | 0x30000000); + + for(int x = 0; x < 240; ++x) { + uint32_t color = backdrop; + uint8_t top = 0x20; + + uint8_t li2 = (uint8_t)(line[2][x]>>24); + uint8_t li3 = (uint8_t)(line[3][x]>>24); + uint8_t li4 = (uint8_t)(line[4][x]>>24); + + uint8_t r = (li3 < li2) ? (li3) : (li2); + + if(li4 < r){ + r = (li4); + } + + if(r < (uint8_t)(color >> 24)) { + if(r == li2){ + color = line[2][x]; + top = 0x04; + }else if(r == li3){ + color = line[3][x]; + top = 0x08; + }else if(r == li4){ + color = line[4][x]; + top = 0x10; + + if(color & 0x00010000) { + // semi-transparent OBJ + uint32_t back = backdrop; + uint8_t top2 = 0x20; + + uint8_t li2 = (uint8_t)(line[2][x]>>24); + uint8_t li3 = (uint8_t)(line[3][x]>>24); + uint8_t r = (li3 < li2) ? (li3) : (li2); + + if(r < (uint8_t)(back >> 24)) { + if(r == li2){ + back = line[2][x]; + top2 = 0x04; + }else if(r == li3){ + back = line[3][x]; + top2 = 0x08; + } + } + + alpha_blend_brightness_switch(); + } + } + } + + + lineMix[x] = CONVERT_COLOR(color); + } + gfxBG2Changed = 0; + gfxBG3Changed = 0; + //gfxLastVCOUNT = io_registers[REG_VCOUNT]; +} + +void mode2RenderLineNoWindow (void) +{ +#ifdef REPORT_VIDEO_MODES + fprintf(stderr, "MODE 2: Render Line No Window\n"); +#endif + INIT_COLOR_DEPTH_LINE_MIX(); + + uint16_t *palette = (uint16_t *)graphics.paletteRAM; + + if(graphics.layerEnable & 0x0400) { + int changed = gfxBG2Changed; +#if 0 + if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) + changed = 3; +#endif + + gfxDrawRotScreen(io_registers[REG_BG2CNT], BG2X_L, BG2X_H, BG2Y_L, BG2Y_H, + io_registers[REG_BG2PA], io_registers[REG_BG2PB], io_registers[REG_BG2PC], io_registers[REG_BG2PD], gfxBG2X, gfxBG2Y, + changed, line[2]); + } + + if(graphics.layerEnable & 0x0800) { + int changed = gfxBG3Changed; +#if 0 + if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) + changed = 3; +#endif + + gfxDrawRotScreen(io_registers[REG_BG3CNT], BG3X_L, BG3X_H, BG3Y_L, BG3Y_H, + io_registers[REG_BG3PA], io_registers[REG_BG3PB], io_registers[REG_BG3PC], io_registers[REG_BG3PD], gfxBG3X, gfxBG3Y, + changed, line[3]); + } + + uint32_t backdrop = (READ16LE(&palette[0]) | 0x30000000); + + for(int x = 0; x < 240; ++x) { + uint32_t color = backdrop; + uint8_t top = 0x20; + + uint8_t li2 = (uint8_t)(line[2][x]>>24); + uint8_t li3 = (uint8_t)(line[3][x]>>24); + uint8_t li4 = (uint8_t)(line[4][x]>>24); + + uint8_t r = (li3 < li2) ? (li3) : (li2); + + if(li4 < r){ + r = (li4); + } + + if(r < (uint8_t)(color >> 24)) { + if(r == li2){ + color = line[2][x]; + top = 0x04; + }else if(r == li3){ + color = line[3][x]; + top = 0x08; + }else if(r == li4){ + color = line[4][x]; + top = 0x10; + } + } + + if(!(color & 0x00010000)) { + switch((BLDMOD >> 6) & 3) { + case 0: + break; + case 1: + if(top & BLDMOD) + { + uint32_t back = backdrop; + uint8_t top2 = 0x20; + + if((top != 0x04) && (uint8_t)(line[2][x]>>24) < (uint8_t)(back >> 24)) { + back = line[2][x]; + top2 = 0x04; + } + + if((top != 0x08) && (uint8_t)(line[3][x]>>24) < (uint8_t)(back >> 24)) { + back = line[3][x]; + top2 = 0x08; + } + + if((top != 0x10) && (uint8_t)(line[4][x]>>24) < (uint8_t)(back >> 24)) { + back = line[4][x]; + top2 = 0x10; + } + + if(top2 & (BLDMOD>>8) && color < 0x80000000) + { + GFX_ALPHA_BLEND(color, back, coeff[COLEV & 0x1F], coeff[(COLEV >> 8) & 0x1F]); + } + } + break; + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } else { + // semi-transparent OBJ + uint32_t back = backdrop; + uint8_t top2 = 0x20; + + uint8_t li2 = (uint8_t)(line[2][x]>>24); + uint8_t li3 = (uint8_t)(line[3][x]>>24); + uint8_t r = (li3 < li2) ? (li3) : (li2); + + if(r < (uint8_t)(back >> 24)) { + if(r == li2){ + back = line[2][x]; + top2 = 0x04; + }else if(r == li3){ + back = line[3][x]; + top2 = 0x08; + } + } + + alpha_blend_brightness_switch(); + } + + lineMix[x] = CONVERT_COLOR(color); + } + gfxBG2Changed = 0; + gfxBG3Changed = 0; + //gfxLastVCOUNT = io_registers[REG_VCOUNT]; +} + +void mode2RenderLineAll (void) +{ +#ifdef REPORT_VIDEO_MODES + fprintf(stderr, "MODE 2: Render Line All\n"); +#endif + INIT_COLOR_DEPTH_LINE_MIX(); + + uint16_t *palette = (uint16_t *)graphics.paletteRAM; + + bool inWindow0 = false; + bool inWindow1 = false; + + if(graphics.layerEnable & 0x2000) + { + uint8_t v0 = io_registers[REG_WIN0V] >> 8; + uint8_t v1 = io_registers[REG_WIN0V] & 255; + inWindow0 = ((v0 == v1) && (v0 >= 0xe8)); +#ifndef ORIGINAL_BRANCHES + uint32_t condition = v1 >= v0; + int32_t condition_mask = ((condition) | -(condition)) >> 31; + inWindow0 = (((inWindow0 | (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1)) & condition_mask) | (((inWindow0 | (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1)) & ~(condition_mask)))); +#else + if(v1 >= v0) + inWindow0 |= (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1); + else + inWindow0 |= (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1); +#endif + } + if(graphics.layerEnable & 0x4000) + { + uint8_t v0 = io_registers[REG_WIN1V] >> 8; + uint8_t v1 = io_registers[REG_WIN1V] & 255; + inWindow1 = ((v0 == v1) && (v0 >= 0xe8)); +#ifndef ORIGINAL_BRANCHES + uint32_t condition = v1 >= v0; + int32_t condition_mask = ((condition) | -(condition)) >> 31; + inWindow1 = (((inWindow1 | (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1)) & condition_mask) | (((inWindow1 | (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1)) & ~(condition_mask)))); +#else + if(v1 >= v0) + inWindow1 |= (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1); + else + inWindow1 |= (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1); +#endif + } + + if(graphics.layerEnable & 0x0400) { + int changed = gfxBG2Changed; +#if 0 + if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) + changed = 3; +#endif + + gfxDrawRotScreen(io_registers[REG_BG2CNT], BG2X_L, BG2X_H, BG2Y_L, BG2Y_H, + io_registers[REG_BG2PA], io_registers[REG_BG2PB], io_registers[REG_BG2PC], io_registers[REG_BG2PD], gfxBG2X, gfxBG2Y, + changed, line[2]); + } + + if(graphics.layerEnable & 0x0800) { + int changed = gfxBG3Changed; +#if 0 + if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) + changed = 3; +#endif + + gfxDrawRotScreen(io_registers[REG_BG3CNT], BG3X_L, BG3X_H, BG3Y_L, BG3Y_H, + io_registers[REG_BG3PA], io_registers[REG_BG3PB], io_registers[REG_BG3PC], io_registers[REG_BG3PD], gfxBG3X, gfxBG3Y, + changed, line[3]); + } + + uint32_t backdrop = (READ16LE(&palette[0]) | 0x30000000); + + uint8_t inWin0Mask = io_registers[REG_WININ] & 0xFF; + uint8_t inWin1Mask = io_registers[REG_WININ] >> 8; + uint8_t outMask = io_registers[REG_WINOUT] & 0xFF; + + for(int x = 0; x < 240; x++) { + uint32_t color = backdrop; + uint8_t top = 0x20; + uint8_t mask = outMask; + + if(!(line[5][x] & 0x80000000)) { + mask = io_registers[REG_WINOUT] >> 8; + } + + int32_t window1_mask = ((inWindow1 & gfxInWin[1][x]) | -(inWindow1 & gfxInWin[1][x])) >> 31; + int32_t window0_mask = ((inWindow0 & gfxInWin[0][x]) | -(inWindow0 & gfxInWin[0][x])) >> 31; + mask = (inWin1Mask & window1_mask) | (mask & ~window1_mask); + mask = (inWin0Mask & window0_mask) | (mask & ~window0_mask); + + if((mask & 4) && line[2][x] < color) { + color = line[2][x]; + top = 0x04; + } + + if((mask & 8) && (uint8_t)(line[3][x]>>24) < (uint8_t)(color >> 24)) { + color = line[3][x]; + top = 0x08; + } + + if((mask & 16) && (uint8_t)(line[4][x]>>24) < (uint8_t)(color >> 24)) { + color = line[4][x]; + top = 0x10; + } + + if(color & 0x00010000) { + // semi-transparent OBJ + uint32_t back = backdrop; + uint8_t top2 = 0x20; + + if((mask & 4) && line[2][x] < back) { + back = line[2][x]; + top2 = 0x04; + } + + if((mask & 8) && (uint8_t)(line[3][x]>>24) < (uint8_t)(back >> 24)) { + back = line[3][x]; + top2 = 0x08; + } + + alpha_blend_brightness_switch(); + } else if(mask & 32) { + // special FX on the window + switch((BLDMOD >> 6) & 3) { + case 0: + break; + case 1: + if(top & BLDMOD) + { + uint32_t back = backdrop; + uint8_t top2 = 0x20; + + if((mask & 4) && (top != 0x04) && line[2][x] < back) { + back = line[2][x]; + top2 = 0x04; + } + + if((mask & 8) && (top != 0x08) && (uint8_t)(line[3][x]>>24) < (uint8_t)(back >> 24)) { + back = line[3][x]; + top2 = 0x08; + } + + if((mask & 16) && (top != 0x10) && (uint8_t)(line[4][x]>>24) < (uint8_t)(back >> 24)) { + back = line[4][x]; + top2 = 0x10; + } + + if(top2 & (BLDMOD>>8) && color < 0x80000000) + { + GFX_ALPHA_BLEND(color, back, coeff[COLEV & 0x1F], coeff[(COLEV >> 8) & 0x1F]); + } + } + break; + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + + lineMix[x] = CONVERT_COLOR(color); + } + gfxBG2Changed = 0; + gfxBG3Changed = 0; + //gfxLastVCOUNT = io_registers[REG_VCOUNT]; +} + +/* +Mode 3 is a 15-bit (32768) colour bitmap graphics mode. +It has a single layer, background layer 2, the same size as the screen. +It doesn't support paging, scrolling, flipping, rotation or tiles. + +These routines only render a single line at a time, because of the way the GBA does events. +*/ + +void mode3RenderLine (void) +{ +#ifdef REPORT_VIDEO_MODES + fprintf(stderr, "MODE 3: Render Line\n"); +#endif + INIT_COLOR_DEPTH_LINE_MIX(); + uint16_t *palette = (uint16_t *)graphics.paletteRAM; + + if(graphics.layerEnable & 0x0400) { + int changed = gfxBG2Changed; + +#if 0 + if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) + changed = 3; +#endif + + gfxDrawRotScreen16Bit(gfxBG2X, gfxBG2Y, changed); + } + + uint32_t background = (READ16LE(&palette[0]) | 0x30000000); + + for(int x = 0; x < 240; ++x) { + uint32_t color = background; + uint8_t top = 0x20; + + if(line[2][x] < color) { + color = line[2][x]; + top = 0x04; + } + + if((uint8_t)(line[4][x]>>24) < (uint8_t)(color >>24)) { + color = line[4][x]; + top = 0x10; + + if(color & 0x00010000) { + // semi-transparent OBJ + uint32_t back = background; + uint8_t top2 = 0x20; + + if(line[2][x] < background) { + back = line[2][x]; + top2 = 0x04; + } + + alpha_blend_brightness_switch(); + } + } + + + lineMix[x] = CONVERT_COLOR(color); + } + gfxBG2Changed = 0; + //gfxLastVCOUNT = io_registers[REG_VCOUNT]; +} + +void mode3RenderLineNoWindow (void) +{ +#ifdef REPORT_VIDEO_MODES + fprintf(stderr, "MODE 3: Render Line No Window\n"); +#endif + INIT_COLOR_DEPTH_LINE_MIX(); + uint16_t *palette = (uint16_t *)graphics.paletteRAM; + + if(graphics.layerEnable & 0x0400) { + int changed = gfxBG2Changed; + +#if 0 + if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) + changed = 3; +#endif + + gfxDrawRotScreen16Bit(gfxBG2X, gfxBG2Y, changed); + } + + uint32_t background = (READ16LE(&palette[0]) | 0x30000000); + + for(int x = 0; x < 240; ++x) { + uint32_t color = background; + uint8_t top = 0x20; + + if(line[2][x] < background) { + color = line[2][x]; + top = 0x04; + } + + if((uint8_t)(line[4][x]>>24) < (uint8_t)(color >>24)) { + color = line[4][x]; + top = 0x10; + } + + if(!(color & 0x00010000)) { + switch((BLDMOD >> 6) & 3) { + case 0: + break; + case 1: + if(top & BLDMOD) + { + uint32_t back = background; + uint8_t top2 = 0x20; + + if(top != 0x04 && (line[2][x] < background) ) { + back = line[2][x]; + top2 = 0x04; + } + + if(top != 0x10 && ((uint8_t)(line[4][x]>>24) < (uint8_t)(back >> 24))) { + back = line[4][x]; + top2 = 0x10; + } + + if(top2 & (BLDMOD>>8) && color < 0x80000000) + { + GFX_ALPHA_BLEND(color, back, coeff[COLEV & 0x1F], coeff[(COLEV >> 8) & 0x1F]); + } + + } + break; + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } else { + // semi-transparent OBJ + uint32_t back = background; + uint8_t top2 = 0x20; + + if(line[2][x] < background) { + back = line[2][x]; + top2 = 0x04; + } + + alpha_blend_brightness_switch(); + } + + lineMix[x] = CONVERT_COLOR(color); + } + gfxBG2Changed = 0; + //gfxLastVCOUNT = io_registers[REG_VCOUNT]; +} + +void mode3RenderLineAll (void) +{ +#ifdef REPORT_VIDEO_MODES + fprintf(stderr, "MODE 3: Render Line All\n"); +#endif + INIT_COLOR_DEPTH_LINE_MIX(); + uint16_t *palette = (uint16_t *)graphics.paletteRAM; + + bool inWindow0 = false; + bool inWindow1 = false; + + if(graphics.layerEnable & 0x2000) + { + uint8_t v0 = io_registers[REG_WIN0V] >> 8; + uint8_t v1 = io_registers[REG_WIN0V] & 255; + inWindow0 = ((v0 == v1) && (v0 >= 0xe8)); +#ifndef ORIGINAL_BRANCHES + uint32_t condition = v1 >= v0; + int32_t condition_mask = ((condition) | -(condition)) >> 31; + inWindow0 = (((inWindow0 | (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1)) & condition_mask) | (((inWindow0 | (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1)) & ~(condition_mask)))); +#else + if(v1 >= v0) + inWindow0 |= (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1); + else + inWindow0 |= (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1); +#endif + } + + if(graphics.layerEnable & 0x4000) + { + uint8_t v0 = io_registers[REG_WIN1V] >> 8; + uint8_t v1 = io_registers[REG_WIN1V] & 255; + inWindow1 = ((v0 == v1) && (v0 >= 0xe8)); +#ifndef ORIGINAL_BRANCHES + uint32_t condition = v1 >= v0; + int32_t condition_mask = ((condition) | -(condition)) >> 31; + inWindow1 = (((inWindow1 | (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1)) & condition_mask) | (((inWindow1 | (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1)) & ~(condition_mask)))); +#else + if(v1 >= v0) + inWindow1 |= (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1); + else + inWindow1 |= (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1); +#endif + } + + if(graphics.layerEnable & 0x0400) { + int changed = gfxBG2Changed; + +#if 0 + if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) + changed = 3; +#endif + + gfxDrawRotScreen16Bit(gfxBG2X, gfxBG2Y, changed); + } + + uint8_t inWin0Mask = io_registers[REG_WININ] & 0xFF; + uint8_t inWin1Mask = io_registers[REG_WININ] >> 8; + uint8_t outMask = io_registers[REG_WINOUT] & 0xFF; + + uint32_t background = (READ16LE(&palette[0]) | 0x30000000); + + for(int x = 0; x < 240; ++x) { + uint32_t color = background; + uint8_t top = 0x20; + uint8_t mask = outMask; + + if(!(line[5][x] & 0x80000000)) { + mask = io_registers[REG_WINOUT] >> 8; + } + + int32_t window1_mask = ((inWindow1 & gfxInWin[1][x]) | -(inWindow1 & gfxInWin[1][x])) >> 31; + int32_t window0_mask = ((inWindow0 & gfxInWin[0][x]) | -(inWindow0 & gfxInWin[0][x])) >> 31; + mask = (inWin1Mask & window1_mask) | (mask & ~window1_mask); + mask = (inWin0Mask & window0_mask) | (mask & ~window0_mask); + + if((mask & 4) && line[2][x] < background) { + color = line[2][x]; + top = 0x04; + } + + if((mask & 16) && ((uint8_t)(line[4][x]>>24) < (uint8_t)(color >>24))) { + color = line[4][x]; + top = 0x10; + } + + if(color & 0x00010000) { + // semi-transparent OBJ + uint32_t back = background; + uint8_t top2 = 0x20; + + if((mask & 4) && line[2][x] < background) { + back = line[2][x]; + top2 = 0x04; + } + + alpha_blend_brightness_switch(); + } else if(mask & 32) { + switch((BLDMOD >> 6) & 3) { + case 0: + break; + case 1: + if(top & BLDMOD) + { + uint32_t back = background; + uint8_t top2 = 0x20; + + if((mask & 4) && (top != 0x04) && line[2][x] < back) { + back = line[2][x]; + top2 = 0x04; + } + + if((mask & 16) && (top != 0x10) && (uint8_t)(line[4][x]>>24) < (uint8_t)(back >> 24)) { + back = line[4][x]; + top2 = 0x10; + } + + if(top2 & (BLDMOD>>8) && color < 0x80000000) + { + GFX_ALPHA_BLEND(color, back, coeff[COLEV & 0x1F], coeff[(COLEV >> 8) & 0x1F]); + } + } + break; + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + + lineMix[x] = CONVERT_COLOR(color); + } + gfxBG2Changed = 0; + //gfxLastVCOUNT = io_registers[REG_VCOUNT]; +} + +/* +Mode 4 is a 256 colour bitmap graphics mode with 2 swappable pages. +It has a single layer, background layer 2, the same size as the screen. +It doesn't support scrolling, flipping, rotation or tiles. + +These routines only render a single line at a time, because of the way the GBA does events. +*/ + +void mode4RenderLine (void) +{ +#ifdef REPORT_VIDEO_MODES + fprintf(stderr, "MODE 4: Render Line\n"); +#endif + INIT_COLOR_DEPTH_LINE_MIX(); + uint16_t *palette = (uint16_t *)graphics.paletteRAM; + + if(graphics.layerEnable & 0x400) + { + int changed = gfxBG2Changed; + +#if 0 + if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) + changed = 3; +#endif + + gfxDrawRotScreen256(gfxBG2X, gfxBG2Y, changed); + } + + uint32_t backdrop = (READ16LE(&palette[0]) | 0x30000000); + + for(int x = 0; x < 240; ++x) + { + uint32_t color = backdrop; + uint8_t top = 0x20; + + if(line[2][x] < backdrop) { + color = line[2][x]; + top = 0x04; + } + + if((uint8_t)(line[4][x]>>24) < (uint8_t)(color >> 24)) { + color = line[4][x]; + top = 0x10; + + if(color & 0x00010000) { + // semi-transparent OBJ + uint32_t back = backdrop; + uint8_t top2 = 0x20; + + if(line[2][x] < backdrop) { + back = line[2][x]; + top2 = 0x04; + } + + alpha_blend_brightness_switch(); + } + } + + + lineMix[x] = CONVERT_COLOR(color); + } + gfxBG2Changed = 0; + //gfxLastVCOUNT = io_registers[REG_VCOUNT]; +} + +void mode4RenderLineNoWindow (void) +{ +#ifdef REPORT_VIDEO_MODES + fprintf(stderr, "MODE 4: Render Line No Window\n"); +#endif + INIT_COLOR_DEPTH_LINE_MIX(); + uint16_t *palette = (uint16_t *)graphics.paletteRAM; + + if(graphics.layerEnable & 0x400) + { + int changed = gfxBG2Changed; + +#if 0 + if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) + changed = 3; +#endif + + gfxDrawRotScreen256(gfxBG2X, gfxBG2Y, changed); + } + + uint32_t backdrop = (READ16LE(&palette[0]) | 0x30000000); + + for(int x = 0; x < 240; ++x) + { + uint32_t color = backdrop; + uint8_t top = 0x20; + + if(line[2][x] < backdrop) { + color = line[2][x]; + top = 0x04; + } + + if((uint8_t)(line[4][x]>>24) < (uint8_t)(color >> 24)) { + color = line[4][x]; + top = 0x10; + } + + if(!(color & 0x00010000)) { + switch((BLDMOD >> 6) & 3) { + case 0: + break; + case 1: + if(top & BLDMOD) + { + uint32_t back = backdrop; + uint8_t top2 = 0x20; + + if((top != 0x04) && line[2][x] < backdrop) { + back = line[2][x]; + top2 = 0x04; + } + + if((top != 0x10) && (uint8_t)(line[4][x]>>24) < (uint8_t)(back >> 24)) { + back = line[4][x]; + top2 = 0x10; + } + + if(top2 & (BLDMOD>>8) && color < 0x80000000) + { + GFX_ALPHA_BLEND(color, back, coeff[COLEV & 0x1F], coeff[(COLEV >> 8) & 0x1F]); + } + } + break; + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } else { + // semi-transparent OBJ + uint32_t back = backdrop; + uint8_t top2 = 0x20; + + if(line[2][x] < back) { + back = line[2][x]; + top2 = 0x04; + } + + alpha_blend_brightness_switch(); + } + + lineMix[x] = CONVERT_COLOR(color); + } + gfxBG2Changed = 0; + //gfxLastVCOUNT = io_registers[REG_VCOUNT]; +} + +void mode4RenderLineAll (void) +{ +#ifdef REPORT_VIDEO_MODES + fprintf(stderr, "MODE 4: Render Line All\n"); +#endif + INIT_COLOR_DEPTH_LINE_MIX(); + uint16_t *palette = (uint16_t *)graphics.paletteRAM; + + bool inWindow0 = false; + bool inWindow1 = false; + + if(graphics.layerEnable & 0x2000) + { + uint8_t v0 = io_registers[REG_WIN0V] >> 8; + uint8_t v1 = io_registers[REG_WIN0V] & 255; + inWindow0 = ((v0 == v1) && (v0 >= 0xe8)); +#ifndef ORIGINAL_BRANCHES + uint32_t condition = v1 >= v0; + int32_t condition_mask = ((condition) | -(condition)) >> 31; + inWindow0 = (((inWindow0 | (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1)) & condition_mask) | (((inWindow0 | (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1)) & ~(condition_mask)))); +#else + if(v1 >= v0) + inWindow0 |= (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1); + else + inWindow0 |= (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1); +#endif + } + + if(graphics.layerEnable & 0x4000) + { + uint8_t v0 = io_registers[REG_WIN1V] >> 8; + uint8_t v1 = io_registers[REG_WIN1V] & 255; + inWindow1 = ((v0 == v1) && (v0 >= 0xe8)); +#ifndef ORIGINAL_BRANCHES + uint32_t condition = v1 >= v0; + int32_t condition_mask = ((condition) | -(condition)) >> 31; + inWindow1 = (((inWindow1 | (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1)) & condition_mask) | (((inWindow1 | (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1)) & ~(condition_mask)))); +#else + if(v1 >= v0) + inWindow1 |= (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1); + else + inWindow1 |= (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1); +#endif + } + + if(graphics.layerEnable & 0x400) + { + int changed = gfxBG2Changed; + +#if 0 + if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) + changed = 3; +#endif + + gfxDrawRotScreen256(gfxBG2X, gfxBG2Y, changed); + } + + uint32_t backdrop = (READ16LE(&palette[0]) | 0x30000000); + + uint8_t inWin0Mask = io_registers[REG_WININ] & 0xFF; + uint8_t inWin1Mask = io_registers[REG_WININ] >> 8; + uint8_t outMask = io_registers[REG_WINOUT] & 0xFF; + + for(int x = 0; x < 240; ++x) { + uint32_t color = backdrop; + uint8_t top = 0x20; + uint8_t mask = outMask; + + if(!(line[5][x] & 0x80000000)) + mask = io_registers[REG_WINOUT] >> 8; + + int32_t window1_mask = ((inWindow1 & gfxInWin[1][x]) | -(inWindow1 & gfxInWin[1][x])) >> 31; + int32_t window0_mask = ((inWindow0 & gfxInWin[0][x]) | -(inWindow0 & gfxInWin[0][x])) >> 31; + mask = (inWin1Mask & window1_mask) | (mask & ~window1_mask); + mask = (inWin0Mask & window0_mask) | (mask & ~window0_mask); + + if((mask & 4) && (line[2][x] < backdrop)) + { + color = line[2][x]; + top = 0x04; + } + + if((mask & 16) && ((uint8_t)(line[4][x]>>24) < (uint8_t)(color >>24))) + { + color = line[4][x]; + top = 0x10; + } + + if(color & 0x00010000) { + // semi-transparent OBJ + uint32_t back = backdrop; + uint8_t top2 = 0x20; + + if((mask & 4) && line[2][x] < back) { + back = line[2][x]; + top2 = 0x04; + } + + alpha_blend_brightness_switch(); + } else if(mask & 32) { + switch((BLDMOD >> 6) & 3) { + case 0: + break; + case 1: + if(top & BLDMOD) + { + uint32_t back = backdrop; + uint8_t top2 = 0x20; + + if((mask & 4) && (top != 0x04) && (line[2][x] < backdrop)) { + back = line[2][x]; + top2 = 0x04; + } + + if((mask & 16) && (top != 0x10) && (uint8_t)(line[4][x]>>24) < (uint8_t)(back >> 24)) { + back = line[4][x]; + top2 = 0x10; + } + + if(top2 & (BLDMOD>>8) && color < 0x80000000) + { + GFX_ALPHA_BLEND(color, back, coeff[COLEV & 0x1F], coeff[(COLEV >> 8) & 0x1F]); + } + } + break; + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + + lineMix[x] = CONVERT_COLOR(color); + } + gfxBG2Changed = 0; + //gfxLastVCOUNT = io_registers[REG_VCOUNT]; +} + +/* +Mode 5 is a low resolution (160x128) 15-bit colour bitmap graphics mode +with 2 swappable pages! +It has a single layer, background layer 2, lower resolution than the screen. +It doesn't support scrolling, flipping, rotation or tiles. + +These routines only render a single line at a time, because of the way the GBA does events. +*/ + +void mode5RenderLine (void) +{ +#ifdef REPORT_VIDEO_MODES + fprintf(stderr, "MODE 5: Render Line\n"); +#endif + INIT_COLOR_DEPTH_LINE_MIX(); + uint16_t *palette = (uint16_t *)graphics.paletteRAM; + + if(graphics.layerEnable & 0x0400) { + int changed = gfxBG2Changed; + +#if 0 + if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) + changed = 3; +#endif + + gfxDrawRotScreen16Bit160(gfxBG2X, gfxBG2Y, changed); + } + + uint32_t background; + background = (READ16LE(&palette[0]) | 0x30000000); + + for(int x = 0; x < 240; ++x) { + uint32_t color = background; + uint8_t top = 0x20; + + if(line[2][x] < background) { + color = line[2][x]; + top = 0x04; + } + + if((uint8_t)(line[4][x]>>24) < (uint8_t)(color >>24)) { + color = line[4][x]; + top = 0x10; + + if(color & 0x00010000) { + // semi-transparent OBJ + uint32_t back = background; + uint8_t top2 = 0x20; + + if(line[2][x] < back) { + back = line[2][x]; + top2 = 0x04; + } + + alpha_blend_brightness_switch(); + } + } + + + lineMix[x] = CONVERT_COLOR(color); + } + gfxBG2Changed = 0; + //gfxLastVCOUNT = io_registers[REG_VCOUNT]; +} + +void mode5RenderLineNoWindow (void) +{ +#ifdef REPORT_VIDEO_MODES + fprintf(stderr, "MODE 5: Render Line No Window\n"); +#endif + INIT_COLOR_DEPTH_LINE_MIX(); + + uint16_t *palette = (uint16_t *)graphics.paletteRAM; + + if(graphics.layerEnable & 0x0400) { + int changed = gfxBG2Changed; + +#if 0 + if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) + changed = 3; +#endif + + gfxDrawRotScreen16Bit160(gfxBG2X, gfxBG2Y, changed); + } + + uint32_t background; + background = (READ16LE(&palette[0]) | 0x30000000); + + for(int x = 0; x < 240; ++x) { + uint32_t color = background; + uint8_t top = 0x20; + + if(line[2][x] < background) { + color = line[2][x]; + top = 0x04; + } + + if((uint8_t)(line[4][x]>>24) < (uint8_t)(color >>24)) { + color = line[4][x]; + top = 0x10; + } + + if(!(color & 0x00010000)) { + switch((BLDMOD >> 6) & 3) { + case 0: + break; + case 1: + if(top & BLDMOD) + { + uint32_t back = background; + uint8_t top2 = 0x20; + + if((top != 0x04) && line[2][x] < background) { + back = line[2][x]; + top2 = 0x04; + } + + if((top != 0x10) && (uint8_t)(line[4][x]>>24) < (uint8_t)(back >> 24)) { + back = line[4][x]; + top2 = 0x10; + } + + if(top2 & (BLDMOD>>8) && color < 0x80000000) + { + GFX_ALPHA_BLEND(color, back, coeff[COLEV & 0x1F], coeff[(COLEV >> 8) & 0x1F]); + } + + } + break; + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } else { + // semi-transparent OBJ + uint32_t back = background; + uint8_t top2 = 0x20; + + if(line[2][x] < back) { + back = line[2][x]; + top2 = 0x04; + } + + alpha_blend_brightness_switch(); + } + + lineMix[x] = CONVERT_COLOR(color); + } + gfxBG2Changed = 0; + //gfxLastVCOUNT = io_registers[REG_VCOUNT]; +} + +void mode5RenderLineAll (void) +{ +#ifdef REPORT_VIDEO_MODES + fprintf(stderr, "MODE 5: Render Line All\n"); +#endif + INIT_COLOR_DEPTH_LINE_MIX(); + + uint16_t *palette = (uint16_t *)graphics.paletteRAM; + + if(graphics.layerEnable & 0x0400) + { + int changed = gfxBG2Changed; + +#if 0 + if(gfxLastVCOUNT > io_registers[REG_VCOUNT]) + changed = 3; +#endif + + gfxDrawRotScreen16Bit160(gfxBG2X, gfxBG2Y, changed); + } + + + + bool inWindow0 = false; + bool inWindow1 = false; + + if(graphics.layerEnable & 0x2000) + { + uint8_t v0 = io_registers[REG_WIN0V] >> 8; + uint8_t v1 = io_registers[REG_WIN0V] & 255; + inWindow0 = ((v0 == v1) && (v0 >= 0xe8)); +#ifndef ORIGINAL_BRANCHES + uint32_t condition = v1 >= v0; + int32_t condition_mask = ((condition) | -(condition)) >> 31; + inWindow0 = (((inWindow0 | (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1)) & condition_mask) | (((inWindow0 | (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1)) & ~(condition_mask)))); +#else + if(v1 >= v0) + inWindow0 |= (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1); + else + inWindow0 |= (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1); +#endif + } + + if(graphics.layerEnable & 0x4000) + { + uint8_t v0 = io_registers[REG_WIN1V] >> 8; + uint8_t v1 = io_registers[REG_WIN1V] & 255; + inWindow1 = ((v0 == v1) && (v0 >= 0xe8)); +#ifndef ORIGINAL_BRANCHES + uint32_t condition = v1 >= v0; + int32_t condition_mask = ((condition) | -(condition)) >> 31; + inWindow1 = (((inWindow1 | (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1)) & condition_mask) | (((inWindow1 | (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1)) & ~(condition_mask)))); +#else + if(v1 >= v0) + inWindow1 |= (io_registers[REG_VCOUNT] >= v0 && io_registers[REG_VCOUNT] < v1); + else + inWindow1 |= (io_registers[REG_VCOUNT] >= v0 || io_registers[REG_VCOUNT] < v1); +#endif + } + + uint8_t inWin0Mask = io_registers[REG_WININ] & 0xFF; + uint8_t inWin1Mask = io_registers[REG_WININ] >> 8; + uint8_t outMask = io_registers[REG_WINOUT] & 0xFF; + + uint32_t background; + background = (READ16LE(&palette[0]) | 0x30000000); + + for(int x = 0; x < 240; ++x) { + uint32_t color = background; + uint8_t top = 0x20; + uint8_t mask = outMask; + + if(!(line[5][x] & 0x80000000)) { + mask = io_registers[REG_WINOUT] >> 8; + } + + int32_t window1_mask = ((inWindow1 & gfxInWin[1][x]) | -(inWindow1 & gfxInWin[1][x])) >> 31; + int32_t window0_mask = ((inWindow0 & gfxInWin[0][x]) | -(inWindow0 & gfxInWin[0][x])) >> 31; + mask = (inWin1Mask & window1_mask) | (mask & ~window1_mask); + mask = (inWin0Mask & window0_mask) | (mask & ~window0_mask); + + if((mask & 4) && (line[2][x] < background)) { + color = line[2][x]; + top = 0x04; + } + + if((mask & 16) && ((uint8_t)(line[4][x]>>24) < (uint8_t)(color >>24))) { + color = line[4][x]; + top = 0x10; + } + + if(color & 0x00010000) { + // semi-transparent OBJ + uint32_t back = background; + uint8_t top2 = 0x20; + + if((mask & 4) && line[2][x] < back) { + back = line[2][x]; + top2 = 0x04; + } + + alpha_blend_brightness_switch(); + } else if(mask & 32) { + switch((BLDMOD >> 6) & 3) { + case 0: + break; + case 1: + if(top & BLDMOD) + { + uint32_t back = background; + uint8_t top2 = 0x20; + + if((mask & 4) && (top != 0x04) && (line[2][x] < background)) { + back = line[2][x]; + top2 = 0x04; + } + + if((mask & 16) && (top != 0x10) && (uint8_t)(line[4][x]>>24) < (uint8_t)(back >> 24)) { + back = line[4][x]; + top2 = 0x10; + } + + if(top2 & (BLDMOD>>8) && color < 0x80000000) + { + GFX_ALPHA_BLEND(color, back, coeff[COLEV & 0x1F], coeff[(COLEV >> 8) & 0x1F]); + } + } + break; + case 2: + if(BLDMOD & top) + color = gfxIncreaseBrightness(color, coeff[COLY & 0x1F]); + break; + case 3: + if(BLDMOD & top) + color = gfxDecreaseBrightness(color, coeff[COLY & 0x1F]); + break; + } + } + + lineMix[x] = CONVERT_COLOR(color); + } + gfxBG2Changed = 0; + //gfxLastVCOUNT = io_registers[REG_VCOUNT]; +} + +void (Gigazoid::*renderLine)(void); +bool render_line_all_enabled; + +#define CPUUpdateRender() \ + render_line_all_enabled = false; \ + switch(io_registers[REG_DISPCNT] & 7) { \ + case 0: \ + if((!fxOn && !windowOn && !(graphics.layerEnable & 0x8000))) \ + renderLine = &Gigazoid::mode0RenderLine; \ + else if(fxOn && !windowOn && !(graphics.layerEnable & 0x8000)) \ + renderLine = &Gigazoid::mode0RenderLineNoWindow; \ + else { \ + renderLine = &Gigazoid::mode0RenderLineAll; \ + render_line_all_enabled = true; \ + } \ + break; \ + case 1: \ + if((!fxOn && !windowOn && !(graphics.layerEnable & 0x8000))) \ + renderLine = &Gigazoid::mode1RenderLine; \ + else if(fxOn && !windowOn && !(graphics.layerEnable & 0x8000)) \ + renderLine = &Gigazoid::mode1RenderLineNoWindow; \ + else { \ + renderLine = &Gigazoid::mode1RenderLineAll; \ + render_line_all_enabled = true; \ + } \ + break; \ + case 2: \ + if((!fxOn && !windowOn && !(graphics.layerEnable & 0x8000))) \ + renderLine = &Gigazoid::mode2RenderLine; \ + else if(fxOn && !windowOn && !(graphics.layerEnable & 0x8000)) \ + renderLine = &Gigazoid::mode2RenderLineNoWindow; \ + else { \ + renderLine = &Gigazoid::mode2RenderLineAll; \ + render_line_all_enabled = true; \ + } \ + break; \ + case 3: \ + if((!fxOn && !windowOn && !(graphics.layerEnable & 0x8000))) \ + renderLine = &Gigazoid::mode3RenderLine; \ + else if(fxOn && !windowOn && !(graphics.layerEnable & 0x8000)) \ + renderLine = &Gigazoid::mode3RenderLineNoWindow; \ + else { \ + renderLine = &Gigazoid::mode3RenderLineAll; \ + render_line_all_enabled = true; \ + } \ + break; \ + case 4: \ + if((!fxOn && !windowOn && !(graphics.layerEnable & 0x8000))) \ + renderLine = &Gigazoid::mode4RenderLine; \ + else if(fxOn && !windowOn && !(graphics.layerEnable & 0x8000)) \ + renderLine = &Gigazoid::mode4RenderLineNoWindow; \ + else { \ + renderLine = &Gigazoid::mode4RenderLineAll; \ + render_line_all_enabled = true; \ + } \ + break; \ + case 5: \ + if((!fxOn && !windowOn && !(graphics.layerEnable & 0x8000))) \ + renderLine = &Gigazoid::mode5RenderLine; \ + else if(fxOn && !windowOn && !(graphics.layerEnable & 0x8000)) \ + renderLine = &Gigazoid::mode5RenderLineNoWindow; \ + else { \ + renderLine = &Gigazoid::mode5RenderLineAll; \ + render_line_all_enabled = true; \ + } \ + } + +#define CPUSwap(a, b) \ +a ^= b; \ +b ^= a; \ +a ^= b; + +void CPUSwitchMode(int mode, bool saveState, bool breakLoop) +{ + CPU_UPDATE_CPSR(); + + switch(armMode) { + case 0x10: + case 0x1F: + bus.reg[R13_USR].I = bus.reg[13].I; + bus.reg[R14_USR].I = bus.reg[14].I; + bus.reg[17].I = bus.reg[16].I; + break; + case 0x11: + CPUSwap(bus.reg[R8_FIQ].I, bus.reg[8].I); + CPUSwap(bus.reg[R9_FIQ].I, bus.reg[9].I); + CPUSwap(bus.reg[R10_FIQ].I, bus.reg[10].I); + CPUSwap(bus.reg[R11_FIQ].I, bus.reg[11].I); + CPUSwap(bus.reg[R12_FIQ].I, bus.reg[12].I); + bus.reg[R13_FIQ].I = bus.reg[13].I; + bus.reg[R14_FIQ].I = bus.reg[14].I; + bus.reg[SPSR_FIQ].I = bus.reg[17].I; + break; + case 0x12: + bus.reg[R13_IRQ].I = bus.reg[13].I; + bus.reg[R14_IRQ].I = bus.reg[14].I; + bus.reg[SPSR_IRQ].I = bus.reg[17].I; + break; + case 0x13: + bus.reg[R13_SVC].I = bus.reg[13].I; + bus.reg[R14_SVC].I = bus.reg[14].I; + bus.reg[SPSR_SVC].I = bus.reg[17].I; + break; + case 0x17: + bus.reg[R13_ABT].I = bus.reg[13].I; + bus.reg[R14_ABT].I = bus.reg[14].I; + bus.reg[SPSR_ABT].I = bus.reg[17].I; + break; + case 0x1b: + bus.reg[R13_UND].I = bus.reg[13].I; + bus.reg[R14_UND].I = bus.reg[14].I; + bus.reg[SPSR_UND].I = bus.reg[17].I; + break; + } + + uint32_t CPSR = bus.reg[16].I; + uint32_t SPSR = bus.reg[17].I; + + switch(mode) { + case 0x10: + case 0x1F: + bus.reg[13].I = bus.reg[R13_USR].I; + bus.reg[14].I = bus.reg[R14_USR].I; + bus.reg[16].I = SPSR; + break; + case 0x11: + CPUSwap(bus.reg[8].I, bus.reg[R8_FIQ].I); + CPUSwap(bus.reg[9].I, bus.reg[R9_FIQ].I); + CPUSwap(bus.reg[10].I, bus.reg[R10_FIQ].I); + CPUSwap(bus.reg[11].I, bus.reg[R11_FIQ].I); + CPUSwap(bus.reg[12].I, bus.reg[R12_FIQ].I); + bus.reg[13].I = bus.reg[R13_FIQ].I; + bus.reg[14].I = bus.reg[R14_FIQ].I; + if(saveState) + bus.reg[17].I = CPSR; else + bus.reg[17].I = bus.reg[SPSR_FIQ].I; + break; + case 0x12: + bus.reg[13].I = bus.reg[R13_IRQ].I; + bus.reg[14].I = bus.reg[R14_IRQ].I; + bus.reg[16].I = SPSR; + if(saveState) + bus.reg[17].I = CPSR; + else + bus.reg[17].I = bus.reg[SPSR_IRQ].I; + break; + case 0x13: + bus.reg[13].I = bus.reg[R13_SVC].I; + bus.reg[14].I = bus.reg[R14_SVC].I; + bus.reg[16].I = SPSR; + if(saveState) + bus.reg[17].I = CPSR; + else + bus.reg[17].I = bus.reg[SPSR_SVC].I; + break; + case 0x17: + bus.reg[13].I = bus.reg[R13_ABT].I; + bus.reg[14].I = bus.reg[R14_ABT].I; + bus.reg[16].I = SPSR; + if(saveState) + bus.reg[17].I = CPSR; + else + bus.reg[17].I = bus.reg[SPSR_ABT].I; + break; + case 0x1b: + bus.reg[13].I = bus.reg[R13_UND].I; + bus.reg[14].I = bus.reg[R14_UND].I; + bus.reg[16].I = SPSR; + if(saveState) + bus.reg[17].I = CPSR; + else + bus.reg[17].I = bus.reg[SPSR_UND].I; + break; + default: + break; + } + armMode = mode; + CPUUpdateFlags(breakLoop); + CPU_UPDATE_CPSR(); +} + + + +void doDMA(uint32_t &s, uint32_t &d, uint32_t si, uint32_t di, uint32_t c, int transfer32) +{ + int sm = s >> 24; + int dm = d >> 24; + int sw = 0; + int dw = 0; + int sc = c; + + cpuDmaCount = c; + // This is done to get the correct waitstates. + int32_t sm_gt_15_mask = ((sm>15) | -(sm>15)) >> 31; + int32_t dm_gt_15_mask = ((dm>15) | -(dm>15)) >> 31; + sm = ((((15) & sm_gt_15_mask) | ((((sm) & ~(sm_gt_15_mask)))))); + dm = ((((15) & dm_gt_15_mask) | ((((dm) & ~(dm_gt_15_mask)))))); + + //if ((sm>=0x05) && (sm<=0x07) || (dm>=0x05) && (dm <=0x07)) + // blank = (((io_registers[REG_DISPSTAT] | ((io_registers[REG_DISPSTAT] >> 1)&1))==1) ? true : false); + + if(transfer32) + { + s &= 0xFFFFFFFC; + if(s < 0x02000000 && (bus.reg[15].I >> 24)) + { + do + { + CPUWriteMemory(d, 0); + d += di; + c--; + }while(c != 0); + } + else + { + do { + CPUWriteMemory(d, CPUReadMemory(s)); + d += di; + s += si; + c--; + }while(c != 0); + } + } + else + { + s &= 0xFFFFFFFE; + si = (int)si >> 1; + di = (int)di >> 1; + if(s < 0x02000000 && (bus.reg[15].I >> 24)) + { + do { + CPUWriteHalfWord(d, 0); + d += di; + c--; + }while(c != 0); + } + else + { + do{ + CPUWriteHalfWord(d, CPUReadHalfWord(s)); + d += di; + s += si; + c--; + }while(c != 0); + } + } + + cpuDmaCount = 0; + + if(transfer32) + { + sw = 1+memoryWaitSeq32[sm & 15]; + dw = 1+memoryWaitSeq32[dm & 15]; + cpuDmaTicksToUpdate += (sw+dw)*(sc-1) + 6 + memoryWait32[sm & 15] + memoryWaitSeq32[dm & 15]; + } + else + { + sw = 1+memoryWaitSeq[sm & 15]; + dw = 1+memoryWaitSeq[dm & 15]; + cpuDmaTicksToUpdate += (sw+dw)*(sc-1) + 6 + memoryWait[sm & 15] + memoryWaitSeq[dm & 15]; + } +} + + +void CPUCheckDMA(int reason, int dmamask) +{ + uint32_t arrayval[] = {4, (uint32_t)-4, 0, 4}; + // DMA 0 + if((DM0CNT_H & 0x8000) && (dmamask & 1)) + { + if(((DM0CNT_H >> 12) & 3) == reason) + { + uint32_t sourceIncrement, destIncrement; + uint32_t condition1 = ((DM0CNT_H >> 7) & 3); + uint32_t condition2 = ((DM0CNT_H >> 5) & 3); + sourceIncrement = arrayval[condition1]; + destIncrement = arrayval[condition2]; + doDMA(dma0Source, dma0Dest, sourceIncrement, destIncrement, + DM0CNT_L ? DM0CNT_L : 0x4000, + DM0CNT_H & 0x0400); + + if(DM0CNT_H & 0x4000) + { + io_registers[REG_IF] |= 0x0100; + UPDATE_REG(0x202, io_registers[REG_IF]); + cpuNextEvent = cpuTotalTicks; + } + + if(((DM0CNT_H >> 5) & 3) == 3) { + dma0Dest = DM0DAD_L | (DM0DAD_H << 16); + } + + if(!(DM0CNT_H & 0x0200) || (reason == 0)) { + DM0CNT_H &= 0x7FFF; + UPDATE_REG(0xBA, DM0CNT_H); + } + } + } + + // DMA 1 + if((DM1CNT_H & 0x8000) && (dmamask & 2)) { + if(((DM1CNT_H >> 12) & 3) == reason) { + uint32_t sourceIncrement, destIncrement; + uint32_t condition1 = ((DM1CNT_H >> 7) & 3); + uint32_t condition2 = ((DM1CNT_H >> 5) & 3); + sourceIncrement = arrayval[condition1]; + destIncrement = arrayval[condition2]; + uint32_t di_value, c_value, transfer_value; + if(reason == 3) + { + di_value = 0; + c_value = 4; + transfer_value = 0x0400; + } + else + { + di_value = destIncrement; + c_value = DM1CNT_L ? DM1CNT_L : 0x4000; + transfer_value = DM1CNT_H & 0x0400; + } + doDMA(dma1Source, dma1Dest, sourceIncrement, di_value, c_value, transfer_value); + + if(DM1CNT_H & 0x4000) { + io_registers[REG_IF] |= 0x0200; + UPDATE_REG(0x202, io_registers[REG_IF]); + cpuNextEvent = cpuTotalTicks; + } + + if(((DM1CNT_H >> 5) & 3) == 3) { + dma1Dest = DM1DAD_L | (DM1DAD_H << 16); + } + + if(!(DM1CNT_H & 0x0200) || (reason == 0)) { + DM1CNT_H &= 0x7FFF; + UPDATE_REG(0xC6, DM1CNT_H); + } + } + } + + // DMA 2 + if((DM2CNT_H & 0x8000) && (dmamask & 4)) { + if(((DM2CNT_H >> 12) & 3) == reason) { + uint32_t sourceIncrement, destIncrement; + uint32_t condition1 = ((DM2CNT_H >> 7) & 3); + uint32_t condition2 = ((DM2CNT_H >> 5) & 3); + sourceIncrement = arrayval[condition1]; + destIncrement = arrayval[condition2]; + uint32_t di_value, c_value, transfer_value; + if(reason == 3) + { + di_value = 0; + c_value = 4; + transfer_value = 0x0400; + } + else + { + di_value = destIncrement; + c_value = DM2CNT_L ? DM2CNT_L : 0x4000; + transfer_value = DM2CNT_H & 0x0400; + } + doDMA(dma2Source, dma2Dest, sourceIncrement, di_value, c_value, transfer_value); + + if(DM2CNT_H & 0x4000) { + io_registers[REG_IF] |= 0x0400; + UPDATE_REG(0x202, io_registers[REG_IF]); + cpuNextEvent = cpuTotalTicks; + } + + if(((DM2CNT_H >> 5) & 3) == 3) { + dma2Dest = DM2DAD_L | (DM2DAD_H << 16); + } + + if(!(DM2CNT_H & 0x0200) || (reason == 0)) { + DM2CNT_H &= 0x7FFF; + UPDATE_REG(0xD2, DM2CNT_H); + } + } + } + + // DMA 3 + if((DM3CNT_H & 0x8000) && (dmamask & 8)) + { + if(((DM3CNT_H >> 12) & 3) == reason) + { + uint32_t sourceIncrement, destIncrement; + uint32_t condition1 = ((DM3CNT_H >> 7) & 3); + uint32_t condition2 = ((DM3CNT_H >> 5) & 3); + sourceIncrement = arrayval[condition1]; + destIncrement = arrayval[condition2]; + doDMA(dma3Source, dma3Dest, sourceIncrement, destIncrement, + DM3CNT_L ? DM3CNT_L : 0x10000, + DM3CNT_H & 0x0400); + if(DM3CNT_H & 0x4000) { + io_registers[REG_IF] |= 0x0800; + UPDATE_REG(0x202, io_registers[REG_IF]); + cpuNextEvent = cpuTotalTicks; + } + + if(((DM3CNT_H >> 5) & 3) == 3) { + dma3Dest = DM3DAD_L | (DM3DAD_H << 16); + } + + if(!(DM3CNT_H & 0x0200) || (reason == 0)) { + DM3CNT_H &= 0x7FFF; + UPDATE_REG(0xDE, DM3CNT_H); + } + } + } +} + +uint16_t *address_lut[0x300]; + +void CPUUpdateRegister(uint32_t address, uint16_t value) +{ + switch(address) + { + case 0x00: + { + if((value & 7) > 5) // display modes above 0-5 are prohibited + io_registers[REG_DISPCNT] = (value & 7); + + bool change = (0 != ((io_registers[REG_DISPCNT] ^ value) & 0x80)); + bool changeBG = (0 != ((io_registers[REG_DISPCNT] ^ value) & 0x0F00)); + uint16_t changeBGon = ((~io_registers[REG_DISPCNT]) & value) & 0x0F00; // these layers are being activated + + io_registers[REG_DISPCNT] = (value & 0xFFF7); // bit 3 can only be accessed by the BIOS to enable GBC mode + UPDATE_REG(0x00, io_registers[REG_DISPCNT]); + + graphics.layerEnable = value; + + if(changeBGon) + { + graphics.layerEnableDelay = 4; + graphics.layerEnable &= ~changeBGon; + } + + windowOn = (graphics.layerEnable & 0x6000) ? true : false; + if(change && !((value & 0x80))) + { + if(!(io_registers[REG_DISPSTAT] & 1)) + { + graphics.lcdTicks = 1008; + io_registers[REG_DISPSTAT] &= 0xFFFC; + UPDATE_REG(0x04, io_registers[REG_DISPSTAT]); + CPUCompareVCOUNT(); + } + } + CPUUpdateRender(); + // we only care about changes in BG0-BG3 + if(changeBG) + { + if(!(graphics.layerEnable & 0x0100)) + memset(line[0], -1, 240 * sizeof(u32)); + if(!(graphics.layerEnable & 0x0200)) + memset(line[1], -1, 240 * sizeof(u32)); + if(!(graphics.layerEnable & 0x0400)) + memset(line[2], -1, 240 * sizeof(u32)); + if(!(graphics.layerEnable & 0x0800)) + memset(line[3], -1, 240 * sizeof(u32)); + } + break; + } + case 0x04: + io_registers[REG_DISPSTAT] = (value & 0xFF38) | (io_registers[REG_DISPSTAT] & 7); + UPDATE_REG(0x04, io_registers[REG_DISPSTAT]); + break; + case 0x06: + // not writable + break; + case 0x08: /* BG0CNT */ + case 0x0A: /* BG1CNT */ + *address_lut[address] = (value & 0xDFCF); + UPDATE_REG(address, *address_lut[address]); + break; + case 0x0C: /* BG2CNT */ + case 0x0E: /* BG3CNT */ + *address_lut[address] = (value & 0xFFCF); + UPDATE_REG(address, *address_lut[address]); + break; + case 0x10: /* BG0HOFS */ + case 0x12: /* BG0VOFS */ + case 0x14: /* BG1HOFS */ + case 0x16: /* BG1VOFS */ + case 0x18: /* BG2HOFS */ + case 0x1A: /* BG2VOFS */ + case 0x1C: /* BG3HOFS */ + case 0x1E: /* BG3VOFS */ + *address_lut[address] = value & 511; + UPDATE_REG(address, *address_lut[address]); + break; + case 0x20: /* BG2PA */ + case 0x22: /* BG2PB */ + case 0x24: /* BG2PC */ + case 0x26: /* BG2PD */ + *address_lut[address] = value; + UPDATE_REG(address, *address_lut[address]); + break; + case 0x28: + BG2X_L = value; + UPDATE_REG(0x28, BG2X_L); + gfxBG2Changed |= 1; + break; + case 0x2A: + BG2X_H = (value & 0xFFF); + UPDATE_REG(0x2A, BG2X_H); + gfxBG2Changed |= 1; + break; + case 0x2C: + BG2Y_L = value; + UPDATE_REG(0x2C, BG2Y_L); + gfxBG2Changed |= 2; + break; + case 0x2E: + BG2Y_H = value & 0xFFF; + UPDATE_REG(0x2E, BG2Y_H); + gfxBG2Changed |= 2; + break; + case 0x30: /* BG3PA */ + case 0x32: /* BG3PB */ + case 0x34: /* BG3PC */ + case 0x36: /* BG3PD */ + *address_lut[address] = value; + UPDATE_REG(address, *address_lut[address]); + break; + case 0x38: + BG3X_L = value; + UPDATE_REG(0x38, BG3X_L); + gfxBG3Changed |= 1; + break; + case 0x3A: + BG3X_H = value & 0xFFF; + UPDATE_REG(0x3A, BG3X_H); + gfxBG3Changed |= 1; + break; + case 0x3C: + BG3Y_L = value; + UPDATE_REG(0x3C, BG3Y_L); + gfxBG3Changed |= 2; + break; + case 0x3E: + BG3Y_H = value & 0xFFF; + UPDATE_REG(0x3E, BG3Y_H); + gfxBG3Changed |= 2; + break; + case 0x40: + io_registers[REG_WIN0H] = value; + UPDATE_REG(0x40, io_registers[REG_WIN0H]); + CPUUpdateWindow0(); + break; + case 0x42: + io_registers[REG_WIN1H] = value; + UPDATE_REG(0x42, io_registers[REG_WIN1H]); + CPUUpdateWindow1(); + break; + case 0x44: + case 0x46: + *address_lut[address] = value; + UPDATE_REG(address, *address_lut[address]); + break; + case 0x48: /* WININ */ + case 0x4A: /* WINOUT */ + *address_lut[address] = value & 0x3F3F; + UPDATE_REG(address, *address_lut[address]); + break; + case 0x4C: + MOSAIC = value; + UPDATE_REG(0x4C, MOSAIC); + break; + case 0x50: + BLDMOD = value & 0x3FFF; + UPDATE_REG(0x50, BLDMOD); + fxOn = ((BLDMOD>>6)&3) != 0; + CPUUpdateRender(); + break; + case 0x52: + COLEV = value & 0x1F1F; + UPDATE_REG(0x52, COLEV); + break; + case 0x54: + COLY = value & 0x1F; + UPDATE_REG(0x54, COLY); + break; + case 0x60: + case 0x62: + case 0x64: + case 0x68: + case 0x6c: + case 0x70: + case 0x72: + case 0x74: + case 0x78: + case 0x7c: + case 0x80: + case 0x84: + { + int gb_addr[2] = {address & 0xFF, (address & 0xFF) + 1}; + uint32_t address_array[2] = {address & 0xFF, (address&0xFF)+1}; + uint8_t data_array[2] = {(uint8_t)(value & 0xFF), (uint8_t)(value>>8)}; + gb_addr[0] = table[gb_addr[0] - 0x60]; + gb_addr[1] = table[gb_addr[1] - 0x60]; + soundEvent_u8_parallel(gb_addr, address_array, data_array); + break; + } + case 0x82: + case 0x88: + case 0xa0: + case 0xa2: + case 0xa4: + case 0xa6: + case 0x90: + case 0x92: + case 0x94: + case 0x96: + case 0x98: + case 0x9a: + case 0x9c: + case 0x9e: + soundEvent_u16(address&0xFF, value); + break; + case 0xB0: + DM0SAD_L = value; + UPDATE_REG(0xB0, DM0SAD_L); + break; + case 0xB2: + DM0SAD_H = value & 0x07FF; + UPDATE_REG(0xB2, DM0SAD_H); + break; + case 0xB4: + DM0DAD_L = value; + UPDATE_REG(0xB4, DM0DAD_L); + break; + case 0xB6: + DM0DAD_H = value & 0x07FF; + UPDATE_REG(0xB6, DM0DAD_H); + break; + case 0xB8: + DM0CNT_L = value & 0x3FFF; + UPDATE_REG(0xB8, 0); + break; + case 0xBA: + { + bool start = ((DM0CNT_H ^ value) & 0x8000) ? true : false; + value &= 0xF7E0; + + DM0CNT_H = value; + UPDATE_REG(0xBA, DM0CNT_H); + + if(start && (value & 0x8000)) + { + dma0Source = DM0SAD_L | (DM0SAD_H << 16); + dma0Dest = DM0DAD_L | (DM0DAD_H << 16); + CPUCheckDMA(0, 1); + } + } + break; + case 0xBC: + DM1SAD_L = value; + UPDATE_REG(0xBC, DM1SAD_L); + break; + case 0xBE: + DM1SAD_H = value & 0x0FFF; + UPDATE_REG(0xBE, DM1SAD_H); + break; + case 0xC0: + DM1DAD_L = value; + UPDATE_REG(0xC0, DM1DAD_L); + break; + case 0xC2: + DM1DAD_H = value & 0x07FF; + UPDATE_REG(0xC2, DM1DAD_H); + break; + case 0xC4: + DM1CNT_L = value & 0x3FFF; + UPDATE_REG(0xC4, 0); + break; + case 0xC6: + { + bool start = ((DM1CNT_H ^ value) & 0x8000) ? true : false; + value &= 0xF7E0; + + DM1CNT_H = value; + UPDATE_REG(0xC6, DM1CNT_H); + + if(start && (value & 0x8000)) + { + dma1Source = DM1SAD_L | (DM1SAD_H << 16); + dma1Dest = DM1DAD_L | (DM1DAD_H << 16); + CPUCheckDMA(0, 2); + } + } + break; + case 0xC8: + DM2SAD_L = value; + UPDATE_REG(0xC8, DM2SAD_L); + break; + case 0xCA: + DM2SAD_H = value & 0x0FFF; + UPDATE_REG(0xCA, DM2SAD_H); + break; + case 0xCC: + DM2DAD_L = value; + UPDATE_REG(0xCC, DM2DAD_L); + break; + case 0xCE: + DM2DAD_H = value & 0x07FF; + UPDATE_REG(0xCE, DM2DAD_H); + break; + case 0xD0: + DM2CNT_L = value & 0x3FFF; + UPDATE_REG(0xD0, 0); + break; + case 0xD2: + { + bool start = ((DM2CNT_H ^ value) & 0x8000) ? true : false; + + value &= 0xF7E0; + + DM2CNT_H = value; + UPDATE_REG(0xD2, DM2CNT_H); + + if(start && (value & 0x8000)) { + dma2Source = DM2SAD_L | (DM2SAD_H << 16); + dma2Dest = DM2DAD_L | (DM2DAD_H << 16); + + CPUCheckDMA(0, 4); + } + } + break; + case 0xD4: + DM3SAD_L = value; + UPDATE_REG(0xD4, DM3SAD_L); + break; + case 0xD6: + DM3SAD_H = value & 0x0FFF; + UPDATE_REG(0xD6, DM3SAD_H); + break; + case 0xD8: + DM3DAD_L = value; + UPDATE_REG(0xD8, DM3DAD_L); + break; + case 0xDA: + DM3DAD_H = value & 0x0FFF; + UPDATE_REG(0xDA, DM3DAD_H); + break; + case 0xDC: + DM3CNT_L = value; + UPDATE_REG(0xDC, 0); + break; + case 0xDE: + { + bool start = ((DM3CNT_H ^ value) & 0x8000) ? true : false; + + value &= 0xFFE0; + + DM3CNT_H = value; + UPDATE_REG(0xDE, DM3CNT_H); + + if(start && (value & 0x8000)) { + dma3Source = DM3SAD_L | (DM3SAD_H << 16); + dma3Dest = DM3DAD_L | (DM3DAD_H << 16); + CPUCheckDMA(0,8); + } + } + break; + case 0x100: + timer0Reload = value; + break; + case 0x102: + timer0Value = value; + timerOnOffDelay|=1; + cpuNextEvent = cpuTotalTicks; + break; + case 0x104: + timer1Reload = value; + break; + case 0x106: + timer1Value = value; + timerOnOffDelay|=2; + cpuNextEvent = cpuTotalTicks; + break; + case 0x108: + timer2Reload = value; + break; + case 0x10A: + timer2Value = value; + timerOnOffDelay|=4; + cpuNextEvent = cpuTotalTicks; + break; + case 0x10C: + timer3Reload = value; + break; + case 0x10E: + timer3Value = value; + timerOnOffDelay|=8; + cpuNextEvent = cpuTotalTicks; + break; + case 0x130: + io_registers[REG_P1] |= (value & 0x3FF); + UPDATE_REG(0x130, io_registers[REG_P1]); + break; + case 0x132: + UPDATE_REG(0x132, value & 0xC3FF); + break; + + + case 0x200: + io_registers[REG_IE] = value & 0x3FFF; + UPDATE_REG(0x200, io_registers[REG_IE]); + if ((io_registers[REG_IME] & 1) && (io_registers[REG_IF] & io_registers[REG_IE]) && armIrqEnable) + cpuNextEvent = cpuTotalTicks; + break; + case 0x202: + io_registers[REG_IF] ^= (value & io_registers[REG_IF]); + UPDATE_REG(0x202, io_registers[REG_IF]); + break; + case 0x204: + { + memoryWait[0x0e] = memoryWaitSeq[0x0e] = gamepakRamWaitState[value & 3]; + + memoryWait[0x08] = memoryWait[0x09] = 3; + memoryWaitSeq[0x08] = memoryWaitSeq[0x09] = 1; + + memoryWait[0x0a] = memoryWait[0x0b] = 3; + memoryWaitSeq[0x0a] = memoryWaitSeq[0x0b] = 1; + + memoryWait[0x0c] = memoryWait[0x0d] = 3; + memoryWaitSeq[0x0c] = memoryWaitSeq[0x0d] = 1; + + memoryWait32[8] = memoryWait[8] + memoryWaitSeq[8] + 1; + memoryWaitSeq32[8] = memoryWaitSeq[8]*2 + 1; + + memoryWait32[9] = memoryWait[9] + memoryWaitSeq[9] + 1; + memoryWaitSeq32[9] = memoryWaitSeq[9]*2 + 1; + + memoryWait32[10] = memoryWait[10] + memoryWaitSeq[10] + 1; + memoryWaitSeq32[10] = memoryWaitSeq[10]*2 + 1; + + memoryWait32[11] = memoryWait[11] + memoryWaitSeq[11] + 1; + memoryWaitSeq32[11] = memoryWaitSeq[11]*2 + 1; + + memoryWait32[12] = memoryWait[12] + memoryWaitSeq[12] + 1; + memoryWaitSeq32[12] = memoryWaitSeq[12]*2 + 1; + + memoryWait32[13] = memoryWait[13] + memoryWaitSeq[13] + 1; + memoryWaitSeq32[13] = memoryWaitSeq[13]*2 + 1; + + memoryWait32[14] = memoryWait[14] + memoryWaitSeq[14] + 1; + memoryWaitSeq32[14] = memoryWaitSeq[14]*2 + 1; + + if((value & 0x4000) == 0x4000) + bus.busPrefetchEnable = true; + else + bus.busPrefetchEnable = false; + + bus.busPrefetch = false; + bus.busPrefetchCount = 0; + + UPDATE_REG(0x204, value & 0x7FFF); + + } + break; + case 0x208: + io_registers[REG_IME] = value & 1; + UPDATE_REG(0x208, io_registers[REG_IME]); + if ((io_registers[REG_IME] & 1) && (io_registers[REG_IF] & io_registers[REG_IE]) && armIrqEnable) + cpuNextEvent = cpuTotalTicks; + break; + case 0x300: + if(value != 0) + value &= 0xFFFE; + UPDATE_REG(0x300, value); + break; + default: + UPDATE_REG(address&0x3FE, value); + break; + } +} + + +void CPUInit(const u8 *biosfile, const u32 biosfilelen) +{ + eepromInUse = false; + switch(cpuSaveType) + { + case 0: // automatic + default: + cpuEEPROMEnabled = true; + cpuEEPROMSensorEnabled = false; + cpuSaveGameFunc = &Gigazoid::flashSaveDecide; // EEPROM usage is automatically detected + break; + case 1: // EEPROM + cpuEEPROMEnabled = true; + cpuEEPROMSensorEnabled = false; + cpuSaveGameFunc = &Gigazoid::dummyWrite; // EEPROM usage is automatically detected + break; + case 2: // SRAM + cpuEEPROMEnabled = false; + cpuEEPROMSensorEnabled = false; + cpuSaveGameFunc = &Gigazoid::sramWrite; + break; + case 3: // FLASH + cpuEEPROMEnabled = false; + cpuEEPROMSensorEnabled = false; + cpuSaveGameFunc = &Gigazoid::flashWrite; + break; + case 4: // EEPROM+Sensor + cpuEEPROMEnabled = true; + cpuEEPROMSensorEnabled = true; + cpuSaveGameFunc = &Gigazoid::dummyWrite; // EEPROM usage is automatically detected + break; + case 5: // NONE + cpuEEPROMEnabled = false; + cpuEEPROMSensorEnabled = false; + cpuSaveGameFunc = &Gigazoid::dummyWrite; + break; + } + + memcpy(bios, biosfile, 16384); + + int i = 0; + + biosProtected[0] = 0x00; + biosProtected[1] = 0xf0; + biosProtected[2] = 0x29; + biosProtected[3] = 0xe1; + + for(i = 0; i < 256; i++) + { + int count = 0; + int j; + for(j = 0; j < 8; j++) + if(i & (1 << j)) + count++; + cpuBitsSet[i] = count; + + for(j = 0; j < 8; j++) + if(i & (1 << j)) + break; + } + + for(i = 0; i < 0x400; i++) + ioReadable[i] = true; + for(i = 0x10; i < 0x48; i++) + ioReadable[i] = false; + for(i = 0x4c; i < 0x50; i++) + ioReadable[i] = false; + for(i = 0x54; i < 0x60; i++) + ioReadable[i] = false; + for(i = 0x8c; i < 0x90; i++) + ioReadable[i] = false; + for(i = 0xa0; i < 0xb8; i++) + ioReadable[i] = false; + for(i = 0xbc; i < 0xc4; i++) + ioReadable[i] = false; + for(i = 0xc8; i < 0xd0; i++) + ioReadable[i] = false; + for(i = 0xd4; i < 0xdc; i++) + ioReadable[i] = false; + for(i = 0xe0; i < 0x100; i++) + ioReadable[i] = false; + for(i = 0x110; i < 0x120; i++) + ioReadable[i] = false; + for(i = 0x12c; i < 0x130; i++) + ioReadable[i] = false; + for(i = 0x138; i < 0x140; i++) + ioReadable[i] = false; + for(i = 0x144; i < 0x150; i++) + ioReadable[i] = false; + for(i = 0x15c; i < 0x200; i++) + ioReadable[i] = false; + for(i = 0x20c; i < 0x300; i++) + ioReadable[i] = false; + for(i = 0x304; i < 0x400; i++) + ioReadable[i] = false; + + // what is this? + if(romSize < 0x1fe2000) { + *((uint16_t *)&rom[0x1fe209c]) = 0xdffa; // SWI 0xFA + *((uint16_t *)&rom[0x1fe209e]) = 0x4770; // BX LR + } + + graphics.layerEnable = 0xff00; + graphics.layerEnableDelay = 1; + io_registers[REG_DISPCNT] = 0x0080; + io_registers[REG_DISPSTAT] = 0; + graphics.lcdTicks = (useBios && !skipBios) ? 1008 : 208; + + /* address lut for use in CPUUpdateRegister */ + address_lut[0x08] = &io_registers[REG_BG0CNT]; + address_lut[0x0A] = &io_registers[REG_BG1CNT]; + address_lut[0x0C] = &io_registers[REG_BG2CNT]; + address_lut[0x0E] = &io_registers[REG_BG3CNT]; + address_lut[0x10] = &io_registers[REG_BG0HOFS]; + address_lut[0x12] = &io_registers[REG_BG0VOFS]; + address_lut[0x14] = &io_registers[REG_BG1HOFS]; + address_lut[0x16] = &io_registers[REG_BG1VOFS]; + address_lut[0x18] = &io_registers[REG_BG2HOFS]; + address_lut[0x1A] = &io_registers[REG_BG2VOFS]; + address_lut[0x1C] = &io_registers[REG_BG3HOFS]; + address_lut[0x1E] = &io_registers[REG_BG3VOFS]; + address_lut[0x20] = &io_registers[REG_BG2PA]; + address_lut[0x22] = &io_registers[REG_BG2PB]; + address_lut[0x24] = &io_registers[REG_BG2PC]; + address_lut[0x26] = &io_registers[REG_BG2PD]; + address_lut[0x48] = &io_registers[REG_WININ]; + address_lut[0x4A] = &io_registers[REG_WINOUT]; + address_lut[0x30] = &io_registers[REG_BG3PA]; + address_lut[0x32] = &io_registers[REG_BG3PB]; + address_lut[0x34] = &io_registers[REG_BG3PC]; + address_lut[0x36] = &io_registers[REG_BG3PD]; + address_lut[0x40] = &io_registers[REG_WIN0H]; + address_lut[0x42] = &io_registers[REG_WIN1H]; + address_lut[0x44] = &io_registers[REG_WIN0V]; + address_lut[0x46] = &io_registers[REG_WIN1V]; +} + +void CPUReset (void) +{ + rtcReset(); + memset(&bus.reg[0], 0, sizeof(bus.reg)); // clean registers + memset(oam, 0, 0x400); // clean OAM + memset(graphics.paletteRAM, 0, 0x400); // clean palette + memset(pix, 0, 4 * 160 * 240); // clean picture + memset(vram, 0, 0x20000); // clean vram + memset(ioMem, 0, 0x400); // clean io memory + + io_registers[REG_DISPCNT] = 0x0080; + io_registers[REG_DISPSTAT] = 0x0000; + io_registers[REG_VCOUNT] = (useBios && !skipBios) ? 0 :0x007E; + io_registers[REG_BG0CNT] = 0x0000; + io_registers[REG_BG1CNT] = 0x0000; + io_registers[REG_BG2CNT] = 0x0000; + io_registers[REG_BG3CNT] = 0x0000; + io_registers[REG_BG0HOFS] = 0x0000; + io_registers[REG_BG0VOFS] = 0x0000; + io_registers[REG_BG1HOFS] = 0x0000; + io_registers[REG_BG1VOFS] = 0x0000; + io_registers[REG_BG2HOFS] = 0x0000; + io_registers[REG_BG2VOFS] = 0x0000; + io_registers[REG_BG3HOFS] = 0x0000; + io_registers[REG_BG3VOFS] = 0x0000; + io_registers[REG_BG2PA] = 0x0100; + io_registers[REG_BG2PB] = 0x0000; + io_registers[REG_BG2PC] = 0x0000; + io_registers[REG_BG2PD] = 0x0100; + BG2X_L = 0x0000; + BG2X_H = 0x0000; + BG2Y_L = 0x0000; + BG2Y_H = 0x0000; + io_registers[REG_BG3PA] = 0x0100; + io_registers[REG_BG3PB] = 0x0000; + io_registers[REG_BG3PC] = 0x0000; + io_registers[REG_BG3PD] = 0x0100; + BG3X_L = 0x0000; + BG3X_H = 0x0000; + BG3Y_L = 0x0000; + BG3Y_H = 0x0000; + io_registers[REG_WIN0H] = 0x0000; + io_registers[REG_WIN1H] = 0x0000; + io_registers[REG_WIN0V] = 0x0000; + io_registers[REG_WIN1V] = 0x0000; + io_registers[REG_WININ] = 0x0000; + io_registers[REG_WINOUT] = 0x0000; + MOSAIC = 0x0000; + BLDMOD = 0x0000; + COLEV = 0x0000; + COLY = 0x0000; + DM0SAD_L = 0x0000; + DM0SAD_H = 0x0000; + DM0DAD_L = 0x0000; + DM0DAD_H = 0x0000; + DM0CNT_L = 0x0000; + DM0CNT_H = 0x0000; + DM1SAD_L = 0x0000; + DM1SAD_H = 0x0000; + DM1DAD_L = 0x0000; + DM1DAD_H = 0x0000; + DM1CNT_L = 0x0000; + DM1CNT_H = 0x0000; + DM2SAD_L = 0x0000; + DM2SAD_H = 0x0000; + DM2DAD_L = 0x0000; + DM2DAD_H = 0x0000; + DM2CNT_L = 0x0000; + DM2CNT_H = 0x0000; + DM3SAD_L = 0x0000; + DM3SAD_H = 0x0000; + DM3DAD_L = 0x0000; + DM3DAD_H = 0x0000; + DM3CNT_L = 0x0000; + DM3CNT_H = 0x0000; + io_registers[REG_TM0D] = 0x0000; + io_registers[REG_TM0CNT] = 0x0000; + io_registers[REG_TM1D] = 0x0000; + io_registers[REG_TM1CNT] = 0x0000; + io_registers[REG_TM2D] = 0x0000; + io_registers[REG_TM2CNT] = 0x0000; + io_registers[REG_TM3D] = 0x0000; + io_registers[REG_TM3CNT] = 0x0000; + io_registers[REG_P1] = 0x03FF; + io_registers[REG_IE] = 0x0000; + io_registers[REG_IF] = 0x0000; + io_registers[REG_IME] = 0x0000; + + armMode = 0x1F; + + if(cpuIsMultiBoot) { + bus.reg[13].I = 0x03007F00; + bus.reg[15].I = 0x02000000; + bus.reg[16].I = 0x00000000; + bus.reg[R13_IRQ].I = 0x03007FA0; + bus.reg[R13_SVC].I = 0x03007FE0; + armIrqEnable = true; + } else { +#ifdef HAVE_HLE_BIOS + if(useBios && !skipBios) + { + bus.reg[15].I = 0x00000000; + armMode = 0x13; + armIrqEnable = false; + } + else + { +#endif + bus.reg[13].I = 0x03007F00; + bus.reg[15].I = 0x08000000; + bus.reg[16].I = 0x00000000; + bus.reg[R13_IRQ].I = 0x03007FA0; + bus.reg[R13_SVC].I = 0x03007FE0; + armIrqEnable = true; +#ifdef HAVE_HLE_BIOS + } +#endif + } + armState = true; + C_FLAG = V_FLAG = N_FLAG = Z_FLAG = false; + UPDATE_REG(0x00, io_registers[REG_DISPCNT]); + UPDATE_REG(0x06, io_registers[REG_VCOUNT]); + UPDATE_REG(0x20, io_registers[REG_BG2PA]); + UPDATE_REG(0x26, io_registers[REG_BG2PD]); + UPDATE_REG(0x30, io_registers[REG_BG3PA]); + UPDATE_REG(0x36, io_registers[REG_BG3PD]); + UPDATE_REG(0x130, io_registers[REG_P1]); + UPDATE_REG(0x88, 0x200); + + // disable FIQ + bus.reg[16].I |= 0x40; + + CPU_UPDATE_CPSR(); + + bus.armNextPC = bus.reg[15].I; + bus.reg[15].I += 4; + + // reset internal state + holdState = false; + + biosProtected[0] = 0x00; + biosProtected[1] = 0xf0; + biosProtected[2] = 0x29; + biosProtected[3] = 0xe1; + + graphics.lcdTicks = (useBios && !skipBios) ? 1008 : 208; + timer0On = false; + timer0Ticks = 0; + timer0Reload = 0; + timer0ClockReload = 0; + timer1On = false; + timer1Ticks = 0; + timer1Reload = 0; + timer1ClockReload = 0; + timer2On = false; + timer2Ticks = 0; + timer2Reload = 0; + timer2ClockReload = 0; + timer3On = false; + timer3Ticks = 0; + timer3Reload = 0; + timer3ClockReload = 0; + dma0Source = 0; + dma0Dest = 0; + dma1Source = 0; + dma1Dest = 0; + dma2Source = 0; + dma2Dest = 0; + dma3Source = 0; + dma3Dest = 0; + renderLine = &Gigazoid::mode0RenderLine; + fxOn = false; + windowOn = false; + graphics.layerEnable = io_registers[REG_DISPCNT]; + + memset(line[0], -1, 240 * sizeof(u32)); + memset(line[1], -1, 240 * sizeof(u32)); + memset(line[2], -1, 240 * sizeof(u32)); + memset(line[3], -1, 240 * sizeof(u32)); + + for(int i = 0; i < 256; i++) { + map[i].address = 0; + map[i].mask = 0; + } + + map[0].address = bios; + map[0].mask = 0x3FFF; + map[2].address = workRAM; + map[2].mask = 0x3FFFF; + map[3].address = internalRAM; + map[3].mask = 0x7FFF; + map[4].address = ioMem; + map[4].mask = 0x3FF; + map[5].address = graphics.paletteRAM; + map[5].mask = 0x3FF; + map[6].address = vram; + map[6].mask = 0x1FFFF; + map[7].address = oam; + map[7].mask = 0x3FF; + map[8].address = rom; + map[8].mask = 0x1FFFFFF; + map[9].address = rom; + map[9].mask = 0x1FFFFFF; + map[10].address = rom; + map[10].mask = 0x1FFFFFF; + map[12].address = rom; + map[12].mask = 0x1FFFFFF; + map[14].address = flashSaveMemory; + map[14].mask = 0xFFFF; + + eepromReset(); + flashReset(); + + soundReset(); + + CPUUpdateWindow0(); + CPUUpdateWindow1(); + + // make sure registers are correctly initialized if not using BIOS + if(cpuIsMultiBoot) + BIOS_RegisterRamReset(0xfe); + else if(!useBios && !cpuIsMultiBoot) + BIOS_RegisterRamReset(0xff); + else if (skipBios) + BIOS_RegisterRamReset(0xff); // ?? + + ARM_PREFETCH; +} + +void CPUInterrupt(void) +{ + uint32_t PC = bus.reg[15].I; + bool savedState = armState; + + if(armMode != 0x12 ) + CPUSwitchMode(0x12, true, false); + + bus.reg[14].I = PC; + if(!savedState) + bus.reg[14].I += 2; + bus.reg[15].I = 0x18; + armState = true; + armIrqEnable = false; + + bus.armNextPC = bus.reg[15].I; + bus.reg[15].I += 4; + ARM_PREFETCH; + + // if(!holdState) + biosProtected[0] = 0x02; + biosProtected[1] = 0xc0; + biosProtected[2] = 0x5e; + biosProtected[3] = 0xe5; +} + +void CPULoop (void) +{ + bus.busPrefetchCount = 0; + int ticks = 300000; + int timerOverflow = 0; + // variable used by the CPU core + cpuTotalTicks = 0; + + cpuNextEvent = CPUUpdateTicks(); + if(cpuNextEvent > ticks) + cpuNextEvent = ticks; + + bool framedone = false; + + do + { + if(!holdState) + { + if(armState) + { + if (!armExecute()) + return; + } + else + { + if (!thumbExecute()) + return; + } + clockTicks = 0; + } + else + clockTicks = CPUUpdateTicks(); + + cpuTotalTicks += clockTicks; + + + if(cpuTotalTicks >= cpuNextEvent) { + int remainingTicks = cpuTotalTicks - cpuNextEvent; + + clockTicks = cpuNextEvent; + cpuTotalTicks = 0; + +updateLoop: + + if (IRQTicks) + { + IRQTicks -= clockTicks; + if (IRQTicks<0) + IRQTicks = 0; + } + + graphics.lcdTicks -= clockTicks; + + soundTicksUp += clockTicks; + + AdvanceRTC(clockTicks); + + if(graphics.lcdTicks <= 0) + { + if(io_registers[REG_DISPSTAT] & 1) + { // V-BLANK + // if in V-Blank mode, keep computing... + if(io_registers[REG_DISPSTAT] & 2) + { + graphics.lcdTicks += 1008; + io_registers[REG_VCOUNT] += 1; + UPDATE_REG(0x06, io_registers[REG_VCOUNT]); + io_registers[REG_DISPSTAT] &= 0xFFFD; + UPDATE_REG(0x04, io_registers[REG_DISPSTAT]); + CPUCompareVCOUNT(); + } + else + { + graphics.lcdTicks += 224; + io_registers[REG_DISPSTAT] |= 2; + UPDATE_REG(0x04, io_registers[REG_DISPSTAT]); + if(io_registers[REG_DISPSTAT] & 16) + { + io_registers[REG_IF] |= 2; + UPDATE_REG(0x202, io_registers[REG_IF]); + } + if (scanlineCallback && scanlineCallbackLine == io_registers[REG_VCOUNT]) + scanlineCallback(); + } + + if(io_registers[REG_VCOUNT] >= 228) + { + //Reaching last line + io_registers[REG_DISPSTAT] &= 0xFFFC; + UPDATE_REG(0x04, io_registers[REG_DISPSTAT]); + io_registers[REG_VCOUNT] = 0; + UPDATE_REG(0x06, io_registers[REG_VCOUNT]); + CPUCompareVCOUNT(); + } + } + else if(io_registers[REG_DISPSTAT] & 2) + { + // if in H-Blank, leave it and move to drawing mode + io_registers[REG_VCOUNT] += 1; + UPDATE_REG(0x06, io_registers[REG_VCOUNT]); + graphics.lcdTicks += 1008; + io_registers[REG_DISPSTAT] &= 0xFFFD; + if(io_registers[REG_VCOUNT] == 160) + { + // moved to start of emulated frame + //UpdateJoypad(); + + io_registers[REG_DISPSTAT] |= 1; + io_registers[REG_DISPSTAT] &= 0xFFFD; + UPDATE_REG(0x04, io_registers[REG_DISPSTAT]); + if(io_registers[REG_DISPSTAT] & 0x0008) + { + io_registers[REG_IF] |= 1; + UPDATE_REG(0x202, io_registers[REG_IF]); + } + CPUCheckDMA(1, 0x0f); + systemDrawScreen(); + + process_sound_tick_fn(); + framedone = true; + } + + UPDATE_REG(0x04, io_registers[REG_DISPSTAT]); + CPUCompareVCOUNT(); + } + else + { + bool draw_objwin = (graphics.layerEnable & 0x9000) == 0x9000; + bool draw_sprites = graphics.layerEnable & 0x1000; + memset(line[4], -1, 240 * sizeof(u32)); // erase all sprites + + if(draw_sprites) + gfxDrawSprites(); + + if(render_line_all_enabled) + { + memset(line[5], -1, 240 * sizeof(u32)); // erase all OBJ Win + if(draw_objwin) + gfxDrawOBJWin(); + } + + (this->*renderLine)(); + + // entering H-Blank + io_registers[REG_DISPSTAT] |= 2; + UPDATE_REG(0x04, io_registers[REG_DISPSTAT]); + graphics.lcdTicks += 224; + CPUCheckDMA(2, 0x0f); + if(io_registers[REG_DISPSTAT] & 16) + { + io_registers[REG_IF] |= 2; + UPDATE_REG(0x202, io_registers[REG_IF]); + } + if (scanlineCallback && scanlineCallbackLine == io_registers[REG_VCOUNT]) + scanlineCallback(); + } + } + + // we shouldn't be doing sound in stop state, but we lose synchronization + // if sound is disabled, so in stop state, soundTick will just produce + // mute sound + + // moving this may have consequences; we'll see + //soundTicksUp += clockTicks; + + if(!stopState) { + if(timer0On) { + timer0Ticks -= clockTicks; + if(timer0Ticks <= 0) { + timer0Ticks += (0x10000 - timer0Reload) << timer0ClockReload; + timerOverflow |= 1; + soundTimerOverflow(0); + if(io_registers[REG_TM0CNT] & 0x40) { + io_registers[REG_IF] |= 0x08; + UPDATE_REG(0x202, io_registers[REG_IF]); + } + } + io_registers[REG_TM0D] = 0xFFFF - (timer0Ticks >> timer0ClockReload); + UPDATE_REG(0x100, io_registers[REG_TM0D]); + } + + if(timer1On) { + if(io_registers[REG_TM1CNT] & 4) { + if(timerOverflow & 1) { + io_registers[REG_TM1D]++; + if(io_registers[REG_TM1D] == 0) { + io_registers[REG_TM1D] += timer1Reload; + timerOverflow |= 2; + soundTimerOverflow(1); + if(io_registers[REG_TM1CNT] & 0x40) { + io_registers[REG_IF] |= 0x10; + UPDATE_REG(0x202, io_registers[REG_IF]); + } + } + UPDATE_REG(0x104, io_registers[REG_TM1D]); + } + } else { + timer1Ticks -= clockTicks; + if(timer1Ticks <= 0) { + timer1Ticks += (0x10000 - timer1Reload) << timer1ClockReload; + timerOverflow |= 2; + soundTimerOverflow(1); + if(io_registers[REG_TM1CNT] & 0x40) { + io_registers[REG_IF] |= 0x10; + UPDATE_REG(0x202, io_registers[REG_IF]); + } + } + io_registers[REG_TM1D] = 0xFFFF - (timer1Ticks >> timer1ClockReload); + UPDATE_REG(0x104, io_registers[REG_TM1D]); + } + } + + if(timer2On) { + if(io_registers[REG_TM2CNT] & 4) { + if(timerOverflow & 2) { + io_registers[REG_TM2D]++; + if(io_registers[REG_TM2D] == 0) { + io_registers[REG_TM2D] += timer2Reload; + timerOverflow |= 4; + if(io_registers[REG_TM2CNT] & 0x40) { + io_registers[REG_IF] |= 0x20; + UPDATE_REG(0x202, io_registers[REG_IF]); + } + } + UPDATE_REG(0x108, io_registers[REG_TM2D]); + } + } else { + timer2Ticks -= clockTicks; + if(timer2Ticks <= 0) { + timer2Ticks += (0x10000 - timer2Reload) << timer2ClockReload; + timerOverflow |= 4; + if(io_registers[REG_TM2CNT] & 0x40) { + io_registers[REG_IF] |= 0x20; + UPDATE_REG(0x202, io_registers[REG_IF]); + } + } + io_registers[REG_TM2D] = 0xFFFF - (timer2Ticks >> timer2ClockReload); + UPDATE_REG(0x108, io_registers[REG_TM2D]); + } + } + + if(timer3On) { + if(io_registers[REG_TM3CNT] & 4) { + if(timerOverflow & 4) { + io_registers[REG_TM3D]++; + if(io_registers[REG_TM3D] == 0) { + io_registers[REG_TM3D] += timer3Reload; + if(io_registers[REG_TM3CNT] & 0x40) { + io_registers[REG_IF] |= 0x40; + UPDATE_REG(0x202, io_registers[REG_IF]); + } + } + UPDATE_REG(0x10C, io_registers[REG_TM3D]); + } + } else { + timer3Ticks -= clockTicks; + if(timer3Ticks <= 0) { + timer3Ticks += (0x10000 - timer3Reload) << timer3ClockReload; + if(io_registers[REG_TM3CNT] & 0x40) { + io_registers[REG_IF] |= 0x40; + UPDATE_REG(0x202, io_registers[REG_IF]); + } + } + io_registers[REG_TM3D] = 0xFFFF - (timer3Ticks >> timer3ClockReload); + UPDATE_REG(0x10C, io_registers[REG_TM3D]); + } + } + } + + timerOverflow = 0; + ticks -= clockTicks; + cpuNextEvent = CPUUpdateTicks(); + + if(cpuDmaTicksToUpdate > 0) + { + if(cpuDmaTicksToUpdate > cpuNextEvent) + clockTicks = cpuNextEvent; + else + clockTicks = cpuDmaTicksToUpdate; + cpuDmaTicksToUpdate -= clockTicks; + if(cpuDmaTicksToUpdate < 0) + cpuDmaTicksToUpdate = 0; + goto skipIRQ; + } + + if(io_registers[REG_IF] && (io_registers[REG_IME] & 1) && armIrqEnable) + { + int res = io_registers[REG_IF] & io_registers[REG_IE]; + if(stopState) + res &= 0x3080; + if(res) + { + if (intState) + { + if (!IRQTicks) + { + CPUInterrupt(); + intState = false; + holdState = false; + stopState = false; + } + } + else + { + if (!holdState) + { + intState = true; + IRQTicks=7; + if (cpuNextEvent> IRQTicks) + cpuNextEvent = IRQTicks; + } + else + { + CPUInterrupt(); + holdState = false; + stopState = false; + } + } + } + } + + skipIRQ: + + if(remainingTicks > 0) { + if(remainingTicks > cpuNextEvent) + clockTicks = cpuNextEvent; + else + clockTicks = remainingTicks; + remainingTicks -= clockTicks; + if(remainingTicks < 0) + remainingTicks = 0; + goto updateLoop; + } + + if (timerOnOffDelay) + { + // Apply Timer + if (timerOnOffDelay & 1) + { + timer0ClockReload = TIMER_TICKS[timer0Value & 3]; + if(!timer0On && (timer0Value & 0x80)) { + // reload the counter + io_registers[REG_TM0D] = timer0Reload; + timer0Ticks = (0x10000 - io_registers[REG_TM0D]) << timer0ClockReload; + UPDATE_REG(0x100, io_registers[REG_TM0D]); + } + timer0On = timer0Value & 0x80 ? true : false; + io_registers[REG_TM0CNT] = timer0Value & 0xC7; + UPDATE_REG(0x102, io_registers[REG_TM0CNT]); + } + if (timerOnOffDelay & 2) + { + timer1ClockReload = TIMER_TICKS[timer1Value & 3]; + if(!timer1On && (timer1Value & 0x80)) { + // reload the counter + io_registers[REG_TM1D] = timer1Reload; + timer1Ticks = (0x10000 - io_registers[REG_TM1D]) << timer1ClockReload; + UPDATE_REG(0x104, io_registers[REG_TM1D]); + } + timer1On = timer1Value & 0x80 ? true : false; + io_registers[REG_TM1CNT] = timer1Value & 0xC7; + UPDATE_REG(0x106, io_registers[REG_TM1CNT]); + } + if (timerOnOffDelay & 4) + { + timer2ClockReload = TIMER_TICKS[timer2Value & 3]; + if(!timer2On && (timer2Value & 0x80)) { + // reload the counter + io_registers[REG_TM2D] = timer2Reload; + timer2Ticks = (0x10000 - io_registers[REG_TM2D]) << timer2ClockReload; + UPDATE_REG(0x108, io_registers[REG_TM2D]); + } + timer2On = timer2Value & 0x80 ? true : false; + io_registers[REG_TM2CNT] = timer2Value & 0xC7; + UPDATE_REG(0x10A, io_registers[REG_TM2CNT]); + } + if (timerOnOffDelay & 8) + { + timer3ClockReload = TIMER_TICKS[timer3Value & 3]; + if(!timer3On && (timer3Value & 0x80)) { + // reload the counter + io_registers[REG_TM3D] = timer3Reload; + timer3Ticks = (0x10000 - io_registers[REG_TM3D]) << timer3ClockReload; + UPDATE_REG(0x10C, io_registers[REG_TM3D]); + } + timer3On = timer3Value & 0x80 ? true : false; + io_registers[REG_TM3CNT] = timer3Value & 0xC7; + UPDATE_REG(0x10E, io_registers[REG_TM3CNT]); + } + cpuNextEvent = CPUUpdateTicks(); + timerOnOffDelay = 0; + // End of Apply Timer + } + + if(cpuNextEvent > ticks) + cpuNextEvent = ticks; + + if(ticks <= 0 || framedone) + break; + + } + }while(1); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// END GBA.CPP +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void Gigazoid_Init() +{ + // one time constructor stuff + + flashState = FLASH_READ_ARRAY; + flashReadState = FLASH_READ_ARRAY; + flashSize = 0x10000; + flashDeviceID = 0x1b; + flashManufacturerID = 0x32; + flashBank = 0; + + eepromMode = EEPROM_IDLE; + + eepromInUse = false; + eepromSize = 512; + + // this is constant now + // soundSampleRate = 22050; + + soundEnableFlag = 0x3ff; /* emulator channels enabled*/ + + armState = true; + armIrqEnable = true; + armMode = 0x1f; + + renderLine = &Gigazoid::mode0RenderLine; + + #define ARRAYINIT(n) memcpy((n), (n##_init), sizeof(n)) + ARRAYINIT(memoryWait); + ARRAYINIT(memoryWaitSeq); + ARRAYINIT(memoryWait32); + ARRAYINIT(memoryWaitSeq32); + #undef ARRAYINIT +} + +u32 *systemVideoFrameDest; +u32 *systemVideoFramePalette; +s16 *systemAudioFrameDest; +int *systemAudioFrameSamp; +bool lagged; + +void (*scanlineCallback)(); +int scanlineCallbackLine; + +void (*fetchCallback)(u32 addr); +void (*writeCallback)(u32 addr); +void (*readCallback)(u32 addr); +void (*traceCallback)(u32 addr, u32 opcode); + +void (*padCallback)(); + +void systemDrawScreen (void) +{ + // upconvert 555->888 (TODO: BETTER) + for (int i = 0; i < 240 * 160; i++) + { + u32 input = pix[i]; + /* + u32 output = 0xff000000 | + input << 9 & 0xf80000 | + input << 6 & 0xf800 | + input << 3 & 0xf8; + */ + u32 output = systemVideoFramePalette[input]; + systemVideoFrameDest[i] = output; + } + systemVideoFrameDest = nullptr; + systemVideoFramePalette = nullptr; +} + +// called at regular intervals on sound clock +void systemOnWriteDataToSoundBuffer(int16_t * finalWave, int length) +{ + memcpy(systemAudioFrameDest, finalWave, length * 2); + systemAudioFrameDest = nullptr; + *systemAudioFrameSamp = length / 2; + systemAudioFrameSamp = nullptr; +} + +void UpdateJoypad() +{ + /* update joystick information */ + io_registers[REG_P1] = 0x03FF ^ (joy & 0x3FF); +#if 0 + if(cpuEEPROMSensorEnabled) + systemUpdateMotionSensor(); +#endif + UPDATE_REG(0x130, io_registers[REG_P1]); + io_registers[REG_P1CNT] = READ16LE(((uint16_t *)&ioMem[0x132])); + + // this seems wrong, but there are cases where the game + // can enter the stop state without requesting an IRQ from + // the joypad. + if((io_registers[REG_P1CNT] & 0x4000) || stopState) { + uint16_t p1 = (0x3FF ^ io_registers[REG_P1CNT]) & 0x3FF; + if(io_registers[REG_P1CNT] & 0x8000) { + if(p1 == (io_registers[REG_P1CNT] & 0x3FF)) { + io_registers[REG_IF] |= 0x1000; + UPDATE_REG(0x202, io_registers[REG_IF]); + } + } else { + if(p1 & io_registers[REG_P1CNT]) { + io_registers[REG_IF] |= 0x1000; + UPDATE_REG(0x202, io_registers[REG_IF]); + } + } + } +} + +public: + +templatevoid SyncState(NewState *ns) +{ + NSS(flashSaveMemory); + NSS(flashState); + NSS(flashReadState); + NSS(flashSize); + NSS(flashDeviceID); + NSS(flashManufacturerID); + NSS(flashBank); + + NSS(eepromMode); + NSS(eepromByte); + NSS(eepromBits); + NSS(eepromAddress); + NSS(eepromData); + NSS(eepromBuffer); + NSS(eepromInUse); + NSS(eepromSize); + + NSS(rtcClockData); + NSS(rtcEnabled); + SSS(rtcInternalTime); + NSS(RTCTicks); + NSS(RTCUseRealTime); + + NSS(soundTicksUp); + NSS(soundEnableFlag); + SSS_HACKY(pcm[0], this); + SSS_HACKY(pcm[1], this); + SSS(pcm_synth); + SSS(bufs_buffer[0]); SSS(bufs_buffer[1]); SSS(bufs_buffer[2]); NSS(mixer_samples_read); - - SSS(gb_apu); - - NSS(cpuNextEvent); - NSS(holdState); - NSS(cpuPrefetch); - NSS(cpuTotalTicks); - NSS(memoryWait); - NSS(memoryWaitSeq); - NSS(memoryWait32); - NSS(memoryWaitSeq32); - NSS(biosProtected); - NSS(cpuBitsSet); - - NSS(N_FLAG); - NSS(C_FLAG); - NSS(Z_FLAG); - NSS(V_FLAG); - NSS(armState); - NSS(armIrqEnable); - NSS(armMode); - - NSS(io_registers); - - NSS(MOSAIC); - - NSS(BG2X_L); - NSS(BG2X_H); - NSS(BG2Y_L); - NSS(BG2Y_H); - NSS(BG3X_L); - NSS(BG3X_H); - NSS(BG3Y_L); - NSS(BG3Y_H); - NSS(BLDMOD); - NSS(COLEV); - NSS(COLY); - NSS(DM0SAD_L); - NSS(DM0SAD_H); - NSS(DM0DAD_L); - NSS(DM0DAD_H); - NSS(DM0CNT_L); - NSS(DM0CNT_H); - NSS(DM1SAD_L); - NSS(DM1SAD_H); - NSS(DM1DAD_L); - NSS(DM1DAD_H); - NSS(DM1CNT_L); - NSS(DM1CNT_H); - NSS(DM2SAD_L); - NSS(DM2SAD_H); - NSS(DM2DAD_L); - NSS(DM2DAD_H); - NSS(DM2CNT_L); - NSS(DM2CNT_H); - NSS(DM3SAD_L); - NSS(DM3SAD_H); - NSS(DM3DAD_L); - NSS(DM3DAD_H); - NSS(DM3CNT_L); - NSS(DM3CNT_H); - - NSS(timerOnOffDelay); - NSS(timer0Value); - NSS(dma0Source); - NSS(dma0Dest); - NSS(dma1Source); - NSS(dma1Dest); - NSS(dma2Source); - NSS(dma2Dest); - NSS(dma3Source); - NSS(dma3Dest); - - EBS(cpuSaveGameFunc, 0); - EVS(cpuSaveGameFunc, &Gigazoid::flashWrite, 1); - EVS(cpuSaveGameFunc, &Gigazoid::sramWrite, 2); - EVS(cpuSaveGameFunc, &Gigazoid::flashSaveDecide, 3); - EVS(cpuSaveGameFunc, &Gigazoid::dummyWrite, 4); - EES(cpuSaveGameFunc, nullptr); - - NSS(fxOn); - NSS(windowOn); - NSS(cpuDmaTicksToUpdate); - NSS(IRQTicks); - NSS(intState); - - NSS(bus); - NSS(graphics); - - // map; // values never change - - NSS(clockTicks); - - NSS(romSize); - NSS(line); - NSS(gfxInWin); - NSS(lineOBJpixleft); - NSS(joy); - - NSS(gfxBG2Changed); - NSS(gfxBG3Changed); - - NSS(gfxBG2X); - NSS(gfxBG2Y); - NSS(gfxBG3X); - NSS(gfxBG3Y); - - NSS(ioReadable); - - NSS(stopState); - - NSS(timer0On); - NSS(timer0Ticks); - NSS(timer0Reload); - NSS(timer0ClockReload); - NSS(timer1Value); - NSS(timer1On); - NSS(timer1Ticks); - NSS(timer1Reload); - NSS(timer1ClockReload); - NSS(timer2Value); - NSS(timer2On); - NSS(timer2Ticks); - NSS(timer2Reload); - NSS(timer2ClockReload); - NSS(timer3Value); - NSS(timer3On); - NSS(timer3Ticks); - NSS(timer3Reload); - NSS(timer3ClockReload); - - NSS(skipBios); - NSS(cpuSaveType); - NSS(mirroringEnable); - - NSS(cpuDmaCount); - - NSS(internalRAM); - NSS(workRAM); - NSS(vram); - NSS(pix); - NSS(oam); - NSS(ioMem); - - NSS(cpuEEPROMEnabled); - NSS(cpuEEPROMSensorEnabled); - - EBS(renderLine, 0); - EVS(renderLine, &Gigazoid::mode0RenderLine, 0x01); - EVS(renderLine, &Gigazoid::mode0RenderLineNoWindow, 0x02); - EVS(renderLine, &Gigazoid::mode0RenderLineAll, 0x03); - EVS(renderLine, &Gigazoid::mode1RenderLine, 0x11); - EVS(renderLine, &Gigazoid::mode1RenderLineNoWindow, 0x12); - EVS(renderLine, &Gigazoid::mode1RenderLineAll, 0x13); - EVS(renderLine, &Gigazoid::mode2RenderLine, 0x21); - EVS(renderLine, &Gigazoid::mode2RenderLineNoWindow, 0x22); - EVS(renderLine, &Gigazoid::mode2RenderLineAll, 0x23); - EVS(renderLine, &Gigazoid::mode3RenderLine, 0x31); - EVS(renderLine, &Gigazoid::mode3RenderLineNoWindow, 0x32); - EVS(renderLine, &Gigazoid::mode3RenderLineAll, 0x33); - EVS(renderLine, &Gigazoid::mode4RenderLine, 0x41); - EVS(renderLine, &Gigazoid::mode4RenderLineNoWindow, 0x42); - EVS(renderLine, &Gigazoid::mode4RenderLineAll, 0x43); - EVS(renderLine, &Gigazoid::mode5RenderLine, 0x51); - EVS(renderLine, &Gigazoid::mode5RenderLineNoWindow, 0x52); - EVS(renderLine, &Gigazoid::mode5RenderLineAll, 0x53); - EES(renderLine, nullptr); - - NSS(render_line_all_enabled); - - // address_lut; // values never change - - NSS(lagged); -} - -// load a legacy battery ram file to a place where it might work, who knows -void LoadLegacyBatteryRam(const char *data, int len) -{ - std::memcpy(eepromData, data, std::min(len, sizeof(eepromData))); - std::memcpy(flashSaveMemory, data, std::min(len, sizeof(flashSaveMemory))); - if (len <= 0x10000) - { - // can salvage broken pokeymans saves in some cases - std::memcpy(flashSaveMemory + 0x10000, data, std::min(len, 0x10000)); - } -} - -bool HasBatteryRam() -{ - return cpuSaveType != 5; -} - -int BatteryRamSize() -{ - switch (cpuSaveType) - { - default: - case 0: // auto - return 0x10000; - case 1: - case 4: // eeprom - return eepromSize; - case 2: // sram - // should only be 32K, but vba uses 64K as a stand-in for both SRAM (guess no game ever checks mirroring?), - // and for 64K flash where the program never issues any flash commands - return 0x10000; - case 3: // flash - return flashSize; - case 5: // none - return 0; - } -} - -void SaveLegacyBatteryRam(char *dest) -{ - switch (cpuSaveType) - { - default: - case 0: // auto - std::memcpy(dest, flashSaveMemory, 0x10000); - return; - case 1: - case 4: // eeprom - std::memcpy(dest, eepromData, eepromSize); - return; - case 2: // sram - // should only be 32K, but vba uses 64K as a stand-in for both SRAM (guess no game ever checks mirroring?), - // and for 64K flash where the program never issues any flash commands - std::memcpy(dest, flashSaveMemory, 0x10000); - return; - case 3: // flash - std::memcpy(dest, flashSaveMemory, flashSize); - return; - case 5: // none - return; - } -} - -templatebool SyncBatteryRam(NewState *ns) -{ - // if we were given a positive ID from the gamedb, we can choose to save/load only that type - // else, we save\load everything -- even if we used our knowledge of the current state to - // save only what was needed, we'd have to save that metadata as well for load - - // file id detection - char batteryramid[8]; - std::memcpy(batteryramid, "GBABATT\0", 8); - NSS(batteryramid); - if (std::memcmp(batteryramid, "GBABATT\0", 8) != 0) - return false; - - int flashFileSize; - int eepromFileSize; - - // when writing, try to figure out the sizes as smartly as we can - switch (cpuSaveType) - { - default: - case 0: // auto - flashFileSize = 0x20000; - eepromFileSize = 0x2000; - break; - case 1: - case 4: // eeprom - flashFileSize = 0; - eepromFileSize = eepromSize; - break; - case 2: // sram - // should only be 32K, but vba uses 64K as a stand-in for both SRAM (guess no game ever checks mirroring?), - // and for 64K flash where the program never issues any flash commands - flashFileSize = 0x10000; - eepromFileSize = 0; - break; - case 3: // flash - flashFileSize = flashSize; - eepromFileSize = 0; - break; - case 5: // none - flashFileSize = 0; - eepromFileSize = 0; - break; - } - NSS(flashFileSize); - NSS(eepromFileSize); - // when reading, cap to allowable limits. any save file with numbers larger than this is not legal. - flashFileSize = std::min(flashFileSize, sizeof(flashSaveMemory)); - eepromFileSize = std::min(eepromFileSize, sizeof(eepromData)); - - PSS(flashSaveMemory, flashFileSize); - PSS(eepromData, eepromFileSize); - - return true; -} - - Gigazoid() - { - Gigazoid_Init(); - } - - ~Gigazoid() - { - } - - bool LoadRom(const u8 *romfile, const u32 romfilelen, const u8 *biosfile, const u32 biosfilelen, const FrontEndSettings &settings) - { - if (biosfilelen != 16384) - return false; - - if (!CPULoadRom(romfile, romfilelen)) - return false; - - cpuSaveType = settings.cpuSaveType; - flashSize = settings.flashSize; - rtcEnabled = settings.enableRtc; - mirroringEnable = settings.mirroringEnable; - skipBios = settings.skipBios; - - RTCUseRealTime = settings.RTCuseRealTime; - rtcInternalTime.hour = settings.RTChour; - rtcInternalTime.mday = settings.RTCmday; - rtcInternalTime.min = settings.RTCmin; - rtcInternalTime.month = settings.RTCmonth; - rtcInternalTime.sec = settings.RTCsec; - rtcInternalTime.wday = settings.RTCwday; - rtcInternalTime.year = settings.RTCyear; - - if(flashSize == 0x10000) - { - flashDeviceID = 0x1b; - flashManufacturerID = 0x32; - } - else - { - flashDeviceID = 0x13; //0x09; - flashManufacturerID = 0x62; //0xc2; - } - - doMirroring(mirroringEnable); - - CPUInit(biosfile, biosfilelen); - CPUReset(); - - // CPUReset already calls this, but if that were to change - // soundReset(); - - return true; - } - - void Reset() - { - CPUReset(); - } - - bool FrameAdvance(int input, u32 *videobuffer, s16 *audiobuffer, int *numsamp, u32 *videopalette) - { - joy = input; - systemVideoFrameDest = videobuffer; - systemVideoFramePalette = videopalette; - systemAudioFrameDest = audiobuffer; - systemAudioFrameSamp = numsamp; - lagged = true; - UpdateJoypad(); - do - { - CPULoop(); - } while (systemVideoFrameDest); - return lagged; - } - - void FillMemoryAreas(MemoryAreas &mem) - { - mem.bios = bios; - mem.iwram = internalRAM; - mem.ewram = workRAM; - mem.palram = graphics.paletteRAM; - mem.mmio = ioMem; - mem.rom = rom; - mem.vram = vram; - mem.oam = oam; - switch (cpuSaveType) - { - default: - case 0: // auto - mem.sram = flashSaveMemory; - mem.sram_size = 0x10000;; - return; - case 1: - case 4: // eeprom - mem.sram = eepromData; - mem.sram_size = eepromSize; - return; - case 2: // sram - mem.sram = flashSaveMemory; - mem.sram_size = 0x10000; - return; - case 3: // flash - mem.sram = flashSaveMemory; - mem.sram_size = flashSize; - return; - case 5: // none - return; - } - } - - void BusWrite(u32 addr, u8 val) - { - CPUWriteByte(addr, val); - } - u8 BusRead(u32 addr) - { - return CPUReadByte(addr); - } - - void SetScanlineCallback(void (*cb)(), int scanline) - { - // the sequence of calls in a frame will be: - // 160,161,...,227,0,1,...,160 - // calls coincide with entering hblank, or something like that - if (scanline < 0 || scanline > 227) - cb = nullptr; - scanlineCallback = cb; - scanlineCallbackLine = scanline; - } - - uint32_t *GetRegisters() - { - return &bus.reg[0].I; - } - - void SetPadCallback(void (*cb)()) - { - // before each read of the pad regs - padCallback = cb; - } - - void SetFetchCallback(void (*cb)(u32 addr)) - { - // before each opcode fetch - fetchCallback = cb; - } - - void SetReadCallback(void (*cb)(u32 addr)) - { - // before each read, not including opcodes, including pad regs - readCallback = cb; - } - - void SetWriteCallback(void (*cb)(u32 addr)) - { - // before each write - writeCallback = cb; - } - - void SetTraceCallback(void (*cb)(u32 addr, u32 opcode)) - { - // before each opcode fetch - traceCallback = cb; - } - -}; // class Gigazoid - -// zeroing mem operators: these are very important -void *operator new(std::size_t n) -{ - void *p = std::malloc(n); - std::memset(p, 0, n); - return p; -} -void operator delete(void *p) -{ - std::free(p); -} - -#define EXPORT extern "C" __declspec(dllexport) - -// public interface follows -EXPORT Gigazoid *Create() -{ - return new Gigazoid(); -} - -EXPORT void Destroy(Gigazoid *g) -{ - delete g; -} - -EXPORT int LoadRom(Gigazoid *g, const u8 *romfile, const u32 romfilelen, const u8 *biosfile, const u32 biosfilelen, const FrontEndSettings *settings) -{ - return g->LoadRom(romfile, romfilelen, biosfile, biosfilelen, *settings); -} - -EXPORT void Reset(Gigazoid *g) -{ - // TODO: this calls a soundreset that seems to remake some buffers. that seems like it should be fixed? - g->Reset(); -} - -EXPORT int FrameAdvance(Gigazoid *g, int input, u32 *videobuffer, s16 *audiobuffer, int *numsamp, u32 *videopalette) -{ - return g->FrameAdvance(input, videobuffer, audiobuffer, numsamp, videopalette); -} - -EXPORT int SaveRamSize(Gigazoid *g) -{ - /* - if (g->HasBatteryRam()) - { - NewStateDummy dummy; - g->SyncBatteryRam(&dummy); - return dummy.GetLength(); - } - else - { - return 0; - }*/ - return g->BatteryRamSize(); -} - -EXPORT int SaveRamSave(Gigazoid *g, char *data, int length) -{ - /* - if (g->HasBatteryRam()) - { - NewStateExternalBuffer saver(data, length); - g->SyncBatteryRam(&saver); - return !saver.Overflow() && saver.GetLength() == length; - } - else - { - return false; - }*/ - if (!g->HasBatteryRam() || length != g->BatteryRamSize()) - return false; - g->SaveLegacyBatteryRam(data); - return true; -} - -EXPORT int SaveRamLoad(Gigazoid *g, const char *data, int length) -{ - if (g->HasBatteryRam()) - { - NewStateExternalBuffer loader(const_cast(data), length); - if (g->SyncBatteryRam(&loader)) - { - return !loader.Overflow() && loader.GetLength() == length; - } - else - { - // couldn't find the magic signature at the top, so try a salvage load - g->LoadLegacyBatteryRam(data, length); - return true; - } - } - else - { - return false; - } -} - -EXPORT int BinStateSize(Gigazoid *g) -{ - NewStateDummy dummy; - g->SyncState(&dummy); - return dummy.GetLength(); -} - -EXPORT int BinStateSave(Gigazoid *g, char *data, int length) -{ - NewStateExternalBuffer saver(data, length); - g->SyncState(&saver); - return !saver.Overflow() && saver.GetLength() == length; -} - -EXPORT int BinStateLoad(Gigazoid *g, const char *data, int length) -{ - NewStateExternalBuffer loader(const_cast(data), length); - g->SyncState(&loader); - return !loader.Overflow() && loader.GetLength() == length; -} - -EXPORT void TxtStateSave(Gigazoid *g, FPtrs *ff) -{ - NewStateExternalFunctions saver(ff); - g->SyncState(&saver); -} - -EXPORT void TxtStateLoad(Gigazoid *g, FPtrs *ff) -{ - NewStateExternalFunctions loader(ff); - g->SyncState(&loader); -} - -EXPORT void GetMemoryAreas(Gigazoid *g, MemoryAreas *mem) -{ - g->FillMemoryAreas(*mem); -} - -EXPORT void SystemBusWrite(Gigazoid *g, u32 addr, u8 val) -{ - g->BusWrite(addr, val); -} - -EXPORT u8 SystemBusRead(Gigazoid *g, u32 addr) -{ - return g->BusRead(addr); -} - -EXPORT void SetScanlineCallback(Gigazoid *g, void (*cb)(), int scanline) -{ - g->SetScanlineCallback(cb, scanline); -} - -EXPORT void SetTraceCallback(Gigazoid *g, void (*cb)(u32 addr, u32 opcode)) -{ - g->SetTraceCallback(cb); -} - -EXPORT u32 *GetRegisters(Gigazoid *g) -{ - return g->GetRegisters(); -} - -EXPORT void SetPadCallback(Gigazoid *g, void (*cb)()) { g->SetPadCallback(cb); } -EXPORT void SetFetchCallback(Gigazoid *g, void (*cb)(u32 addr)) { g->SetFetchCallback(cb); } -EXPORT void SetReadCallback(Gigazoid *g, void (*cb)(u32 addr)) { g->SetReadCallback(cb); } -EXPORT void SetWriteCallback(Gigazoid *g, void (*cb)(u32 addr)) { g->SetWriteCallback(cb); } - - -#include "optable.inc" + + SSS(gb_apu); + + NSS(cpuNextEvent); + NSS(holdState); + NSS(cpuPrefetch); + NSS(cpuTotalTicks); + NSS(memoryWait); + NSS(memoryWaitSeq); + NSS(memoryWait32); + NSS(memoryWaitSeq32); + NSS(biosProtected); + NSS(cpuBitsSet); + + NSS(N_FLAG); + NSS(C_FLAG); + NSS(Z_FLAG); + NSS(V_FLAG); + NSS(armState); + NSS(armIrqEnable); + NSS(armMode); + + NSS(io_registers); + + NSS(MOSAIC); + + NSS(BG2X_L); + NSS(BG2X_H); + NSS(BG2Y_L); + NSS(BG2Y_H); + NSS(BG3X_L); + NSS(BG3X_H); + NSS(BG3Y_L); + NSS(BG3Y_H); + NSS(BLDMOD); + NSS(COLEV); + NSS(COLY); + NSS(DM0SAD_L); + NSS(DM0SAD_H); + NSS(DM0DAD_L); + NSS(DM0DAD_H); + NSS(DM0CNT_L); + NSS(DM0CNT_H); + NSS(DM1SAD_L); + NSS(DM1SAD_H); + NSS(DM1DAD_L); + NSS(DM1DAD_H); + NSS(DM1CNT_L); + NSS(DM1CNT_H); + NSS(DM2SAD_L); + NSS(DM2SAD_H); + NSS(DM2DAD_L); + NSS(DM2DAD_H); + NSS(DM2CNT_L); + NSS(DM2CNT_H); + NSS(DM3SAD_L); + NSS(DM3SAD_H); + NSS(DM3DAD_L); + NSS(DM3DAD_H); + NSS(DM3CNT_L); + NSS(DM3CNT_H); + + NSS(timerOnOffDelay); + NSS(timer0Value); + NSS(dma0Source); + NSS(dma0Dest); + NSS(dma1Source); + NSS(dma1Dest); + NSS(dma2Source); + NSS(dma2Dest); + NSS(dma3Source); + NSS(dma3Dest); + + EBS(cpuSaveGameFunc, 0); + EVS(cpuSaveGameFunc, &Gigazoid::flashWrite, 1); + EVS(cpuSaveGameFunc, &Gigazoid::sramWrite, 2); + EVS(cpuSaveGameFunc, &Gigazoid::flashSaveDecide, 3); + EVS(cpuSaveGameFunc, &Gigazoid::dummyWrite, 4); + EES(cpuSaveGameFunc, nullptr); + + NSS(fxOn); + NSS(windowOn); + NSS(cpuDmaTicksToUpdate); + NSS(IRQTicks); + NSS(intState); + + NSS(bus); + NSS(graphics); + + // map; // values never change + + NSS(clockTicks); + + NSS(romSize); + NSS(line); + NSS(gfxInWin); + NSS(lineOBJpixleft); + NSS(joy); + + NSS(gfxBG2Changed); + NSS(gfxBG3Changed); + + NSS(gfxBG2X); + NSS(gfxBG2Y); + NSS(gfxBG3X); + NSS(gfxBG3Y); + + NSS(ioReadable); + + NSS(stopState); + + NSS(timer0On); + NSS(timer0Ticks); + NSS(timer0Reload); + NSS(timer0ClockReload); + NSS(timer1Value); + NSS(timer1On); + NSS(timer1Ticks); + NSS(timer1Reload); + NSS(timer1ClockReload); + NSS(timer2Value); + NSS(timer2On); + NSS(timer2Ticks); + NSS(timer2Reload); + NSS(timer2ClockReload); + NSS(timer3Value); + NSS(timer3On); + NSS(timer3Ticks); + NSS(timer3Reload); + NSS(timer3ClockReload); + + NSS(skipBios); + NSS(cpuSaveType); + NSS(mirroringEnable); + + NSS(cpuDmaCount); + + NSS(internalRAM); + NSS(workRAM); + NSS(vram); + NSS(pix); + NSS(oam); + NSS(ioMem); + + NSS(cpuEEPROMEnabled); + NSS(cpuEEPROMSensorEnabled); + + EBS(renderLine, 0); + EVS(renderLine, &Gigazoid::mode0RenderLine, 0x01); + EVS(renderLine, &Gigazoid::mode0RenderLineNoWindow, 0x02); + EVS(renderLine, &Gigazoid::mode0RenderLineAll, 0x03); + EVS(renderLine, &Gigazoid::mode1RenderLine, 0x11); + EVS(renderLine, &Gigazoid::mode1RenderLineNoWindow, 0x12); + EVS(renderLine, &Gigazoid::mode1RenderLineAll, 0x13); + EVS(renderLine, &Gigazoid::mode2RenderLine, 0x21); + EVS(renderLine, &Gigazoid::mode2RenderLineNoWindow, 0x22); + EVS(renderLine, &Gigazoid::mode2RenderLineAll, 0x23); + EVS(renderLine, &Gigazoid::mode3RenderLine, 0x31); + EVS(renderLine, &Gigazoid::mode3RenderLineNoWindow, 0x32); + EVS(renderLine, &Gigazoid::mode3RenderLineAll, 0x33); + EVS(renderLine, &Gigazoid::mode4RenderLine, 0x41); + EVS(renderLine, &Gigazoid::mode4RenderLineNoWindow, 0x42); + EVS(renderLine, &Gigazoid::mode4RenderLineAll, 0x43); + EVS(renderLine, &Gigazoid::mode5RenderLine, 0x51); + EVS(renderLine, &Gigazoid::mode5RenderLineNoWindow, 0x52); + EVS(renderLine, &Gigazoid::mode5RenderLineAll, 0x53); + EES(renderLine, nullptr); + + NSS(render_line_all_enabled); + + // address_lut; // values never change + + NSS(lagged); +} + +// load a legacy battery ram file to a place where it might work, who knows +void LoadLegacyBatteryRam(const char *data, int len) +{ + std::memcpy(eepromData, data, std::min(len, sizeof(eepromData))); + std::memcpy(flashSaveMemory, data, std::min(len, sizeof(flashSaveMemory))); + if (len <= 0x10000) + { + // can salvage broken pokeymans saves in some cases + std::memcpy(flashSaveMemory + 0x10000, data, std::min(len, 0x10000)); + } +} + +bool HasBatteryRam() +{ + return cpuSaveType != 5; +} + +int BatteryRamSize() +{ + switch (cpuSaveType) + { + default: + case 0: // auto + return 0x10000; + case 1: + case 4: // eeprom + return eepromSize; + case 2: // sram + // should only be 32K, but vba uses 64K as a stand-in for both SRAM (guess no game ever checks mirroring?), + // and for 64K flash where the program never issues any flash commands + return 0x10000; + case 3: // flash + return flashSize; + case 5: // none + return 0; + } +} + +void SaveLegacyBatteryRam(char *dest) +{ + switch (cpuSaveType) + { + default: + case 0: // auto + std::memcpy(dest, flashSaveMemory, 0x10000); + return; + case 1: + case 4: // eeprom + std::memcpy(dest, eepromData, eepromSize); + return; + case 2: // sram + // should only be 32K, but vba uses 64K as a stand-in for both SRAM (guess no game ever checks mirroring?), + // and for 64K flash where the program never issues any flash commands + std::memcpy(dest, flashSaveMemory, 0x10000); + return; + case 3: // flash + std::memcpy(dest, flashSaveMemory, flashSize); + return; + case 5: // none + return; + } +} + +templatebool SyncBatteryRam(NewState *ns) +{ + // if we were given a positive ID from the gamedb, we can choose to save/load only that type + // else, we save\load everything -- even if we used our knowledge of the current state to + // save only what was needed, we'd have to save that metadata as well for load + + // file id detection + char batteryramid[8]; + std::memcpy(batteryramid, "GBABATT\0", 8); + NSS(batteryramid); + if (std::memcmp(batteryramid, "GBABATT\0", 8) != 0) + return false; + + int flashFileSize; + int eepromFileSize; + + // when writing, try to figure out the sizes as smartly as we can + switch (cpuSaveType) + { + default: + case 0: // auto + flashFileSize = 0x20000; + eepromFileSize = 0x2000; + break; + case 1: + case 4: // eeprom + flashFileSize = 0; + eepromFileSize = eepromSize; + break; + case 2: // sram + // should only be 32K, but vba uses 64K as a stand-in for both SRAM (guess no game ever checks mirroring?), + // and for 64K flash where the program never issues any flash commands + flashFileSize = 0x10000; + eepromFileSize = 0; + break; + case 3: // flash + flashFileSize = flashSize; + eepromFileSize = 0; + break; + case 5: // none + flashFileSize = 0; + eepromFileSize = 0; + break; + } + NSS(flashFileSize); + NSS(eepromFileSize); + // when reading, cap to allowable limits. any save file with numbers larger than this is not legal. + flashFileSize = std::min(flashFileSize, sizeof(flashSaveMemory)); + eepromFileSize = std::min(eepromFileSize, sizeof(eepromData)); + + PSS(flashSaveMemory, flashFileSize); + PSS(eepromData, eepromFileSize); + + return true; +} + + Gigazoid() + { + Gigazoid_Init(); + } + + ~Gigazoid() + { + } + + bool LoadRom(const u8 *romfile, const u32 romfilelen, const u8 *biosfile, const u32 biosfilelen, const FrontEndSettings &settings) + { + if (biosfilelen != 16384) + return false; + + if (!CPULoadRom(romfile, romfilelen)) + return false; + + cpuSaveType = settings.cpuSaveType; + flashSize = settings.flashSize; + rtcEnabled = settings.enableRtc; + mirroringEnable = settings.mirroringEnable; + skipBios = settings.skipBios; + + RTCUseRealTime = settings.RTCuseRealTime; + rtcInternalTime.hour = settings.RTChour; + rtcInternalTime.mday = settings.RTCmday; + rtcInternalTime.min = settings.RTCmin; + rtcInternalTime.month = settings.RTCmonth; + rtcInternalTime.sec = settings.RTCsec; + rtcInternalTime.wday = settings.RTCwday; + rtcInternalTime.year = settings.RTCyear; + + if(flashSize == 0x10000) + { + flashDeviceID = 0x1b; + flashManufacturerID = 0x32; + } + else + { + flashDeviceID = 0x13; //0x09; + flashManufacturerID = 0x62; //0xc2; + } + + doMirroring(mirroringEnable); + + CPUInit(biosfile, biosfilelen); + CPUReset(); + + // CPUReset already calls this, but if that were to change + // soundReset(); + + return true; + } + + void Reset() + { + CPUReset(); + } + + bool FrameAdvance(int input, u32 *videobuffer, s16 *audiobuffer, int *numsamp, u32 *videopalette) + { + joy = input; + systemVideoFrameDest = videobuffer; + systemVideoFramePalette = videopalette; + systemAudioFrameDest = audiobuffer; + systemAudioFrameSamp = numsamp; + lagged = true; + UpdateJoypad(); + do + { + CPULoop(); + } while (systemVideoFrameDest); + return lagged; + } + + void FillMemoryAreas(MemoryAreas &mem) + { + mem.bios = bios; + mem.iwram = internalRAM; + mem.ewram = workRAM; + mem.palram = graphics.paletteRAM; + mem.mmio = ioMem; + mem.rom = rom; + mem.vram = vram; + mem.oam = oam; + switch (cpuSaveType) + { + default: + case 0: // auto + mem.sram = flashSaveMemory; + mem.sram_size = 0x10000;; + return; + case 1: + case 4: // eeprom + mem.sram = eepromData; + mem.sram_size = eepromSize; + return; + case 2: // sram + mem.sram = flashSaveMemory; + mem.sram_size = 0x10000; + return; + case 3: // flash + mem.sram = flashSaveMemory; + mem.sram_size = flashSize; + return; + case 5: // none + return; + } + } + + void BusWrite(u32 addr, u8 val) + { + CPUWriteByte(addr, val); + } + u8 BusRead(u32 addr) + { + return CPUReadByte(addr); + } + + void SetScanlineCallback(void (*cb)(), int scanline) + { + // the sequence of calls in a frame will be: + // 160,161,...,227,0,1,...,160 + // calls coincide with entering hblank, or something like that + if (scanline < 0 || scanline > 227) + cb = nullptr; + scanlineCallback = cb; + scanlineCallbackLine = scanline; + } + + uint32_t *GetRegisters() + { + return &bus.reg[0].I; + } + + void SetPadCallback(void (*cb)()) + { + // before each read of the pad regs + padCallback = cb; + } + + void SetFetchCallback(void (*cb)(u32 addr)) + { + // before each opcode fetch + fetchCallback = cb; + } + + void SetReadCallback(void (*cb)(u32 addr)) + { + // before each read, not including opcodes, including pad regs + readCallback = cb; + } + + void SetWriteCallback(void (*cb)(u32 addr)) + { + // before each write + writeCallback = cb; + } + + void SetTraceCallback(void (*cb)(u32 addr, u32 opcode)) + { + // before each opcode fetch + traceCallback = cb; + } + +}; // class Gigazoid + +// zeroing mem operators: these are very important +void *operator new(std::size_t n) +{ + void *p = std::malloc(n); + std::memset(p, 0, n); + return p; +} +void operator delete(void *p) +{ + std::free(p); +} + + +#ifdef _WIN32 +#define EXPORT extern "C" __declspec(dllexport) +#elif __linux__ +#define EXPORT extern "C" +#endif + +// public interface follows +EXPORT Gigazoid *Create() +{ + return new Gigazoid(); +} + +EXPORT void Destroy(Gigazoid *g) +{ + delete g; +} + +EXPORT int LoadRom(Gigazoid *g, const u8 *romfile, const u32 romfilelen, const u8 *biosfile, const u32 biosfilelen, const FrontEndSettings *settings) +{ + return g->LoadRom(romfile, romfilelen, biosfile, biosfilelen, *settings); +} + +EXPORT void Reset(Gigazoid *g) +{ + // TODO: this calls a soundreset that seems to remake some buffers. that seems like it should be fixed? + g->Reset(); +} + +EXPORT int FrameAdvance(Gigazoid *g, int input, u32 *videobuffer, s16 *audiobuffer, int *numsamp, u32 *videopalette) +{ + return g->FrameAdvance(input, videobuffer, audiobuffer, numsamp, videopalette); +} + +EXPORT int SaveRamSize(Gigazoid *g) +{ + /* + if (g->HasBatteryRam()) + { + NewStateDummy dummy; + g->SyncBatteryRam(&dummy); + return dummy.GetLength(); + } + else + { + return 0; + }*/ + return g->BatteryRamSize(); +} + +EXPORT int SaveRamSave(Gigazoid *g, char *data, int length) +{ + /* + if (g->HasBatteryRam()) + { + NewStateExternalBuffer saver(data, length); + g->SyncBatteryRam(&saver); + return !saver.Overflow() && saver.GetLength() == length; + } + else + { + return false; + }*/ + if (!g->HasBatteryRam() || length != g->BatteryRamSize()) + return false; + g->SaveLegacyBatteryRam(data); + return true; +} + +EXPORT int SaveRamLoad(Gigazoid *g, const char *data, int length) +{ + if (g->HasBatteryRam()) + { + NewStateExternalBuffer loader(const_cast(data), length); + if (g->SyncBatteryRam(&loader)) + { + return !loader.Overflow() && loader.GetLength() == length; + } + else + { + // couldn't find the magic signature at the top, so try a salvage load + g->LoadLegacyBatteryRam(data, length); + return true; + } + } + else + { + return false; + } +} + +EXPORT int BinStateSize(Gigazoid *g) +{ + NewStateDummy dummy; + g->SyncState(&dummy); + return dummy.GetLength(); +} + +EXPORT int BinStateSave(Gigazoid *g, char *data, int length) +{ + NewStateExternalBuffer saver(data, length); + g->SyncState(&saver); + return !saver.Overflow() && saver.GetLength() == length; +} + +EXPORT int BinStateLoad(Gigazoid *g, const char *data, int length) +{ + NewStateExternalBuffer loader(const_cast(data), length); + g->SyncState(&loader); + return !loader.Overflow() && loader.GetLength() == length; +} + +EXPORT void TxtStateSave(Gigazoid *g, FPtrs *ff) +{ + NewStateExternalFunctions saver(ff); + g->SyncState(&saver); +} + +EXPORT void TxtStateLoad(Gigazoid *g, FPtrs *ff) +{ + NewStateExternalFunctions loader(ff); + g->SyncState(&loader); +} + +EXPORT void GetMemoryAreas(Gigazoid *g, MemoryAreas *mem) +{ + g->FillMemoryAreas(*mem); +} + +EXPORT void SystemBusWrite(Gigazoid *g, u32 addr, u8 val) +{ + g->BusWrite(addr, val); +} + +EXPORT u8 SystemBusRead(Gigazoid *g, u32 addr) +{ + return g->BusRead(addr); +} + +EXPORT void SetScanlineCallback(Gigazoid *g, void (*cb)(), int scanline) +{ + g->SetScanlineCallback(cb, scanline); +} + +EXPORT void SetTraceCallback(Gigazoid *g, void (*cb)(u32 addr, u32 opcode)) +{ + g->SetTraceCallback(cb); +} + +EXPORT u32 *GetRegisters(Gigazoid *g) +{ + return g->GetRegisters(); +} + +EXPORT void SetPadCallback(Gigazoid *g, void (*cb)()) { g->SetPadCallback(cb); } +EXPORT void SetFetchCallback(Gigazoid *g, void (*cb)(u32 addr)) { g->SetFetchCallback(cb); } +EXPORT void SetReadCallback(Gigazoid *g, void (*cb)(u32 addr)) { g->SetReadCallback(cb); } +EXPORT void SetWriteCallback(Gigazoid *g, void (*cb)(u32 addr)) { g->SetWriteCallback(cb); } + + +#include "optable.inc" diff --git a/vbanext/mingw/Makefile b/vbanext/mingw/Makefile index e5556e477e..05f14773d1 100644 --- a/vbanext/mingw/Makefile +++ b/vbanext/mingw/Makefile @@ -1,7 +1,7 @@ CXX = g++ CXXFLAGS = -Wall -O3 -fpermissive -Wno-unused-but-set-variable \ -Wno-strict-aliasing -Wzero-as-null-pointer-constant -Wno-unused-variable \ - -Wno-parentheses -Wno-sign-compare -std=gnu++11 -fomit-frame-pointer -fno-exceptions + -Wno-parentheses -Wno-sign-compare -std=gnu++11 -fomit-frame-pointer -fno-exceptions -fPIC MACHINE = $(shell $(CXX) -dumpmachine) ifneq (,$(findstring i686,$(MACHINE)))