forked from sin365/AxibugEmuOnline
5741 lines
190 KiB
C#
5741 lines
190 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Runtime.InteropServices;
|
|
using System.Threading;
|
|
using System.Xml;
|
|
|
|
namespace MyNes.Core
|
|
{
|
|
public class NesEmu
|
|
{
|
|
[StructLayout(LayoutKind.Explicit)]
|
|
private struct CPURegister
|
|
{
|
|
[FieldOffset(0)]
|
|
internal byte l;
|
|
|
|
[FieldOffset(1)]
|
|
internal byte h;
|
|
|
|
[FieldOffset(0)]
|
|
internal ushort v;
|
|
}
|
|
|
|
private enum RequestMode
|
|
{
|
|
None,
|
|
HardReset,
|
|
SoftReset,
|
|
LoadState,
|
|
SaveState,
|
|
TakeSnapshot
|
|
}
|
|
|
|
private static int[][] dmc_freq_table = new int[3][]
|
|
{
|
|
new int[16]
|
|
{
|
|
428, 380, 340, 320, 286, 254, 226, 214, 190, 160,
|
|
142, 128, 106, 84, 72, 54
|
|
},
|
|
new int[16]
|
|
{
|
|
398, 354, 316, 298, 276, 236, 210, 198, 176, 148,
|
|
132, 118, 98, 78, 66, 50
|
|
},
|
|
new int[16]
|
|
{
|
|
428, 380, 340, 320, 286, 254, 226, 214, 190, 160,
|
|
142, 128, 106, 84, 72, 54
|
|
}
|
|
};
|
|
|
|
private static int dmc_output_a;
|
|
|
|
private static int dmc_output;
|
|
|
|
private static int dmc_period_devider;
|
|
|
|
private static bool dmc_irq_enabled;
|
|
|
|
private static bool dmc_loop_flag;
|
|
|
|
private static byte dmc_rate_index;
|
|
|
|
private static ushort dmc_addr_refresh;
|
|
|
|
private static int dmc_size_refresh;
|
|
|
|
private static bool dmc_dmaEnabled;
|
|
|
|
private static byte dmc_dmaByte;
|
|
|
|
private static int dmc_dmaBits;
|
|
|
|
private static bool dmc_bufferFull;
|
|
|
|
private static byte dmc_dmaBuffer;
|
|
|
|
private static int dmc_dmaSize;
|
|
|
|
private static ushort dmc_dmaAddr;
|
|
|
|
private static ushort[][] nos_freq_table = new ushort[3][]
|
|
{
|
|
new ushort[16]
|
|
{
|
|
4, 8, 16, 32, 64, 96, 128, 160, 202, 254,
|
|
380, 508, 762, 1016, 2034, 4068
|
|
},
|
|
new ushort[16]
|
|
{
|
|
4, 7, 14, 30, 60, 88, 118, 148, 188, 236,
|
|
354, 472, 708, 944, 1890, 3778
|
|
},
|
|
new ushort[16]
|
|
{
|
|
4, 8, 16, 32, 64, 96, 128, 160, 202, 254,
|
|
380, 508, 762, 1016, 2034, 4068
|
|
}
|
|
};
|
|
|
|
private static bool nos_length_halt;
|
|
|
|
private static bool nos_constant_volume_envelope;
|
|
|
|
private static byte nos_volume_devider_period;
|
|
|
|
private static ushort nos_timer;
|
|
|
|
private static bool nos_mode;
|
|
|
|
private static int nos_period_devider;
|
|
|
|
private static bool nos_length_enabled;
|
|
|
|
private static int nos_length_counter;
|
|
|
|
private static bool nos_envelope_start_flag;
|
|
|
|
private static byte nos_envelope_devider;
|
|
|
|
private static byte nos_envelope_decay_level_counter;
|
|
|
|
private static byte nos_envelope;
|
|
|
|
private static int nos_output;
|
|
|
|
private static int nos_shift_reg;
|
|
|
|
private static int nos_feedback;
|
|
|
|
private static bool nos_ignore_reload;
|
|
|
|
private static readonly byte[][] sq_duty_cycle_sequences = new byte[4][]
|
|
{
|
|
new byte[8] { 0, 0, 0, 0, 0, 0, 0, 1 },
|
|
new byte[8] { 0, 0, 0, 0, 0, 0, 1, 1 },
|
|
new byte[8] { 0, 0, 0, 0, 1, 1, 1, 1 },
|
|
new byte[8] { 1, 1, 1, 1, 1, 1, 0, 0 }
|
|
};
|
|
|
|
private static readonly byte[] sq_duration_table = new byte[32]
|
|
{
|
|
10, 254, 20, 2, 40, 4, 80, 6, 160, 8,
|
|
60, 10, 14, 12, 26, 14, 12, 16, 24, 18,
|
|
48, 20, 96, 22, 192, 24, 72, 26, 16, 28,
|
|
32, 30
|
|
};
|
|
|
|
private static byte sq1_duty_cycle;
|
|
|
|
private static bool sq1_length_halt;
|
|
|
|
private static bool sq1_constant_volume_envelope;
|
|
|
|
private static byte sq1_volume_devider_period;
|
|
|
|
private static bool sq1_sweep_enable;
|
|
|
|
private static byte sq1_sweep_devider_period;
|
|
|
|
private static bool sq1_sweep_negate;
|
|
|
|
private static byte sq1_sweep_shift_count;
|
|
|
|
private static int sq1_timer;
|
|
|
|
private static int sq1_period_devider;
|
|
|
|
private static byte sq1_seqencer;
|
|
|
|
private static bool sq1_length_enabled;
|
|
|
|
private static int sq1_length_counter;
|
|
|
|
private static bool sq1_envelope_start_flag;
|
|
|
|
private static byte sq1_envelope_devider;
|
|
|
|
private static byte sq1_envelope_decay_level_counter;
|
|
|
|
private static byte sq1_envelope;
|
|
|
|
private static int sq1_sweep_counter;
|
|
|
|
private static bool sq1_sweep_reload;
|
|
|
|
private static int sq1_sweep_change;
|
|
|
|
private static bool sq1_valid_freq;
|
|
|
|
private static int sq1_output;
|
|
|
|
private static bool sq1_ignore_reload;
|
|
|
|
private static readonly byte[] trl_step_seq = new byte[32]
|
|
{
|
|
15, 14, 13, 12, 11, 10, 9, 8, 7, 6,
|
|
5, 4, 3, 2, 1, 0, 0, 1, 2, 3,
|
|
4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
|
|
14, 15
|
|
};
|
|
|
|
private static bool trl_liner_control_flag;
|
|
|
|
private static byte trl_liner_control_reload;
|
|
|
|
private static ushort trl_timer;
|
|
|
|
private static bool trl_length_enabled;
|
|
|
|
private static byte trl_length_counter;
|
|
|
|
private static bool trl_liner_control_reload_flag;
|
|
|
|
private static byte trl_liner_counter;
|
|
|
|
private static int trl_output;
|
|
|
|
private static int trl_period_devider;
|
|
|
|
private static int trl_step;
|
|
|
|
private static bool trl_ignore_reload;
|
|
|
|
private static byte apu_reg_io_db;
|
|
|
|
private static byte apu_reg_io_addr;
|
|
|
|
private static bool apu_reg_access_happened;
|
|
|
|
private static bool apu_reg_access_w;
|
|
|
|
private static Action[] apu_reg_update_func;
|
|
|
|
private static Action[] apu_reg_read_func;
|
|
|
|
private static Action[] apu_reg_write_func;
|
|
|
|
private static Action apu_update_playback_func;
|
|
|
|
private static bool apu_odd_cycle;
|
|
|
|
private static bool apu_irq_enabled;
|
|
|
|
private static bool apu_irq_flag;
|
|
|
|
internal static bool apu_irq_delta_occur;
|
|
|
|
private static bool apu_seq_mode;
|
|
|
|
private static int apu_ferq_f;
|
|
|
|
private static int apu_ferq_l;
|
|
|
|
private static int apu_ferq_e;
|
|
|
|
private static int apu_cycle_f;
|
|
|
|
private static int apu_cycle_f_t;
|
|
|
|
private static int apu_cycle_e;
|
|
|
|
private static int apu_cycle_l;
|
|
|
|
private static bool apu_odd_l;
|
|
|
|
private static bool apu_check_irq;
|
|
|
|
private static bool apu_do_env;
|
|
|
|
private static bool apu_do_length;
|
|
|
|
public static bool SoundEnabled;
|
|
|
|
public static double audio_playback_amplitude = 1.5;
|
|
|
|
public static int audio_playback_peek_limit = 124;
|
|
|
|
private static bool audio_playback_dac_initialized;
|
|
|
|
public static int cpu_speed;
|
|
|
|
private static short[] audio_samples;
|
|
|
|
private static int audio_w_pos;
|
|
|
|
private static int audio_samples_added;
|
|
|
|
internal static int audio_samples_count;
|
|
|
|
private static int[][][][][] mix_table;
|
|
|
|
private static double audio_x;
|
|
|
|
private static double audio_y;
|
|
|
|
private static double audio_y_av;
|
|
|
|
private static double audio_y_timer;
|
|
|
|
public static double audio_timer_ratio = 40.0;
|
|
|
|
private static double audio_timer;
|
|
|
|
private static SoundLowPassFilter audio_low_pass_filter_14K;
|
|
|
|
private static SoundHighPassFilter audio_high_pass_filter_90;
|
|
|
|
private static SoundHighPassFilter audio_high_pass_filter_440;
|
|
|
|
private static bool audio_sq1_outputable;
|
|
|
|
private static bool audio_sq2_outputable;
|
|
|
|
private static bool audio_nos_outputable;
|
|
|
|
private static bool audio_trl_outputable;
|
|
|
|
private static bool audio_dmc_outputable;
|
|
|
|
private static bool audio_signal_outputed;
|
|
|
|
private static bool apu_use_external_sound;
|
|
|
|
private static CPURegister cpu_reg_pc;
|
|
|
|
private static CPURegister cpu_reg_sp;
|
|
|
|
private static CPURegister cpu_reg_ea;
|
|
|
|
private static byte cpu_reg_a;
|
|
|
|
private static byte cpu_reg_x;
|
|
|
|
private static byte cpu_reg_y;
|
|
|
|
private static bool cpu_flag_n;
|
|
|
|
private static bool cpu_flag_v;
|
|
|
|
private static bool cpu_flag_d;
|
|
|
|
private static bool cpu_flag_i;
|
|
|
|
private static bool cpu_flag_z;
|
|
|
|
private static bool cpu_flag_c;
|
|
|
|
private static byte cpu_m;
|
|
|
|
private static byte cpu_opcode;
|
|
|
|
private static byte cpu_byte_temp;
|
|
|
|
private static int cpu_int_temp;
|
|
|
|
private static int cpu_int_temp1;
|
|
|
|
private static byte cpu_dummy;
|
|
|
|
private static bool cpu_bool_tmp;
|
|
|
|
private static CPURegister temp_add;
|
|
|
|
private static bool CPU_IRQ_PIN;
|
|
|
|
private static bool CPU_NMI_PIN;
|
|
|
|
private static bool cpu_suspend_nmi;
|
|
|
|
private static bool cpu_suspend_irq;
|
|
|
|
private static Action[] cpu_addressings;
|
|
|
|
private static Action[] cpu_instructions;
|
|
|
|
private static int dma_DMCDMAWaitCycles;
|
|
|
|
private static int dma_OAMDMAWaitCycles;
|
|
|
|
private static bool dma_isOamDma;
|
|
|
|
private static int dma_oamdma_i;
|
|
|
|
private static bool dma_DMCOn;
|
|
|
|
private static bool dma_OAMOn;
|
|
|
|
private static bool dma_DMC_occurring;
|
|
|
|
private static bool dma_OAM_occurring;
|
|
|
|
private static int dma_OAMFinishCounter;
|
|
|
|
private static ushort dma_Oamaddress;
|
|
|
|
private static int dma_OAMCYCLE;
|
|
|
|
private static byte dma_latch;
|
|
|
|
private static byte dma_dummy;
|
|
|
|
private static ushort reg_2004;
|
|
|
|
internal static int IRQFlags = 0;
|
|
|
|
private static bool PPU_NMI_Current;
|
|
|
|
private static bool PPU_NMI_Old;
|
|
|
|
private const int IRQ_APU = 1;
|
|
|
|
internal const int IRQ_DMC = 2;
|
|
|
|
internal const int IRQ_BOARD = 8;
|
|
|
|
private static ushort InterruptVector;
|
|
|
|
private static byte[] mem_wram;
|
|
|
|
internal static Board mem_board;
|
|
|
|
private static MemReadAccess[] mem_read_accesses;
|
|
|
|
private static MemWriteAccess[] mem_write_accesses;
|
|
|
|
private static bool BUS_RW;
|
|
|
|
private static ushort BUS_ADDRESS;
|
|
|
|
private static string SRAMFileName;
|
|
|
|
public static string GMFileName;
|
|
|
|
private static int PORT0;
|
|
|
|
private static int PORT1;
|
|
|
|
private static int inputStrobe;
|
|
|
|
private static IJoypadConnecter joypad1;
|
|
|
|
private static IJoypadConnecter joypad2;
|
|
|
|
private static IJoypadConnecter joypad3;
|
|
|
|
private static IJoypadConnecter joypad4;
|
|
|
|
public static bool IsFourPlayers;
|
|
|
|
private static readonly byte[] reverseLookup = new byte[256]
|
|
{
|
|
0, 128, 64, 192, 32, 160, 96, 224, 16, 144,
|
|
80, 208, 48, 176, 112, 240, 8, 136, 72, 200,
|
|
40, 168, 104, 232, 24, 152, 88, 216, 56, 184,
|
|
120, 248, 4, 132, 68, 196, 36, 164, 100, 228,
|
|
20, 148, 84, 212, 52, 180, 116, 244, 12, 140,
|
|
76, 204, 44, 172, 108, 236, 28, 156, 92, 220,
|
|
60, 188, 124, 252, 2, 130, 66, 194, 34, 162,
|
|
98, 226, 18, 146, 82, 210, 50, 178, 114, 242,
|
|
10, 138, 74, 202, 42, 170, 106, 234, 26, 154,
|
|
90, 218, 58, 186, 122, 250, 6, 134, 70, 198,
|
|
38, 166, 102, 230, 22, 150, 86, 214, 54, 182,
|
|
118, 246, 14, 142, 78, 206, 46, 174, 110, 238,
|
|
30, 158, 94, 222, 62, 190, 126, 254, 1, 129,
|
|
65, 193, 33, 161, 97, 225, 17, 145, 81, 209,
|
|
49, 177, 113, 241, 9, 137, 73, 201, 41, 169,
|
|
105, 233, 25, 153, 89, 217, 57, 185, 121, 249,
|
|
5, 133, 69, 197, 37, 165, 101, 229, 21, 149,
|
|
85, 213, 53, 181, 117, 245, 13, 141, 77, 205,
|
|
45, 173, 109, 237, 29, 157, 93, 221, 61, 189,
|
|
125, 253, 3, 131, 67, 195, 35, 163, 99, 227,
|
|
19, 147, 83, 211, 51, 179, 115, 243, 11, 139,
|
|
75, 203, 43, 171, 107, 235, 27, 155, 91, 219,
|
|
59, 187, 123, 251, 7, 135, 71, 199, 39, 167,
|
|
103, 231, 23, 151, 87, 215, 55, 183, 119, 247,
|
|
15, 143, 79, 207, 47, 175, 111, 239, 31, 159,
|
|
95, 223, 63, 191, 127, 255
|
|
};
|
|
|
|
private static Action[] ppu_v_clocks;
|
|
|
|
private static Action[] ppu_h_clocks;
|
|
|
|
private static Action[] ppu_bkg_fetches;
|
|
|
|
private static Action[] ppu_spr_fetches;
|
|
|
|
private static Action[] ppu_oam_phases;
|
|
|
|
private static int[] ppu_bkg_pixels;
|
|
|
|
private static int[] ppu_spr_pixels;
|
|
|
|
private static int[] ppu_screen_pixels;
|
|
|
|
private static int[] ppu_palette;
|
|
|
|
private static int ppu_clock_h;
|
|
|
|
internal static ushort ppu_clock_v;
|
|
|
|
private static ushort ppu_clock_vblank_start;
|
|
|
|
private static ushort ppu_clock_vblank_end;
|
|
|
|
private static bool ppu_use_odd_cycle;
|
|
|
|
private static bool ppu_use_odd_swap;
|
|
|
|
private static bool ppu_odd_swap_done;
|
|
|
|
private static bool ppu_is_nmi_time;
|
|
|
|
private static bool ppu_frame_finished;
|
|
|
|
private static byte[] ppu_oam_bank;
|
|
|
|
private static byte[] ppu_oam_bank_secondary;
|
|
|
|
private static byte[] ppu_palette_bank;
|
|
|
|
private static byte ppu_reg_io_db;
|
|
|
|
private static byte ppu_reg_io_addr;
|
|
|
|
private static bool ppu_reg_access_happened;
|
|
|
|
private static bool ppu_reg_access_w;
|
|
|
|
private static Action[] ppu_reg_update_func;
|
|
|
|
private static Action[] ppu_reg_read_func;
|
|
|
|
private static byte ppu_reg_2000_vram_address_increament;
|
|
|
|
private static ushort ppu_reg_2000_sprite_pattern_table_address_for_8x8_sprites;
|
|
|
|
private static ushort ppu_reg_2000_background_pattern_table_address;
|
|
|
|
internal static byte ppu_reg_2000_Sprite_size;
|
|
|
|
private static bool ppu_reg_2000_VBI;
|
|
|
|
private static bool ppu_reg_2001_show_background_in_leftmost_8_pixels_of_screen;
|
|
|
|
private static bool ppu_reg_2001_show_sprites_in_leftmost_8_pixels_of_screen;
|
|
|
|
private static bool ppu_reg_2001_show_background;
|
|
|
|
private static bool ppu_reg_2001_show_sprites;
|
|
|
|
private static int ppu_reg_2001_grayscale;
|
|
|
|
private static int ppu_reg_2001_emphasis;
|
|
|
|
private static bool ppu_reg_2002_SpriteOverflow;
|
|
|
|
private static bool ppu_reg_2002_Sprite0Hit;
|
|
|
|
private static bool ppu_reg_2002_VblankStartedFlag;
|
|
|
|
private static byte ppu_reg_2003_oam_addr;
|
|
|
|
private static ushort ppu_vram_addr;
|
|
|
|
private static byte ppu_vram_data;
|
|
|
|
private static ushort ppu_vram_addr_temp;
|
|
|
|
private static ushort ppu_vram_addr_access_temp;
|
|
|
|
private static bool ppu_vram_flip_flop;
|
|
|
|
private static byte ppu_vram_finex;
|
|
|
|
private static ushort ppu_bkgfetch_nt_addr;
|
|
|
|
private static byte ppu_bkgfetch_nt_data;
|
|
|
|
private static ushort ppu_bkgfetch_at_addr;
|
|
|
|
private static byte ppu_bkgfetch_at_data;
|
|
|
|
private static ushort ppu_bkgfetch_lb_addr;
|
|
|
|
private static byte ppu_bkgfetch_lb_data;
|
|
|
|
private static ushort ppu_bkgfetch_hb_addr;
|
|
|
|
private static byte ppu_bkgfetch_hb_data;
|
|
|
|
private static int ppu_sprfetch_slot;
|
|
|
|
private static byte ppu_sprfetch_y_data;
|
|
|
|
private static byte ppu_sprfetch_t_data;
|
|
|
|
private static byte ppu_sprfetch_at_data;
|
|
|
|
private static byte ppu_sprfetch_x_data;
|
|
|
|
private static ushort ppu_sprfetch_lb_addr;
|
|
|
|
private static byte ppu_sprfetch_lb_data;
|
|
|
|
private static ushort ppu_sprfetch_hb_addr;
|
|
|
|
private static byte ppu_sprfetch_hb_data;
|
|
|
|
internal static bool ppu_is_sprfetch;
|
|
|
|
private static int ppu_bkg_render_i;
|
|
|
|
private static int ppu_bkg_render_pos;
|
|
|
|
private static int ppu_bkg_render_tmp_val;
|
|
|
|
private static int ppu_bkg_current_pixel;
|
|
|
|
private static int ppu_spr_current_pixel;
|
|
|
|
private static int ppu_current_pixel;
|
|
|
|
private static int ppu_render_x;
|
|
|
|
private static int ppu_render_y;
|
|
|
|
private static byte ppu_oamev_n;
|
|
|
|
private static byte ppu_oamev_m;
|
|
|
|
private static bool ppu_oamev_compare;
|
|
|
|
private static byte ppu_oamev_slot;
|
|
|
|
private static byte ppu_fetch_data;
|
|
|
|
private static byte ppu_phase_index;
|
|
|
|
private static bool ppu_sprite0_should_hit;
|
|
|
|
private static int ppu_temp_comparator;
|
|
|
|
public static bool ON;
|
|
|
|
public static bool PAUSED;
|
|
|
|
public static bool isPaused;
|
|
|
|
public static string CurrentFilePath;
|
|
|
|
public static bool FrameLimiterEnabled;
|
|
|
|
private static Thread mainThread;
|
|
|
|
private static double fps_time_period;
|
|
|
|
private static double emu_time_target_fps = 60.0;
|
|
|
|
private static bool render_initialized;
|
|
|
|
private static RenderVideoFrame render_video;
|
|
|
|
private static RenderAudioSamples render_audio;
|
|
|
|
private static TogglePause render_audio_toggle_pause;
|
|
|
|
private static GetIsPlaying render_audio_get_is_playing;
|
|
|
|
private static bool render_audio_is_playing;
|
|
|
|
public static EmuRegion Region;
|
|
|
|
private static int SystemIndex;
|
|
|
|
private static RequestMode emu_request_mode = RequestMode.None;
|
|
|
|
public static bool FrameSkipEnabled;
|
|
|
|
public static int FrameSkipInterval;
|
|
|
|
private static int FrameSkipCounter;
|
|
|
|
private static byte sq2_duty_cycle;
|
|
|
|
private static bool sq2_length_halt;
|
|
|
|
private static bool sq2_constant_volume_envelope;
|
|
|
|
private static byte sq2_volume_devider_period;
|
|
|
|
private static bool sq2_sweep_enable;
|
|
|
|
private static byte sq2_sweep_devider_period;
|
|
|
|
private static bool sq2_sweep_negate;
|
|
|
|
private static byte sq2_sweep_shift_count;
|
|
|
|
private static int sq2_timer;
|
|
|
|
private static int sq2_period_devider;
|
|
|
|
private static byte sq2_seqencer;
|
|
|
|
private static bool sq2_length_enabled;
|
|
|
|
private static int sq2_length_counter;
|
|
|
|
private static bool sq2_envelope_start_flag;
|
|
|
|
private static byte sq2_envelope_devider;
|
|
|
|
private static byte sq2_envelope_decay_level_counter;
|
|
|
|
private static byte sq2_envelope;
|
|
|
|
private static int sq2_sweep_counter;
|
|
|
|
private static bool sq2_sweep_reload;
|
|
|
|
private static int sq2_sweep_change;
|
|
|
|
private static bool sq2_valid_freq;
|
|
|
|
private static int sq2_output;
|
|
|
|
private static bool sq2_ignore_reload;
|
|
|
|
private static byte register_p
|
|
{
|
|
get
|
|
{
|
|
return (byte)((cpu_flag_n ? 128u : 0u) | (cpu_flag_v ? 64u : 0u) | (cpu_flag_d ? 8u : 0u) | (cpu_flag_i ? 4u : 0u) | (cpu_flag_z ? 2u : 0u) | (cpu_flag_c ? 1u : 0u) | 0x20u);
|
|
}
|
|
set
|
|
{
|
|
cpu_flag_n = (value & 0x80) != 0;
|
|
cpu_flag_v = (value & 0x40) != 0;
|
|
cpu_flag_d = (value & 8) != 0;
|
|
cpu_flag_i = (value & 4) != 0;
|
|
cpu_flag_z = (value & 2) != 0;
|
|
cpu_flag_c = (value & 1) != 0;
|
|
}
|
|
}
|
|
|
|
public static GameGenieCode[] GameGenieCodes
|
|
{
|
|
get
|
|
{
|
|
if (mem_board != null)
|
|
{
|
|
return mem_board.GameGenieCodes;
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public static bool IsGameGenieActive
|
|
{
|
|
get
|
|
{
|
|
if (mem_board != null)
|
|
{
|
|
return mem_board.IsGameGenieActive;
|
|
}
|
|
return false;
|
|
}
|
|
set
|
|
{
|
|
if (mem_board != null)
|
|
{
|
|
mem_board.IsGameGenieActive = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
public static bool IsGameFoundOnDB
|
|
{
|
|
get
|
|
{
|
|
if (mem_board != null)
|
|
{
|
|
return mem_board.IsGameFoundOnDB;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public static NesCartDatabaseGameInfo GameInfo
|
|
{
|
|
get
|
|
{
|
|
if (mem_board != null)
|
|
{
|
|
return mem_board.GameInfo;
|
|
}
|
|
return NesCartDatabaseGameInfo.Empty;
|
|
}
|
|
}
|
|
|
|
public static NesCartDatabaseCartridgeInfo GameCartInfo
|
|
{
|
|
get
|
|
{
|
|
if (mem_board != null)
|
|
{
|
|
return mem_board.GameCartInfo;
|
|
}
|
|
return new NesCartDatabaseCartridgeInfo();
|
|
}
|
|
}
|
|
|
|
public static string SHA1 => mem_board.SHA1;
|
|
|
|
public static event EventHandler EmuShutdown;
|
|
|
|
private static void DMCHardReset()
|
|
{
|
|
dmc_output_a = 0;
|
|
dmc_output = 0;
|
|
dmc_period_devider = 0;
|
|
dmc_loop_flag = false;
|
|
dmc_rate_index = 0;
|
|
dmc_irq_enabled = false;
|
|
dmc_dmaAddr = 49152;
|
|
dmc_addr_refresh = 49152;
|
|
dmc_size_refresh = 0;
|
|
dmc_dmaBits = 1;
|
|
dmc_dmaByte = 1;
|
|
dmc_period_devider = 0;
|
|
dmc_dmaEnabled = false;
|
|
dmc_bufferFull = false;
|
|
dmc_dmaSize = 0;
|
|
}
|
|
|
|
private static void DMCSoftReset()
|
|
{
|
|
DMCHardReset();
|
|
}
|
|
|
|
private static void DMCClock()
|
|
{
|
|
dmc_period_devider--;
|
|
if (dmc_period_devider > 0)
|
|
{
|
|
return;
|
|
}
|
|
dmc_period_devider = dmc_freq_table[SystemIndex][dmc_rate_index];
|
|
if (dmc_dmaEnabled)
|
|
{
|
|
if (((uint)dmc_dmaByte & (true ? 1u : 0u)) != 0)
|
|
{
|
|
if (dmc_output_a <= 125)
|
|
{
|
|
dmc_output_a += 2;
|
|
}
|
|
}
|
|
else if (dmc_output_a >= 2)
|
|
{
|
|
dmc_output_a -= 2;
|
|
}
|
|
dmc_dmaByte >>= 1;
|
|
}
|
|
dmc_dmaBits--;
|
|
if (dmc_dmaBits == 0)
|
|
{
|
|
dmc_dmaBits = 8;
|
|
if (dmc_bufferFull)
|
|
{
|
|
dmc_bufferFull = false;
|
|
dmc_dmaEnabled = true;
|
|
dmc_dmaByte = dmc_dmaBuffer;
|
|
if (dmc_dmaSize > 0)
|
|
{
|
|
AssertDMCDMA();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dmc_dmaEnabled = false;
|
|
}
|
|
}
|
|
if (audio_dmc_outputable)
|
|
{
|
|
dmc_output = dmc_output_a;
|
|
}
|
|
audio_signal_outputed = true;
|
|
}
|
|
|
|
private static void DMCDoDMA()
|
|
{
|
|
dmc_bufferFull = true;
|
|
Read(ref dmc_dmaAddr, out dmc_dmaBuffer);
|
|
if (dmc_dmaAddr == ushort.MaxValue)
|
|
{
|
|
dmc_dmaAddr = 32768;
|
|
}
|
|
else
|
|
{
|
|
dmc_dmaAddr++;
|
|
}
|
|
if (dmc_dmaSize > 0)
|
|
{
|
|
dmc_dmaSize--;
|
|
}
|
|
if (dmc_dmaSize == 0)
|
|
{
|
|
if (dmc_loop_flag)
|
|
{
|
|
dmc_dmaSize = dmc_size_refresh;
|
|
dmc_dmaAddr = dmc_addr_refresh;
|
|
}
|
|
else if (dmc_irq_enabled)
|
|
{
|
|
IRQFlags |= 2;
|
|
apu_irq_delta_occur = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void APUOnRegister4010()
|
|
{
|
|
if (apu_reg_access_w)
|
|
{
|
|
dmc_irq_enabled = (apu_reg_io_db & 0x80) != 0;
|
|
dmc_loop_flag = (apu_reg_io_db & 0x40) != 0;
|
|
if (!dmc_irq_enabled)
|
|
{
|
|
apu_irq_delta_occur = false;
|
|
IRQFlags &= -3;
|
|
}
|
|
dmc_rate_index = (byte)(apu_reg_io_db & 0xFu);
|
|
}
|
|
}
|
|
|
|
private static void APUOnRegister4011()
|
|
{
|
|
if (apu_reg_access_w)
|
|
{
|
|
dmc_output_a = (byte)(apu_reg_io_db & 0x7F);
|
|
}
|
|
}
|
|
|
|
private static void APUOnRegister4012()
|
|
{
|
|
if (apu_reg_access_w)
|
|
{
|
|
dmc_addr_refresh = (ushort)((uint)(apu_reg_io_db << 6) | 0xC000u);
|
|
}
|
|
}
|
|
|
|
private static void APUOnRegister4013()
|
|
{
|
|
if (apu_reg_access_w)
|
|
{
|
|
dmc_size_refresh = (apu_reg_io_db << 4) | 1;
|
|
}
|
|
}
|
|
|
|
private static void DMCOn4015()
|
|
{
|
|
apu_irq_delta_occur = false;
|
|
IRQFlags &= -3;
|
|
}
|
|
|
|
private static void DMCRead4015()
|
|
{
|
|
if (dmc_dmaSize > 0)
|
|
{
|
|
apu_reg_io_db = (byte)((apu_reg_io_db & 0xEFu) | 0x10u);
|
|
}
|
|
}
|
|
|
|
private static void DMCWriteState(ref BinaryWriter bin)
|
|
{
|
|
bin.Write(dmc_output_a);
|
|
bin.Write(dmc_output);
|
|
bin.Write(dmc_period_devider);
|
|
bin.Write(dmc_irq_enabled);
|
|
bin.Write(dmc_loop_flag);
|
|
bin.Write(dmc_rate_index);
|
|
bin.Write(dmc_addr_refresh);
|
|
bin.Write(dmc_size_refresh);
|
|
bin.Write(dmc_dmaEnabled);
|
|
bin.Write(dmc_dmaByte);
|
|
bin.Write(dmc_dmaBits);
|
|
bin.Write(dmc_bufferFull);
|
|
bin.Write(dmc_dmaBuffer);
|
|
bin.Write(dmc_dmaSize);
|
|
bin.Write(dmc_dmaAddr);
|
|
}
|
|
|
|
private static void DMCReadState(ref BinaryReader bin)
|
|
{
|
|
dmc_output_a = bin.ReadInt32();
|
|
dmc_output = bin.ReadInt32();
|
|
dmc_period_devider = bin.ReadInt32();
|
|
dmc_irq_enabled = bin.ReadBoolean();
|
|
dmc_loop_flag = bin.ReadBoolean();
|
|
dmc_rate_index = bin.ReadByte();
|
|
dmc_addr_refresh = bin.ReadUInt16();
|
|
dmc_size_refresh = bin.ReadInt32();
|
|
dmc_dmaEnabled = bin.ReadBoolean();
|
|
dmc_dmaByte = bin.ReadByte();
|
|
dmc_dmaBits = bin.ReadInt32();
|
|
dmc_bufferFull = bin.ReadBoolean();
|
|
dmc_dmaBuffer = bin.ReadByte();
|
|
dmc_dmaSize = bin.ReadInt32();
|
|
dmc_dmaAddr = bin.ReadUInt16();
|
|
}
|
|
|
|
private static void NOSHardReset()
|
|
{
|
|
nos_length_halt = false;
|
|
nos_constant_volume_envelope = false;
|
|
nos_volume_devider_period = 0;
|
|
nos_shift_reg = 1;
|
|
nos_timer = 0;
|
|
nos_mode = false;
|
|
nos_period_devider = 0;
|
|
nos_length_enabled = false;
|
|
nos_length_counter = 0;
|
|
nos_envelope_start_flag = false;
|
|
nos_envelope_devider = 0;
|
|
nos_envelope_decay_level_counter = 0;
|
|
nos_envelope = 0;
|
|
nos_output = 0;
|
|
nos_feedback = 0;
|
|
nos_ignore_reload = false;
|
|
}
|
|
|
|
private static void NOSSoftReset()
|
|
{
|
|
NOSHardReset();
|
|
}
|
|
|
|
private static void NOSClock()
|
|
{
|
|
nos_period_devider--;
|
|
if (nos_period_devider > 0)
|
|
{
|
|
return;
|
|
}
|
|
nos_period_devider = nos_timer;
|
|
if (nos_mode)
|
|
{
|
|
nos_feedback = ((nos_shift_reg >> 6) & 1) ^ (nos_shift_reg & 1);
|
|
}
|
|
else
|
|
{
|
|
nos_feedback = ((nos_shift_reg >> 1) & 1) ^ (nos_shift_reg & 1);
|
|
}
|
|
nos_shift_reg >>= 1;
|
|
nos_shift_reg = (nos_shift_reg & 0x3FFF) | ((nos_feedback & 1) << 14);
|
|
if (nos_length_counter > 0 && (nos_shift_reg & 1) == 0)
|
|
{
|
|
if (audio_nos_outputable)
|
|
{
|
|
nos_output = nos_envelope;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
nos_output = 0;
|
|
}
|
|
audio_signal_outputed = true;
|
|
}
|
|
|
|
private static void NOSClockLength()
|
|
{
|
|
if (nos_length_counter > 0 && !nos_length_halt)
|
|
{
|
|
nos_length_counter--;
|
|
if (apu_reg_access_happened && apu_reg_io_addr == 15 && apu_reg_access_w)
|
|
{
|
|
nos_ignore_reload = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void NOSClockEnvelope()
|
|
{
|
|
if (nos_envelope_start_flag)
|
|
{
|
|
nos_envelope_start_flag = false;
|
|
nos_envelope_decay_level_counter = 15;
|
|
nos_envelope_devider = (byte)(nos_volume_devider_period + 1);
|
|
}
|
|
else if (nos_envelope_devider > 0)
|
|
{
|
|
nos_envelope_devider--;
|
|
}
|
|
else
|
|
{
|
|
nos_envelope_devider = (byte)(nos_volume_devider_period + 1);
|
|
if (nos_envelope_decay_level_counter > 0)
|
|
{
|
|
nos_envelope_decay_level_counter--;
|
|
}
|
|
else if (nos_length_halt)
|
|
{
|
|
nos_envelope_decay_level_counter = 15;
|
|
}
|
|
}
|
|
nos_envelope = (nos_constant_volume_envelope ? nos_volume_devider_period : nos_envelope_decay_level_counter);
|
|
}
|
|
|
|
private static void APUOnRegister400C()
|
|
{
|
|
if (apu_reg_access_w)
|
|
{
|
|
nos_volume_devider_period = (byte)(apu_reg_io_db & 0xFu);
|
|
nos_length_halt = (apu_reg_io_db & 0x20) != 0;
|
|
nos_constant_volume_envelope = (apu_reg_io_db & 0x10) != 0;
|
|
nos_envelope = (nos_constant_volume_envelope ? nos_volume_devider_period : nos_envelope_decay_level_counter);
|
|
}
|
|
}
|
|
|
|
private static void APUOnRegister400D()
|
|
{
|
|
}
|
|
|
|
private static void APUOnRegister400E()
|
|
{
|
|
if (apu_reg_access_w)
|
|
{
|
|
nos_timer = (ushort)(nos_freq_table[SystemIndex][apu_reg_io_db & 0xF] / 2);
|
|
nos_mode = (apu_reg_io_db & 0x80) == 128;
|
|
}
|
|
}
|
|
|
|
private static void APUOnRegister400F()
|
|
{
|
|
if (apu_reg_access_w)
|
|
{
|
|
if (nos_length_enabled && !nos_ignore_reload)
|
|
{
|
|
nos_length_counter = sq_duration_table[apu_reg_io_db >> 3];
|
|
}
|
|
if (nos_ignore_reload)
|
|
{
|
|
nos_ignore_reload = false;
|
|
}
|
|
nos_envelope_start_flag = true;
|
|
}
|
|
}
|
|
|
|
private static void NOSOn4015()
|
|
{
|
|
nos_length_enabled = (apu_reg_io_db & 8) != 0;
|
|
if (!nos_length_enabled)
|
|
{
|
|
nos_length_counter = 0;
|
|
}
|
|
}
|
|
|
|
private static void NOSRead4015()
|
|
{
|
|
if (nos_length_counter > 0)
|
|
{
|
|
apu_reg_io_db = (byte)((apu_reg_io_db & 0xF7u) | 8u);
|
|
}
|
|
}
|
|
|
|
private static void NOSWriteState(ref BinaryWriter bin)
|
|
{
|
|
bin.Write(nos_length_halt);
|
|
bin.Write(nos_constant_volume_envelope);
|
|
bin.Write(nos_volume_devider_period);
|
|
bin.Write(nos_timer);
|
|
bin.Write(nos_mode);
|
|
bin.Write(nos_period_devider);
|
|
bin.Write(nos_length_enabled);
|
|
bin.Write(nos_length_counter);
|
|
bin.Write(nos_envelope_start_flag);
|
|
bin.Write(nos_envelope_devider);
|
|
bin.Write(nos_envelope_decay_level_counter);
|
|
bin.Write(nos_envelope);
|
|
bin.Write(nos_output);
|
|
bin.Write(nos_shift_reg);
|
|
bin.Write(nos_feedback);
|
|
bin.Write(nos_ignore_reload);
|
|
}
|
|
|
|
private static void NOSReadState(ref BinaryReader bin)
|
|
{
|
|
nos_length_halt = bin.ReadBoolean();
|
|
nos_constant_volume_envelope = bin.ReadBoolean();
|
|
nos_volume_devider_period = bin.ReadByte();
|
|
nos_timer = bin.ReadUInt16();
|
|
nos_mode = bin.ReadBoolean();
|
|
nos_period_devider = bin.ReadInt32();
|
|
nos_length_enabled = bin.ReadBoolean();
|
|
nos_length_counter = bin.ReadInt32();
|
|
nos_envelope_start_flag = bin.ReadBoolean();
|
|
nos_envelope_devider = bin.ReadByte();
|
|
nos_envelope_decay_level_counter = bin.ReadByte();
|
|
nos_envelope = bin.ReadByte();
|
|
nos_output = bin.ReadInt32();
|
|
nos_shift_reg = bin.ReadInt32();
|
|
nos_feedback = bin.ReadInt32();
|
|
nos_ignore_reload = bin.ReadBoolean();
|
|
}
|
|
|
|
private static void SQ1HardReset()
|
|
{
|
|
sq1_duty_cycle = 0;
|
|
sq1_length_halt = false;
|
|
sq1_constant_volume_envelope = false;
|
|
sq1_volume_devider_period = 0;
|
|
sq1_sweep_enable = false;
|
|
sq1_sweep_devider_period = 0;
|
|
sq1_sweep_negate = false;
|
|
sq1_sweep_shift_count = 0;
|
|
sq1_timer = 0;
|
|
sq1_period_devider = 0;
|
|
sq1_seqencer = 0;
|
|
sq1_length_enabled = false;
|
|
sq1_length_counter = 0;
|
|
sq1_envelope_start_flag = false;
|
|
sq1_envelope_devider = 0;
|
|
sq1_envelope_decay_level_counter = 0;
|
|
sq1_envelope = 0;
|
|
sq1_sweep_counter = 0;
|
|
sq1_sweep_reload = false;
|
|
sq1_sweep_change = 0;
|
|
sq1_valid_freq = false;
|
|
sq1_output = 0;
|
|
sq1_ignore_reload = false;
|
|
}
|
|
|
|
private static void SQ1SoftReset()
|
|
{
|
|
SQ1HardReset();
|
|
}
|
|
|
|
private static void SQ1Clock()
|
|
{
|
|
sq1_period_devider--;
|
|
if (sq1_period_devider > 0)
|
|
{
|
|
return;
|
|
}
|
|
sq1_period_devider = sq1_timer + 1;
|
|
sq1_seqencer = (byte)((uint)(sq1_seqencer + 1) & 7u);
|
|
if (sq1_length_counter > 0 && sq1_valid_freq)
|
|
{
|
|
if (audio_sq1_outputable)
|
|
{
|
|
sq1_output = sq_duty_cycle_sequences[sq1_duty_cycle][sq1_seqencer] * sq1_envelope;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sq1_output = 0;
|
|
}
|
|
audio_signal_outputed = true;
|
|
}
|
|
|
|
private static void SQ1ClockLength()
|
|
{
|
|
if (sq1_length_counter > 0 && !sq1_length_halt)
|
|
{
|
|
sq1_length_counter--;
|
|
if (apu_reg_access_happened && apu_reg_io_addr == 3 && apu_reg_access_w)
|
|
{
|
|
sq1_ignore_reload = true;
|
|
}
|
|
}
|
|
sq1_sweep_counter--;
|
|
if (sq1_sweep_counter == 0)
|
|
{
|
|
sq1_sweep_counter = sq1_sweep_devider_period + 1;
|
|
if (sq1_sweep_enable && sq1_sweep_shift_count > 0 && sq1_valid_freq)
|
|
{
|
|
sq1_sweep_change = sq1_timer >> (int)sq1_sweep_shift_count;
|
|
sq1_timer += (sq1_sweep_negate ? (~sq1_sweep_change) : sq1_sweep_change);
|
|
SQ1CalculateValidFreq();
|
|
}
|
|
}
|
|
if (sq1_sweep_reload)
|
|
{
|
|
sq1_sweep_counter = sq1_sweep_devider_period + 1;
|
|
sq1_sweep_reload = false;
|
|
}
|
|
}
|
|
|
|
private static void SQ1ClockEnvelope()
|
|
{
|
|
if (sq1_envelope_start_flag)
|
|
{
|
|
sq1_envelope_start_flag = false;
|
|
sq1_envelope_decay_level_counter = 15;
|
|
sq1_envelope_devider = (byte)(sq1_volume_devider_period + 1);
|
|
}
|
|
else if (sq1_envelope_devider > 0)
|
|
{
|
|
sq1_envelope_devider--;
|
|
}
|
|
else
|
|
{
|
|
sq1_envelope_devider = (byte)(sq1_volume_devider_period + 1);
|
|
if (sq1_envelope_decay_level_counter > 0)
|
|
{
|
|
sq1_envelope_decay_level_counter--;
|
|
}
|
|
else if (sq1_length_halt)
|
|
{
|
|
sq1_envelope_decay_level_counter = 15;
|
|
}
|
|
}
|
|
sq1_envelope = (sq1_constant_volume_envelope ? sq1_volume_devider_period : sq1_envelope_decay_level_counter);
|
|
}
|
|
|
|
private static void APUOnRegister4000()
|
|
{
|
|
if (apu_reg_access_w)
|
|
{
|
|
sq1_duty_cycle = (byte)((apu_reg_io_db & 0xC0) >> 6);
|
|
sq1_volume_devider_period = (byte)(apu_reg_io_db & 0xFu);
|
|
sq1_length_halt = (apu_reg_io_db & 0x20) != 0;
|
|
sq1_constant_volume_envelope = (apu_reg_io_db & 0x10) != 0;
|
|
sq1_envelope = (sq1_constant_volume_envelope ? sq1_volume_devider_period : sq1_envelope_decay_level_counter);
|
|
}
|
|
}
|
|
|
|
private static void APUOnRegister4001()
|
|
{
|
|
if (apu_reg_access_w)
|
|
{
|
|
sq1_sweep_enable = (apu_reg_io_db & 0x80) == 128;
|
|
sq1_sweep_devider_period = (byte)((uint)(apu_reg_io_db >> 4) & 7u);
|
|
sq1_sweep_negate = (apu_reg_io_db & 8) == 8;
|
|
sq1_sweep_shift_count = (byte)(apu_reg_io_db & 7u);
|
|
sq1_sweep_reload = true;
|
|
SQ1CalculateValidFreq();
|
|
}
|
|
}
|
|
|
|
private static void APUOnRegister4002()
|
|
{
|
|
if (apu_reg_access_w)
|
|
{
|
|
sq1_timer = (sq1_timer & 0xFF00) | apu_reg_io_db;
|
|
SQ1CalculateValidFreq();
|
|
}
|
|
}
|
|
|
|
private static void APUOnRegister4003()
|
|
{
|
|
if (apu_reg_access_w)
|
|
{
|
|
sq1_timer = (sq1_timer & 0xFF) | ((apu_reg_io_db & 7) << 8);
|
|
if (sq1_length_enabled && !sq1_ignore_reload)
|
|
{
|
|
sq1_length_counter = sq_duration_table[apu_reg_io_db >> 3];
|
|
}
|
|
if (sq1_ignore_reload)
|
|
{
|
|
sq1_ignore_reload = false;
|
|
}
|
|
sq1_seqencer = 0;
|
|
sq1_envelope_start_flag = true;
|
|
SQ1CalculateValidFreq();
|
|
}
|
|
}
|
|
|
|
private static void SQ1On4015()
|
|
{
|
|
sq1_length_enabled = (apu_reg_io_db & 1) != 0;
|
|
if (!sq1_length_enabled)
|
|
{
|
|
sq1_length_counter = 0;
|
|
}
|
|
}
|
|
|
|
private static void SQ1Read4015()
|
|
{
|
|
if (sq1_length_counter > 0)
|
|
{
|
|
apu_reg_io_db = (byte)((apu_reg_io_db & 0xFEu) | 1u);
|
|
}
|
|
}
|
|
|
|
private static void SQ1CalculateValidFreq()
|
|
{
|
|
sq1_valid_freq = sq1_timer >= 8 && (sq1_sweep_negate || ((sq1_timer + (sq1_timer >> (int)sq1_sweep_shift_count)) & 0x800) == 0);
|
|
}
|
|
|
|
private static void SQ1WriteState(ref BinaryWriter bin)
|
|
{
|
|
bin.Write(sq1_duty_cycle);
|
|
bin.Write(sq1_length_halt);
|
|
bin.Write(sq1_constant_volume_envelope);
|
|
bin.Write(sq1_volume_devider_period);
|
|
bin.Write(sq1_sweep_enable);
|
|
bin.Write(sq1_sweep_devider_period);
|
|
bin.Write(sq1_sweep_negate);
|
|
bin.Write(sq1_sweep_shift_count);
|
|
bin.Write(sq1_timer);
|
|
bin.Write(sq1_period_devider);
|
|
bin.Write(sq1_seqencer);
|
|
bin.Write(sq1_length_enabled);
|
|
bin.Write(sq1_length_counter);
|
|
bin.Write(sq1_envelope_start_flag);
|
|
bin.Write(sq1_envelope_devider);
|
|
bin.Write(sq1_envelope_decay_level_counter);
|
|
bin.Write(sq1_envelope);
|
|
bin.Write(sq1_sweep_counter);
|
|
bin.Write(sq1_sweep_reload);
|
|
bin.Write(sq1_sweep_change);
|
|
bin.Write(sq1_valid_freq);
|
|
bin.Write(sq1_output);
|
|
bin.Write(sq1_ignore_reload);
|
|
}
|
|
|
|
private static void SQ1ReadState(ref BinaryReader bin)
|
|
{
|
|
sq1_duty_cycle = bin.ReadByte();
|
|
sq1_length_halt = bin.ReadBoolean();
|
|
sq1_constant_volume_envelope = bin.ReadBoolean();
|
|
sq1_volume_devider_period = bin.ReadByte();
|
|
sq1_sweep_enable = bin.ReadBoolean();
|
|
sq1_sweep_devider_period = bin.ReadByte();
|
|
sq1_sweep_negate = bin.ReadBoolean();
|
|
sq1_sweep_shift_count = bin.ReadByte();
|
|
sq1_timer = bin.ReadInt32();
|
|
sq1_period_devider = bin.ReadInt32();
|
|
sq1_seqencer = bin.ReadByte();
|
|
sq1_length_enabled = bin.ReadBoolean();
|
|
sq1_length_counter = bin.ReadInt32();
|
|
sq1_envelope_start_flag = bin.ReadBoolean();
|
|
sq1_envelope_devider = bin.ReadByte();
|
|
sq1_envelope_decay_level_counter = bin.ReadByte();
|
|
sq1_envelope = bin.ReadByte();
|
|
sq1_sweep_counter = bin.ReadInt32();
|
|
sq1_sweep_reload = bin.ReadBoolean();
|
|
sq1_sweep_change = bin.ReadInt32();
|
|
sq1_valid_freq = bin.ReadBoolean();
|
|
sq1_output = bin.ReadInt32();
|
|
sq1_ignore_reload = bin.ReadBoolean();
|
|
}
|
|
|
|
private static void TRLHardReset()
|
|
{
|
|
trl_liner_control_flag = false;
|
|
trl_liner_control_reload = 0;
|
|
trl_timer = 0;
|
|
trl_length_enabled = false;
|
|
trl_length_counter = 0;
|
|
trl_liner_control_reload_flag = false;
|
|
trl_liner_counter = 0;
|
|
trl_output = 0;
|
|
trl_period_devider = 0;
|
|
trl_step = 0;
|
|
trl_ignore_reload = false;
|
|
}
|
|
|
|
private static void TRLSoftReset()
|
|
{
|
|
TRLHardReset();
|
|
}
|
|
|
|
private static void TRLClock()
|
|
{
|
|
trl_period_devider--;
|
|
if (trl_period_devider > 0)
|
|
{
|
|
return;
|
|
}
|
|
trl_period_devider = trl_timer + 1;
|
|
if (trl_length_counter > 0 && trl_liner_counter > 0 && trl_timer >= 4)
|
|
{
|
|
trl_step++;
|
|
trl_step &= 31;
|
|
if (audio_trl_outputable)
|
|
{
|
|
trl_output = trl_step_seq[trl_step];
|
|
}
|
|
}
|
|
audio_signal_outputed = true;
|
|
}
|
|
|
|
private static void TRLClockLength()
|
|
{
|
|
if (trl_length_counter > 0 && !trl_liner_control_flag)
|
|
{
|
|
trl_length_counter--;
|
|
if (apu_reg_access_happened && apu_reg_io_addr == 11 && apu_reg_access_w)
|
|
{
|
|
trl_ignore_reload = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void TRLClockEnvelope()
|
|
{
|
|
if (trl_liner_control_reload_flag)
|
|
{
|
|
trl_liner_counter = trl_liner_control_reload;
|
|
}
|
|
else if (trl_liner_counter > 0)
|
|
{
|
|
trl_liner_counter--;
|
|
}
|
|
if (!trl_liner_control_flag)
|
|
{
|
|
trl_liner_control_reload_flag = false;
|
|
}
|
|
}
|
|
|
|
private static void APUOnRegister4008()
|
|
{
|
|
if (apu_reg_access_w)
|
|
{
|
|
trl_liner_control_flag = (apu_reg_io_db & 0x80) == 128;
|
|
trl_liner_control_reload = (byte)(apu_reg_io_db & 0x7Fu);
|
|
}
|
|
}
|
|
|
|
private static void APUOnRegister4009()
|
|
{
|
|
}
|
|
|
|
private static void APUOnRegister400A()
|
|
{
|
|
if (apu_reg_access_w)
|
|
{
|
|
trl_timer = (ushort)((trl_timer & 0x7F00u) | apu_reg_io_db);
|
|
}
|
|
}
|
|
|
|
private static void APUOnRegister400B()
|
|
{
|
|
if (apu_reg_access_w)
|
|
{
|
|
trl_timer = (ushort)((trl_timer & 0xFFu) | (uint)((apu_reg_io_db & 7) << 8));
|
|
if (trl_length_enabled && !trl_ignore_reload)
|
|
{
|
|
trl_length_counter = sq_duration_table[apu_reg_io_db >> 3];
|
|
}
|
|
if (trl_ignore_reload)
|
|
{
|
|
trl_ignore_reload = false;
|
|
}
|
|
trl_liner_control_reload_flag = true;
|
|
}
|
|
}
|
|
|
|
private static void TRLOn4015()
|
|
{
|
|
trl_length_enabled = (apu_reg_io_db & 4) != 0;
|
|
if (!trl_length_enabled)
|
|
{
|
|
trl_length_counter = 0;
|
|
}
|
|
}
|
|
|
|
private static void TRLRead4015()
|
|
{
|
|
if (trl_length_counter > 0)
|
|
{
|
|
apu_reg_io_db = (byte)((apu_reg_io_db & 0xFBu) | 4u);
|
|
}
|
|
}
|
|
|
|
private static void TRLWriteState(ref BinaryWriter bin)
|
|
{
|
|
bin.Write(trl_liner_control_flag);
|
|
bin.Write(trl_liner_control_reload);
|
|
bin.Write(trl_timer);
|
|
bin.Write(trl_length_enabled);
|
|
bin.Write(trl_length_counter);
|
|
bin.Write(trl_liner_control_reload_flag);
|
|
bin.Write(trl_liner_counter);
|
|
bin.Write(trl_output);
|
|
bin.Write(trl_period_devider);
|
|
bin.Write(trl_step);
|
|
bin.Write(trl_ignore_reload);
|
|
}
|
|
|
|
private static void TRLReadState(ref BinaryReader bin)
|
|
{
|
|
trl_liner_control_flag = bin.ReadBoolean();
|
|
trl_liner_control_reload = bin.ReadByte();
|
|
trl_timer = bin.ReadUInt16();
|
|
trl_length_enabled = bin.ReadBoolean();
|
|
trl_length_counter = bin.ReadByte();
|
|
trl_liner_control_reload_flag = bin.ReadBoolean();
|
|
trl_liner_counter = bin.ReadByte();
|
|
trl_output = bin.ReadInt32();
|
|
trl_period_devider = bin.ReadInt32();
|
|
trl_step = bin.ReadInt32();
|
|
trl_ignore_reload = bin.ReadBoolean();
|
|
}
|
|
|
|
private static void APUInitialize()
|
|
{
|
|
apu_reg_update_func = new Action[32];
|
|
apu_reg_read_func = new Action[32];
|
|
apu_reg_write_func = new Action[32];
|
|
for (int i = 0; i < 32; i++)
|
|
{
|
|
apu_reg_update_func[i] = APUBlankAccess;
|
|
apu_reg_read_func[i] = APUBlankAccess;
|
|
apu_reg_write_func[i] = APUBlankAccess;
|
|
}
|
|
apu_reg_update_func[0] = APUOnRegister4000;
|
|
apu_reg_update_func[1] = APUOnRegister4001;
|
|
apu_reg_update_func[2] = APUOnRegister4002;
|
|
apu_reg_update_func[3] = APUOnRegister4003;
|
|
apu_reg_update_func[4] = APUOnRegister4004;
|
|
apu_reg_update_func[5] = APUOnRegister4005;
|
|
apu_reg_update_func[6] = APUOnRegister4006;
|
|
apu_reg_update_func[7] = APUOnRegister4007;
|
|
apu_reg_update_func[8] = APUOnRegister4008;
|
|
apu_reg_update_func[9] = APUOnRegister4009;
|
|
apu_reg_update_func[10] = APUOnRegister400A;
|
|
apu_reg_update_func[11] = APUOnRegister400B;
|
|
apu_reg_update_func[12] = APUOnRegister400C;
|
|
apu_reg_update_func[13] = APUOnRegister400D;
|
|
apu_reg_update_func[14] = APUOnRegister400E;
|
|
apu_reg_update_func[15] = APUOnRegister400F;
|
|
apu_reg_update_func[16] = APUOnRegister4010;
|
|
apu_reg_update_func[17] = APUOnRegister4011;
|
|
apu_reg_update_func[18] = APUOnRegister4012;
|
|
apu_reg_update_func[19] = APUOnRegister4013;
|
|
apu_reg_update_func[21] = APUOnRegister4015;
|
|
apu_reg_update_func[22] = APUOnRegister4016;
|
|
apu_reg_update_func[23] = APUOnRegister4017;
|
|
apu_reg_read_func[21] = APURead4015;
|
|
apu_reg_read_func[22] = APURead4016;
|
|
apu_reg_read_func[23] = APURead4017;
|
|
apu_reg_write_func[20] = APUWrite4014;
|
|
apu_reg_write_func[21] = APUWrite4015;
|
|
audio_low_pass_filter_14K = new SoundLowPassFilter(0.00815686);
|
|
audio_high_pass_filter_90 = new SoundHighPassFilter(0.999835);
|
|
audio_high_pass_filter_440 = new SoundHighPassFilter(0.996039);
|
|
apu_update_playback_func = APUUpdatePlaybackWithFilters;
|
|
}
|
|
|
|
public static void ApplyAudioSettings(bool all = true)
|
|
{
|
|
SoundEnabled = MyNesMain.RendererSettings.Audio_SoundEnabled;
|
|
audio_sq1_outputable = MyNesMain.RendererSettings.Audio_ChannelEnabled_SQ1;
|
|
audio_sq2_outputable = MyNesMain.RendererSettings.Audio_ChannelEnabled_SQ2;
|
|
audio_nos_outputable = MyNesMain.RendererSettings.Audio_ChannelEnabled_NOZ;
|
|
audio_trl_outputable = MyNesMain.RendererSettings.Audio_ChannelEnabled_TRL;
|
|
audio_dmc_outputable = MyNesMain.RendererSettings.Audio_ChannelEnabled_DMC;
|
|
if (apu_use_external_sound)
|
|
{
|
|
mem_board.APUApplyChannelsSettings();
|
|
}
|
|
if (all)
|
|
{
|
|
CalculateAudioPlaybackValues();
|
|
}
|
|
}
|
|
|
|
private static void APUHardReset()
|
|
{
|
|
apu_reg_io_db = 0;
|
|
apu_reg_io_addr = 0;
|
|
apu_reg_access_happened = false;
|
|
apu_reg_access_w = false;
|
|
apu_seq_mode = false;
|
|
apu_odd_cycle = true;
|
|
apu_cycle_f_t = 0;
|
|
apu_cycle_e = 4;
|
|
apu_cycle_f = 4;
|
|
apu_cycle_l = 4;
|
|
apu_odd_l = false;
|
|
apu_check_irq = false;
|
|
apu_do_env = false;
|
|
apu_do_length = false;
|
|
switch (Region)
|
|
{
|
|
case EmuRegion.NTSC:
|
|
cpu_speed = 1789773;
|
|
apu_ferq_f = 14914;
|
|
apu_ferq_e = 3728;
|
|
apu_ferq_l = 7456;
|
|
break;
|
|
case EmuRegion.PALB:
|
|
cpu_speed = 1662607;
|
|
apu_ferq_f = 14914;
|
|
apu_ferq_e = 3728;
|
|
apu_ferq_l = 7456;
|
|
break;
|
|
case EmuRegion.DENDY:
|
|
cpu_speed = 1773448;
|
|
apu_ferq_f = 14914;
|
|
apu_ferq_e = 3728;
|
|
apu_ferq_l = 7456;
|
|
break;
|
|
}
|
|
Tracer.WriteLine("NES: cpu speed = " + cpu_speed);
|
|
SQ1HardReset();
|
|
SQ2HardReset();
|
|
NOSHardReset();
|
|
DMCHardReset();
|
|
TRLHardReset();
|
|
apu_irq_enabled = true;
|
|
apu_irq_flag = false;
|
|
reg_2004 = 8196;
|
|
CalculateAudioPlaybackValues();
|
|
apu_use_external_sound = mem_board.enable_external_sound;
|
|
if (apu_use_external_sound)
|
|
{
|
|
Tracer.WriteInformation("External sound channels has been enabled on apu.");
|
|
}
|
|
}
|
|
|
|
private static void APUSoftReset()
|
|
{
|
|
apu_reg_io_db = 0;
|
|
apu_reg_io_addr = 0;
|
|
apu_reg_access_happened = false;
|
|
apu_reg_access_w = false;
|
|
apu_seq_mode = false;
|
|
apu_odd_cycle = false;
|
|
apu_cycle_f_t = 0;
|
|
apu_cycle_e = 4;
|
|
apu_cycle_f = 4;
|
|
apu_cycle_l = 4;
|
|
apu_odd_l = false;
|
|
apu_check_irq = false;
|
|
apu_do_env = false;
|
|
apu_do_length = false;
|
|
apu_irq_enabled = true;
|
|
apu_irq_flag = false;
|
|
SQ1SoftReset();
|
|
SQ2SoftReset();
|
|
TRLSoftReset();
|
|
NOSSoftReset();
|
|
DMCSoftReset();
|
|
}
|
|
|
|
private static void APUIORead(ref ushort addr, out byte value)
|
|
{
|
|
if (addr >= 16416)
|
|
{
|
|
mem_board.ReadEX(ref addr, out value);
|
|
return;
|
|
}
|
|
apu_reg_io_addr = (byte)(addr & 0x1Fu);
|
|
apu_reg_access_happened = true;
|
|
apu_reg_access_w = false;
|
|
apu_reg_read_func[apu_reg_io_addr]();
|
|
value = apu_reg_io_db;
|
|
}
|
|
|
|
private static void APUIOWrite(ref ushort addr, ref byte value)
|
|
{
|
|
if (addr >= 16416)
|
|
{
|
|
mem_board.WriteEX(ref addr, ref value);
|
|
return;
|
|
}
|
|
apu_reg_io_addr = (byte)(addr & 0x1Fu);
|
|
apu_reg_io_db = value;
|
|
apu_reg_access_w = true;
|
|
apu_reg_access_happened = true;
|
|
apu_reg_write_func[apu_reg_io_addr]();
|
|
}
|
|
|
|
private static void APUBlankAccess()
|
|
{
|
|
}
|
|
|
|
private static void APUWrite4014()
|
|
{
|
|
dma_Oamaddress = (ushort)(apu_reg_io_db << 8);
|
|
AssertOAMDMA();
|
|
}
|
|
|
|
private static void APUWrite4015()
|
|
{
|
|
if ((apu_reg_io_db & 0x10u) != 0)
|
|
{
|
|
if (dmc_dmaSize == 0)
|
|
{
|
|
dmc_dmaSize = dmc_size_refresh;
|
|
dmc_dmaAddr = dmc_addr_refresh;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dmc_dmaSize = 0;
|
|
}
|
|
if (!dmc_bufferFull && dmc_dmaSize > 0)
|
|
{
|
|
AssertDMCDMA();
|
|
}
|
|
}
|
|
|
|
private static void APUOnRegister4015()
|
|
{
|
|
if (apu_reg_access_w)
|
|
{
|
|
SQ1On4015();
|
|
SQ2On4015();
|
|
NOSOn4015();
|
|
TRLOn4015();
|
|
DMCOn4015();
|
|
}
|
|
else
|
|
{
|
|
apu_irq_flag = false;
|
|
IRQFlags &= -2;
|
|
}
|
|
}
|
|
|
|
private static void APUOnRegister4016()
|
|
{
|
|
if (!apu_reg_access_w)
|
|
{
|
|
return;
|
|
}
|
|
if (inputStrobe > (apu_reg_io_db & 1))
|
|
{
|
|
if (IsFourPlayers)
|
|
{
|
|
PORT0 = (joypad3.GetData() << 8) | joypad1.GetData() | 0x1010000;
|
|
PORT1 = (joypad4.GetData() << 8) | joypad2.GetData() | 0x2020000;
|
|
}
|
|
else
|
|
{
|
|
PORT0 = joypad1.GetData() | 0x1010100;
|
|
PORT1 = joypad2.GetData() | 0x2020200;
|
|
}
|
|
}
|
|
inputStrobe = apu_reg_io_db & 1;
|
|
}
|
|
|
|
private static void APUOnRegister4017()
|
|
{
|
|
if (apu_reg_access_w)
|
|
{
|
|
apu_seq_mode = (apu_reg_io_db & 0x80) != 0;
|
|
apu_irq_enabled = (apu_reg_io_db & 0x40) == 0;
|
|
apu_cycle_e = -1;
|
|
apu_cycle_l = -1;
|
|
apu_cycle_f = -1;
|
|
apu_odd_l = false;
|
|
apu_do_length = apu_seq_mode;
|
|
apu_do_env = apu_seq_mode;
|
|
apu_check_irq = false;
|
|
if (!apu_irq_enabled)
|
|
{
|
|
apu_irq_flag = false;
|
|
IRQFlags &= -2;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void APURead4015()
|
|
{
|
|
apu_reg_io_db &= 32;
|
|
SQ1Read4015();
|
|
SQ2Read4015();
|
|
NOSRead4015();
|
|
TRLRead4015();
|
|
DMCRead4015();
|
|
if (apu_irq_flag)
|
|
{
|
|
apu_reg_io_db = (byte)((apu_reg_io_db & 0xBFu) | 0x40u);
|
|
}
|
|
if (apu_irq_delta_occur)
|
|
{
|
|
apu_reg_io_db = (byte)((apu_reg_io_db & 0x7Fu) | 0x80u);
|
|
}
|
|
}
|
|
|
|
private static void APURead4016()
|
|
{
|
|
apu_reg_io_db = (byte)((uint)PORT0 & 1u);
|
|
PORT0 >>= 1;
|
|
}
|
|
|
|
private static void APURead4017()
|
|
{
|
|
apu_reg_io_db = (byte)((uint)PORT1 & 1u);
|
|
PORT1 >>= 1;
|
|
}
|
|
|
|
private static void APUClock()
|
|
{
|
|
apu_odd_cycle = !apu_odd_cycle;
|
|
if (apu_do_env)
|
|
{
|
|
APUClockEnvelope();
|
|
}
|
|
if (apu_do_length)
|
|
{
|
|
APUClockDuration();
|
|
}
|
|
if (apu_odd_cycle)
|
|
{
|
|
apu_cycle_f++;
|
|
if (apu_cycle_f >= apu_ferq_f)
|
|
{
|
|
apu_cycle_f = -1;
|
|
apu_check_irq = true;
|
|
apu_cycle_f_t = 3;
|
|
}
|
|
apu_cycle_e++;
|
|
if (apu_cycle_e >= apu_ferq_e)
|
|
{
|
|
apu_cycle_e = -1;
|
|
if (apu_check_irq)
|
|
{
|
|
if (!apu_seq_mode)
|
|
{
|
|
apu_do_env = true;
|
|
}
|
|
else
|
|
{
|
|
apu_cycle_e = 4;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
apu_do_env = true;
|
|
}
|
|
}
|
|
apu_cycle_l++;
|
|
if (apu_cycle_l >= apu_ferq_l)
|
|
{
|
|
apu_odd_l = !apu_odd_l;
|
|
apu_cycle_l = (apu_odd_l ? (-2) : (-1));
|
|
if (apu_check_irq && apu_seq_mode)
|
|
{
|
|
apu_cycle_l = 3730;
|
|
apu_odd_l = true;
|
|
}
|
|
else
|
|
{
|
|
apu_do_length = true;
|
|
}
|
|
}
|
|
SQ1Clock();
|
|
SQ2Clock();
|
|
NOSClock();
|
|
if (apu_use_external_sound)
|
|
{
|
|
mem_board.OnAPUClock();
|
|
}
|
|
if (apu_reg_access_happened)
|
|
{
|
|
apu_reg_access_happened = false;
|
|
apu_reg_update_func[apu_reg_io_addr]();
|
|
}
|
|
}
|
|
TRLClock();
|
|
DMCClock();
|
|
if (apu_check_irq)
|
|
{
|
|
if (!apu_seq_mode)
|
|
{
|
|
APUCheckIRQ();
|
|
}
|
|
apu_cycle_f_t--;
|
|
if (apu_cycle_f_t == 0)
|
|
{
|
|
apu_check_irq = false;
|
|
}
|
|
}
|
|
if (apu_use_external_sound)
|
|
{
|
|
mem_board.OnAPUClockSingle();
|
|
}
|
|
apu_update_playback_func();
|
|
}
|
|
|
|
private static void APUClockDuration()
|
|
{
|
|
SQ1ClockLength();
|
|
SQ2ClockLength();
|
|
NOSClockLength();
|
|
TRLClockLength();
|
|
if (apu_use_external_sound)
|
|
{
|
|
mem_board.OnAPUClockDuration();
|
|
}
|
|
apu_do_length = false;
|
|
}
|
|
|
|
private static void APUClockEnvelope()
|
|
{
|
|
SQ1ClockEnvelope();
|
|
SQ2ClockEnvelope();
|
|
NOSClockEnvelope();
|
|
TRLClockEnvelope();
|
|
if (apu_use_external_sound)
|
|
{
|
|
mem_board.OnAPUClockEnvelope();
|
|
}
|
|
apu_do_env = false;
|
|
}
|
|
|
|
private static void APUCheckIRQ()
|
|
{
|
|
if (apu_irq_enabled)
|
|
{
|
|
apu_irq_flag = true;
|
|
}
|
|
if (apu_irq_flag)
|
|
{
|
|
IRQFlags |= 1;
|
|
}
|
|
}
|
|
|
|
private static void CalculateAudioPlaybackValues()
|
|
{
|
|
audio_timer_ratio = (double)cpu_speed / (double)MyNesMain.RendererSettings.Audio_Frequency;
|
|
audio_playback_peek_limit = MyNesMain.RendererSettings.Audio_InternalPeekLimit;
|
|
audio_samples_count = MyNesMain.RendererSettings.Audio_InternalSamplesCount;
|
|
audio_playback_amplitude = MyNesMain.RendererSettings.Audio_PlaybackAmplitude;
|
|
audio_samples = new short[audio_samples_count];
|
|
audio_w_pos = 0;
|
|
audio_samples_added = 0;
|
|
audio_timer = 0.0;
|
|
audio_x = (audio_y = 0.0);
|
|
Tracer.WriteLine("AUDIO: frequency = " + MyNesMain.RendererSettings.Audio_Frequency);
|
|
Tracer.WriteLine("AUDIO: timer ratio = " + audio_timer_ratio);
|
|
Tracer.WriteLine("AUDIO: internal samples count = " + audio_samples_count);
|
|
Tracer.WriteLine("AUDIO: amplitude = " + audio_playback_amplitude);
|
|
if (MyNesMain.RendererSettings.Audio_EnableFilters)
|
|
{
|
|
apu_update_playback_func = APUUpdatePlaybackWithFilters;
|
|
audio_low_pass_filter_14K = new SoundLowPassFilter(SoundLowPassFilter.GetK((double)cpu_speed / 14000.0, 14000.0));
|
|
audio_high_pass_filter_90 = new SoundHighPassFilter(SoundHighPassFilter.GetK((double)cpu_speed / 90.0, 90.0));
|
|
audio_high_pass_filter_440 = new SoundHighPassFilter(SoundHighPassFilter.GetK((double)cpu_speed / 440.0, 440.0));
|
|
}
|
|
else
|
|
{
|
|
apu_update_playback_func = APUUpdatePlaybackWithoutFilters;
|
|
}
|
|
InitializeDACTables(force_intitialize: false);
|
|
}
|
|
|
|
public static void InitializeDACTables(bool force_intitialize)
|
|
{
|
|
if (audio_playback_dac_initialized && !force_intitialize)
|
|
{
|
|
return;
|
|
}
|
|
int[] array = new int[5];
|
|
mix_table = new int[16][][][][];
|
|
for (int i = 0; i < 16; i++)
|
|
{
|
|
mix_table[i] = new int[16][][][];
|
|
for (int j = 0; j < 16; j++)
|
|
{
|
|
mix_table[i][j] = new int[16][][];
|
|
for (int k = 0; k < 16; k++)
|
|
{
|
|
mix_table[i][j][k] = new int[16][];
|
|
for (int l = 0; l < 16; l++)
|
|
{
|
|
mix_table[i][j][k][l] = new int[128];
|
|
for (int m = 0; m < 128; m++)
|
|
{
|
|
if (MyNesMain.RendererSettings.Audio_UseDefaultMixer)
|
|
{
|
|
double num = 95.88 / (8128.0 / (double)(i + j) + 100.0);
|
|
double num2 = 159.79 / (1.0 / ((double)k / 8227.0 + (double)l / 12241.0 + (double)m / 22638.0) + 100.0);
|
|
mix_table[i][j][k][l][m] = (int)Math.Ceiling((num + num2) * audio_playback_amplitude);
|
|
continue;
|
|
}
|
|
GetPrec(i, 255, 2048, out array[0]);
|
|
GetPrec(j, 255, 2048, out array[1]);
|
|
GetPrec(l, 255, 2048, out array[2]);
|
|
GetPrec(k, 255, 2048, out array[3]);
|
|
GetPrec(m, 255, 2048, out array[4]);
|
|
array[4] /= 2;
|
|
int num3 = array[0] + array[1] + array[2] + array[3] + array[4];
|
|
num3 /= 5;
|
|
mix_table[i][j][k][l][m] = num3;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
audio_playback_dac_initialized = true;
|
|
}
|
|
|
|
private static void APUUpdatePlaybackWithFilters()
|
|
{
|
|
if (!SoundEnabled)
|
|
{
|
|
return;
|
|
}
|
|
audio_x = mix_table[sq1_output][sq2_output][trl_output][nos_output][dmc_output];
|
|
if (apu_use_external_sound)
|
|
{
|
|
audio_x = (audio_x + mem_board.APUGetSample() * audio_playback_amplitude) / 2.0;
|
|
}
|
|
audio_high_pass_filter_90.DoFiltering(audio_x, out audio_y);
|
|
audio_high_pass_filter_440.DoFiltering(audio_y, out audio_y);
|
|
audio_low_pass_filter_14K.DoFiltering(audio_y, out audio_y);
|
|
audio_y_av += audio_y;
|
|
audio_y_timer += 1.0;
|
|
audio_timer += 1.0;
|
|
if (!(audio_timer >= audio_timer_ratio))
|
|
{
|
|
return;
|
|
}
|
|
if (audio_y_timer > 0.0)
|
|
{
|
|
audio_y = audio_y_av / audio_y_timer;
|
|
}
|
|
else
|
|
{
|
|
audio_y = 0.0;
|
|
}
|
|
audio_y_av = 0.0;
|
|
audio_y_timer = 0.0;
|
|
audio_timer -= audio_timer_ratio;
|
|
if (audio_w_pos < audio_samples_count)
|
|
{
|
|
if (audio_y > (double)audio_playback_peek_limit)
|
|
{
|
|
audio_y = audio_playback_peek_limit;
|
|
}
|
|
if (audio_y < (double)(-audio_playback_peek_limit))
|
|
{
|
|
audio_y = -audio_playback_peek_limit;
|
|
}
|
|
audio_samples[audio_w_pos] = (short)audio_y;
|
|
if (MyNesMain.WaveRecorder.IsRecording)
|
|
{
|
|
MyNesMain.WaveRecorder.AddSample((short)audio_y);
|
|
}
|
|
audio_w_pos++;
|
|
audio_samples_added++;
|
|
}
|
|
audio_y = 0.0;
|
|
}
|
|
|
|
private static void APUUpdatePlaybackWithoutFilters()
|
|
{
|
|
if (!SoundEnabled)
|
|
{
|
|
return;
|
|
}
|
|
audio_y = mix_table[sq1_output][sq2_output][trl_output][nos_output][dmc_output] / 2;
|
|
if (apu_use_external_sound)
|
|
{
|
|
audio_y = (audio_y + mem_board.APUGetSample() * audio_playback_amplitude) / 2.0;
|
|
}
|
|
audio_y_av += audio_y;
|
|
audio_y_timer += 1.0;
|
|
audio_timer += 1.0;
|
|
if (!(audio_timer >= audio_timer_ratio))
|
|
{
|
|
return;
|
|
}
|
|
if (audio_y_timer > 0.0)
|
|
{
|
|
audio_y = audio_y_av / audio_y_timer;
|
|
}
|
|
else
|
|
{
|
|
audio_y = 0.0;
|
|
}
|
|
audio_y_av = 0.0;
|
|
audio_y_timer = 0.0;
|
|
audio_timer -= audio_timer_ratio;
|
|
if (audio_w_pos < audio_samples_count)
|
|
{
|
|
if (audio_y > (double)audio_playback_peek_limit)
|
|
{
|
|
audio_y = audio_playback_peek_limit;
|
|
}
|
|
if (audio_y < (double)(-audio_playback_peek_limit))
|
|
{
|
|
audio_y = -audio_playback_peek_limit;
|
|
}
|
|
audio_samples[audio_w_pos] = (short)audio_y;
|
|
if (MyNesMain.WaveRecorder.IsRecording)
|
|
{
|
|
MyNesMain.WaveRecorder.AddSample((short)audio_y);
|
|
}
|
|
audio_w_pos++;
|
|
audio_samples_added++;
|
|
}
|
|
audio_y = 0.0;
|
|
}
|
|
|
|
private static void GetPrec(int inVal, int inMax, int outMax, out int val)
|
|
{
|
|
val = outMax * inVal / inMax;
|
|
}
|
|
|
|
private static void APUWriteState(ref BinaryWriter bin)
|
|
{
|
|
bin.Write(apu_reg_io_db);
|
|
bin.Write(apu_reg_io_addr);
|
|
bin.Write(apu_reg_access_happened);
|
|
bin.Write(apu_reg_access_w);
|
|
bin.Write(apu_odd_cycle);
|
|
bin.Write(apu_irq_enabled);
|
|
bin.Write(apu_irq_flag);
|
|
bin.Write(apu_irq_delta_occur);
|
|
bin.Write(apu_seq_mode);
|
|
bin.Write(apu_ferq_f);
|
|
bin.Write(apu_ferq_l);
|
|
bin.Write(apu_ferq_e);
|
|
bin.Write(apu_cycle_f);
|
|
bin.Write(apu_cycle_e);
|
|
bin.Write(apu_cycle_l);
|
|
bin.Write(apu_odd_l);
|
|
bin.Write(apu_cycle_f_t);
|
|
bin.Write(apu_check_irq);
|
|
bin.Write(apu_do_env);
|
|
bin.Write(apu_do_length);
|
|
SQ1WriteState(ref bin);
|
|
SQ2WriteState(ref bin);
|
|
NOSWriteState(ref bin);
|
|
TRLWriteState(ref bin);
|
|
DMCWriteState(ref bin);
|
|
}
|
|
|
|
private static void APUReadState(ref BinaryReader bin)
|
|
{
|
|
apu_reg_io_db = bin.ReadByte();
|
|
apu_reg_io_addr = bin.ReadByte();
|
|
apu_reg_access_happened = bin.ReadBoolean();
|
|
apu_reg_access_w = bin.ReadBoolean();
|
|
apu_odd_cycle = bin.ReadBoolean();
|
|
apu_irq_enabled = bin.ReadBoolean();
|
|
apu_irq_flag = bin.ReadBoolean();
|
|
apu_irq_delta_occur = bin.ReadBoolean();
|
|
apu_seq_mode = bin.ReadBoolean();
|
|
apu_ferq_f = bin.ReadInt32();
|
|
apu_ferq_l = bin.ReadInt32();
|
|
apu_ferq_e = bin.ReadInt32();
|
|
apu_cycle_f = bin.ReadInt32();
|
|
apu_cycle_e = bin.ReadInt32();
|
|
apu_cycle_l = bin.ReadInt32();
|
|
apu_odd_l = bin.ReadBoolean();
|
|
apu_cycle_f_t = bin.ReadInt32();
|
|
apu_check_irq = bin.ReadBoolean();
|
|
apu_do_env = bin.ReadBoolean();
|
|
apu_do_length = bin.ReadBoolean();
|
|
SQ1ReadState(ref bin);
|
|
SQ2ReadState(ref bin);
|
|
NOSReadState(ref bin);
|
|
TRLReadState(ref bin);
|
|
DMCReadState(ref bin);
|
|
}
|
|
|
|
private static byte register_pb()
|
|
{
|
|
return (byte)((cpu_flag_n ? 128u : 0u) | (cpu_flag_v ? 64u : 0u) | (cpu_flag_d ? 8u : 0u) | (cpu_flag_i ? 4u : 0u) | (cpu_flag_z ? 2u : 0u) | (cpu_flag_c ? 1u : 0u) | 0x30u);
|
|
}
|
|
|
|
private static void CPUInitialize()
|
|
{
|
|
cpu_addressings = new Action[256]
|
|
{
|
|
Imp____, IndX_R_, ImA____, IndX_W_, Zpg_R__, Zpg_R__, Zpg_RW_, Zpg_W__, ImA____, Imm____,
|
|
ImA____, Imm____, Abs_R__, Abs_R__, Abs_RW_, Abs_W__, Imp____, IndY_R_, Imp____, IndY_W_,
|
|
ZpgX_R_, ZpgX_R_, ZpgX_RW, ZpgX_W_, ImA____, AbsY_R_, ImA____, AbsY_W_, AbsX_R_, AbsX_R_,
|
|
AbsX_RW, AbsX_W_, Imp____, IndX_R_, ImA____, IndX_W_, Zpg_R__, Zpg_R__, Zpg_RW_, Zpg_W__,
|
|
ImA____, Imm____, ImA____, Imm____, Abs_R__, Abs_R__, Abs_RW_, Abs_W__, Imp____, IndY_R_,
|
|
Imp____, IndY_W_, ZpgX_R_, ZpgX_R_, ZpgX_RW, ZpgX_W_, ImA____, AbsY_R_, ImA____, AbsY_W_,
|
|
AbsX_R_, AbsX_R_, AbsX_RW, AbsX_W_, ImA____, IndX_R_, ImA____, IndX_W_, Zpg_R__, Zpg_R__,
|
|
Zpg_RW_, Zpg_W__, ImA____, Imm____, ImA____, Imm____, Abs_W__, Abs_R__, Abs_RW_, Abs_W__,
|
|
Imp____, IndY_R_, Imp____, IndY_W_, ZpgX_R_, ZpgX_R_, ZpgX_RW, ZpgX_W_, ImA____, AbsY_R_,
|
|
ImA____, AbsY_W_, AbsX_R_, AbsX_R_, AbsX_RW, AbsX_W_, ImA____, IndX_R_, ImA____, IndX_W_,
|
|
Zpg_R__, Zpg_R__, Zpg_RW_, Zpg_W__, ImA____, Imm____, ImA____, Imm____, Imp____, Abs_R__,
|
|
Abs_RW_, Abs_W__, Imp____, IndY_R_, Imp____, IndY_W_, ZpgX_R_, ZpgX_R_, ZpgX_RW, ZpgX_W_,
|
|
ImA____, AbsY_R_, ImA____, AbsY_W_, AbsX_R_, AbsX_R_, AbsX_RW, AbsX_W_, Imm____, IndX_W_,
|
|
Imm____, IndX_W_, Zpg_W__, Zpg_W__, Zpg_W__, Zpg_W__, ImA____, Imm____, ImA____, Imm____,
|
|
Abs_W__, Abs_W__, Abs_W__, Abs_W__, Imp____, IndY_W_, Imp____, IndY_W_, ZpgX_W_, ZpgX_W_,
|
|
ZpgY_W_, ZpgY_W_, ImA____, AbsY_W_, ImA____, AbsY_W_, Abs_W__, AbsX_W_, Abs_W__, AbsY_W_,
|
|
Imm____, IndX_R_, Imm____, IndX_R_, Zpg_R__, Zpg_R__, Zpg_R__, Zpg_R__, ImA____, Imm____,
|
|
ImA____, Imm____, Abs_R__, Abs_R__, Abs_R__, Abs_R__, Imp____, IndY_R_, Imp____, IndY_R_,
|
|
ZpgX_R_, ZpgX_R_, ZpgY_R_, ZpgY_R_, ImA____, AbsY_R_, ImA____, AbsY_R_, AbsX_R_, AbsX_R_,
|
|
AbsY_R_, AbsY_R_, Imm____, IndX_R_, Imm____, IndX_R_, Zpg_R__, Zpg_R__, Zpg_RW_, Zpg_R__,
|
|
ImA____, Imm____, ImA____, Imm____, Abs_R__, Abs_R__, Abs_RW_, Abs_R__, Imp____, IndY_R_,
|
|
Imp____, IndY_RW, ZpgX_R_, ZpgX_R_, ZpgX_RW, ZpgX_RW, ImA____, AbsY_R_, ImA____, AbsY_RW,
|
|
AbsX_R_, AbsX_R_, AbsX_RW, AbsX_RW, Imm____, IndX_R_, Imm____, IndX_W_, Zpg_R__, Zpg_R__,
|
|
Zpg_RW_, Zpg_W__, ImA____, Imm____, ImA____, Imm____, Abs_R__, Abs_R__, Abs_RW_, Abs_W__,
|
|
Imp____, IndY_R_, Imp____, IndY_W_, ZpgX_R_, ZpgX_R_, ZpgX_RW, ZpgX_W_, ImA____, AbsY_R_,
|
|
ImA____, AbsY_W_, AbsX_R_, AbsX_R_, AbsX_RW, AbsX_W_
|
|
};
|
|
cpu_instructions = new Action[256]
|
|
{
|
|
BRK__, ORA__, NOP__, SLO__, NOP__, ORA__, ASL_M, SLO__, PHP__, ORA__,
|
|
ASL_A, ANC__, NOP__, ORA__, ASL_M, SLO__, BPL__, ORA__, NOP__, SLO__,
|
|
NOP__, ORA__, ASL_M, SLO__, CLC__, ORA__, NOP__, SLO__, NOP__, ORA__,
|
|
ASL_M, SLO__, JSR__, AND__, NOP__, RLA__, BIT__, AND__, ROL_M, RLA__,
|
|
PLP__, AND__, ROL_A, ANC__, BIT__, AND__, ROL_M, RLA__, BMI__, AND__,
|
|
NOP__, RLA__, NOP__, AND__, ROL_M, RLA__, SEC__, AND__, NOP__, RLA__,
|
|
NOP__, AND__, ROL_M, RLA__, RTI__, EOR__, NOP__, SRE__, NOP__, EOR__,
|
|
LSR_M, SRE__, PHA__, EOR__, LSR_A, ALR__, JMP__, EOR__, LSR_M, SRE__,
|
|
BVC__, EOR__, NOP__, SRE__, NOP__, EOR__, LSR_M, SRE__, CLI__, EOR__,
|
|
NOP__, SRE__, NOP__, EOR__, LSR_M, SRE__, RTS__, ADC__, NOP__, RRA__,
|
|
NOP__, ADC__, ROR_M, RRA__, PLA__, ADC__, ROR_A, ARR__, JMP_I, ADC__,
|
|
ROR_M, RRA__, BVS__, ADC__, NOP__, RRA__, NOP__, ADC__, ROR_M, RRA__,
|
|
SEI__, ADC__, NOP__, RRA__, NOP__, ADC__, ROR_M, RRA__, NOP__, STA__,
|
|
NOP__, SAX__, STY__, STA__, STX__, SAX__, DEY__, NOP__, TXA__, XAA__,
|
|
STY__, STA__, STX__, SAX__, BCC__, STA__, NOP__, AHX__, STY__, STA__,
|
|
STX__, SAX__, TYA__, STA__, TXS__, XAS__, SHY__, STA__, SHX__, AHX__,
|
|
LDY__, LDA__, LDX__, LAX__, LDY__, LDA__, LDX__, LAX__, TAY__, LDA__,
|
|
TAX__, LAX__, LDY__, LDA__, LDX__, LAX__, BCS__, LDA__, NOP__, LAX__,
|
|
LDY__, LDA__, LDX__, LAX__, CLV__, LDA__, TSX__, LAR__, LDY__, LDA__,
|
|
LDX__, LAX__, CPY__, CMP__, NOP__, DCP__, CPY__, CMP__, DEC__, DCP__,
|
|
INY__, CMP__, DEX__, AXS__, CPY__, CMP__, DEC__, DCP__, BNE__, CMP__,
|
|
NOP__, DCP__, NOP__, CMP__, DEC__, DCP__, CLD__, CMP__, NOP__, DCP__,
|
|
NOP__, CMP__, DEC__, DCP__, CPX__, SBC__, NOP__, ISC__, CPX__, SBC__,
|
|
INC__, ISC__, INX__, SBC__, NOP__, SBC__, CPX__, SBC__, INC__, ISC__,
|
|
BEQ__, SBC__, NOP__, ISC__, NOP__, SBC__, INC__, ISC__, SED__, SBC__,
|
|
NOP__, ISC__, NOP__, SBC__, INC__, ISC__
|
|
};
|
|
}
|
|
|
|
private static void CPUClock()
|
|
{
|
|
Read(ref cpu_reg_pc.v, out cpu_opcode);
|
|
cpu_reg_pc.v++;
|
|
cpu_addressings[cpu_opcode]();
|
|
cpu_instructions[cpu_opcode]();
|
|
if (CPU_IRQ_PIN || CPU_NMI_PIN)
|
|
{
|
|
Read(ref cpu_reg_pc.v, out cpu_dummy);
|
|
Read(ref cpu_reg_pc.v, out cpu_dummy);
|
|
Interrupt();
|
|
}
|
|
}
|
|
|
|
private static void CPUHardReset()
|
|
{
|
|
cpu_reg_a = 0;
|
|
cpu_reg_x = 0;
|
|
cpu_reg_y = 0;
|
|
cpu_reg_sp.l = 253;
|
|
cpu_reg_sp.h = 1;
|
|
ushort addr = 65532;
|
|
mem_board.ReadPRG(ref addr, out cpu_reg_pc.l);
|
|
addr++;
|
|
mem_board.ReadPRG(ref addr, out cpu_reg_pc.h);
|
|
register_p = 0;
|
|
cpu_flag_i = true;
|
|
cpu_reg_ea.v = 0;
|
|
cpu_opcode = 0;
|
|
CPU_IRQ_PIN = false;
|
|
CPU_NMI_PIN = false;
|
|
cpu_suspend_nmi = false;
|
|
cpu_suspend_irq = false;
|
|
IRQFlags = 0;
|
|
}
|
|
|
|
private static void CPUSoftReset()
|
|
{
|
|
cpu_flag_i = true;
|
|
cpu_reg_sp.v -= 3;
|
|
ushort addr = 65532;
|
|
Read(ref addr, out cpu_reg_pc.l);
|
|
addr++;
|
|
Read(ref addr, out cpu_reg_pc.h);
|
|
}
|
|
|
|
private static void Imp____()
|
|
{
|
|
}
|
|
|
|
private static void IndX_R_()
|
|
{
|
|
temp_add.h = 0;
|
|
Read(ref cpu_reg_pc.v, out temp_add.l);
|
|
cpu_reg_pc.v++;
|
|
Read(ref temp_add.v, out cpu_dummy);
|
|
temp_add.l += cpu_reg_x;
|
|
Read(ref temp_add.v, out cpu_reg_ea.l);
|
|
temp_add.l++;
|
|
Read(ref temp_add.v, out cpu_reg_ea.h);
|
|
Read(ref cpu_reg_ea.v, out cpu_m);
|
|
}
|
|
|
|
private static void IndX_W_()
|
|
{
|
|
temp_add.h = 0;
|
|
Read(ref cpu_reg_pc.v, out temp_add.l);
|
|
cpu_reg_pc.v++;
|
|
Read(ref temp_add.v, out cpu_dummy);
|
|
temp_add.l += cpu_reg_x;
|
|
Read(ref temp_add.v, out cpu_reg_ea.l);
|
|
temp_add.l++;
|
|
Read(ref temp_add.v, out cpu_reg_ea.h);
|
|
}
|
|
|
|
private static void IndX_RW()
|
|
{
|
|
temp_add.h = 0;
|
|
Read(ref cpu_reg_pc.v, out temp_add.l);
|
|
cpu_reg_pc.v++;
|
|
Read(ref temp_add.v, out cpu_dummy);
|
|
temp_add.l += cpu_reg_x;
|
|
Read(ref temp_add.v, out cpu_reg_ea.l);
|
|
temp_add.l++;
|
|
Read(ref temp_add.v, out cpu_reg_ea.h);
|
|
Read(ref cpu_reg_ea.v, out cpu_m);
|
|
}
|
|
|
|
private static void IndY_R_()
|
|
{
|
|
temp_add.h = 0;
|
|
Read(ref cpu_reg_pc.v, out temp_add.l);
|
|
cpu_reg_pc.v++;
|
|
Read(ref temp_add.v, out cpu_reg_ea.l);
|
|
temp_add.l++;
|
|
Read(ref temp_add.v, out cpu_reg_ea.h);
|
|
cpu_reg_ea.l += cpu_reg_y;
|
|
Read(ref cpu_reg_ea.v, out cpu_m);
|
|
if (cpu_reg_ea.l < cpu_reg_y)
|
|
{
|
|
cpu_reg_ea.h++;
|
|
Read(ref cpu_reg_ea.v, out cpu_m);
|
|
}
|
|
}
|
|
|
|
private static void IndY_W_()
|
|
{
|
|
temp_add.h = 0;
|
|
Read(ref cpu_reg_pc.v, out temp_add.l);
|
|
cpu_reg_pc.v++;
|
|
Read(ref temp_add.v, out cpu_reg_ea.l);
|
|
temp_add.l++;
|
|
Read(ref temp_add.v, out cpu_reg_ea.h);
|
|
cpu_reg_ea.l += cpu_reg_y;
|
|
Read(ref cpu_reg_ea.v, out cpu_m);
|
|
if (cpu_reg_ea.l < cpu_reg_y)
|
|
{
|
|
cpu_reg_ea.h++;
|
|
}
|
|
}
|
|
|
|
private static void IndY_RW()
|
|
{
|
|
temp_add.h = 0;
|
|
Read(ref cpu_reg_pc.v, out temp_add.l);
|
|
cpu_reg_pc.v++;
|
|
Read(ref temp_add.v, out cpu_reg_ea.l);
|
|
temp_add.l++;
|
|
Read(ref temp_add.v, out cpu_reg_ea.h);
|
|
cpu_reg_ea.l += cpu_reg_y;
|
|
Read(ref cpu_reg_ea.v, out cpu_dummy);
|
|
if (cpu_reg_ea.l < cpu_reg_y)
|
|
{
|
|
cpu_reg_ea.h++;
|
|
}
|
|
Read(ref cpu_reg_ea.v, out cpu_m);
|
|
}
|
|
|
|
private static void Zpg_R__()
|
|
{
|
|
cpu_reg_ea.h = 0;
|
|
Read(ref cpu_reg_pc.v, out cpu_reg_ea.l);
|
|
cpu_reg_pc.v++;
|
|
Read(ref cpu_reg_ea.v, out cpu_m);
|
|
}
|
|
|
|
private static void Zpg_W__()
|
|
{
|
|
cpu_reg_ea.h = 0;
|
|
Read(ref cpu_reg_pc.v, out cpu_reg_ea.l);
|
|
cpu_reg_pc.v++;
|
|
}
|
|
|
|
private static void Zpg_RW_()
|
|
{
|
|
cpu_reg_ea.h = 0;
|
|
Read(ref cpu_reg_pc.v, out cpu_reg_ea.l);
|
|
cpu_reg_pc.v++;
|
|
Read(ref cpu_reg_ea.v, out cpu_m);
|
|
}
|
|
|
|
private static void ZpgX_R_()
|
|
{
|
|
cpu_reg_ea.h = 0;
|
|
Read(ref cpu_reg_pc.v, out cpu_reg_ea.l);
|
|
cpu_reg_pc.v++;
|
|
Read(ref cpu_reg_ea.v, out cpu_dummy);
|
|
cpu_reg_ea.l += cpu_reg_x;
|
|
Read(ref cpu_reg_ea.v, out cpu_m);
|
|
}
|
|
|
|
private static void ZpgX_W_()
|
|
{
|
|
cpu_reg_ea.h = 0;
|
|
Read(ref cpu_reg_pc.v, out cpu_reg_ea.l);
|
|
cpu_reg_pc.v++;
|
|
Read(ref cpu_reg_ea.v, out cpu_dummy);
|
|
cpu_reg_ea.l += cpu_reg_x;
|
|
}
|
|
|
|
private static void ZpgX_RW()
|
|
{
|
|
cpu_reg_ea.h = 0;
|
|
Read(ref cpu_reg_pc.v, out cpu_reg_ea.l);
|
|
cpu_reg_pc.v++;
|
|
Read(ref cpu_reg_ea.v, out cpu_dummy);
|
|
cpu_reg_ea.l += cpu_reg_x;
|
|
Read(ref cpu_reg_ea.v, out cpu_m);
|
|
}
|
|
|
|
private static void ZpgY_R_()
|
|
{
|
|
cpu_reg_ea.h = 0;
|
|
Read(ref cpu_reg_pc.v, out cpu_reg_ea.l);
|
|
cpu_reg_pc.v++;
|
|
Read(ref cpu_reg_ea.v, out cpu_dummy);
|
|
cpu_reg_ea.l += cpu_reg_y;
|
|
Read(ref cpu_reg_ea.v, out cpu_m);
|
|
}
|
|
|
|
private static void ZpgY_W_()
|
|
{
|
|
cpu_reg_ea.h = 0;
|
|
Read(ref cpu_reg_pc.v, out cpu_reg_ea.l);
|
|
cpu_reg_pc.v++;
|
|
Read(ref cpu_reg_ea.v, out cpu_dummy);
|
|
cpu_reg_ea.l += cpu_reg_y;
|
|
}
|
|
|
|
private static void ZpgY_RW()
|
|
{
|
|
cpu_reg_ea.h = 0;
|
|
Read(ref cpu_reg_pc.v, out cpu_reg_ea.l);
|
|
cpu_reg_pc.v++;
|
|
Read(ref cpu_reg_ea.v, out cpu_dummy);
|
|
cpu_reg_ea.l += cpu_reg_y;
|
|
Read(ref cpu_reg_ea.v, out cpu_m);
|
|
}
|
|
|
|
private static void Imm____()
|
|
{
|
|
Read(ref cpu_reg_pc.v, out cpu_m);
|
|
cpu_reg_pc.v++;
|
|
}
|
|
|
|
private static void ImA____()
|
|
{
|
|
Read(ref cpu_reg_pc.v, out cpu_dummy);
|
|
}
|
|
|
|
private static void Abs_R__()
|
|
{
|
|
Read(ref cpu_reg_pc.v, out cpu_reg_ea.l);
|
|
cpu_reg_pc.v++;
|
|
Read(ref cpu_reg_pc.v, out cpu_reg_ea.h);
|
|
cpu_reg_pc.v++;
|
|
Read(ref cpu_reg_ea.v, out cpu_m);
|
|
}
|
|
|
|
private static void Abs_W__()
|
|
{
|
|
Read(ref cpu_reg_pc.v, out cpu_reg_ea.l);
|
|
cpu_reg_pc.v++;
|
|
Read(ref cpu_reg_pc.v, out cpu_reg_ea.h);
|
|
cpu_reg_pc.v++;
|
|
}
|
|
|
|
private static void Abs_RW_()
|
|
{
|
|
Read(ref cpu_reg_pc.v, out cpu_reg_ea.l);
|
|
cpu_reg_pc.v++;
|
|
Read(ref cpu_reg_pc.v, out cpu_reg_ea.h);
|
|
cpu_reg_pc.v++;
|
|
Read(ref cpu_reg_ea.v, out cpu_m);
|
|
}
|
|
|
|
private static void AbsX_R_()
|
|
{
|
|
Read(ref cpu_reg_pc.v, out cpu_reg_ea.l);
|
|
cpu_reg_pc.v++;
|
|
Read(ref cpu_reg_pc.v, out cpu_reg_ea.h);
|
|
cpu_reg_pc.v++;
|
|
cpu_reg_ea.l += cpu_reg_x;
|
|
Read(ref cpu_reg_ea.v, out cpu_m);
|
|
if (cpu_reg_ea.l < cpu_reg_x)
|
|
{
|
|
cpu_reg_ea.h++;
|
|
Read(ref cpu_reg_ea.v, out cpu_m);
|
|
}
|
|
}
|
|
|
|
private static void AbsX_W_()
|
|
{
|
|
Read(ref cpu_reg_pc.v, out cpu_reg_ea.l);
|
|
cpu_reg_pc.v++;
|
|
Read(ref cpu_reg_pc.v, out cpu_reg_ea.h);
|
|
cpu_reg_pc.v++;
|
|
cpu_reg_ea.l += cpu_reg_x;
|
|
Read(ref cpu_reg_ea.v, out cpu_m);
|
|
if (cpu_reg_ea.l < cpu_reg_x)
|
|
{
|
|
cpu_reg_ea.h++;
|
|
}
|
|
}
|
|
|
|
private static void AbsX_RW()
|
|
{
|
|
Read(ref cpu_reg_pc.v, out cpu_reg_ea.l);
|
|
cpu_reg_pc.v++;
|
|
Read(ref cpu_reg_pc.v, out cpu_reg_ea.h);
|
|
cpu_reg_pc.v++;
|
|
cpu_reg_ea.l += cpu_reg_x;
|
|
Read(ref cpu_reg_ea.v, out cpu_dummy);
|
|
if (cpu_reg_ea.l < cpu_reg_x)
|
|
{
|
|
cpu_reg_ea.h++;
|
|
}
|
|
Read(ref cpu_reg_ea.v, out cpu_m);
|
|
}
|
|
|
|
private static void AbsY_R_()
|
|
{
|
|
Read(ref cpu_reg_pc.v, out cpu_reg_ea.l);
|
|
cpu_reg_pc.v++;
|
|
Read(ref cpu_reg_pc.v, out cpu_reg_ea.h);
|
|
cpu_reg_pc.v++;
|
|
cpu_reg_ea.l += cpu_reg_y;
|
|
Read(ref cpu_reg_ea.v, out cpu_m);
|
|
if (cpu_reg_ea.l < cpu_reg_y)
|
|
{
|
|
cpu_reg_ea.h++;
|
|
Read(ref cpu_reg_ea.v, out cpu_m);
|
|
}
|
|
}
|
|
|
|
private static void AbsY_W_()
|
|
{
|
|
Read(ref cpu_reg_pc.v, out cpu_reg_ea.l);
|
|
cpu_reg_pc.v++;
|
|
Read(ref cpu_reg_pc.v, out cpu_reg_ea.h);
|
|
cpu_reg_pc.v++;
|
|
cpu_reg_ea.l += cpu_reg_y;
|
|
Read(ref cpu_reg_ea.v, out cpu_m);
|
|
if (cpu_reg_ea.l < cpu_reg_y)
|
|
{
|
|
cpu_reg_ea.h++;
|
|
}
|
|
}
|
|
|
|
private static void AbsY_RW()
|
|
{
|
|
Read(ref cpu_reg_pc.v, out cpu_reg_ea.l);
|
|
cpu_reg_pc.v++;
|
|
Read(ref cpu_reg_pc.v, out cpu_reg_ea.h);
|
|
cpu_reg_pc.v++;
|
|
cpu_reg_ea.l += cpu_reg_y;
|
|
Read(ref cpu_reg_ea.v, out cpu_m);
|
|
if (cpu_reg_ea.l < cpu_reg_y)
|
|
{
|
|
cpu_reg_ea.h++;
|
|
}
|
|
Read(ref cpu_reg_ea.v, out cpu_m);
|
|
}
|
|
|
|
private static void Interrupt()
|
|
{
|
|
Push(ref cpu_reg_pc.h);
|
|
Push(ref cpu_reg_pc.l);
|
|
cpu_dummy = ((cpu_opcode == 0) ? register_pb() : register_p);
|
|
Push(ref cpu_dummy);
|
|
temp_add.v = InterruptVector;
|
|
cpu_suspend_nmi = true;
|
|
cpu_flag_i = true;
|
|
CPU_NMI_PIN = false;
|
|
Read(ref temp_add.v, out cpu_reg_pc.l);
|
|
temp_add.v++;
|
|
Read(ref temp_add.v, out cpu_reg_pc.h);
|
|
cpu_suspend_nmi = false;
|
|
}
|
|
|
|
private static void Branch(ref bool condition)
|
|
{
|
|
Read(ref cpu_reg_pc.v, out cpu_byte_temp);
|
|
cpu_reg_pc.v++;
|
|
if (!condition)
|
|
{
|
|
return;
|
|
}
|
|
cpu_suspend_irq = true;
|
|
Read(ref cpu_reg_pc.v, out cpu_dummy);
|
|
cpu_reg_pc.l += cpu_byte_temp;
|
|
cpu_suspend_irq = false;
|
|
if (cpu_byte_temp >= 128)
|
|
{
|
|
if (cpu_reg_pc.l >= cpu_byte_temp)
|
|
{
|
|
Read(ref cpu_reg_pc.v, out cpu_dummy);
|
|
cpu_reg_pc.h--;
|
|
}
|
|
}
|
|
else if (cpu_reg_pc.l < cpu_byte_temp)
|
|
{
|
|
Read(ref cpu_reg_pc.v, out cpu_dummy);
|
|
cpu_reg_pc.h++;
|
|
}
|
|
}
|
|
|
|
private static void Push(ref byte val)
|
|
{
|
|
Write(ref cpu_reg_sp.v, ref val);
|
|
cpu_reg_sp.l--;
|
|
}
|
|
|
|
private static void Pull(out byte val)
|
|
{
|
|
cpu_reg_sp.l++;
|
|
Read(ref cpu_reg_sp.v, out val);
|
|
}
|
|
|
|
private static void ADC__()
|
|
{
|
|
cpu_int_temp = cpu_reg_a + cpu_m + (cpu_flag_c ? 1 : 0);
|
|
cpu_flag_v = ((cpu_int_temp ^ cpu_reg_a) & (cpu_int_temp ^ cpu_m) & 0x80) != 0;
|
|
cpu_flag_n = (cpu_int_temp & 0x80) != 0;
|
|
cpu_flag_z = (cpu_int_temp & 0xFF) == 0;
|
|
cpu_flag_c = cpu_int_temp >> 8 != 0;
|
|
cpu_reg_a = (byte)((uint)cpu_int_temp & 0xFFu);
|
|
}
|
|
|
|
private static void AHX__()
|
|
{
|
|
cpu_byte_temp = (byte)((uint)(cpu_reg_a & cpu_reg_x) & 7u);
|
|
Write(ref cpu_reg_ea.v, ref cpu_byte_temp);
|
|
}
|
|
|
|
private static void ALR__()
|
|
{
|
|
cpu_reg_a &= cpu_m;
|
|
cpu_flag_c = (cpu_reg_a & 1) != 0;
|
|
cpu_reg_a >>= 1;
|
|
cpu_flag_n = (cpu_reg_a & 0x80) != 0;
|
|
cpu_flag_z = cpu_reg_a == 0;
|
|
}
|
|
|
|
private static void ANC__()
|
|
{
|
|
cpu_reg_a &= cpu_m;
|
|
cpu_flag_n = (cpu_reg_a & 0x80) != 0;
|
|
cpu_flag_z = cpu_reg_a == 0;
|
|
cpu_flag_c = (cpu_reg_a & 0x80) != 0;
|
|
}
|
|
|
|
private static void AND__()
|
|
{
|
|
cpu_reg_a &= cpu_m;
|
|
cpu_flag_n = (cpu_reg_a & 0x80) == 128;
|
|
cpu_flag_z = cpu_reg_a == 0;
|
|
}
|
|
|
|
private static void ARR__()
|
|
{
|
|
cpu_reg_a = (byte)((uint)((cpu_m & cpu_reg_a) >> 1) | (cpu_flag_c ? 128u : 0u));
|
|
cpu_flag_z = (cpu_reg_a & 0xFF) == 0;
|
|
cpu_flag_n = (cpu_reg_a & 0x80) != 0;
|
|
cpu_flag_c = (cpu_reg_a & 0x40) != 0;
|
|
cpu_flag_v = (((cpu_reg_a << 1) ^ cpu_reg_a) & 0x40) != 0;
|
|
}
|
|
|
|
private static void AXS__()
|
|
{
|
|
cpu_int_temp = (cpu_reg_a & cpu_reg_x) - cpu_m;
|
|
cpu_flag_n = (cpu_int_temp & 0x80) != 0;
|
|
cpu_flag_z = (cpu_int_temp & 0xFF) == 0;
|
|
cpu_flag_c = ~cpu_int_temp >> 8 != 0;
|
|
cpu_reg_x = (byte)((uint)cpu_int_temp & 0xFFu);
|
|
}
|
|
|
|
private static void ASL_M()
|
|
{
|
|
cpu_flag_c = (cpu_m & 0x80) == 128;
|
|
Write(ref cpu_reg_ea.v, ref cpu_m);
|
|
cpu_m = (byte)((uint)(cpu_m << 1) & 0xFEu);
|
|
Write(ref cpu_reg_ea.v, ref cpu_m);
|
|
cpu_flag_n = (cpu_m & 0x80) == 128;
|
|
cpu_flag_z = cpu_m == 0;
|
|
}
|
|
|
|
private static void ASL_A()
|
|
{
|
|
cpu_flag_c = (cpu_reg_a & 0x80) == 128;
|
|
cpu_reg_a = (byte)((uint)(cpu_reg_a << 1) & 0xFEu);
|
|
cpu_flag_n = (cpu_reg_a & 0x80) == 128;
|
|
cpu_flag_z = cpu_reg_a == 0;
|
|
}
|
|
|
|
private static void BCC__()
|
|
{
|
|
cpu_bool_tmp = !cpu_flag_c;
|
|
Branch(ref cpu_bool_tmp);
|
|
}
|
|
|
|
private static void BCS__()
|
|
{
|
|
Branch(ref cpu_flag_c);
|
|
}
|
|
|
|
private static void BEQ__()
|
|
{
|
|
Branch(ref cpu_flag_z);
|
|
}
|
|
|
|
private static void BIT__()
|
|
{
|
|
cpu_flag_n = (cpu_m & 0x80) != 0;
|
|
cpu_flag_v = (cpu_m & 0x40) != 0;
|
|
cpu_flag_z = (cpu_m & cpu_reg_a) == 0;
|
|
}
|
|
|
|
private static void BRK__()
|
|
{
|
|
Read(ref cpu_reg_pc.v, out cpu_dummy);
|
|
cpu_reg_pc.v++;
|
|
Interrupt();
|
|
}
|
|
|
|
private static void BPL__()
|
|
{
|
|
cpu_bool_tmp = !cpu_flag_n;
|
|
Branch(ref cpu_bool_tmp);
|
|
}
|
|
|
|
private static void BNE__()
|
|
{
|
|
cpu_bool_tmp = !cpu_flag_z;
|
|
Branch(ref cpu_bool_tmp);
|
|
}
|
|
|
|
private static void BMI__()
|
|
{
|
|
Branch(ref cpu_flag_n);
|
|
}
|
|
|
|
private static void BVC__()
|
|
{
|
|
cpu_bool_tmp = !cpu_flag_v;
|
|
Branch(ref cpu_bool_tmp);
|
|
}
|
|
|
|
private static void BVS__()
|
|
{
|
|
Branch(ref cpu_flag_v);
|
|
}
|
|
|
|
private static void SED__()
|
|
{
|
|
cpu_flag_d = true;
|
|
}
|
|
|
|
private static void CLC__()
|
|
{
|
|
cpu_flag_c = false;
|
|
}
|
|
|
|
private static void CLD__()
|
|
{
|
|
cpu_flag_d = false;
|
|
}
|
|
|
|
private static void CLV__()
|
|
{
|
|
cpu_flag_v = false;
|
|
}
|
|
|
|
private static void CMP__()
|
|
{
|
|
cpu_int_temp = cpu_reg_a - cpu_m;
|
|
cpu_flag_n = (cpu_int_temp & 0x80) == 128;
|
|
cpu_flag_c = cpu_reg_a >= cpu_m;
|
|
cpu_flag_z = cpu_int_temp == 0;
|
|
}
|
|
|
|
private static void CPX__()
|
|
{
|
|
cpu_int_temp = cpu_reg_x - cpu_m;
|
|
cpu_flag_n = (cpu_int_temp & 0x80) == 128;
|
|
cpu_flag_c = cpu_reg_x >= cpu_m;
|
|
cpu_flag_z = cpu_int_temp == 0;
|
|
}
|
|
|
|
private static void CPY__()
|
|
{
|
|
cpu_int_temp = cpu_reg_y - cpu_m;
|
|
cpu_flag_n = (cpu_int_temp & 0x80) == 128;
|
|
cpu_flag_c = cpu_reg_y >= cpu_m;
|
|
cpu_flag_z = cpu_int_temp == 0;
|
|
}
|
|
|
|
private static void CLI__()
|
|
{
|
|
cpu_flag_i = false;
|
|
}
|
|
|
|
private static void DCP__()
|
|
{
|
|
Write(ref cpu_reg_ea.v, ref cpu_m);
|
|
cpu_m--;
|
|
Write(ref cpu_reg_ea.v, ref cpu_m);
|
|
cpu_int_temp = cpu_reg_a - cpu_m;
|
|
cpu_flag_n = (cpu_int_temp & 0x80) != 0;
|
|
cpu_flag_z = cpu_int_temp == 0;
|
|
cpu_flag_c = ~cpu_int_temp >> 8 != 0;
|
|
}
|
|
|
|
private static void DEC__()
|
|
{
|
|
Write(ref cpu_reg_ea.v, ref cpu_m);
|
|
cpu_m--;
|
|
Write(ref cpu_reg_ea.v, ref cpu_m);
|
|
cpu_flag_n = (cpu_m & 0x80) == 128;
|
|
cpu_flag_z = cpu_m == 0;
|
|
}
|
|
|
|
private static void DEY__()
|
|
{
|
|
cpu_reg_y--;
|
|
cpu_flag_z = cpu_reg_y == 0;
|
|
cpu_flag_n = (cpu_reg_y & 0x80) == 128;
|
|
}
|
|
|
|
private static void DEX__()
|
|
{
|
|
cpu_reg_x--;
|
|
cpu_flag_z = cpu_reg_x == 0;
|
|
cpu_flag_n = (cpu_reg_x & 0x80) == 128;
|
|
}
|
|
|
|
private static void EOR__()
|
|
{
|
|
cpu_reg_a ^= cpu_m;
|
|
cpu_flag_n = (cpu_reg_a & 0x80) == 128;
|
|
cpu_flag_z = cpu_reg_a == 0;
|
|
}
|
|
|
|
private static void INC__()
|
|
{
|
|
Write(ref cpu_reg_ea.v, ref cpu_m);
|
|
cpu_m++;
|
|
Write(ref cpu_reg_ea.v, ref cpu_m);
|
|
cpu_flag_n = (cpu_m & 0x80) == 128;
|
|
cpu_flag_z = cpu_m == 0;
|
|
}
|
|
|
|
private static void INX__()
|
|
{
|
|
cpu_reg_x++;
|
|
cpu_flag_z = cpu_reg_x == 0;
|
|
cpu_flag_n = (cpu_reg_x & 0x80) == 128;
|
|
}
|
|
|
|
private static void INY__()
|
|
{
|
|
cpu_reg_y++;
|
|
cpu_flag_n = (cpu_reg_y & 0x80) == 128;
|
|
cpu_flag_z = cpu_reg_y == 0;
|
|
}
|
|
|
|
private static void ISC__()
|
|
{
|
|
Read(ref cpu_reg_ea.v, out cpu_byte_temp);
|
|
Write(ref cpu_reg_ea.v, ref cpu_byte_temp);
|
|
cpu_byte_temp++;
|
|
Write(ref cpu_reg_ea.v, ref cpu_byte_temp);
|
|
cpu_int_temp = cpu_byte_temp ^ 0xFF;
|
|
cpu_int_temp1 = cpu_reg_a + cpu_int_temp + (cpu_flag_c ? 1 : 0);
|
|
cpu_flag_n = (cpu_int_temp1 & 0x80) != 0;
|
|
cpu_flag_v = ((cpu_int_temp1 ^ cpu_reg_a) & (cpu_int_temp1 ^ cpu_int_temp) & 0x80) != 0;
|
|
cpu_flag_z = (cpu_int_temp1 & 0xFF) == 0;
|
|
cpu_flag_c = cpu_int_temp1 >> 8 != 0;
|
|
cpu_reg_a = (byte)((uint)cpu_int_temp1 & 0xFFu);
|
|
}
|
|
|
|
private static void JMP__()
|
|
{
|
|
cpu_reg_pc.v = cpu_reg_ea.v;
|
|
}
|
|
|
|
private static void JMP_I()
|
|
{
|
|
Read(ref cpu_reg_pc.v, out cpu_reg_ea.l);
|
|
cpu_reg_pc.v++;
|
|
Read(ref cpu_reg_pc.v, out cpu_reg_ea.h);
|
|
Read(ref cpu_reg_ea.v, out cpu_reg_pc.l);
|
|
cpu_reg_ea.l++;
|
|
Read(ref cpu_reg_ea.v, out cpu_reg_pc.h);
|
|
}
|
|
|
|
private static void JSR__()
|
|
{
|
|
Read(ref cpu_reg_pc.v, out cpu_reg_ea.l);
|
|
cpu_reg_pc.v++;
|
|
Write(ref cpu_reg_sp.v, ref cpu_reg_ea.l);
|
|
Push(ref cpu_reg_pc.h);
|
|
Push(ref cpu_reg_pc.l);
|
|
Read(ref cpu_reg_pc.v, out cpu_reg_ea.h);
|
|
cpu_reg_pc.v = cpu_reg_ea.v;
|
|
}
|
|
|
|
private static void LAR__()
|
|
{
|
|
cpu_reg_sp.l &= cpu_m;
|
|
cpu_reg_a = cpu_reg_sp.l;
|
|
cpu_reg_x = cpu_reg_sp.l;
|
|
cpu_flag_n = (cpu_reg_sp.l & 0x80) != 0;
|
|
cpu_flag_z = (cpu_reg_sp.l & 0xFF) == 0;
|
|
}
|
|
|
|
private static void LAX__()
|
|
{
|
|
cpu_reg_x = (cpu_reg_a = cpu_m);
|
|
cpu_flag_n = (cpu_reg_x & 0x80) != 0;
|
|
cpu_flag_z = (cpu_reg_x & 0xFF) == 0;
|
|
}
|
|
|
|
private static void LDA__()
|
|
{
|
|
cpu_reg_a = cpu_m;
|
|
cpu_flag_n = (cpu_reg_a & 0x80) == 128;
|
|
cpu_flag_z = cpu_reg_a == 0;
|
|
}
|
|
|
|
private static void LDX__()
|
|
{
|
|
cpu_reg_x = cpu_m;
|
|
cpu_flag_n = (cpu_reg_x & 0x80) == 128;
|
|
cpu_flag_z = cpu_reg_x == 0;
|
|
}
|
|
|
|
private static void LDY__()
|
|
{
|
|
cpu_reg_y = cpu_m;
|
|
cpu_flag_n = (cpu_reg_y & 0x80) == 128;
|
|
cpu_flag_z = cpu_reg_y == 0;
|
|
}
|
|
|
|
private static void LSR_A()
|
|
{
|
|
cpu_flag_c = (cpu_reg_a & 1) == 1;
|
|
cpu_reg_a >>= 1;
|
|
cpu_flag_z = cpu_reg_a == 0;
|
|
cpu_flag_n = (cpu_reg_a & 0x80) != 0;
|
|
}
|
|
|
|
private static void LSR_M()
|
|
{
|
|
cpu_flag_c = (cpu_m & 1) == 1;
|
|
Write(ref cpu_reg_ea.v, ref cpu_m);
|
|
cpu_m >>= 1;
|
|
Write(ref cpu_reg_ea.v, ref cpu_m);
|
|
cpu_flag_z = cpu_m == 0;
|
|
cpu_flag_n = (cpu_m & 0x80) != 0;
|
|
}
|
|
|
|
private static void NOP__()
|
|
{
|
|
}
|
|
|
|
private static void ORA__()
|
|
{
|
|
cpu_reg_a |= cpu_m;
|
|
cpu_flag_n = (cpu_reg_a & 0x80) == 128;
|
|
cpu_flag_z = cpu_reg_a == 0;
|
|
}
|
|
|
|
private static void PHA__()
|
|
{
|
|
Push(ref cpu_reg_a);
|
|
}
|
|
|
|
private static void PHP__()
|
|
{
|
|
cpu_dummy = register_pb();
|
|
Push(ref cpu_dummy);
|
|
}
|
|
|
|
private static void PLA__()
|
|
{
|
|
Read(ref cpu_reg_sp.v, out cpu_dummy);
|
|
Pull(out cpu_reg_a);
|
|
cpu_flag_n = (cpu_reg_a & 0x80) == 128;
|
|
cpu_flag_z = cpu_reg_a == 0;
|
|
}
|
|
|
|
private static void PLP__()
|
|
{
|
|
Read(ref cpu_reg_sp.v, out cpu_dummy);
|
|
Pull(out cpu_dummy);
|
|
register_p = cpu_dummy;
|
|
}
|
|
|
|
private static void RLA__()
|
|
{
|
|
Read(ref cpu_reg_ea.v, out cpu_byte_temp);
|
|
Write(ref cpu_reg_ea.v, ref cpu_byte_temp);
|
|
cpu_dummy = (byte)((uint)(cpu_byte_temp << 1) | (cpu_flag_c ? 1u : 0u));
|
|
Write(ref cpu_reg_ea.v, ref cpu_dummy);
|
|
cpu_flag_n = (cpu_dummy & 0x80) != 0;
|
|
cpu_flag_z = (cpu_dummy & 0xFF) == 0;
|
|
cpu_flag_c = (cpu_byte_temp & 0x80) != 0;
|
|
cpu_reg_a &= cpu_dummy;
|
|
cpu_flag_n = (cpu_reg_a & 0x80) != 0;
|
|
cpu_flag_z = (cpu_reg_a & 0xFF) == 0;
|
|
}
|
|
|
|
private static void ROL_A()
|
|
{
|
|
cpu_byte_temp = (byte)((uint)(cpu_reg_a << 1) | (cpu_flag_c ? 1u : 0u));
|
|
cpu_flag_n = (cpu_byte_temp & 0x80) != 0;
|
|
cpu_flag_z = (cpu_byte_temp & 0xFF) == 0;
|
|
cpu_flag_c = (cpu_reg_a & 0x80) != 0;
|
|
cpu_reg_a = cpu_byte_temp;
|
|
}
|
|
|
|
private static void ROL_M()
|
|
{
|
|
Write(ref cpu_reg_ea.v, ref cpu_m);
|
|
cpu_byte_temp = (byte)((uint)(cpu_m << 1) | (cpu_flag_c ? 1u : 0u));
|
|
Write(ref cpu_reg_ea.v, ref cpu_byte_temp);
|
|
cpu_flag_n = (cpu_byte_temp & 0x80) != 0;
|
|
cpu_flag_z = (cpu_byte_temp & 0xFF) == 0;
|
|
cpu_flag_c = (cpu_m & 0x80) != 0;
|
|
}
|
|
|
|
private static void ROR_A()
|
|
{
|
|
cpu_byte_temp = (byte)((uint)(cpu_reg_a >> 1) | (cpu_flag_c ? 128u : 0u));
|
|
cpu_flag_n = (cpu_byte_temp & 0x80) != 0;
|
|
cpu_flag_z = (cpu_byte_temp & 0xFF) == 0;
|
|
cpu_flag_c = (cpu_reg_a & 1) != 0;
|
|
cpu_reg_a = cpu_byte_temp;
|
|
}
|
|
|
|
private static void ROR_M()
|
|
{
|
|
Write(ref cpu_reg_ea.v, ref cpu_m);
|
|
cpu_byte_temp = (byte)((uint)(cpu_m >> 1) | (cpu_flag_c ? 128u : 0u));
|
|
Write(ref cpu_reg_ea.v, ref cpu_byte_temp);
|
|
cpu_flag_n = (cpu_byte_temp & 0x80) != 0;
|
|
cpu_flag_z = (cpu_byte_temp & 0xFF) == 0;
|
|
cpu_flag_c = (cpu_m & 1) != 0;
|
|
}
|
|
|
|
private static void RRA__()
|
|
{
|
|
Read(ref cpu_reg_ea.v, out cpu_byte_temp);
|
|
Write(ref cpu_reg_ea.v, ref cpu_byte_temp);
|
|
cpu_dummy = (byte)((uint)(cpu_byte_temp >> 1) | (cpu_flag_c ? 128u : 0u));
|
|
Write(ref cpu_reg_ea.v, ref cpu_dummy);
|
|
cpu_flag_n = (cpu_dummy & 0x80) != 0;
|
|
cpu_flag_z = (cpu_dummy & 0xFF) == 0;
|
|
cpu_flag_c = (cpu_byte_temp & 1) != 0;
|
|
cpu_byte_temp = cpu_dummy;
|
|
cpu_int_temp = cpu_reg_a + cpu_byte_temp + (cpu_flag_c ? 1 : 0);
|
|
cpu_flag_n = (cpu_int_temp & 0x80) != 0;
|
|
cpu_flag_v = ((cpu_int_temp ^ cpu_reg_a) & (cpu_int_temp ^ cpu_byte_temp) & 0x80) != 0;
|
|
cpu_flag_z = (cpu_int_temp & 0xFF) == 0;
|
|
cpu_flag_c = cpu_int_temp >> 8 != 0;
|
|
cpu_reg_a = (byte)cpu_int_temp;
|
|
}
|
|
|
|
private static void RTI__()
|
|
{
|
|
Read(ref cpu_reg_sp.v, out cpu_dummy);
|
|
Pull(out cpu_dummy);
|
|
register_p = cpu_dummy;
|
|
Pull(out cpu_reg_pc.l);
|
|
Pull(out cpu_reg_pc.h);
|
|
}
|
|
|
|
private static void RTS__()
|
|
{
|
|
Read(ref cpu_reg_sp.v, out cpu_dummy);
|
|
Pull(out cpu_reg_pc.l);
|
|
Pull(out cpu_reg_pc.h);
|
|
cpu_reg_pc.v++;
|
|
Read(ref cpu_reg_pc.v, out cpu_dummy);
|
|
}
|
|
|
|
private static void SAX__()
|
|
{
|
|
cpu_dummy = (byte)(cpu_reg_x & cpu_reg_a);
|
|
Write(ref cpu_reg_ea.v, ref cpu_dummy);
|
|
}
|
|
|
|
private static void SBC__()
|
|
{
|
|
cpu_m ^= byte.MaxValue;
|
|
cpu_int_temp = cpu_reg_a + cpu_m + (cpu_flag_c ? 1 : 0);
|
|
cpu_flag_n = (cpu_int_temp & 0x80) != 0;
|
|
cpu_flag_v = ((cpu_int_temp ^ cpu_reg_a) & (cpu_int_temp ^ cpu_m) & 0x80) != 0;
|
|
cpu_flag_z = (cpu_int_temp & 0xFF) == 0;
|
|
cpu_flag_c = cpu_int_temp >> 8 != 0;
|
|
cpu_reg_a = (byte)cpu_int_temp;
|
|
}
|
|
|
|
private static void SEC__()
|
|
{
|
|
cpu_flag_c = true;
|
|
}
|
|
|
|
private static void SEI__()
|
|
{
|
|
cpu_flag_i = true;
|
|
}
|
|
|
|
private static void SHX__()
|
|
{
|
|
cpu_byte_temp = (byte)(cpu_reg_x & (cpu_reg_ea.h + 1));
|
|
Read(ref cpu_reg_ea.v, out cpu_dummy);
|
|
cpu_reg_ea.l += cpu_reg_y;
|
|
if (cpu_reg_ea.l < cpu_reg_y)
|
|
{
|
|
cpu_reg_ea.h = cpu_byte_temp;
|
|
}
|
|
Write(ref cpu_reg_ea.v, ref cpu_byte_temp);
|
|
}
|
|
|
|
private static void SHY__()
|
|
{
|
|
cpu_byte_temp = (byte)(cpu_reg_y & (cpu_reg_ea.h + 1));
|
|
Read(ref cpu_reg_ea.v, out cpu_dummy);
|
|
cpu_reg_ea.l += cpu_reg_x;
|
|
if (cpu_reg_ea.l < cpu_reg_x)
|
|
{
|
|
cpu_reg_ea.h = cpu_byte_temp;
|
|
}
|
|
Write(ref cpu_reg_ea.v, ref cpu_byte_temp);
|
|
}
|
|
|
|
private static void SLO__()
|
|
{
|
|
Read(ref cpu_reg_ea.v, out cpu_byte_temp);
|
|
cpu_flag_c = (cpu_byte_temp & 0x80) != 0;
|
|
Write(ref cpu_reg_ea.v, ref cpu_byte_temp);
|
|
cpu_byte_temp <<= 1;
|
|
Write(ref cpu_reg_ea.v, ref cpu_byte_temp);
|
|
cpu_flag_n = (cpu_byte_temp & 0x80) != 0;
|
|
cpu_flag_z = (cpu_byte_temp & 0xFF) == 0;
|
|
cpu_reg_a |= cpu_byte_temp;
|
|
cpu_flag_n = (cpu_reg_a & 0x80) != 0;
|
|
cpu_flag_z = (cpu_reg_a & 0xFF) == 0;
|
|
}
|
|
|
|
private static void SRE__()
|
|
{
|
|
Read(ref cpu_reg_ea.v, out cpu_byte_temp);
|
|
cpu_flag_c = (cpu_byte_temp & 1) != 0;
|
|
Write(ref cpu_reg_ea.v, ref cpu_byte_temp);
|
|
cpu_byte_temp >>= 1;
|
|
Write(ref cpu_reg_ea.v, ref cpu_byte_temp);
|
|
cpu_flag_n = (cpu_byte_temp & 0x80) != 0;
|
|
cpu_flag_z = (cpu_byte_temp & 0xFF) == 0;
|
|
cpu_reg_a ^= cpu_byte_temp;
|
|
cpu_flag_n = (cpu_reg_a & 0x80) != 0;
|
|
cpu_flag_z = (cpu_reg_a & 0xFF) == 0;
|
|
}
|
|
|
|
private static void STA__()
|
|
{
|
|
Write(ref cpu_reg_ea.v, ref cpu_reg_a);
|
|
}
|
|
|
|
private static void STX__()
|
|
{
|
|
Write(ref cpu_reg_ea.v, ref cpu_reg_x);
|
|
}
|
|
|
|
private static void STY__()
|
|
{
|
|
Write(ref cpu_reg_ea.v, ref cpu_reg_y);
|
|
}
|
|
|
|
private static void TAX__()
|
|
{
|
|
cpu_reg_x = cpu_reg_a;
|
|
cpu_flag_n = (cpu_reg_x & 0x80) == 128;
|
|
cpu_flag_z = cpu_reg_x == 0;
|
|
}
|
|
|
|
private static void TAY__()
|
|
{
|
|
cpu_reg_y = cpu_reg_a;
|
|
cpu_flag_n = (cpu_reg_y & 0x80) == 128;
|
|
cpu_flag_z = cpu_reg_y == 0;
|
|
}
|
|
|
|
private static void TSX__()
|
|
{
|
|
cpu_reg_x = cpu_reg_sp.l;
|
|
cpu_flag_n = (cpu_reg_x & 0x80) != 0;
|
|
cpu_flag_z = cpu_reg_x == 0;
|
|
}
|
|
|
|
private static void TXA__()
|
|
{
|
|
cpu_reg_a = cpu_reg_x;
|
|
cpu_flag_n = (cpu_reg_a & 0x80) == 128;
|
|
cpu_flag_z = cpu_reg_a == 0;
|
|
}
|
|
|
|
private static void TXS__()
|
|
{
|
|
cpu_reg_sp.l = cpu_reg_x;
|
|
}
|
|
|
|
private static void TYA__()
|
|
{
|
|
cpu_reg_a = cpu_reg_y;
|
|
cpu_flag_n = (cpu_reg_a & 0x80) == 128;
|
|
cpu_flag_z = cpu_reg_a == 0;
|
|
}
|
|
|
|
private static void XAA__()
|
|
{
|
|
cpu_reg_a = (byte)(cpu_reg_x & cpu_m);
|
|
cpu_flag_n = (cpu_reg_a & 0x80) != 0;
|
|
cpu_flag_z = (cpu_reg_a & 0xFF) == 0;
|
|
}
|
|
|
|
private static void XAS__()
|
|
{
|
|
cpu_reg_sp.l = (byte)(cpu_reg_a & cpu_reg_x);
|
|
Write(ref cpu_reg_ea.v, ref cpu_reg_sp.l);
|
|
}
|
|
|
|
private static void CPUWriteState(ref BinaryWriter bin)
|
|
{
|
|
bin.Write(cpu_reg_pc.v);
|
|
bin.Write(cpu_reg_sp.v);
|
|
bin.Write(cpu_reg_ea.v);
|
|
bin.Write(cpu_reg_a);
|
|
bin.Write(cpu_reg_x);
|
|
bin.Write(cpu_reg_y);
|
|
bin.Write(cpu_flag_n);
|
|
bin.Write(cpu_flag_v);
|
|
bin.Write(cpu_flag_d);
|
|
bin.Write(cpu_flag_i);
|
|
bin.Write(cpu_flag_z);
|
|
bin.Write(cpu_flag_c);
|
|
bin.Write(cpu_m);
|
|
bin.Write(cpu_opcode);
|
|
bin.Write(cpu_byte_temp);
|
|
bin.Write(cpu_int_temp);
|
|
bin.Write(cpu_int_temp1);
|
|
bin.Write(cpu_dummy);
|
|
bin.Write(cpu_bool_tmp);
|
|
bin.Write(temp_add.v);
|
|
bin.Write(CPU_IRQ_PIN);
|
|
bin.Write(CPU_NMI_PIN);
|
|
bin.Write(cpu_suspend_nmi);
|
|
bin.Write(cpu_suspend_irq);
|
|
}
|
|
|
|
private static void CPUReadState(ref BinaryReader bin)
|
|
{
|
|
cpu_reg_pc.v = bin.ReadUInt16();
|
|
cpu_reg_sp.v = bin.ReadUInt16();
|
|
cpu_reg_ea.v = bin.ReadUInt16();
|
|
cpu_reg_a = bin.ReadByte();
|
|
cpu_reg_x = bin.ReadByte();
|
|
cpu_reg_y = bin.ReadByte();
|
|
cpu_flag_n = bin.ReadBoolean();
|
|
cpu_flag_v = bin.ReadBoolean();
|
|
cpu_flag_d = bin.ReadBoolean();
|
|
cpu_flag_i = bin.ReadBoolean();
|
|
cpu_flag_z = bin.ReadBoolean();
|
|
cpu_flag_c = bin.ReadBoolean();
|
|
cpu_m = bin.ReadByte();
|
|
cpu_opcode = bin.ReadByte();
|
|
cpu_byte_temp = bin.ReadByte();
|
|
cpu_int_temp = bin.ReadInt32();
|
|
cpu_int_temp1 = bin.ReadInt32();
|
|
cpu_dummy = bin.ReadByte();
|
|
cpu_bool_tmp = bin.ReadBoolean();
|
|
temp_add.v = bin.ReadUInt16();
|
|
CPU_IRQ_PIN = bin.ReadBoolean();
|
|
CPU_NMI_PIN = bin.ReadBoolean();
|
|
cpu_suspend_nmi = bin.ReadBoolean();
|
|
cpu_suspend_irq = bin.ReadBoolean();
|
|
}
|
|
|
|
private static void DMAHardReset()
|
|
{
|
|
dma_DMCDMAWaitCycles = 0;
|
|
dma_OAMDMAWaitCycles = 0;
|
|
dma_isOamDma = false;
|
|
dma_oamdma_i = 0;
|
|
dma_DMCOn = false;
|
|
dma_OAMOn = false;
|
|
dma_DMC_occurring = false;
|
|
dma_OAM_occurring = false;
|
|
dma_OAMFinishCounter = 0;
|
|
dma_Oamaddress = 0;
|
|
dma_OAMCYCLE = 0;
|
|
dma_latch = 0;
|
|
reg_2004 = 8196;
|
|
}
|
|
|
|
private static void DMASoftReset()
|
|
{
|
|
dma_DMCDMAWaitCycles = 0;
|
|
dma_OAMDMAWaitCycles = 0;
|
|
dma_isOamDma = false;
|
|
dma_oamdma_i = 0;
|
|
dma_DMCOn = false;
|
|
dma_OAMOn = false;
|
|
dma_DMC_occurring = false;
|
|
dma_OAM_occurring = false;
|
|
dma_OAMFinishCounter = 0;
|
|
dma_Oamaddress = 0;
|
|
dma_OAMCYCLE = 0;
|
|
dma_latch = 0;
|
|
}
|
|
|
|
internal static void AssertDMCDMA()
|
|
{
|
|
if (dma_OAM_occurring)
|
|
{
|
|
if (dma_OAMCYCLE < 508)
|
|
{
|
|
dma_DMCDMAWaitCycles = (BUS_RW ? 1 : 0);
|
|
}
|
|
else
|
|
{
|
|
dma_DMCDMAWaitCycles = 4 - (512 - dma_OAMCYCLE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (dma_DMC_occurring)
|
|
{
|
|
return;
|
|
}
|
|
dma_DMCDMAWaitCycles = (BUS_RW ? 3 : 2);
|
|
if (dma_OAMFinishCounter == 3)
|
|
{
|
|
dma_DMCDMAWaitCycles++;
|
|
}
|
|
}
|
|
dma_isOamDma = false;
|
|
dma_DMCOn = true;
|
|
}
|
|
|
|
private static void AssertOAMDMA()
|
|
{
|
|
if (!dma_OAM_occurring)
|
|
{
|
|
dma_OAMDMAWaitCycles = (apu_odd_cycle ? 1 : 2);
|
|
dma_isOamDma = true;
|
|
dma_OAMOn = true;
|
|
}
|
|
}
|
|
|
|
private static void DMAClock()
|
|
{
|
|
if (dma_OAMFinishCounter > 0)
|
|
{
|
|
dma_OAMFinishCounter--;
|
|
}
|
|
if (!BUS_RW)
|
|
{
|
|
if (dma_DMCDMAWaitCycles > 0)
|
|
{
|
|
dma_DMCDMAWaitCycles--;
|
|
}
|
|
if (dma_OAMDMAWaitCycles > 0)
|
|
{
|
|
dma_OAMDMAWaitCycles--;
|
|
}
|
|
return;
|
|
}
|
|
if (dma_DMCOn)
|
|
{
|
|
dma_DMC_occurring = true;
|
|
dma_DMCOn = false;
|
|
if (dma_DMCDMAWaitCycles > 0)
|
|
{
|
|
if (BUS_ADDRESS == 16406 || BUS_ADDRESS == 16407)
|
|
{
|
|
Read(ref BUS_ADDRESS, out dma_dummy);
|
|
dma_DMCDMAWaitCycles--;
|
|
while (dma_DMCDMAWaitCycles > 0)
|
|
{
|
|
EmuClockComponents();
|
|
dma_DMCDMAWaitCycles--;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (dma_DMCDMAWaitCycles > 0)
|
|
{
|
|
EmuClockComponents();
|
|
dma_DMCDMAWaitCycles--;
|
|
}
|
|
while (dma_DMCDMAWaitCycles > 0)
|
|
{
|
|
Read(ref BUS_ADDRESS, out dma_dummy);
|
|
dma_DMCDMAWaitCycles--;
|
|
}
|
|
}
|
|
}
|
|
DMCDoDMA();
|
|
dma_DMC_occurring = false;
|
|
}
|
|
if (!dma_OAMOn)
|
|
{
|
|
return;
|
|
}
|
|
dma_OAM_occurring = true;
|
|
dma_OAMOn = false;
|
|
if (dma_OAMDMAWaitCycles > 0)
|
|
{
|
|
if (BUS_ADDRESS == 16406 || BUS_ADDRESS == 16407)
|
|
{
|
|
Read(ref BUS_ADDRESS, out dma_dummy);
|
|
dma_OAMDMAWaitCycles--;
|
|
while (dma_OAMDMAWaitCycles > 0)
|
|
{
|
|
EmuClockComponents();
|
|
dma_OAMDMAWaitCycles--;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (dma_OAMDMAWaitCycles > 0)
|
|
{
|
|
EmuClockComponents();
|
|
dma_OAMDMAWaitCycles--;
|
|
}
|
|
while (dma_OAMDMAWaitCycles > 0)
|
|
{
|
|
Read(ref BUS_ADDRESS, out dma_dummy);
|
|
dma_OAMDMAWaitCycles--;
|
|
}
|
|
}
|
|
}
|
|
dma_OAMCYCLE = 0;
|
|
for (dma_oamdma_i = 0; dma_oamdma_i < 256; dma_oamdma_i++)
|
|
{
|
|
Read(ref dma_Oamaddress, out dma_latch);
|
|
dma_OAMCYCLE++;
|
|
Write(ref reg_2004, ref dma_latch);
|
|
dma_OAMCYCLE++;
|
|
dma_Oamaddress = (ushort)(++dma_Oamaddress & 0xFFFFu);
|
|
}
|
|
dma_OAMCYCLE = 0;
|
|
dma_OAMFinishCounter = 5;
|
|
dma_OAM_occurring = false;
|
|
}
|
|
|
|
private static void DMAWriteState(ref BinaryWriter bin)
|
|
{
|
|
bin.Write(dma_DMCDMAWaitCycles);
|
|
bin.Write(dma_OAMDMAWaitCycles);
|
|
bin.Write(dma_isOamDma);
|
|
bin.Write(dma_oamdma_i);
|
|
bin.Write(dma_DMCOn);
|
|
bin.Write(dma_OAMOn);
|
|
bin.Write(dma_DMC_occurring);
|
|
bin.Write(dma_OAM_occurring);
|
|
bin.Write(dma_OAMFinishCounter);
|
|
bin.Write(dma_Oamaddress);
|
|
bin.Write(dma_OAMCYCLE);
|
|
bin.Write(dma_latch);
|
|
bin.Write(dma_dummy);
|
|
}
|
|
|
|
private static void DMAReadState(ref BinaryReader bin)
|
|
{
|
|
dma_DMCDMAWaitCycles = bin.ReadInt32();
|
|
dma_OAMDMAWaitCycles = bin.ReadInt32();
|
|
dma_isOamDma = bin.ReadBoolean();
|
|
dma_oamdma_i = bin.ReadInt32();
|
|
dma_DMCOn = bin.ReadBoolean();
|
|
dma_OAMOn = bin.ReadBoolean();
|
|
dma_DMC_occurring = bin.ReadBoolean();
|
|
dma_OAM_occurring = bin.ReadBoolean();
|
|
dma_OAMFinishCounter = bin.ReadInt32();
|
|
dma_Oamaddress = bin.ReadUInt16();
|
|
dma_OAMCYCLE = bin.ReadInt32();
|
|
dma_latch = bin.ReadByte();
|
|
dma_dummy = bin.ReadByte();
|
|
}
|
|
|
|
private static void PollInterruptStatus()
|
|
{
|
|
if (!cpu_suspend_nmi)
|
|
{
|
|
if (PPU_NMI_Current & !PPU_NMI_Old)
|
|
{
|
|
CPU_NMI_PIN = true;
|
|
}
|
|
PPU_NMI_Old = (PPU_NMI_Current = false);
|
|
}
|
|
if (!cpu_suspend_irq)
|
|
{
|
|
CPU_IRQ_PIN = !cpu_flag_i && IRQFlags != 0;
|
|
}
|
|
if (CPU_NMI_PIN)
|
|
{
|
|
InterruptVector = 65530;
|
|
}
|
|
else
|
|
{
|
|
InterruptVector = 65534;
|
|
}
|
|
}
|
|
|
|
private static void InterruptsWriteState(ref BinaryWriter bin)
|
|
{
|
|
bin.Write(IRQFlags);
|
|
bin.Write(PPU_NMI_Current);
|
|
bin.Write(PPU_NMI_Old);
|
|
bin.Write(InterruptVector);
|
|
}
|
|
|
|
private static void InterruptsReadState(ref BinaryReader bin)
|
|
{
|
|
IRQFlags = bin.ReadInt32();
|
|
PPU_NMI_Current = bin.ReadBoolean();
|
|
PPU_NMI_Old = bin.ReadBoolean();
|
|
InterruptVector = bin.ReadUInt16();
|
|
}
|
|
|
|
public static void SetupGameGenie(bool IsGameGenieActive, GameGenieCode[] GameGenieCodes)
|
|
{
|
|
if (mem_board != null)
|
|
{
|
|
mem_board.SetupGameGenie(IsGameGenieActive, GameGenieCodes);
|
|
}
|
|
}
|
|
|
|
private static void MEMInitialize(IRom rom)
|
|
{
|
|
Tracer.WriteLine("Looking for mapper # " + rom.MapperNumber + "....");
|
|
if (MyNesMain.IsBoardExist(rom.MapperNumber))
|
|
{
|
|
Tracer.WriteLine("Mapper # " + rom.MapperNumber + " located, assigning...");
|
|
mem_board = MyNesMain.GetBoard(rom.MapperNumber);
|
|
Tracer.WriteInformation("Mapper # " + rom.MapperNumber + " assigned successfully.");
|
|
if (mem_board.HasIssues)
|
|
{
|
|
Tracer.WriteWarning(MNInterfaceLanguage.Mapper + " # " + mem_board.MapperNumber + " [" + mem_board.Name + "] " + MNInterfaceLanguage.Message_Error17);
|
|
MyNesMain.VideoProvider.WriteWarningNotification(MNInterfaceLanguage.Mapper + " # " + mem_board.MapperNumber + " [" + mem_board.Name + "] " + MNInterfaceLanguage.Message_Error17, instant: false);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Tracer.WriteError("Mapper # " + rom.MapperNumber + " IS NOT LOCATED, mapper is not supported or unable to find it.");
|
|
MyNesMain.VideoProvider.WriteErrorNotification(MNInterfaceLanguage.Mapper + " # " + rom.MapperNumber + " " + MNInterfaceLanguage.Message_Error14, instant: false);
|
|
mem_board = MyNesMain.GetBoard(0);
|
|
Tracer.WriteWarning("Mapper # 0 [NROM] will be used instead, assigned successfully.");
|
|
MyNesMain.VideoProvider.WriteErrorNotification(MNInterfaceLanguage.Mapper + " # 0 [NROM] " + MNInterfaceLanguage.Message_Error15, instant: false);
|
|
}
|
|
mem_read_accesses = new MemReadAccess[65536];
|
|
mem_write_accesses = new MemWriteAccess[65536];
|
|
MEMMap(MEMReadWRAM, new ushort[2] { 0, 4096 });
|
|
MEMMap(MEMWriteWRAM, new ushort[2] { 0, 4096 });
|
|
MEMMap(PPUIORead, new ushort[2] { 8192, 12288 });
|
|
MEMMap(PPUIOWrite, new ushort[2] { 8192, 12288 });
|
|
MEMMap(APUIORead, new ushort[1] { 16384 });
|
|
MEMMap(APUIOWrite, new ushort[1] { 16384 });
|
|
MEMMap(mem_board.ReadEX, new ushort[1] { 20480 });
|
|
MEMMap(mem_board.WriteEX, new ushort[1] { 20480 });
|
|
MEMMap(mem_board.ReadSRM, new ushort[2] { 24576, 28672 });
|
|
MEMMap(mem_board.WriteSRM, new ushort[2] { 24576, 28672 });
|
|
MEMMap(mem_board.ReadPRG, new ushort[8] { 32768, 36864, 40960, 45056, 49152, 53248, 57344, 61440 });
|
|
MEMMap(mem_board.WritePRG, new ushort[8] { 32768, 36864, 40960, 45056, 49152, 53248, 57344, 61440 });
|
|
mem_board.Initialize(rom);
|
|
mem_wram = new byte[2048];
|
|
}
|
|
|
|
private static void MEMHardReset()
|
|
{
|
|
mem_wram = new byte[2048];
|
|
mem_wram[8] = 247;
|
|
mem_wram[9] = 239;
|
|
mem_wram[10] = 223;
|
|
mem_wram[15] = 191;
|
|
Tracer.WriteLine("Reading SRAM ...");
|
|
SRAMFileName = Path.Combine(MyNesMain.EmuSettings.SRAMFolder, Path.GetFileNameWithoutExtension(CurrentFilePath) + ".srm");
|
|
if (File.Exists(SRAMFileName))
|
|
{
|
|
FileStream fileStream = new FileStream(SRAMFileName, FileMode.Open, FileAccess.Read);
|
|
byte[] array = new byte[fileStream.Length];
|
|
fileStream.Read(array, 0, array.Length);
|
|
fileStream.Flush();
|
|
fileStream.Close();
|
|
byte[] outData = new byte[0];
|
|
ZlipWrapper.DecompressData(array, out outData);
|
|
mem_board.LoadSRAM(outData);
|
|
Tracer.WriteLine("SRAM read successfully.");
|
|
}
|
|
else
|
|
{
|
|
Tracer.WriteLine("SRAM file not found; rom has no SRAM or file not exist.");
|
|
}
|
|
ReloadGameGenieCodes();
|
|
mem_board.HardReset();
|
|
}
|
|
|
|
public static void ReloadGameGenieCodes()
|
|
{
|
|
Tracer.WriteLine("Reading game genie codes (if available)....");
|
|
GMFileName = Path.Combine(MyNesMain.EmuSettings.GameGenieFolder, Path.GetFileNameWithoutExtension(CurrentFilePath) + ".txt");
|
|
mem_board.GameGenieCodes = new GameGenieCode[0];
|
|
if (File.Exists(GMFileName))
|
|
{
|
|
XmlReaderSettings xmlReaderSettings = new XmlReaderSettings();
|
|
xmlReaderSettings.DtdProcessing = DtdProcessing.Ignore;
|
|
xmlReaderSettings.IgnoreWhitespace = true;
|
|
XmlReader xmlReader = XmlReader.Create(GMFileName, xmlReaderSettings);
|
|
xmlReader.Read();
|
|
xmlReader.Read();
|
|
if (xmlReader.Name != "MyNesGameGenieCodesList")
|
|
{
|
|
xmlReader.Close();
|
|
return;
|
|
}
|
|
GameGenie gameGenie = new GameGenie();
|
|
List<GameGenieCode> list = new List<GameGenieCode>();
|
|
while (xmlReader.Read())
|
|
{
|
|
if (xmlReader.Name == "Code")
|
|
{
|
|
GameGenieCode item = default(GameGenieCode);
|
|
item.Enabled = true;
|
|
xmlReader.MoveToAttribute("code");
|
|
item.Name = xmlReader.Value.ToString();
|
|
if (item.Name.Length == 6)
|
|
{
|
|
item.Address = gameGenie.GetGGAddress(gameGenie.GetCodeAsHEX(item.Name), 6) | 0x8000;
|
|
item.Value = gameGenie.GetGGValue(gameGenie.GetCodeAsHEX(item.Name), 6);
|
|
item.IsCompare = false;
|
|
}
|
|
else
|
|
{
|
|
item.Address = gameGenie.GetGGAddress(gameGenie.GetCodeAsHEX(item.Name), 8) | 0x8000;
|
|
item.Value = gameGenie.GetGGValue(gameGenie.GetCodeAsHEX(item.Name), 8);
|
|
item.Compare = gameGenie.GetGGCompareValue(gameGenie.GetCodeAsHEX(item.Name));
|
|
item.IsCompare = true;
|
|
}
|
|
list.Add(item);
|
|
}
|
|
}
|
|
xmlReader.Close();
|
|
if (list.Count > 0)
|
|
{
|
|
mem_board.GameGenieCodes = list.ToArray();
|
|
Tracer.WriteInformation("Game Genie codes loaded successfully, total of " + list.Count);
|
|
}
|
|
else
|
|
{
|
|
Tracer.WriteError("There is no Game Genie code in the file to load.");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Tracer.WriteWarning("No Game Genie file found for this game.");
|
|
}
|
|
}
|
|
|
|
private static void MEMMap(MemReadAccess readAccess, ushort[] addresses)
|
|
{
|
|
for (int i = 0; i < addresses.Length; i++)
|
|
{
|
|
mem_read_accesses[(addresses[i] & 0xF000) >> 12] = readAccess;
|
|
}
|
|
}
|
|
|
|
private static void MEMMap(MemWriteAccess writeAccess, ushort[] addresses)
|
|
{
|
|
for (int i = 0; i < addresses.Length; i++)
|
|
{
|
|
mem_write_accesses[(addresses[i] & 0xF000) >> 12] = writeAccess;
|
|
}
|
|
}
|
|
|
|
private static void MEMReadWRAM(ref ushort addr, out byte value)
|
|
{
|
|
value = mem_wram[addr & 0x7FF];
|
|
}
|
|
|
|
private static void MEMWriteWRAM(ref ushort addr, ref byte value)
|
|
{
|
|
mem_wram[addr & 0x7FF] = value;
|
|
}
|
|
|
|
internal static void Read(ref ushort addr, out byte value)
|
|
{
|
|
BUS_RW = true;
|
|
BUS_ADDRESS = addr;
|
|
EmuClockComponents();
|
|
mem_read_accesses[(addr & 0xF000) >> 12](ref addr, out value);
|
|
}
|
|
|
|
private static void Write(ref ushort addr, ref byte value)
|
|
{
|
|
BUS_RW = false;
|
|
BUS_ADDRESS = addr;
|
|
EmuClockComponents();
|
|
mem_write_accesses[(addr & 0xF000) >> 12](ref addr, ref value);
|
|
}
|
|
|
|
internal static void SaveSRAM()
|
|
{
|
|
if (mem_board != null && MyNesMain.EmuSettings.SaveSRAMAtEmuShutdown && mem_board.SRAMSaveRequired)
|
|
{
|
|
Tracer.WriteLine("Saving SRAM ...");
|
|
byte[] outData = new byte[0];
|
|
ZlipWrapper.CompressData(mem_board.GetSRAMBuffer(), out outData);
|
|
FileStream fileStream = new FileStream(SRAMFileName, FileMode.Create, FileAccess.Write);
|
|
fileStream.Write(outData, 0, outData.Length);
|
|
fileStream.Flush();
|
|
fileStream.Close();
|
|
Tracer.WriteLine("SRAM saved successfully.");
|
|
}
|
|
}
|
|
|
|
private static void MEMWriteState(ref BinaryWriter bin)
|
|
{
|
|
mem_board.WriteStateData(ref bin);
|
|
bin.Write(mem_wram);
|
|
bin.Write(BUS_RW);
|
|
bin.Write(BUS_ADDRESS);
|
|
}
|
|
|
|
private static void MEMReadState(ref BinaryReader bin)
|
|
{
|
|
mem_board.ReadStateData(ref bin);
|
|
bin.Read(mem_wram, 0, mem_wram.Length);
|
|
BUS_RW = bin.ReadBoolean();
|
|
BUS_ADDRESS = bin.ReadUInt16();
|
|
}
|
|
|
|
private static void PORTSInitialize()
|
|
{
|
|
if (joypad1 == null)
|
|
{
|
|
joypad1 = new BlankJoypad();
|
|
}
|
|
if (joypad2 == null)
|
|
{
|
|
joypad2 = new BlankJoypad();
|
|
}
|
|
if (joypad3 == null)
|
|
{
|
|
joypad3 = new BlankJoypad();
|
|
}
|
|
if (joypad4 == null)
|
|
{
|
|
joypad4 = new BlankJoypad();
|
|
}
|
|
}
|
|
|
|
public static void SetupControllers(IJoypadConnecter joy1, IJoypadConnecter joy2, IJoypadConnecter joy3, IJoypadConnecter joy4)
|
|
{
|
|
joypad1 = joy1;
|
|
joypad2 = joy2;
|
|
joypad3 = joy3;
|
|
joypad4 = joy4;
|
|
}
|
|
|
|
public static void SetupVSUnisystemDIP(IVSUnisystemDIPConnecter uni)
|
|
{
|
|
}
|
|
|
|
public static void SetupControllersP1(IJoypadConnecter joy)
|
|
{
|
|
joypad1 = joy;
|
|
}
|
|
|
|
public static void SetupControllersP2(IJoypadConnecter joy)
|
|
{
|
|
joypad2 = joy;
|
|
}
|
|
|
|
public static void SetupControllersP3(IJoypadConnecter joy)
|
|
{
|
|
joypad3 = joy;
|
|
}
|
|
|
|
public static void SetupControllersP4(IJoypadConnecter joy)
|
|
{
|
|
joypad4 = joy;
|
|
}
|
|
|
|
public static void DestroyJoypads()
|
|
{
|
|
if (joypad1 == null)
|
|
{
|
|
joypad1 = new BlankJoypad();
|
|
}
|
|
else
|
|
{
|
|
joypad1.Destroy();
|
|
}
|
|
if (joypad2 == null)
|
|
{
|
|
joypad2 = new BlankJoypad();
|
|
}
|
|
else
|
|
{
|
|
joypad1.Destroy();
|
|
}
|
|
if (joypad3 == null)
|
|
{
|
|
joypad3 = new BlankJoypad();
|
|
}
|
|
else
|
|
{
|
|
joypad1.Destroy();
|
|
}
|
|
if (joypad4 == null)
|
|
{
|
|
joypad4 = new BlankJoypad();
|
|
}
|
|
else
|
|
{
|
|
joypad1.Destroy();
|
|
}
|
|
}
|
|
|
|
private static void PORTWriteState(ref BinaryWriter bin)
|
|
{
|
|
bin.Write(PORT0);
|
|
bin.Write(PORT1);
|
|
bin.Write(inputStrobe);
|
|
}
|
|
|
|
private static void PORTReadState(ref BinaryReader bin)
|
|
{
|
|
PORT0 = bin.ReadInt32();
|
|
PORT1 = bin.ReadInt32();
|
|
inputStrobe = bin.ReadInt32();
|
|
}
|
|
|
|
public static void SetupPalette(int[] pal)
|
|
{
|
|
ppu_palette = pal;
|
|
}
|
|
|
|
private static void PPUInitialize()
|
|
{
|
|
ppu_reg_update_func = new Action[8] { PPUOnRegister2000, PPUOnRegister2001, PPUOnRegister2002, PPUOnRegister2003, PPUOnRegister2004, PPUOnRegister2005, PPUOnRegister2006, PPUOnRegister2007 };
|
|
ppu_reg_read_func = new Action[8] { PPURead2000, PPURead2001, PPURead2002, PPURead2003, PPURead2004, PPURead2005, PPURead2006, PPURead2007 };
|
|
ppu_bkg_fetches = new Action[8] { PPUBKFetch0, PPUBKFetch1, PPUBKFetch2, PPUBKFetch3, PPUBKFetch4, PPUBKFetch5, PPUBKFetch6, PPUBKFetch7 };
|
|
ppu_spr_fetches = new Action[8] { PPUBKFetch0, PPUBKFetch1, PPUBKFetch2, PPUBKFetch3, PPUSPRFetch0, PPUSPRFetch1, PPUSPRFetch2, PPUSPRFetch3 };
|
|
ppu_oam_phases = new Action[9] { PPUOamPhase0, PPUOamPhase1, PPUOamPhase2, PPUOamPhase3, PPUOamPhase4, PPUOamPhase5, PPUOamPhase6, PPUOamPhase7, PPUOamPhase8 };
|
|
ppu_h_clocks = new Action[341];
|
|
ppu_h_clocks[0] = PPUHClock_000_Idle;
|
|
for (int i = 1; i < 257; i++)
|
|
{
|
|
ppu_h_clocks[i] = PPUHClock_1_256_BKGClocks;
|
|
}
|
|
for (int j = 257; j < 321; j++)
|
|
{
|
|
ppu_h_clocks[j] = PPUHClock_257_320_SPRClocks;
|
|
}
|
|
for (int k = 321; k < 337; k++)
|
|
{
|
|
ppu_h_clocks[k] = PPUHClock_321_336_DUMClocks;
|
|
}
|
|
for (int l = 337; l < 341; l++)
|
|
{
|
|
ppu_h_clocks[l] = PPUHClock_337_340_DUMClocks;
|
|
}
|
|
ppu_v_clocks = new Action[320];
|
|
for (int m = 0; m < 240; m++)
|
|
{
|
|
ppu_v_clocks[m] = PPUScanlineRender;
|
|
}
|
|
ppu_v_clocks[240] = PPUScanlineVBLANK;
|
|
ppu_oam_bank = new byte[256];
|
|
ppu_oam_bank_secondary = new byte[32];
|
|
ppu_palette_bank = new byte[32];
|
|
ppu_bkg_pixels = new int[512];
|
|
ppu_spr_pixels = new int[512];
|
|
ppu_screen_pixels = new int[61440];
|
|
ppu_palette = NTSCPaletteGenerator.GeneratePalette();
|
|
}
|
|
|
|
private static void PPUHardReset()
|
|
{
|
|
ppu_reg_2001_grayscale = 243;
|
|
switch (Region)
|
|
{
|
|
case EmuRegion.NTSC:
|
|
ppu_clock_vblank_start = 241;
|
|
ppu_clock_vblank_end = 261;
|
|
ppu_use_odd_cycle = true;
|
|
break;
|
|
case EmuRegion.PALB:
|
|
ppu_clock_vblank_start = 241;
|
|
ppu_clock_vblank_end = 311;
|
|
ppu_use_odd_cycle = false;
|
|
break;
|
|
case EmuRegion.DENDY:
|
|
{
|
|
ppu_clock_vblank_start = 291;
|
|
ppu_clock_vblank_end = 311;
|
|
for (int i = 241; i <= 290; i++)
|
|
{
|
|
ppu_v_clocks[i] = PPUScanlineVBLANK;
|
|
}
|
|
ppu_use_odd_cycle = false;
|
|
break;
|
|
}
|
|
}
|
|
ppu_v_clocks[ppu_clock_vblank_start] = PPUScanlineVBLANKStart;
|
|
for (int j = ppu_clock_vblank_start + 1; j <= ppu_clock_vblank_end - 1; j++)
|
|
{
|
|
ppu_v_clocks[j] = PPUScanlineVBLANK;
|
|
}
|
|
ppu_v_clocks[ppu_clock_vblank_end] = PPUScanlineVBLANKEnd;
|
|
ppu_oam_bank = new byte[256];
|
|
ppu_oam_bank_secondary = new byte[32];
|
|
PPUOamReset();
|
|
ppu_palette_bank = new byte[32]
|
|
{
|
|
9, 1, 0, 1, 0, 2, 2, 13, 8, 16,
|
|
8, 36, 0, 0, 4, 44, 9, 1, 52, 3,
|
|
0, 4, 0, 20, 8, 58, 0, 2, 0, 32,
|
|
44, 8
|
|
};
|
|
ppu_reg_io_db = 0;
|
|
ppu_reg_io_addr = 0;
|
|
ppu_reg_access_happened = false;
|
|
ppu_reg_access_w = false;
|
|
ppu_reg_2000_vram_address_increament = 1;
|
|
ppu_reg_2000_sprite_pattern_table_address_for_8x8_sprites = 0;
|
|
ppu_reg_2000_background_pattern_table_address = 0;
|
|
ppu_reg_2000_Sprite_size = 0;
|
|
ppu_reg_2000_VBI = false;
|
|
ppu_reg_2001_show_background_in_leftmost_8_pixels_of_screen = false;
|
|
ppu_reg_2001_show_sprites_in_leftmost_8_pixels_of_screen = false;
|
|
ppu_reg_2001_show_background = false;
|
|
ppu_reg_2001_show_sprites = false;
|
|
ppu_reg_2001_grayscale = 63;
|
|
ppu_reg_2001_emphasis = 0;
|
|
ppu_reg_2002_SpriteOverflow = false;
|
|
ppu_reg_2002_Sprite0Hit = false;
|
|
ppu_reg_2002_VblankStartedFlag = false;
|
|
ppu_reg_2003_oam_addr = 0;
|
|
ppu_is_sprfetch = false;
|
|
ppu_use_odd_swap = false;
|
|
ppu_clock_h = 0;
|
|
ppu_clock_v = 0;
|
|
}
|
|
|
|
private static void PPUClock()
|
|
{
|
|
mem_board.OnPPUClock();
|
|
ppu_v_clocks[ppu_clock_v]();
|
|
ppu_clock_h++;
|
|
if (ppu_clock_h >= 341)
|
|
{
|
|
mem_board.OnPPUScanlineTick();
|
|
if (ppu_clock_v == ppu_clock_vblank_end)
|
|
{
|
|
ppu_clock_v = 0;
|
|
ppu_frame_finished = true;
|
|
}
|
|
else
|
|
{
|
|
ppu_clock_v++;
|
|
}
|
|
ppu_clock_h -= 341;
|
|
}
|
|
if (ppu_reg_access_happened)
|
|
{
|
|
ppu_reg_access_happened = false;
|
|
ppu_reg_update_func[ppu_reg_io_addr]();
|
|
}
|
|
}
|
|
|
|
public static int GetPixel(int x, int y)
|
|
{
|
|
return ppu_screen_pixels[y * 256 + x];
|
|
}
|
|
|
|
private static void PPUScanlineRender()
|
|
{
|
|
ppu_h_clocks[ppu_clock_h]();
|
|
}
|
|
|
|
private static void PPUScanlineVBLANKStart()
|
|
{
|
|
ppu_is_nmi_time = (ppu_clock_h >= 1) & (ppu_clock_h <= 3);
|
|
if (ppu_is_nmi_time)
|
|
{
|
|
if (ppu_clock_h == 1)
|
|
{
|
|
ppu_reg_2002_VblankStartedFlag = true;
|
|
}
|
|
PPU_NMI_Current = ppu_reg_2002_VblankStartedFlag & ppu_reg_2000_VBI;
|
|
}
|
|
}
|
|
|
|
private static void PPUScanlineVBLANKEnd()
|
|
{
|
|
ppu_is_nmi_time = (ppu_clock_h >= 1) & (ppu_clock_h <= 3);
|
|
if (ppu_clock_h == 1)
|
|
{
|
|
ppu_reg_2002_Sprite0Hit = false;
|
|
ppu_reg_2002_VblankStartedFlag = false;
|
|
ppu_reg_2002_SpriteOverflow = false;
|
|
}
|
|
PPUScanlineRender();
|
|
if (ppu_use_odd_cycle && ppu_clock_h == 339)
|
|
{
|
|
ppu_use_odd_swap = !ppu_use_odd_swap;
|
|
if (!ppu_use_odd_swap & (ppu_reg_2001_show_background || ppu_reg_2001_show_sprites))
|
|
{
|
|
ppu_odd_swap_done = true;
|
|
ppu_clock_h++;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void PPUScanlineVBLANK()
|
|
{
|
|
}
|
|
|
|
private static void PPUHClock_000_Idle()
|
|
{
|
|
if (ppu_odd_swap_done)
|
|
{
|
|
ppu_bkg_fetches[1]();
|
|
ppu_odd_swap_done = false;
|
|
}
|
|
}
|
|
|
|
private static void PPUHClock_1_256_BKGClocks()
|
|
{
|
|
if (ppu_reg_2001_show_background || ppu_reg_2001_show_sprites)
|
|
{
|
|
if (ppu_clock_v != ppu_clock_vblank_end)
|
|
{
|
|
if (ppu_clock_h > 0 && ppu_clock_h < 65)
|
|
{
|
|
ppu_oam_bank_secondary[(ppu_clock_h - 1) & 0x1F] = byte.MaxValue;
|
|
}
|
|
else
|
|
{
|
|
if (ppu_clock_h == 65)
|
|
{
|
|
PPUOamReset();
|
|
}
|
|
if (((ppu_clock_h - 1) & 1) == 0)
|
|
{
|
|
PPUOamEvFetch();
|
|
}
|
|
else
|
|
{
|
|
ppu_oam_phases[ppu_phase_index]();
|
|
}
|
|
if (ppu_clock_h == 256)
|
|
{
|
|
PPUOamClear();
|
|
}
|
|
}
|
|
}
|
|
ppu_bkg_fetches[(ppu_clock_h - 1) & 7]();
|
|
if (ppu_clock_v < 240)
|
|
{
|
|
RenderPixel();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (ppu_clock_v >= 240)
|
|
{
|
|
return;
|
|
}
|
|
if ((ppu_vram_addr & 0x3F00) == 16128)
|
|
{
|
|
if ((ppu_vram_addr & 3) == 0)
|
|
{
|
|
ppu_screen_pixels[ppu_clock_h - 1 + ppu_clock_v * 256] = ppu_palette[(ppu_palette_bank[ppu_vram_addr & 0xC] & ppu_reg_2001_grayscale) | ppu_reg_2001_emphasis];
|
|
}
|
|
else
|
|
{
|
|
ppu_screen_pixels[ppu_clock_h - 1 + ppu_clock_v * 256] = ppu_palette[(ppu_palette_bank[ppu_vram_addr & 0x1F] & ppu_reg_2001_grayscale) | ppu_reg_2001_emphasis];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ppu_screen_pixels[ppu_clock_h - 1 + ppu_clock_v * 256] = ppu_palette[(ppu_palette_bank[0] & ppu_reg_2001_grayscale) | ppu_reg_2001_emphasis];
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void PPUHClock_257_320_SPRClocks()
|
|
{
|
|
if (ppu_reg_2001_show_background || ppu_reg_2001_show_sprites)
|
|
{
|
|
ppu_spr_fetches[(ppu_clock_h - 1) & 7]();
|
|
if (ppu_clock_h == 257)
|
|
{
|
|
ppu_vram_addr = (ushort)((ppu_vram_addr & 0x7BE0u) | (ppu_vram_addr_temp & 0x41Fu));
|
|
}
|
|
if (ppu_clock_v == ppu_clock_vblank_end && ppu_clock_h >= 280 && ppu_clock_h <= 304)
|
|
{
|
|
ppu_vram_addr = (ushort)((ppu_vram_addr & 0x41Fu) | (ppu_vram_addr_temp & 0x7BE0u));
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void PPUHClock_321_336_DUMClocks()
|
|
{
|
|
if (ppu_reg_2001_show_background || ppu_reg_2001_show_sprites)
|
|
{
|
|
ppu_bkg_fetches[(ppu_clock_h - 1) & 7]();
|
|
}
|
|
}
|
|
|
|
private static void PPUHClock_337_340_DUMClocks()
|
|
{
|
|
if (ppu_reg_2001_show_background || ppu_reg_2001_show_sprites)
|
|
{
|
|
ppu_bkg_fetches[(ppu_clock_h - 1) & 1]();
|
|
}
|
|
}
|
|
|
|
private static void PPUBKFetch0()
|
|
{
|
|
ppu_bkgfetch_nt_addr = (ushort)(0x2000u | (ppu_vram_addr & 0xFFFu));
|
|
mem_board.OnPPUAddressUpdate(ref ppu_bkgfetch_nt_addr);
|
|
}
|
|
|
|
private static void PPUBKFetch1()
|
|
{
|
|
mem_board.ReadNMT(ref ppu_bkgfetch_nt_addr, out ppu_bkgfetch_nt_data);
|
|
}
|
|
|
|
private static void PPUBKFetch2()
|
|
{
|
|
ppu_bkgfetch_at_addr = (ushort)(0x23C0u | (ppu_vram_addr & 0xC00u) | ((uint)(ppu_vram_addr >> 4) & 0x38u) | ((uint)(ppu_vram_addr >> 2) & 7u));
|
|
mem_board.OnPPUAddressUpdate(ref ppu_bkgfetch_at_addr);
|
|
}
|
|
|
|
private static void PPUBKFetch3()
|
|
{
|
|
mem_board.ReadNMT(ref ppu_bkgfetch_at_addr, out ppu_bkgfetch_at_data);
|
|
ppu_bkgfetch_at_data = (byte)(ppu_bkgfetch_at_data >> (((ppu_vram_addr >> 4) & 4) | (ppu_vram_addr & 2)));
|
|
}
|
|
|
|
private static void PPUBKFetch4()
|
|
{
|
|
ppu_bkgfetch_lb_addr = (ushort)((uint)(ppu_reg_2000_background_pattern_table_address | (ppu_bkgfetch_nt_data << 4)) | ((uint)(ppu_vram_addr >> 12) & 7u));
|
|
mem_board.OnPPUAddressUpdate(ref ppu_bkgfetch_lb_addr);
|
|
}
|
|
|
|
private static void PPUBKFetch5()
|
|
{
|
|
mem_board.ReadCHR(ref ppu_bkgfetch_lb_addr, out ppu_bkgfetch_lb_data);
|
|
}
|
|
|
|
private static void PPUBKFetch6()
|
|
{
|
|
ppu_bkgfetch_hb_addr = (ushort)((uint)(ppu_reg_2000_background_pattern_table_address | (ppu_bkgfetch_nt_data << 4)) | 8u | ((uint)(ppu_vram_addr >> 12) & 7u));
|
|
mem_board.OnPPUAddressUpdate(ref ppu_bkgfetch_hb_addr);
|
|
}
|
|
|
|
private static void PPUBKFetch7()
|
|
{
|
|
mem_board.ReadCHR(ref ppu_bkgfetch_hb_addr, out ppu_bkgfetch_hb_data);
|
|
ppu_bkg_render_pos = ppu_clock_h + 8;
|
|
ppu_bkg_render_pos %= 336;
|
|
if (ppu_clock_h == 256)
|
|
{
|
|
if ((ppu_vram_addr & 0x7000) != 28672)
|
|
{
|
|
ppu_vram_addr += 4096;
|
|
}
|
|
else
|
|
{
|
|
ppu_vram_addr ^= 28672;
|
|
switch (ppu_vram_addr & 0x3E0)
|
|
{
|
|
case 928:
|
|
ppu_vram_addr ^= 2976;
|
|
break;
|
|
case 992:
|
|
ppu_vram_addr ^= 992;
|
|
break;
|
|
default:
|
|
ppu_vram_addr += 32;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if ((ppu_vram_addr & 0x1F) == 31)
|
|
{
|
|
ppu_vram_addr ^= 1055;
|
|
}
|
|
else
|
|
{
|
|
ppu_vram_addr++;
|
|
}
|
|
for (ppu_bkg_render_i = 0; ppu_bkg_render_i < 8; ppu_bkg_render_i++)
|
|
{
|
|
ppu_bkg_render_tmp_val = ((ppu_bkgfetch_at_data << 2) & 0xC) | ((ppu_bkgfetch_lb_data >> 7) & 1) | ((ppu_bkgfetch_hb_data >> 6) & 2);
|
|
ppu_bkg_pixels[ppu_bkg_render_i + ppu_bkg_render_pos] = ppu_bkg_render_tmp_val;
|
|
ppu_bkgfetch_lb_data <<= 1;
|
|
ppu_bkgfetch_hb_data <<= 1;
|
|
}
|
|
}
|
|
|
|
private static void PPUSPRFetch0()
|
|
{
|
|
ppu_sprfetch_slot = (ppu_clock_h - 1 >> 3) & 7;
|
|
ppu_sprfetch_slot = 7 - ppu_sprfetch_slot;
|
|
ppu_sprfetch_y_data = ppu_oam_bank_secondary[ppu_sprfetch_slot * 4];
|
|
ppu_sprfetch_t_data = ppu_oam_bank_secondary[ppu_sprfetch_slot * 4 + 1];
|
|
ppu_sprfetch_at_data = ppu_oam_bank_secondary[ppu_sprfetch_slot * 4 + 2];
|
|
ppu_sprfetch_x_data = ppu_oam_bank_secondary[ppu_sprfetch_slot * 4 + 3];
|
|
ppu_temp_comparator = (ppu_clock_v - ppu_sprfetch_y_data) ^ (((ppu_sprfetch_at_data & 0x80u) != 0) ? 15 : 0);
|
|
if (ppu_reg_2000_Sprite_size == 16)
|
|
{
|
|
ppu_sprfetch_lb_addr = (ushort)(((uint)(ppu_sprfetch_t_data << 12) & 0x1000u) | ((uint)(ppu_sprfetch_t_data << 4) & 0xFE0u) | ((uint)(ppu_temp_comparator << 1) & 0x10u) | ((uint)ppu_temp_comparator & 7u));
|
|
}
|
|
else
|
|
{
|
|
ppu_sprfetch_lb_addr = (ushort)((uint)(ppu_reg_2000_sprite_pattern_table_address_for_8x8_sprites | (ppu_sprfetch_t_data << 4)) | ((uint)ppu_temp_comparator & 7u));
|
|
}
|
|
mem_board.OnPPUAddressUpdate(ref ppu_sprfetch_lb_addr);
|
|
}
|
|
|
|
private static void PPUSPRFetch1()
|
|
{
|
|
ppu_is_sprfetch = true;
|
|
mem_board.ReadCHR(ref ppu_sprfetch_lb_addr, out ppu_sprfetch_lb_data);
|
|
ppu_is_sprfetch = false;
|
|
if ((ppu_sprfetch_at_data & 0x40u) != 0)
|
|
{
|
|
ppu_sprfetch_lb_data = reverseLookup[ppu_sprfetch_lb_data];
|
|
}
|
|
}
|
|
|
|
private static void PPUSPRFetch2()
|
|
{
|
|
ppu_sprfetch_hb_addr = (ushort)(ppu_sprfetch_lb_addr | 8u);
|
|
mem_board.OnPPUAddressUpdate(ref ppu_sprfetch_hb_addr);
|
|
}
|
|
|
|
private static void PPUSPRFetch3()
|
|
{
|
|
ppu_is_sprfetch = true;
|
|
mem_board.ReadCHR(ref ppu_sprfetch_hb_addr, out ppu_sprfetch_hb_data);
|
|
ppu_is_sprfetch = false;
|
|
if ((ppu_sprfetch_at_data & 0x40u) != 0)
|
|
{
|
|
ppu_sprfetch_hb_data = reverseLookup[ppu_sprfetch_hb_data];
|
|
}
|
|
if (ppu_sprfetch_x_data == byte.MaxValue)
|
|
{
|
|
return;
|
|
}
|
|
for (ppu_bkg_render_i = 0; ppu_bkg_render_i < 8; ppu_bkg_render_i++)
|
|
{
|
|
if (ppu_sprfetch_x_data < byte.MaxValue)
|
|
{
|
|
ppu_bkg_render_tmp_val = ((ppu_sprfetch_at_data << 2) & 0xC) | ((ppu_sprfetch_lb_data >> 7) & 1) | ((ppu_sprfetch_hb_data >> 6) & 2);
|
|
if (((uint)ppu_bkg_render_tmp_val & 3u) != 0)
|
|
{
|
|
ppu_spr_pixels[ppu_sprfetch_x_data] = ppu_bkg_render_tmp_val;
|
|
if (ppu_sprfetch_slot == 0 && ppu_sprite0_should_hit)
|
|
{
|
|
ppu_spr_pixels[ppu_sprfetch_x_data] |= 16384;
|
|
}
|
|
if ((ppu_sprfetch_at_data & 0x20) == 0)
|
|
{
|
|
ppu_spr_pixels[ppu_sprfetch_x_data] |= 32768;
|
|
}
|
|
}
|
|
ppu_sprfetch_lb_data <<= 1;
|
|
ppu_sprfetch_hb_data <<= 1;
|
|
ppu_sprfetch_x_data++;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void PPUOamReset()
|
|
{
|
|
ppu_oamev_n = 0;
|
|
ppu_oamev_m = 0;
|
|
ppu_oamev_slot = 0;
|
|
ppu_phase_index = 0;
|
|
ppu_sprite0_should_hit = false;
|
|
}
|
|
|
|
private static void PPUOamClear()
|
|
{
|
|
for (int i = 0; i < ppu_spr_pixels.Length; i++)
|
|
{
|
|
ppu_spr_pixels[i] = 0;
|
|
}
|
|
}
|
|
|
|
private static void PPUOamEvFetch()
|
|
{
|
|
ppu_fetch_data = ppu_oam_bank[ppu_oamev_n * 4 + ppu_oamev_m];
|
|
}
|
|
|
|
private static void PPUOamPhase0()
|
|
{
|
|
ppu_oamev_compare = ppu_clock_v >= ppu_fetch_data && ppu_clock_v < ppu_fetch_data + ppu_reg_2000_Sprite_size;
|
|
if (ppu_oamev_compare)
|
|
{
|
|
ppu_oam_bank_secondary[ppu_oamev_slot * 4] = ppu_fetch_data;
|
|
ppu_oamev_m = 1;
|
|
ppu_phase_index++;
|
|
if (ppu_oamev_n == 0)
|
|
{
|
|
ppu_sprite0_should_hit = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ppu_oamev_m = 0;
|
|
ppu_oamev_n++;
|
|
if (ppu_oamev_n == 64)
|
|
{
|
|
ppu_oamev_n = 0;
|
|
ppu_phase_index = 8;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void PPUOamPhase1()
|
|
{
|
|
ppu_oam_bank_secondary[ppu_oamev_slot * 4 + ppu_oamev_m] = ppu_fetch_data;
|
|
ppu_oamev_m = 2;
|
|
ppu_phase_index++;
|
|
}
|
|
|
|
private static void PPUOamPhase2()
|
|
{
|
|
ppu_oam_bank_secondary[ppu_oamev_slot * 4 + ppu_oamev_m] = ppu_fetch_data;
|
|
ppu_oamev_m = 3;
|
|
ppu_phase_index++;
|
|
}
|
|
|
|
private static void PPUOamPhase3()
|
|
{
|
|
ppu_oam_bank_secondary[ppu_oamev_slot * 4 + ppu_oamev_m] = ppu_fetch_data;
|
|
ppu_oamev_m = 0;
|
|
ppu_oamev_n++;
|
|
ppu_oamev_slot++;
|
|
if (ppu_oamev_n == 64)
|
|
{
|
|
ppu_oamev_n = 0;
|
|
ppu_phase_index = 8;
|
|
}
|
|
else if (ppu_oamev_slot < 8)
|
|
{
|
|
ppu_phase_index = 0;
|
|
}
|
|
else if (ppu_oamev_slot == 8)
|
|
{
|
|
ppu_phase_index = 4;
|
|
}
|
|
}
|
|
|
|
private static void PPUOamPhase4()
|
|
{
|
|
ppu_oamev_compare = ppu_clock_v >= ppu_fetch_data && ppu_clock_v < ppu_fetch_data + ppu_reg_2000_Sprite_size;
|
|
if (ppu_oamev_compare)
|
|
{
|
|
ppu_oamev_m = 1;
|
|
ppu_phase_index++;
|
|
ppu_reg_2002_SpriteOverflow = true;
|
|
return;
|
|
}
|
|
ppu_oamev_m++;
|
|
if (ppu_oamev_m == 4)
|
|
{
|
|
ppu_oamev_m = 0;
|
|
}
|
|
ppu_oamev_n++;
|
|
if (ppu_oamev_n == 64)
|
|
{
|
|
ppu_oamev_n = 0;
|
|
ppu_phase_index = 8;
|
|
}
|
|
else
|
|
{
|
|
ppu_phase_index = 4;
|
|
}
|
|
}
|
|
|
|
private static void PPUOamPhase5()
|
|
{
|
|
ppu_oamev_m = 2;
|
|
ppu_phase_index++;
|
|
}
|
|
|
|
private static void PPUOamPhase6()
|
|
{
|
|
ppu_oamev_m = 3;
|
|
ppu_phase_index++;
|
|
}
|
|
|
|
private static void PPUOamPhase7()
|
|
{
|
|
ppu_oamev_m = 0;
|
|
ppu_oamev_n++;
|
|
if (ppu_oamev_n == 64)
|
|
{
|
|
ppu_oamev_n = 0;
|
|
}
|
|
ppu_phase_index = 8;
|
|
}
|
|
|
|
private static void PPUOamPhase8()
|
|
{
|
|
ppu_oamev_n++;
|
|
if (ppu_oamev_n >= 64)
|
|
{
|
|
ppu_oamev_n = 0;
|
|
}
|
|
}
|
|
|
|
private static void RenderPixel()
|
|
{
|
|
if (ppu_clock_v == ppu_clock_vblank_end)
|
|
{
|
|
return;
|
|
}
|
|
ppu_render_x = ppu_clock_h - 1;
|
|
ppu_render_y = ppu_clock_v * 256;
|
|
if (ppu_render_x < 8)
|
|
{
|
|
if (ppu_reg_2001_show_background_in_leftmost_8_pixels_of_screen)
|
|
{
|
|
ppu_bkg_current_pixel = 0x3F00 | ppu_bkg_pixels[ppu_render_x + ppu_vram_finex];
|
|
}
|
|
else
|
|
{
|
|
ppu_bkg_current_pixel = 16128;
|
|
}
|
|
if (ppu_reg_2001_show_sprites_in_leftmost_8_pixels_of_screen)
|
|
{
|
|
ppu_spr_current_pixel = 0x3F10 | ppu_spr_pixels[ppu_render_x];
|
|
}
|
|
else
|
|
{
|
|
ppu_spr_current_pixel = 16144;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!ppu_reg_2001_show_background)
|
|
{
|
|
ppu_bkg_current_pixel = 16128;
|
|
}
|
|
else
|
|
{
|
|
ppu_bkg_current_pixel = 0x3F00 | ppu_bkg_pixels[ppu_render_x + ppu_vram_finex];
|
|
}
|
|
if (!ppu_reg_2001_show_sprites || ppu_clock_v == 0)
|
|
{
|
|
ppu_spr_current_pixel = 16144;
|
|
}
|
|
else
|
|
{
|
|
ppu_spr_current_pixel = 0x3F10 | ppu_spr_pixels[ppu_render_x];
|
|
}
|
|
}
|
|
ppu_current_pixel = 0;
|
|
if (((uint)ppu_spr_current_pixel & 0x8000u) != 0)
|
|
{
|
|
ppu_current_pixel = ppu_spr_current_pixel;
|
|
}
|
|
else
|
|
{
|
|
ppu_current_pixel = ppu_bkg_current_pixel;
|
|
}
|
|
if ((ppu_bkg_current_pixel & 3) == 0)
|
|
{
|
|
ppu_current_pixel = ppu_spr_current_pixel;
|
|
}
|
|
else if ((ppu_spr_current_pixel & 3) == 0)
|
|
{
|
|
ppu_current_pixel = ppu_bkg_current_pixel;
|
|
}
|
|
else if (((uint)ppu_spr_pixels[ppu_render_x] & 0x4000u) != 0)
|
|
{
|
|
ppu_reg_2002_Sprite0Hit = true;
|
|
}
|
|
if ((ppu_current_pixel & 3) == 0)
|
|
{
|
|
ppu_screen_pixels[ppu_render_x + ppu_render_y] = ppu_palette[(ppu_palette_bank[ppu_current_pixel & 0xC] & ppu_reg_2001_grayscale) | ppu_reg_2001_emphasis];
|
|
}
|
|
else
|
|
{
|
|
ppu_screen_pixels[ppu_render_x + ppu_render_y] = ppu_palette[(ppu_palette_bank[ppu_current_pixel & 0x1F] & ppu_reg_2001_grayscale) | ppu_reg_2001_emphasis];
|
|
}
|
|
}
|
|
|
|
private static void PPUIORead(ref ushort addr, out byte value)
|
|
{
|
|
ppu_reg_io_addr = (byte)(addr & 7u);
|
|
ppu_reg_access_happened = true;
|
|
ppu_reg_access_w = false;
|
|
ppu_reg_read_func[ppu_reg_io_addr]();
|
|
value = ppu_reg_io_db;
|
|
}
|
|
|
|
private static void PPUIOWrite(ref ushort addr, ref byte value)
|
|
{
|
|
ppu_reg_io_addr = (byte)(addr & 7u);
|
|
ppu_reg_io_db = value;
|
|
ppu_reg_access_w = true;
|
|
ppu_reg_access_happened = true;
|
|
}
|
|
|
|
private static void PPUOnRegister2000()
|
|
{
|
|
if (ppu_reg_access_w)
|
|
{
|
|
ppu_vram_addr_temp = (ushort)((ppu_vram_addr_temp & 0x73FFu) | (uint)((ppu_reg_io_db & 3) << 10));
|
|
if ((ppu_reg_io_db & 4u) != 0)
|
|
{
|
|
ppu_reg_2000_vram_address_increament = 32;
|
|
}
|
|
else
|
|
{
|
|
ppu_reg_2000_vram_address_increament = 1;
|
|
}
|
|
if ((ppu_reg_io_db & 8u) != 0)
|
|
{
|
|
ppu_reg_2000_sprite_pattern_table_address_for_8x8_sprites = 4096;
|
|
}
|
|
else
|
|
{
|
|
ppu_reg_2000_sprite_pattern_table_address_for_8x8_sprites = 0;
|
|
}
|
|
if ((ppu_reg_io_db & 0x10u) != 0)
|
|
{
|
|
ppu_reg_2000_background_pattern_table_address = 4096;
|
|
}
|
|
else
|
|
{
|
|
ppu_reg_2000_background_pattern_table_address = 0;
|
|
}
|
|
if ((ppu_reg_io_db & 0x20u) != 0)
|
|
{
|
|
ppu_reg_2000_Sprite_size = 16;
|
|
}
|
|
else
|
|
{
|
|
ppu_reg_2000_Sprite_size = 8;
|
|
}
|
|
if (!ppu_reg_2000_VBI && (ppu_reg_io_db & 0x80u) != 0 && ppu_reg_2002_VblankStartedFlag)
|
|
{
|
|
PPU_NMI_Current = true;
|
|
}
|
|
ppu_reg_2000_VBI = (ppu_reg_io_db & 0x80) != 0;
|
|
if (!ppu_reg_2000_VBI && ppu_is_nmi_time)
|
|
{
|
|
PPU_NMI_Current = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void PPUOnRegister2001()
|
|
{
|
|
if (ppu_reg_access_w)
|
|
{
|
|
ppu_reg_2001_show_background_in_leftmost_8_pixels_of_screen = (ppu_reg_io_db & 2) != 0;
|
|
ppu_reg_2001_show_sprites_in_leftmost_8_pixels_of_screen = (ppu_reg_io_db & 4) != 0;
|
|
ppu_reg_2001_show_background = (ppu_reg_io_db & 8) != 0;
|
|
ppu_reg_2001_show_sprites = (ppu_reg_io_db & 0x10) != 0;
|
|
ppu_reg_2001_grayscale = ((((uint)ppu_reg_io_db & (true ? 1u : 0u)) != 0) ? 48 : 63);
|
|
ppu_reg_2001_emphasis = (ppu_reg_io_db & 0xE0) << 1;
|
|
}
|
|
}
|
|
|
|
private static void PPUOnRegister2002()
|
|
{
|
|
if (!ppu_reg_access_w)
|
|
{
|
|
ppu_vram_flip_flop = false;
|
|
ppu_reg_2002_VblankStartedFlag = false;
|
|
if (ppu_clock_v == ppu_clock_vblank_start)
|
|
{
|
|
PPU_NMI_Current = ppu_reg_2002_VblankStartedFlag & ppu_reg_2000_VBI;
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void PPUOnRegister2003()
|
|
{
|
|
if (ppu_reg_access_w)
|
|
{
|
|
ppu_reg_2003_oam_addr = ppu_reg_io_db;
|
|
}
|
|
}
|
|
|
|
private static void PPUOnRegister2004()
|
|
{
|
|
if (ppu_reg_access_w)
|
|
{
|
|
if (ppu_clock_v < 240 && IsRenderingOn())
|
|
{
|
|
ppu_reg_io_db = byte.MaxValue;
|
|
}
|
|
if ((ppu_reg_2003_oam_addr & 3) == 2)
|
|
{
|
|
ppu_reg_io_db &= 227;
|
|
}
|
|
ppu_oam_bank[ppu_reg_2003_oam_addr] = ppu_reg_io_db;
|
|
ppu_reg_2003_oam_addr = (byte)((uint)(ppu_reg_2003_oam_addr + 1) & 0xFFu);
|
|
}
|
|
}
|
|
|
|
private static void PPUOnRegister2005()
|
|
{
|
|
if (ppu_reg_access_w)
|
|
{
|
|
if (!ppu_vram_flip_flop)
|
|
{
|
|
ppu_vram_addr_temp = (ushort)((ppu_vram_addr_temp & 0x7FE0u) | (uint)((ppu_reg_io_db & 0xF8) >> 3));
|
|
ppu_vram_finex = (byte)(ppu_reg_io_db & 7u);
|
|
}
|
|
else
|
|
{
|
|
ppu_vram_addr_temp = (ushort)((ppu_vram_addr_temp & 0xC1Fu) | (uint)((ppu_reg_io_db & 7) << 12) | (uint)((ppu_reg_io_db & 0xF8) << 2));
|
|
}
|
|
ppu_vram_flip_flop = !ppu_vram_flip_flop;
|
|
}
|
|
}
|
|
|
|
private static void PPUOnRegister2006()
|
|
{
|
|
if (ppu_reg_access_w)
|
|
{
|
|
if (!ppu_vram_flip_flop)
|
|
{
|
|
ppu_vram_addr_temp = (ushort)((ppu_vram_addr_temp & 0xFFu) | (uint)((ppu_reg_io_db & 0x3F) << 8));
|
|
}
|
|
else
|
|
{
|
|
ppu_vram_addr_temp = (ushort)((ppu_vram_addr_temp & 0x7F00u) | ppu_reg_io_db);
|
|
ppu_vram_addr = ppu_vram_addr_temp;
|
|
mem_board.OnPPUAddressUpdate(ref ppu_vram_addr);
|
|
}
|
|
ppu_vram_flip_flop = !ppu_vram_flip_flop;
|
|
}
|
|
}
|
|
|
|
private static void PPUOnRegister2007()
|
|
{
|
|
if (ppu_reg_access_w)
|
|
{
|
|
ppu_vram_addr_access_temp = (ushort)(ppu_vram_addr & 0x3FFFu);
|
|
if (ppu_vram_addr_access_temp < 8192)
|
|
{
|
|
mem_board.WriteCHR(ref ppu_vram_addr_access_temp, ref ppu_reg_io_db);
|
|
}
|
|
else if (ppu_vram_addr_access_temp < 16128)
|
|
{
|
|
mem_board.WriteNMT(ref ppu_vram_addr_access_temp, ref ppu_reg_io_db);
|
|
}
|
|
else if ((ppu_vram_addr_access_temp & 3u) != 0)
|
|
{
|
|
ppu_palette_bank[ppu_vram_addr_access_temp & 0x1F] = ppu_reg_io_db;
|
|
}
|
|
else
|
|
{
|
|
ppu_palette_bank[ppu_vram_addr_access_temp & 0xC] = ppu_reg_io_db;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((ppu_vram_addr & 0x3F00) == 16128)
|
|
{
|
|
ppu_vram_addr_access_temp = (ushort)(ppu_vram_addr & 0x2FFFu);
|
|
}
|
|
else
|
|
{
|
|
ppu_vram_addr_access_temp = (ushort)(ppu_vram_addr & 0x3FFFu);
|
|
}
|
|
if (ppu_vram_addr_access_temp < 8192)
|
|
{
|
|
mem_board.ReadCHR(ref ppu_vram_addr_access_temp, out ppu_vram_data);
|
|
}
|
|
else if (ppu_vram_addr_access_temp < 16128)
|
|
{
|
|
mem_board.ReadNMT(ref ppu_vram_addr_access_temp, out ppu_vram_data);
|
|
}
|
|
}
|
|
ppu_vram_addr = (ushort)((uint)(ppu_vram_addr + ppu_reg_2000_vram_address_increament) & 0x7FFFu);
|
|
mem_board.OnPPUAddressUpdate(ref ppu_vram_addr);
|
|
}
|
|
|
|
private static void PPURead2000()
|
|
{
|
|
}
|
|
|
|
private static void PPURead2001()
|
|
{
|
|
}
|
|
|
|
private static void PPURead2002()
|
|
{
|
|
ppu_reg_io_db = (byte)((ppu_reg_io_db & 0xDFu) | (ppu_reg_2002_SpriteOverflow ? 32u : 0u));
|
|
ppu_reg_io_db = (byte)((ppu_reg_io_db & 0xBFu) | (ppu_reg_2002_Sprite0Hit ? 64u : 0u));
|
|
ppu_reg_io_db = (byte)((ppu_reg_io_db & 0x7Fu) | (ppu_reg_2002_VblankStartedFlag ? 128u : 0u));
|
|
}
|
|
|
|
private static void PPURead2003()
|
|
{
|
|
}
|
|
|
|
private static void PPURead2004()
|
|
{
|
|
ppu_reg_io_db = ppu_oam_bank[ppu_reg_2003_oam_addr];
|
|
if (ppu_clock_v < 240 && IsRenderingOn())
|
|
{
|
|
if (ppu_clock_h < 64)
|
|
{
|
|
ppu_reg_io_db = byte.MaxValue;
|
|
}
|
|
else if (ppu_clock_h < 192)
|
|
{
|
|
ppu_reg_io_db = ppu_oam_bank[(ppu_clock_h - 64 << 1) & 0xFC];
|
|
}
|
|
else if (ppu_clock_h < 256)
|
|
{
|
|
ppu_reg_io_db = (((ppu_clock_h & 1) == 1) ? ppu_oam_bank[252] : ppu_oam_bank[(ppu_clock_h - 192 << 1) & 0xFC]);
|
|
}
|
|
else if (ppu_clock_h < 320)
|
|
{
|
|
ppu_reg_io_db = byte.MaxValue;
|
|
}
|
|
else
|
|
{
|
|
ppu_reg_io_db = ppu_oam_bank[0];
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void PPURead2005()
|
|
{
|
|
}
|
|
|
|
private static void PPURead2006()
|
|
{
|
|
}
|
|
|
|
private static void PPURead2007()
|
|
{
|
|
ppu_vram_addr_access_temp = (ushort)(ppu_vram_addr & 0x3FFFu);
|
|
if (ppu_vram_addr_access_temp < 16128)
|
|
{
|
|
ppu_reg_io_db = ppu_vram_data;
|
|
}
|
|
else if ((ppu_vram_addr_access_temp & 3u) != 0)
|
|
{
|
|
ppu_reg_io_db = ppu_palette_bank[ppu_vram_addr_access_temp & 0x1F];
|
|
}
|
|
else
|
|
{
|
|
ppu_reg_io_db = ppu_palette_bank[ppu_vram_addr_access_temp & 0xC];
|
|
}
|
|
}
|
|
|
|
internal static bool IsRenderingOn()
|
|
{
|
|
if (!ppu_reg_2001_show_background)
|
|
{
|
|
return ppu_reg_2001_show_sprites;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
internal static bool IsInRender()
|
|
{
|
|
if (ppu_clock_v >= 240)
|
|
{
|
|
return ppu_clock_v == ppu_clock_vblank_end;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private static void PPUWriteState(ref BinaryWriter bin)
|
|
{
|
|
bin.Write(ppu_clock_h);
|
|
bin.Write(ppu_clock_v);
|
|
bin.Write(ppu_clock_vblank_start);
|
|
bin.Write(ppu_clock_vblank_end);
|
|
bin.Write(ppu_use_odd_cycle);
|
|
bin.Write(ppu_use_odd_swap);
|
|
bin.Write(ppu_is_nmi_time);
|
|
bin.Write(ppu_frame_finished);
|
|
bin.Write(ppu_oam_bank);
|
|
bin.Write(ppu_oam_bank_secondary);
|
|
bin.Write(ppu_palette_bank);
|
|
bin.Write(ppu_reg_io_db);
|
|
bin.Write(ppu_reg_io_addr);
|
|
bin.Write(ppu_reg_access_happened);
|
|
bin.Write(ppu_reg_access_w);
|
|
bin.Write(ppu_reg_2000_vram_address_increament);
|
|
bin.Write(ppu_reg_2000_sprite_pattern_table_address_for_8x8_sprites);
|
|
bin.Write(ppu_reg_2000_background_pattern_table_address);
|
|
bin.Write(ppu_reg_2000_Sprite_size);
|
|
bin.Write(ppu_reg_2000_VBI);
|
|
bin.Write(ppu_reg_2001_show_background_in_leftmost_8_pixels_of_screen);
|
|
bin.Write(ppu_reg_2001_show_sprites_in_leftmost_8_pixels_of_screen);
|
|
bin.Write(ppu_reg_2001_show_background);
|
|
bin.Write(ppu_reg_2001_show_sprites);
|
|
bin.Write(ppu_reg_2001_grayscale);
|
|
bin.Write(ppu_reg_2001_emphasis);
|
|
bin.Write(ppu_reg_2002_SpriteOverflow);
|
|
bin.Write(ppu_reg_2002_Sprite0Hit);
|
|
bin.Write(ppu_reg_2002_VblankStartedFlag);
|
|
bin.Write(ppu_reg_2003_oam_addr);
|
|
bin.Write(ppu_vram_addr);
|
|
bin.Write(ppu_vram_data);
|
|
bin.Write(ppu_vram_addr_temp);
|
|
bin.Write(ppu_vram_addr_access_temp);
|
|
bin.Write(ppu_vram_flip_flop);
|
|
bin.Write(ppu_vram_finex);
|
|
bin.Write(ppu_bkgfetch_nt_addr);
|
|
bin.Write(ppu_bkgfetch_nt_data);
|
|
bin.Write(ppu_bkgfetch_at_addr);
|
|
bin.Write(ppu_bkgfetch_at_data);
|
|
bin.Write(ppu_bkgfetch_lb_addr);
|
|
bin.Write(ppu_bkgfetch_lb_data);
|
|
bin.Write(ppu_bkgfetch_hb_addr);
|
|
bin.Write(ppu_bkgfetch_hb_data);
|
|
bin.Write(ppu_sprfetch_slot);
|
|
bin.Write(ppu_sprfetch_y_data);
|
|
bin.Write(ppu_sprfetch_t_data);
|
|
bin.Write(ppu_sprfetch_at_data);
|
|
bin.Write(ppu_sprfetch_x_data);
|
|
bin.Write(ppu_sprfetch_lb_addr);
|
|
bin.Write(ppu_sprfetch_lb_data);
|
|
bin.Write(ppu_sprfetch_hb_addr);
|
|
bin.Write(ppu_sprfetch_hb_data);
|
|
bin.Write(ppu_bkg_render_i);
|
|
bin.Write(ppu_bkg_render_pos);
|
|
bin.Write(ppu_bkg_render_tmp_val);
|
|
bin.Write(ppu_bkg_current_pixel);
|
|
bin.Write(ppu_spr_current_pixel);
|
|
bin.Write(ppu_current_pixel);
|
|
bin.Write(ppu_render_x);
|
|
bin.Write(0);
|
|
bin.Write(ppu_oamev_n);
|
|
bin.Write(ppu_oamev_m);
|
|
bin.Write(ppu_oamev_compare);
|
|
bin.Write(ppu_oamev_slot);
|
|
bin.Write(ppu_fetch_data);
|
|
bin.Write(ppu_phase_index);
|
|
bin.Write(ppu_sprite0_should_hit);
|
|
}
|
|
|
|
private static void PPUReadState(ref BinaryReader bin)
|
|
{
|
|
ppu_clock_h = bin.ReadInt32();
|
|
ppu_clock_v = bin.ReadUInt16();
|
|
ppu_clock_vblank_start = bin.ReadUInt16();
|
|
ppu_clock_vblank_end = bin.ReadUInt16();
|
|
ppu_use_odd_cycle = bin.ReadBoolean();
|
|
ppu_use_odd_swap = bin.ReadBoolean();
|
|
ppu_is_nmi_time = bin.ReadBoolean();
|
|
ppu_frame_finished = bin.ReadBoolean();
|
|
bin.Read(ppu_oam_bank, 0, ppu_oam_bank.Length);
|
|
bin.Read(ppu_oam_bank_secondary, 0, ppu_oam_bank_secondary.Length);
|
|
bin.Read(ppu_palette_bank, 0, ppu_palette_bank.Length);
|
|
ppu_reg_io_db = bin.ReadByte();
|
|
ppu_reg_io_addr = bin.ReadByte();
|
|
ppu_reg_access_happened = bin.ReadBoolean();
|
|
ppu_reg_access_w = bin.ReadBoolean();
|
|
ppu_reg_2000_vram_address_increament = bin.ReadByte();
|
|
ppu_reg_2000_sprite_pattern_table_address_for_8x8_sprites = bin.ReadUInt16();
|
|
ppu_reg_2000_background_pattern_table_address = bin.ReadUInt16();
|
|
ppu_reg_2000_Sprite_size = bin.ReadByte();
|
|
ppu_reg_2000_VBI = bin.ReadBoolean();
|
|
ppu_reg_2001_show_background_in_leftmost_8_pixels_of_screen = bin.ReadBoolean();
|
|
ppu_reg_2001_show_sprites_in_leftmost_8_pixels_of_screen = bin.ReadBoolean();
|
|
ppu_reg_2001_show_background = bin.ReadBoolean();
|
|
ppu_reg_2001_show_sprites = bin.ReadBoolean();
|
|
ppu_reg_2001_grayscale = bin.ReadInt32();
|
|
ppu_reg_2001_emphasis = bin.ReadInt32();
|
|
ppu_reg_2002_SpriteOverflow = bin.ReadBoolean();
|
|
ppu_reg_2002_Sprite0Hit = bin.ReadBoolean();
|
|
ppu_reg_2002_VblankStartedFlag = bin.ReadBoolean();
|
|
ppu_reg_2003_oam_addr = bin.ReadByte();
|
|
ppu_vram_addr = bin.ReadUInt16();
|
|
ppu_vram_data = bin.ReadByte();
|
|
ppu_vram_addr_temp = bin.ReadUInt16();
|
|
ppu_vram_addr_access_temp = bin.ReadUInt16();
|
|
ppu_vram_flip_flop = bin.ReadBoolean();
|
|
ppu_vram_finex = bin.ReadByte();
|
|
ppu_bkgfetch_nt_addr = bin.ReadUInt16();
|
|
ppu_bkgfetch_nt_data = bin.ReadByte();
|
|
ppu_bkgfetch_at_addr = bin.ReadUInt16();
|
|
ppu_bkgfetch_at_data = bin.ReadByte();
|
|
ppu_bkgfetch_lb_addr = bin.ReadUInt16();
|
|
ppu_bkgfetch_lb_data = bin.ReadByte();
|
|
ppu_bkgfetch_hb_addr = bin.ReadUInt16();
|
|
ppu_bkgfetch_hb_data = bin.ReadByte();
|
|
ppu_sprfetch_slot = bin.ReadInt32();
|
|
ppu_sprfetch_y_data = bin.ReadByte();
|
|
ppu_sprfetch_t_data = bin.ReadByte();
|
|
ppu_sprfetch_at_data = bin.ReadByte();
|
|
ppu_sprfetch_x_data = bin.ReadByte();
|
|
ppu_sprfetch_lb_addr = bin.ReadUInt16();
|
|
ppu_sprfetch_lb_data = bin.ReadByte();
|
|
ppu_sprfetch_hb_addr = bin.ReadUInt16();
|
|
ppu_sprfetch_hb_data = bin.ReadByte();
|
|
ppu_bkg_render_i = bin.ReadInt32();
|
|
ppu_bkg_render_pos = bin.ReadInt32();
|
|
ppu_bkg_render_tmp_val = bin.ReadInt32();
|
|
ppu_bkg_current_pixel = bin.ReadInt32();
|
|
ppu_spr_current_pixel = bin.ReadInt32();
|
|
ppu_current_pixel = bin.ReadInt32();
|
|
ppu_render_x = bin.ReadInt32();
|
|
bin.ReadInt32();
|
|
ppu_oamev_n = bin.ReadByte();
|
|
ppu_oamev_m = bin.ReadByte();
|
|
ppu_oamev_compare = bin.ReadBoolean();
|
|
ppu_oamev_slot = bin.ReadByte();
|
|
ppu_fetch_data = bin.ReadByte();
|
|
ppu_phase_index = bin.ReadByte();
|
|
ppu_sprite0_should_hit = bin.ReadBoolean();
|
|
}
|
|
|
|
internal static void CheckGame(string fileName, out bool valid)
|
|
{
|
|
string text = Path.GetExtension(fileName).ToLower();
|
|
if (text != null && text == ".nes")
|
|
{
|
|
Tracer.WriteLine("Checking INES header ...");
|
|
INes nes = new INes();
|
|
nes.Load(fileName, loadDumps: false);
|
|
valid = nes.IsValid;
|
|
Tracer.WriteLine("INES header is valid.");
|
|
}
|
|
else
|
|
{
|
|
Tracer.WriteWarning("File format is not supported. Format: " + Path.GetExtension(fileName));
|
|
valid = false;
|
|
}
|
|
}
|
|
|
|
internal static void Initialize()
|
|
{
|
|
Tracer.WriteLine("Loading database file ...");
|
|
NesCartDatabase.LoadDatabase(out bool success);
|
|
|
|
if (success)
|
|
{
|
|
Tracer.WriteInformation("Nes Cart database file loaded successfully.");
|
|
}
|
|
else
|
|
{
|
|
Tracer.WriteError("Error loading Nes Cart database file.");
|
|
}
|
|
|
|
FrameLimiterEnabled = true;
|
|
CPUInitialize();
|
|
PPUInitialize();
|
|
APUInitialize();
|
|
PORTSInitialize();
|
|
}
|
|
|
|
internal static void SetupRenderingMethods(RenderVideoFrame renderVideo, RenderAudioSamples renderAudio, TogglePause renderTogglePause, GetIsPlaying renderGetIsPlaying)
|
|
{
|
|
render_initialized = false;
|
|
render_video = renderVideo;
|
|
render_audio = renderAudio;
|
|
render_audio_toggle_pause = renderTogglePause;
|
|
render_audio_get_is_playing = renderGetIsPlaying;
|
|
render_initialized = render_video != null && render_audio != null && render_audio_toggle_pause != null && render_audio_get_is_playing != null;
|
|
if (render_initialized)
|
|
{
|
|
Tracer.WriteInformation("Renderer methods initialized successfully.");
|
|
return;
|
|
}
|
|
Tracer.WriteError("ERROR RENDERER INITIALIZING !!");
|
|
Tracer.WriteError("Faild to initialize the renderers methods. Please use the method 'SetupRenderingMethods' to initialize the renderers methods before you can run the emulation.");
|
|
}
|
|
|
|
public static void LoadGame(string fileName, out bool success, bool useThread)
|
|
{
|
|
if (!render_initialized)
|
|
{
|
|
Tracer.WriteError("NO RENDERER INITIALIZED !! EMU CANNOT BE INTIALIZED WITHOUT A RENDERER !!");
|
|
Tracer.WriteError("Please use the method 'SetupRenderingMethods' to initialize the renderers methods before you can run the emulation.");
|
|
success = false;
|
|
return;
|
|
}
|
|
Tracer.WriteLine("Checking INES header ...");
|
|
INes nes = new INes();
|
|
nes.Load(fileName, loadDumps: true);
|
|
if (nes.IsValid)
|
|
{
|
|
emu_request_mode = RequestMode.None;
|
|
CurrentFilePath = fileName;
|
|
if (ON)
|
|
{
|
|
ShutDown();
|
|
}
|
|
Tracer.WriteLine("INES header is valid, loading game ...");
|
|
ApplyRegionSetting();
|
|
MEMInitialize(nes);
|
|
ApplyAudioSettings();
|
|
ApplyFrameSkipSettings();
|
|
ApplyPaletteSetting();
|
|
PORTSInitialize();
|
|
hardReset();
|
|
Tracer.WriteLine("EMU is ready.");
|
|
success = true;
|
|
ON = true;
|
|
PAUSED = false;
|
|
if (useThread)
|
|
{
|
|
Tracer.WriteLine("Running in a thread ... using custom frame limiter.");
|
|
FrameLimiterEnabled = true;
|
|
currentFrame = 0;
|
|
mainThread = new Thread(EmuClock);
|
|
mainThread.Start();
|
|
}
|
|
MyNesMain.VideoProvider.SignalToggle(started: true);
|
|
MyNesMain.AudioProvider.SignalToggle(started: true);
|
|
}
|
|
else
|
|
{
|
|
success = false;
|
|
}
|
|
}
|
|
|
|
public static void HardReset()
|
|
{
|
|
PAUSED = true;
|
|
emu_request_mode = RequestMode.HardReset;
|
|
}
|
|
|
|
private static void hardReset()
|
|
{
|
|
if (MyNesMain.WaveRecorder.IsRecording)
|
|
{
|
|
MyNesMain.WaveRecorder.Stop();
|
|
}
|
|
render_audio_toggle_pause(paused: true);
|
|
switch (Region)
|
|
{
|
|
case EmuRegion.NTSC:
|
|
emu_time_target_fps = 60.0988;
|
|
break;
|
|
case EmuRegion.PALB:
|
|
case EmuRegion.DENDY:
|
|
emu_time_target_fps = 50.0;
|
|
break;
|
|
}
|
|
fps_time_period = 1.0 / emu_time_target_fps;
|
|
MEMHardReset();
|
|
CPUHardReset();
|
|
PPUHardReset();
|
|
APUHardReset();
|
|
DMAHardReset();
|
|
render_audio_toggle_pause(paused: false);
|
|
MyNesMain.VideoProvider.WriteWarningNotification(MNInterfaceLanguage.Message_HardReset, instant: false);
|
|
}
|
|
|
|
public static void SoftReset()
|
|
{
|
|
PAUSED = true;
|
|
emu_request_mode = RequestMode.SoftReset;
|
|
}
|
|
|
|
private static void softReset()
|
|
{
|
|
CPUSoftReset();
|
|
APUSoftReset();
|
|
MyNesMain.VideoProvider.WriteWarningNotification(MNInterfaceLanguage.Message_SoftReset, instant: false);
|
|
}
|
|
|
|
public static void SaveState()
|
|
{
|
|
PAUSED = true;
|
|
emu_request_mode = RequestMode.SaveState;
|
|
}
|
|
|
|
public static void LoadState()
|
|
{
|
|
PAUSED = true;
|
|
emu_request_mode = RequestMode.LoadState;
|
|
}
|
|
|
|
internal static void TakeSnapshot()
|
|
{
|
|
PAUSED = true;
|
|
emu_request_mode = RequestMode.TakeSnapshot;
|
|
}
|
|
|
|
public static void ShutDown()
|
|
{
|
|
MyNesMain.VideoProvider.SignalToggle(started: false);
|
|
MyNesMain.AudioProvider.SignalToggle(started: false);
|
|
if (MyNesMain.WaveRecorder.IsRecording)
|
|
{
|
|
MyNesMain.WaveRecorder.Stop();
|
|
}
|
|
render_audio_get_is_playing(out render_audio_is_playing);
|
|
if (render_audio_is_playing)
|
|
{
|
|
render_audio_toggle_pause(paused: true);
|
|
}
|
|
Tracer.WriteLine("Shutting down the emulation core...");
|
|
ON = false;
|
|
if (mainThread != null)
|
|
{
|
|
Tracer.WriteLine("Aborting thread ..");
|
|
mainThread.Abort();
|
|
mainThread = null;
|
|
}
|
|
SaveSRAM();
|
|
Tracer.WriteInformation("Emulation core shutdown successfully.");
|
|
NesEmu.EmuShutdown?.Invoke(null, new EventArgs());
|
|
}
|
|
|
|
private static Stopwatch sw = new Stopwatch();
|
|
private static double fixTime;
|
|
public static ulong currentFrame;
|
|
private static void EmuClock()
|
|
{
|
|
while (ON)
|
|
{
|
|
if (!PAUSED)
|
|
{
|
|
var waitTime = GetTime() + fps_time_period + fixTime;
|
|
|
|
while (!ppu_frame_finished)
|
|
CPUClock();
|
|
|
|
FrameFinished();
|
|
|
|
fixTime = waitTime - GetTime();
|
|
while (fixTime > 0)
|
|
{
|
|
fixTime = waitTime - GetTime();
|
|
};
|
|
|
|
currentFrame++;
|
|
|
|
continue;
|
|
}
|
|
render_audio_get_is_playing(out render_audio_is_playing);
|
|
if (render_audio_is_playing)
|
|
{
|
|
render_audio_toggle_pause(paused: true);
|
|
}
|
|
Thread.Sleep(100);
|
|
switch (emu_request_mode)
|
|
{
|
|
case RequestMode.HardReset:
|
|
hardReset();
|
|
PAUSED = false;
|
|
emu_request_mode = RequestMode.None;
|
|
break;
|
|
case RequestMode.SoftReset:
|
|
softReset();
|
|
PAUSED = false;
|
|
emu_request_mode = RequestMode.None;
|
|
break;
|
|
case RequestMode.SaveState:
|
|
StateHandler.SaveState();
|
|
PAUSED = false;
|
|
emu_request_mode = RequestMode.None;
|
|
break;
|
|
case RequestMode.LoadState:
|
|
StateHandler.LoadState();
|
|
PAUSED = false;
|
|
emu_request_mode = RequestMode.None;
|
|
break;
|
|
case RequestMode.TakeSnapshot:
|
|
MyNesMain.VideoProvider.TakeSnapshot();
|
|
PAUSED = false;
|
|
emu_request_mode = RequestMode.None;
|
|
break;
|
|
}
|
|
isPaused = true;
|
|
}
|
|
}
|
|
|
|
internal static void EmuClockComponents()
|
|
{
|
|
PPUClock();
|
|
PollInterruptStatus();
|
|
PPUClock();
|
|
PPUClock();
|
|
APUClock();
|
|
DMAClock();
|
|
mem_board.OnCPUClock();
|
|
}
|
|
|
|
internal static void ApplyFrameSkipSettings()
|
|
{
|
|
FrameSkipEnabled = MyNesMain.RendererSettings.FrameSkipEnabled;
|
|
FrameSkipInterval = MyNesMain.RendererSettings.FrameSkipInterval;
|
|
}
|
|
|
|
private static void FrameFinished()
|
|
{
|
|
if (!FrameSkipEnabled)
|
|
{
|
|
render_video(ref ppu_screen_pixels);
|
|
}
|
|
else
|
|
{
|
|
FrameSkipCounter++;
|
|
if (FrameSkipCounter >= FrameSkipInterval)
|
|
{
|
|
render_video(ref ppu_screen_pixels);
|
|
FrameSkipCounter = 0;
|
|
}
|
|
}
|
|
isPaused = false;
|
|
ppu_frame_finished = false;
|
|
joypad1.Update();
|
|
joypad2.Update();
|
|
if (IsFourPlayers)
|
|
{
|
|
joypad3.Update();
|
|
joypad4.Update();
|
|
}
|
|
if (SoundEnabled)
|
|
{
|
|
render_audio_get_is_playing(out render_audio_is_playing);
|
|
if (!render_audio_is_playing)
|
|
{
|
|
render_audio_toggle_pause(paused: false);
|
|
}
|
|
render_audio(ref audio_samples, ref audio_samples_added);
|
|
audio_w_pos = 0;
|
|
audio_samples_added = 0;
|
|
audio_timer = 0.0;
|
|
}
|
|
}
|
|
|
|
private static double GetTime()
|
|
{
|
|
return (double)Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency;
|
|
}
|
|
|
|
|
|
public static void SetFramePeriod(ref double period)
|
|
{
|
|
fps_time_period = period;
|
|
}
|
|
|
|
public static void RevertFramePeriod()
|
|
{
|
|
fps_time_period = 1 / emu_time_target_fps;
|
|
}
|
|
|
|
public static void ApplyRegionSetting()
|
|
{
|
|
switch ((RegionSetting)MyNesMain.EmuSettings.RegionSetting)
|
|
{
|
|
case RegionSetting.AUTO:
|
|
Tracer.WriteLine("REGION = AUTO");
|
|
Region = EmuRegion.NTSC;
|
|
if (CurrentFilePath.Contains("(E)"))
|
|
{
|
|
Region = EmuRegion.PALB;
|
|
}
|
|
Tracer.WriteLine("REGION SELECTED: " + Region);
|
|
break;
|
|
case RegionSetting.ForceNTSC:
|
|
Tracer.WriteLine("REGION: FORCE NTSC");
|
|
Region = EmuRegion.NTSC;
|
|
break;
|
|
case RegionSetting.ForcePALB:
|
|
Tracer.WriteLine("REGION: FORCE PALB");
|
|
Region = EmuRegion.PALB;
|
|
break;
|
|
case RegionSetting.ForceDENDY:
|
|
Tracer.WriteLine("REGION: FORCE DENDY");
|
|
Region = EmuRegion.DENDY;
|
|
break;
|
|
}
|
|
SystemIndex = (int)Region;
|
|
}
|
|
|
|
public static void ApplyPaletteSetting()
|
|
{
|
|
Tracer.WriteLine("Loading palette generators values from settings...");
|
|
NTSCPaletteGenerator.brightness = MyNesMain.RendererSettings.Palette_NTSC_brightness;
|
|
NTSCPaletteGenerator.contrast = MyNesMain.RendererSettings.Palette_NTSC_contrast;
|
|
NTSCPaletteGenerator.gamma = MyNesMain.RendererSettings.Palette_NTSC_gamma;
|
|
NTSCPaletteGenerator.hue_tweak = MyNesMain.RendererSettings.Palette_NTSC_hue_tweak;
|
|
NTSCPaletteGenerator.saturation = MyNesMain.RendererSettings.Palette_NTSC_saturation;
|
|
PALBPaletteGenerator.brightness = MyNesMain.RendererSettings.Palette_PALB_brightness;
|
|
PALBPaletteGenerator.contrast = MyNesMain.RendererSettings.Palette_PALB_contrast;
|
|
PALBPaletteGenerator.gamma = MyNesMain.RendererSettings.Palette_PALB_gamma;
|
|
PALBPaletteGenerator.hue_tweak = MyNesMain.RendererSettings.Palette_PALB_hue_tweak;
|
|
PALBPaletteGenerator.saturation = MyNesMain.RendererSettings.Palette_PALB_saturation;
|
|
Tracer.WriteLine("Setting up palette ....");
|
|
switch ((PaletteSelectSetting)MyNesMain.RendererSettings.Palette_PaletteSetting)
|
|
{
|
|
case PaletteSelectSetting.AUTO:
|
|
Tracer.WriteLine("Palette set to auto detect depending on region.");
|
|
switch (Region)
|
|
{
|
|
case EmuRegion.NTSC:
|
|
SetupPalette(NTSCPaletteGenerator.GeneratePalette());
|
|
Tracer.WriteLine("Region is NTSC, Palette set from NTSC generator.");
|
|
break;
|
|
case EmuRegion.PALB:
|
|
case EmuRegion.DENDY:
|
|
SetupPalette(PALBPaletteGenerator.GeneratePalette());
|
|
Tracer.WriteLine("Region is PALB/DENDY, Palette set from PALB generator.");
|
|
break;
|
|
}
|
|
break;
|
|
case PaletteSelectSetting.ForceNTSC:
|
|
Tracer.WriteLine("Palette set to always use NTSC palette generator.");
|
|
SetupPalette(NTSCPaletteGenerator.GeneratePalette());
|
|
Tracer.WriteLine("Palette set from NTSC generator.");
|
|
break;
|
|
case PaletteSelectSetting.ForcePALB:
|
|
Tracer.WriteLine("Palette set to always use PALB palette generator.");
|
|
SetupPalette(NTSCPaletteGenerator.GeneratePalette());
|
|
Tracer.WriteLine("Palette set from PALB generator.");
|
|
break;
|
|
case PaletteSelectSetting.File:
|
|
{
|
|
Tracer.WriteLine("Palette set to load from file.");
|
|
|
|
var paletteFileStream = MyNesMain.Supporter.OpenPaletteFile();
|
|
if (paletteFileStream != null)
|
|
{
|
|
PaletteFileWrapper.LoadFile(paletteFileStream, out var palette);
|
|
SetupPalette(palette);
|
|
Tracer.WriteLine("Palette set from file");
|
|
break;
|
|
}
|
|
Tracer.WriteError("Palette from file is not exist is not exist. Setting up palette from generators.");
|
|
switch (Region)
|
|
{
|
|
case EmuRegion.NTSC:
|
|
SetupPalette(NTSCPaletteGenerator.GeneratePalette());
|
|
Tracer.WriteLine("Region is NTSC, Palette set from NTSC generator.");
|
|
break;
|
|
case EmuRegion.PALB:
|
|
case EmuRegion.DENDY:
|
|
SetupPalette(PALBPaletteGenerator.GeneratePalette());
|
|
Tracer.WriteLine("Region is PALB/DENDY, Palette set from PALB generator.");
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal static void WriteStateData(ref BinaryWriter bin)
|
|
{
|
|
APUWriteState(ref bin);
|
|
CPUWriteState(ref bin);
|
|
DMAWriteState(ref bin);
|
|
InterruptsWriteState(ref bin);
|
|
MEMWriteState(ref bin);
|
|
PORTWriteState(ref bin);
|
|
PPUWriteState(ref bin);
|
|
}
|
|
|
|
internal static void ReadStateData(ref BinaryReader bin)
|
|
{
|
|
APUReadState(ref bin);
|
|
CPUReadState(ref bin);
|
|
DMAReadState(ref bin);
|
|
InterruptsReadState(ref bin);
|
|
MEMReadState(ref bin);
|
|
PORTReadState(ref bin);
|
|
PPUReadState(ref bin);
|
|
}
|
|
|
|
private static void SQ2HardReset()
|
|
{
|
|
sq2_duty_cycle = 0;
|
|
sq2_length_halt = false;
|
|
sq2_constant_volume_envelope = false;
|
|
sq2_volume_devider_period = 0;
|
|
sq2_sweep_enable = false;
|
|
sq2_sweep_devider_period = 0;
|
|
sq2_sweep_negate = false;
|
|
sq2_sweep_shift_count = 0;
|
|
sq2_timer = 0;
|
|
sq2_period_devider = 0;
|
|
sq2_seqencer = 0;
|
|
sq2_length_enabled = false;
|
|
sq2_length_counter = 0;
|
|
sq2_envelope_start_flag = false;
|
|
sq2_envelope_devider = 0;
|
|
sq2_envelope_decay_level_counter = 0;
|
|
sq2_envelope = 0;
|
|
sq2_sweep_counter = 0;
|
|
sq2_sweep_reload = false;
|
|
sq2_sweep_change = 0;
|
|
sq2_valid_freq = false;
|
|
sq2_output = 0;
|
|
sq2_ignore_reload = false;
|
|
}
|
|
|
|
private static void SQ2SoftReset()
|
|
{
|
|
SQ2HardReset();
|
|
}
|
|
|
|
private static void SQ2Clock()
|
|
{
|
|
sq2_period_devider--;
|
|
if (sq2_period_devider > 0)
|
|
{
|
|
return;
|
|
}
|
|
sq2_period_devider = sq2_timer + 1;
|
|
sq2_seqencer = (byte)((uint)(sq2_seqencer + 1) & 7u);
|
|
if (sq2_length_counter > 0 && sq2_valid_freq)
|
|
{
|
|
if (audio_sq2_outputable)
|
|
{
|
|
sq2_output = sq_duty_cycle_sequences[sq2_duty_cycle][sq2_seqencer] * sq2_envelope;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sq2_output = 0;
|
|
}
|
|
audio_signal_outputed = true;
|
|
}
|
|
|
|
private static void SQ2ClockLength()
|
|
{
|
|
if (sq2_length_counter > 0 && !sq2_length_halt)
|
|
{
|
|
sq2_length_counter--;
|
|
if (apu_reg_access_happened && apu_reg_io_addr == 7 && apu_reg_access_w)
|
|
{
|
|
sq2_ignore_reload = true;
|
|
}
|
|
}
|
|
sq2_sweep_counter--;
|
|
if (sq2_sweep_counter == 0)
|
|
{
|
|
sq2_sweep_counter = sq2_sweep_devider_period + 1;
|
|
if (sq2_sweep_enable && sq2_sweep_shift_count > 0 && sq2_valid_freq)
|
|
{
|
|
sq2_sweep_change = sq2_timer >> (int)sq2_sweep_shift_count;
|
|
sq2_timer += (sq2_sweep_negate ? (-sq2_sweep_change) : sq2_sweep_change);
|
|
SQ2CalculateValidFreq();
|
|
}
|
|
}
|
|
else if (sq2_sweep_reload)
|
|
{
|
|
sq2_sweep_counter = sq2_sweep_devider_period + 1;
|
|
sq2_sweep_reload = false;
|
|
}
|
|
}
|
|
|
|
private static void SQ2ClockEnvelope()
|
|
{
|
|
if (sq2_envelope_start_flag)
|
|
{
|
|
sq2_envelope_start_flag = false;
|
|
sq2_envelope_decay_level_counter = 15;
|
|
sq2_envelope_devider = (byte)(sq2_volume_devider_period + 1);
|
|
}
|
|
else if (sq2_envelope_devider > 0)
|
|
{
|
|
sq2_envelope_devider--;
|
|
}
|
|
else
|
|
{
|
|
sq2_envelope_devider = (byte)(sq2_volume_devider_period + 1);
|
|
if (sq2_envelope_decay_level_counter > 0)
|
|
{
|
|
sq2_envelope_decay_level_counter--;
|
|
}
|
|
else if (sq2_length_halt)
|
|
{
|
|
sq2_envelope_decay_level_counter = 15;
|
|
}
|
|
}
|
|
sq2_envelope = (sq2_constant_volume_envelope ? sq2_volume_devider_period : sq2_envelope_decay_level_counter);
|
|
}
|
|
|
|
private static void APUOnRegister4004()
|
|
{
|
|
if (apu_reg_access_w)
|
|
{
|
|
sq2_duty_cycle = (byte)((apu_reg_io_db & 0xC0) >> 6);
|
|
sq2_volume_devider_period = (byte)(apu_reg_io_db & 0xFu);
|
|
sq2_length_halt = (apu_reg_io_db & 0x20) != 0;
|
|
sq2_constant_volume_envelope = (apu_reg_io_db & 0x10) != 0;
|
|
sq2_envelope = (sq2_constant_volume_envelope ? sq2_volume_devider_period : sq2_envelope_decay_level_counter);
|
|
}
|
|
}
|
|
|
|
private static void APUOnRegister4005()
|
|
{
|
|
if (apu_reg_access_w)
|
|
{
|
|
sq2_sweep_enable = (apu_reg_io_db & 0x80) == 128;
|
|
sq2_sweep_devider_period = (byte)((uint)(apu_reg_io_db >> 4) & 7u);
|
|
sq2_sweep_negate = (apu_reg_io_db & 8) == 8;
|
|
sq2_sweep_shift_count = (byte)(apu_reg_io_db & 7u);
|
|
sq2_sweep_reload = true;
|
|
SQ2CalculateValidFreq();
|
|
}
|
|
}
|
|
|
|
private static void APUOnRegister4006()
|
|
{
|
|
if (apu_reg_access_w)
|
|
{
|
|
sq2_timer = (sq2_timer & 0xFF00) | apu_reg_io_db;
|
|
SQ2CalculateValidFreq();
|
|
}
|
|
}
|
|
|
|
private static void APUOnRegister4007()
|
|
{
|
|
if (apu_reg_access_w)
|
|
{
|
|
sq2_timer = (sq2_timer & 0xFF) | ((apu_reg_io_db & 7) << 8);
|
|
if (sq2_length_enabled && !sq2_ignore_reload)
|
|
{
|
|
sq2_length_counter = sq_duration_table[apu_reg_io_db >> 3];
|
|
}
|
|
if (sq2_ignore_reload)
|
|
{
|
|
sq2_ignore_reload = false;
|
|
}
|
|
sq2_seqencer = 0;
|
|
sq2_envelope_start_flag = true;
|
|
SQ2CalculateValidFreq();
|
|
}
|
|
}
|
|
|
|
private static void SQ2On4015()
|
|
{
|
|
sq2_length_enabled = (apu_reg_io_db & 2) != 0;
|
|
if (!sq2_length_enabled)
|
|
{
|
|
sq2_length_counter = 0;
|
|
}
|
|
}
|
|
|
|
private static void SQ2Read4015()
|
|
{
|
|
if (sq2_length_counter > 0)
|
|
{
|
|
apu_reg_io_db = (byte)((apu_reg_io_db & 0xFDu) | 2u);
|
|
}
|
|
}
|
|
|
|
private static void SQ2CalculateValidFreq()
|
|
{
|
|
sq2_valid_freq = sq2_timer >= 8 && (sq2_sweep_negate || ((sq2_timer + (sq2_timer >> (int)sq2_sweep_shift_count)) & 0x800) == 0);
|
|
}
|
|
|
|
private static void SQ2WriteState(ref BinaryWriter bin)
|
|
{
|
|
bin.Write(sq2_duty_cycle);
|
|
bin.Write(sq2_length_halt);
|
|
bin.Write(sq2_constant_volume_envelope);
|
|
bin.Write(sq2_volume_devider_period);
|
|
bin.Write(sq2_sweep_enable);
|
|
bin.Write(sq2_sweep_devider_period);
|
|
bin.Write(sq2_sweep_negate);
|
|
bin.Write(sq2_sweep_shift_count);
|
|
bin.Write(sq2_timer);
|
|
bin.Write(sq2_period_devider);
|
|
bin.Write(sq2_seqencer);
|
|
bin.Write(sq2_length_enabled);
|
|
bin.Write(sq2_length_counter);
|
|
bin.Write(sq2_envelope_start_flag);
|
|
bin.Write(sq2_envelope_devider);
|
|
bin.Write(sq2_envelope_decay_level_counter);
|
|
bin.Write(sq2_envelope);
|
|
bin.Write(sq2_sweep_counter);
|
|
bin.Write(sq2_sweep_reload);
|
|
bin.Write(sq2_sweep_change);
|
|
bin.Write(sq2_valid_freq);
|
|
bin.Write(sq2_output);
|
|
bin.Write(sq2_ignore_reload);
|
|
}
|
|
|
|
private static void SQ2ReadState(ref BinaryReader bin)
|
|
{
|
|
sq2_duty_cycle = bin.ReadByte();
|
|
sq2_length_halt = bin.ReadBoolean();
|
|
sq2_constant_volume_envelope = bin.ReadBoolean();
|
|
sq2_volume_devider_period = bin.ReadByte();
|
|
sq2_sweep_enable = bin.ReadBoolean();
|
|
sq2_sweep_devider_period = bin.ReadByte();
|
|
sq2_sweep_negate = bin.ReadBoolean();
|
|
sq2_sweep_shift_count = bin.ReadByte();
|
|
sq2_timer = bin.ReadInt32();
|
|
sq2_period_devider = bin.ReadInt32();
|
|
sq2_seqencer = bin.ReadByte();
|
|
sq2_length_enabled = bin.ReadBoolean();
|
|
sq2_length_counter = bin.ReadInt32();
|
|
sq2_envelope_start_flag = bin.ReadBoolean();
|
|
sq2_envelope_devider = bin.ReadByte();
|
|
sq2_envelope_decay_level_counter = bin.ReadByte();
|
|
sq2_envelope = bin.ReadByte();
|
|
sq2_sweep_counter = bin.ReadInt32();
|
|
sq2_sweep_reload = bin.ReadBoolean();
|
|
sq2_sweep_change = bin.ReadInt32();
|
|
sq2_valid_freq = bin.ReadBoolean();
|
|
sq2_output = bin.ReadInt32();
|
|
sq2_ignore_reload = bin.ReadBoolean();
|
|
}
|
|
}
|
|
}
|