commit 7febc61ef58e8a5a2f27dfb4dfa2146b8b317f25 Author: aNNiMON Date: Tue Feb 13 23:11:18 2024 +0200 Initial commit diff --git a/res/games/nes_list.txt b/res/games/nes_list.txt new file mode 100644 index 0000000..4fe6f9a --- /dev/null +++ b/res/games/nes_list.txt @@ -0,0 +1,5 @@ +Adventure Island +Battletoads II +Battle City +Super Mario +Contra \ No newline at end of file diff --git a/res/nescube.png b/res/nescube.png new file mode 100644 index 0000000..dc40574 Binary files /dev/null and b/res/nescube.png differ diff --git a/res/resource/icons/about/help.png b/res/resource/icons/about/help.png new file mode 100644 index 0000000..9f088a3 Binary files /dev/null and b/res/resource/icons/about/help.png differ diff --git a/res/resource/icons/about/rom.png b/res/resource/icons/about/rom.png new file mode 100644 index 0000000..a399bb5 Binary files /dev/null and b/res/resource/icons/about/rom.png differ diff --git a/res/resource/icons/about/system.png b/res/resource/icons/about/system.png new file mode 100644 index 0000000..431c9a0 Binary files /dev/null and b/res/resource/icons/about/system.png differ diff --git a/res/resource/icons/mainmenu/about.png b/res/resource/icons/mainmenu/about.png new file mode 100644 index 0000000..17636b6 Binary files /dev/null and b/res/resource/icons/mainmenu/about.png differ diff --git a/res/resource/icons/mainmenu/exit.png b/res/resource/icons/mainmenu/exit.png new file mode 100644 index 0000000..c334e52 Binary files /dev/null and b/res/resource/icons/mainmenu/exit.png differ diff --git a/res/resource/icons/mainmenu/open.png b/res/resource/icons/mainmenu/open.png new file mode 100644 index 0000000..1714335 Binary files /dev/null and b/res/resource/icons/mainmenu/open.png differ diff --git a/res/resource/icons/mainmenu/settings.png b/res/resource/icons/mainmenu/settings.png new file mode 100644 index 0000000..3962037 Binary files /dev/null and b/res/resource/icons/mainmenu/settings.png differ diff --git a/res/resource/icons/other/cart.png b/res/resource/icons/other/cart.png new file mode 100644 index 0000000..3526242 Binary files /dev/null and b/res/resource/icons/other/cart.png differ diff --git a/res/resource/icons/other/palette.png b/res/resource/icons/other/palette.png new file mode 100644 index 0000000..7daa6d0 Binary files /dev/null and b/res/resource/icons/other/palette.png differ diff --git a/res/resource/icons/settings/control.png b/res/resource/icons/settings/control.png new file mode 100644 index 0000000..3b5fb6b Binary files /dev/null and b/res/resource/icons/settings/control.png differ diff --git a/res/resource/icons/settings/display.png b/res/resource/icons/settings/display.png new file mode 100644 index 0000000..c3c74bf Binary files /dev/null and b/res/resource/icons/settings/display.png differ diff --git a/res/resource/icons/settings/engine.png b/res/resource/icons/settings/engine.png new file mode 100644 index 0000000..2d96708 Binary files /dev/null and b/res/resource/icons/settings/engine.png differ diff --git a/res/resource/icons/settings/other.png b/res/resource/icons/settings/other.png new file mode 100644 index 0000000..9a55be3 Binary files /dev/null and b/res/resource/icons/settings/other.png differ diff --git a/res/resource/icons/settings/sound.png b/res/resource/icons/settings/sound.png new file mode 100644 index 0000000..3a3009b Binary files /dev/null and b/res/resource/icons/settings/sound.png differ diff --git a/res/resource/images/nescube.png b/res/resource/images/nescube.png new file mode 100644 index 0000000..aac40cf Binary files /dev/null and b/res/resource/images/nescube.png differ diff --git a/res/resource/palettes/FCEUltra.pal b/res/resource/palettes/FCEUltra.pal new file mode 100644 index 0000000..ec8fa3d Binary files /dev/null and b/res/resource/palettes/FCEUltra.pal differ diff --git a/res/resource/palettes/NTSC.pal b/res/resource/palettes/NTSC.pal new file mode 100644 index 0000000..e958862 Binary files /dev/null and b/res/resource/palettes/NTSC.pal differ diff --git a/res/resource/palettes/Nescube.pal b/res/resource/palettes/Nescube.pal new file mode 100644 index 0000000..46c9d57 Binary files /dev/null and b/res/resource/palettes/Nescube.pal differ diff --git a/res/resource/palettes/Nesticle.pal b/res/resource/palettes/Nesticle.pal new file mode 100644 index 0000000..e755110 Binary files /dev/null and b/res/resource/palettes/Nesticle.pal differ diff --git a/res/resource/palettes/PAL.pal b/res/resource/palettes/PAL.pal new file mode 100644 index 0000000..e958862 Binary files /dev/null and b/res/resource/palettes/PAL.pal differ diff --git a/res/resource/palettes/pal_list.txt b/res/resource/palettes/pal_list.txt new file mode 100644 index 0000000..c7b5bd0 --- /dev/null +++ b/res/resource/palettes/pal_list.txt @@ -0,0 +1,5 @@ +Nescube +FCEUltra +Nesticle +NTSC +PAL \ No newline at end of file diff --git a/src/demo.java b/src/demo.java new file mode 100644 index 0000000..cea264a --- /dev/null +++ b/src/demo.java @@ -0,0 +1,67 @@ +// ======================================================================== +// Nescube Startup Demo Class by Dr.Lion/RSM +// ======================================================================== +import javax.microedition.lcdui.game.GameCanvas; +import javax.microedition.lcdui.*; + +public class demo extends GameCanvas implements Runnable { +// ======================================================================== +// Class Variables +// ======================================================================== + private boolean key_pressed = false; + private Image img = null; + private nescube parent; + private Thread thread; + private Graphics g; +// ======================================================================== +// Class Constructor +// ======================================================================== + public demo(nescube parent){ + super(false); this.parent = parent; + g = getGraphics(); setFullScreenMode(true); + try { + img = Image.createImage("/resource/images/nescube.png"); + if(img.getHeight() > getHeight()) img = null; + } catch (Exception e) {} + } +// ======================================================================== +// Main Tread Control +// ======================================================================== + protected final void hideNotify(){ thread = null; } + protected final void showNotify(){ thread = new Thread(this); thread.start(); } +// ======================================================================== +// Main Tread +// ======================================================================== + public final void run(){ + g.setColor(0x000000); + g.fillRect(0, 0, getWidth(), getHeight()); + g.translate(getWidth() / 2, getHeight() / 2); + boolean flash = false; + while(!key_pressed) { + if(img == null) { + g.translate(-getWidth() / 2, -getHeight() / 2); + g.setColor(0); g.fillRect(0, 0, getWidth(), getHeight()); + g.translate(getWidth() / 2, getHeight() / 2); + draw_string("NESCUBE", -40, 1); + draw_string("NES emulator for J2ME", +40, 2); + } else g.drawImage(img, 0, 0, g.VCENTER + g.HCENTER); + if(flash = !flash) draw_string("press any key", 0, 0); + flushGraphics(); + try { Thread.currentThread().sleep(500L); } + catch( InterruptedException e ){ } + } + parent.returnFromDemo(); + } +// ======================================================================== + private void draw_string(String text, int y_offset, int font_style) { + switch(font_style) { + case 0: g.setFont(Font.getFont(Font.FACE_MONOSPACE, Font.STYLE_PLAIN, Font.SIZE_MEDIUM)); break; + case 1: g.setFont(Font.getFont(Font.FACE_MONOSPACE, Font.STYLE_BOLD, Font.SIZE_LARGE)); break; + case 2: g.setFont(Font.getFont(Font.FACE_MONOSPACE, Font.STYLE_PLAIN, Font.SIZE_SMALL)); + } + g.setColor(0x666666); g.drawString(text, 1, 1 + y_offset, g.BASELINE + g.HCENTER); + g.setColor(0xFFFFFF); g.drawString(text, 0, 0 + y_offset, g.BASELINE + g.HCENTER); + } +// ======================================================================== + protected final void keyPressed(int keyCode) { key_pressed = true; } +} \ No newline at end of file diff --git a/src/engine.java b/src/engine.java new file mode 100644 index 0000000..1c9cc0a --- /dev/null +++ b/src/engine.java @@ -0,0 +1,4566 @@ +// ============================================================================ +// Nescube v1.1 beta [05.12.2006] nes engine class by Dr. Lion/RSM +// ============================================================================ +import javax.microedition.lcdui.game.GameCanvas; +import javax.microedition.lcdui.*; +import javax.microedition.media.control.*; +import javax.microedition.media.*; +import javax.microedition.rms.*; +import java.io.*; + +public class engine extends GameCanvas implements Runnable { +// ============================================================================ +// [+] NES Hardware переменные и константы экземпляра +// ============================================================================ + public static final String CAMERA[] = {"Scale LQ","Scale HQ","Dynamic","Static","Disabled"}; + private static final int OSD_FONTS[] = {Font.SIZE_SMALL,Font.SIZE_MEDIUM,Font.SIZE_LARGE}; + private static final int OSD_COLOR[] = {0x00FFFFFF,0x00FFFF00,0x0000FFFF,0x000000FF}; + private static final int REACTIONS[] = {0x1E,0x3C,0x5A}; + private static final String emul_ver = "nescube_nes_emul"; +// ---------------------------------------------------------------------------- + public static final boolean def_emuscroff = false; + public static final boolean def_accuS0hit = false; + public static final boolean def_fastvideo = false; + public static final boolean def_ntsc_mode = true; + public static final int def_frameskip = 5; + public static final int def_cpu_accel = 2; + public static final int def_frame_del = 0; +// ---------------------------------------------------------------------------- + public static final boolean def_apu_enabl = false; + public static final boolean def_sq1_enabl = true; + public static final boolean def_sq2_enabl = true; + public static final boolean def_tri_enabl = true; + public static final int def_apu_sqr_patch = 80; + public static final int def_apu_tri_patch = 73; +// ---------------------------------------------------------------------------- + public static final String def_pal_name = "Nescube"; + public static final boolean def_full_scr = false; + public static final int def_saturation = 100; + public static final int def_brightness = 100; + public static final int def_reaction = 1; + public static final int def_vmode = 0; + public static final int def_rmode = 0; +// ---------------------------------------------------------------------------- + public static final boolean def_osd_enable = true; + public static final boolean def_fps_enable = true; + public static final boolean def_auto_sload = false; + public static final boolean def_keep_sets = false; + public static final int def_osd_colour = 1; + public static final int def_osd_font = 0; +// ---------------------------------------------------------------------------- + public static final boolean def_turboen_a = false; + public static final boolean def_turboen_b = false; + public static final byte[][] def_keys_map = { + {0x7,0x3,0x2,0x1,0x8,0x8,0x8,0x5,0xA,0x4,0xB,0x2,0x6,0x1,0x7,0x3,0x7,0x9,0x7,0x8,0x7,0x8}, + {0x2,0x1,0x3,0x7,0x8,0x0,0x0,0x0,0xA,0x0,0xB,0x0,0x0,0x0,0x0,0x0,0x0,0x9,0x7,0x8,0x7,0x8}, + {0x1,0x2,0x7,0x3,0x8,0x0,0x0,0x0,0xA,0x0,0xB,0x0,0x0,0x0,0x0,0x0,0x0,0x9,0x7,0x8,0x7,0x8} + }; +// ---------------------------------------------------------------------------- + public static boolean engine_ok = false; + private static byte memory[]; + private static nescube main; + private Graphics graphics; +// ============================================================================ +// [+] NES Hardware переменные экземпляра (настройки движка) +// ============================================================================ + public boolean midi_device = false; + public String nes_name = null; + public int osd_time = 2000; +// ---------------------------------------------------------------------------- + public static boolean hid_fscr = false; + public boolean hid_invc = false; +// ---------------------------------------------------------------------------- + public static boolean emuscroff = false; + public static boolean accuS0hit = false; + public boolean fastvideo = false; + public boolean ntsc_mode = false; + public int frameskip = 0; + public int cpu_accel = 0; + public int frame_del = 0; +// ---------------------------------------------------------------------------- + public boolean apu_enabl = false; + public boolean sq1_enabl = false; + public boolean sq2_enabl = false; + public boolean tri_enabl = false; + public int apu_sqr_patch = 0; + public int apu_tri_patch = 0; +// ---------------------------------------------------------------------------- + public static boolean full_scr = false; + public int saturation = 0; + public int brightness = 0; + public int reaction = 0; + public static int vmode = 0; + public static int rmode = 0; + public String pal_name = null; +// ---------------------------------------------------------------------------- + public boolean osd_enable = false; + public boolean fps_enable = false; + public boolean auto_sload = false; + public boolean keep_sets = false; + public int osd_colour = 0; + public int osd_font = 0; +// ---------------------------------------------------------------------------- + public boolean turboen_a = false; + public boolean turboen_b = false; + public byte[][] keys_map = new byte[3][22]; +// ============================================================================ +// [+] NES Hardware конструктор класса +// ============================================================================ + public engine(nescube main){ + super(false); graphics = getGraphics(); this.main = main; + LoadSettings(); open_apu(); + } +// ============================================================================ +// [+] Управление потоком эмуляции при отображении данного объекта +// ============================================================================ + protected final void showNotify(){ + if(engine_ok) { + open_apu(); + emulThreadControl(ACT_START); + } + System.gc(); + } +// ============================================================================ + protected final void hideNotify(){ + emulThreadControl(ACT_STOP); + close_apu(); + System.gc(); + } +// ============================================================================ +// [?] Emulation thread control +// ============================================================================ + private boolean threadWasPause = false; + private boolean threadMustStop = false; + private final int ACT_START = 0; + private final int ACT_STOP = 1; + private final int ACT_RESUME = 2; + private Thread thread = new Thread(this); +// ============================================================================ + private final void emulThreadControl(int action) { + switch(action) { + case ACT_START: + if(!thread.isAlive()) { + threadWasPause = false; + threadMustStop = false; + thread = null; thread = new Thread(this); thread.start(); + } + return; + case ACT_STOP: + if(threadWasPause = thread.isAlive()) { + threadMustStop = true; + try { thread.join(); } catch(InterruptedException e) {} + } + return; + case ACT_RESUME: + if(!thread.isAlive() && threadWasPause) { + threadMustStop = false; + thread = null; thread = new Thread(this); thread.start(); + } + return; + } + } +// ============================================================================ +// [?] Load/Save settings +// ============================================================================ + private boolean last_keep_sets = keep_sets; + private final void LoadSettings() { + byte[] glob_sets = null; byte[] game_sets = null; int index; + glob_sets = GetDataFromRMS(emul_ver + "_set_a"); + if(glob_sets != null) { + index = 0; + osd_enable = glob_sets[index++] != 0; + fps_enable = glob_sets[index++] != 0; + osd_colour = glob_sets[index++] & 0xFF; + osd_font = glob_sets[index++] & 0xFF; + auto_sload = glob_sets[index++] != 0; + keep_sets = glob_sets[index++] != 0; + saturation = glob_sets[index++] & 0xFF; + brightness = glob_sets[index++] & 0xFF; + full_scr = glob_sets[index++] != 0; + reaction = glob_sets[index++] & 0xFF; + vmode = glob_sets[index++] & 0xFF; + rmode = glob_sets[index++] & 0xFF; + pal_name = new String(glob_sets, index + 1, glob_sets[index]); + glob_sets = null; + glob_sets = GetDataFromRMS(emul_ver + "_set_b"); + game_sets = GetDataFromRMS(nes_name + "_set_b"); + if(keep_sets && game_sets != null) glob_sets = game_sets; + } else { + osd_enable = def_osd_enable; + fps_enable = def_fps_enable; + osd_colour = def_osd_colour; + osd_font = def_osd_font; + auto_sload = def_auto_sload; + keep_sets = def_keep_sets; + saturation = def_saturation; + brightness = def_brightness; + full_scr = def_full_scr; + reaction = def_reaction; + vmode = def_vmode; + rmode = def_rmode; + pal_name = def_pal_name; + } + if(glob_sets != null) { + index = 0; + frameskip = glob_sets[index++] & 0xFF; + cpu_accel = glob_sets[index++] & 0xFF; + frame_del = glob_sets[index++] & 0xFF; + fastvideo = glob_sets[index++] != 0; + ntsc_mode = glob_sets[index++] != 0; + accuS0hit = glob_sets[index++] != 0; + emuscroff = glob_sets[index++] != 0; + apu_sqr_patch = glob_sets[index++] & 0xFF; + apu_tri_patch = glob_sets[index++] & 0xFF; + apu_enabl = glob_sets[index++] != 0; + sq1_enabl = glob_sets[index++] != 0; + sq2_enabl = glob_sets[index++] != 0; + tri_enabl = glob_sets[index++] != 0; + turboen_a = glob_sets[index++] != 0; + turboen_b = glob_sets[index++] != 0; + System.arraycopy(glob_sets, index + 0 * keys_map[0].length, keys_map[0], 0, keys_map[0].length); + System.arraycopy(glob_sets, index + 1 * keys_map[1].length, keys_map[1], 0, keys_map[1].length); + System.arraycopy(glob_sets, index + 2 * keys_map[2].length, keys_map[2], 0, keys_map[2].length); + } else { + frameskip = def_frameskip; + cpu_accel = def_cpu_accel; + frame_del = def_frame_del; + fastvideo = def_fastvideo; + ntsc_mode = def_ntsc_mode; + accuS0hit = def_accuS0hit; + emuscroff = def_emuscroff; + apu_sqr_patch = def_apu_sqr_patch; + apu_tri_patch = def_apu_tri_patch; + apu_enabl = def_apu_enabl; + sq1_enabl = def_sq1_enabl; + sq2_enabl = def_sq2_enabl; + tri_enabl = def_tri_enabl; + turboen_a = def_turboen_a; + turboen_b = def_turboen_b; + System.arraycopy(def_keys_map[0], 0, keys_map[0], 0, keys_map[0].length); + System.arraycopy(def_keys_map[1], 0, keys_map[1], 0, keys_map[1].length); + System.arraycopy(def_keys_map[2], 0, keys_map[2], 0, keys_map[2].length); + SaveSettings(); + } + } +// ============================================================================ + private final void SaveSettings() { + ByteArrayOutputStream sets = null; + try { + sets = new ByteArrayOutputStream(); + sets.write(osd_enable ? 1 : 0 ); + sets.write(fps_enable ? 1 : 0 ); + sets.write(osd_colour & 0xFF ); + sets.write(osd_font & 0xFF ); + sets.write(auto_sload ? 1 : 0 ); + sets.write(keep_sets ? 1 : 0 ); + sets.write(saturation & 0xFF ); + sets.write(brightness & 0xFF ); + sets.write(full_scr ? 1 : 0 ); + sets.write(reaction & 0xFF ); + sets.write(vmode & 0xFF ); + sets.write(rmode & 0xFF ); + sets.write(pal_name.length() ); + sets.write(pal_name.getBytes() ); + sets.close(); + PutDataToRMS(emul_ver + "_set_a", sets.toByteArray()); + sets = null; + sets = new ByteArrayOutputStream(); + sets.write(frameskip & 0xFF ); + sets.write(cpu_accel & 0xFF ); + sets.write(frame_del & 0xFF ); + sets.write(fastvideo ? 1 : 0 ); + sets.write(ntsc_mode ? 1 : 0 ); + sets.write(accuS0hit ? 1 : 0 ); + sets.write(emuscroff ? 1 : 0 ); + sets.write(apu_sqr_patch & 0xFF ); + sets.write(apu_tri_patch & 0xFF ); + sets.write(apu_enabl ? 1 : 0 ); + sets.write(sq1_enabl ? 1 : 0 ); + sets.write(sq2_enabl ? 1 : 0 ); + sets.write(tri_enabl ? 1 : 0 ); + sets.write(turboen_a ? 1 : 0 ); + sets.write(turboen_b ? 1 : 0 ); + sets.write(keys_map[0] ); + sets.write(keys_map[1] ); + sets.write(keys_map[2] ); + sets.close(); + PutDataToRMS(emul_ver + "_set_b", sets.toByteArray()); + if(nes_name != null && keep_sets) { + if(last_keep_sets) + // сохранение индивидуальных настроек для запущенной игры + PutDataToRMS(nes_name + "_set_b", sets.toByteArray()); + else + // Загрузка настроек для текущей игры при включении keep_sets + LoadSettings(); + } + last_keep_sets = keep_sets; + } catch(IOException e) {} + } +// ============================================================================ +// [-] Save/Load game state +// ============================================================================ + public final void SaveState(int state_num) { + emulThreadControl(ACT_STOP); + try { + ByteArrayOutputStream state = new ByteArrayOutputStream(); +// запись ram + sram и ссылок на банки rom + state.write(memory, 0, 2048 + 8192); + for(int i = 0; i < 8; i++) { + state.write((cpu_bank[i] >> 0x00) & 0xFF); + state.write((cpu_bank[i] >> 0x08) & 0xFF); + state.write((cpu_bank[i] >> 0x10) & 0xFF); + state.write((cpu_bank[i] >> 0x18) & 0xFF); + } +// запись состояния cpu, ppu и mapper + SaveState_cpu(state); + SaveState_ppu(state); + SaveState_map(state); + state.close(); + PutDataToRMS(nes_name + "_sv" + state_num, state.toByteArray()); + print_osd("State is saved"); + } catch(Exception e) {} + emulThreadControl(ACT_RESUME); + } +// ============================================================================ + public final void LoadState(int state_num) { + emulThreadControl(ACT_STOP); + try { + byte temp_array[] = GetDataFromRMS(nes_name + "_sv" + state_num); + ByteArrayInputStream state = new ByteArrayInputStream(temp_array); +// чтение ram + sram и ссылок на банки rom + int a = state.read(memory, 0, 2048 + 8192); + for(int i = 0; i < 8; i++) { + cpu_bank[i] = (state.read() & 0xFF) << 0x00; + cpu_bank[i] |= (state.read() & 0xFF) << 0x08; + cpu_bank[i] |= (state.read() & 0xFF) << 0x10; + cpu_bank[i] |= (state.read() & 0xFF) << 0x18; + } +// чтение состояния cpu, ppu и mapper + LoadState_cpu(state); + LoadState_ppu(state); + LoadState_map(state); + state.close(); + print_osd("State is loaded"); + } catch(Exception e) {} + emulThreadControl(ACT_RESUME); + } +// ============================================================================ +// [+] Record Managment Storage +// ============================================================================ + private final void PutDataToRMS(String file, byte[] data) { + RecordStore record = null; + try { + RecordStore.deleteRecordStore(file); + } catch(Exception e) {} + try { + record = RecordStore.openRecordStore(file, true); + record.addRecord(data, 0, data.length); + record.closeRecordStore(); + } catch(Exception e) {} + } +// ============================================================================ + private final byte[] GetDataFromRMS(String file) { + RecordStore record = null; + byte[] data = null; + try{ + record = RecordStore.openRecordStore(file, false); + data = record.getRecord(record.getNextRecordID() - 1); + record.closeRecordStore(); + } catch(Exception e) {} + return data; + } +// ============================================================================ +// [+] Разные служебные процедуры +// ============================================================================ + public final void reset_system() { + print_osd("Reset [map: " + mapper + "]"); + for(int i = 0; i < cpu_bank.length; i++) + cpu_bank[i] = (i == 3) ? 2048 - (i << 13) : -(i << 13); + reset_ppu(); reset_map(); reset_apu(); reset_cpu(); + } +// ============================================================================ + public final void start_emulation(String filename) { + engine_ok = true; + try { + unload_curr_rom(); + nes_name = filename; + LoadSettings(); + load_new_palette(); + set_camera_mode(); + load_and_test_nes_file(); + reset_system(); + if(auto_sload) LoadState(0); + } catch(Exception e) { + ShowError(e.toString()); + } + } +// ============================================================================ + public final void apply_settings() { + engine_ok = true; + try { + load_new_palette(); + set_camera_mode(); + SaveSettings(); + } catch(Exception e) { + ShowError(e.toString()); + } + } +// ????????==================================================================== + private final void ShowError(String err_msg) { + engine_ok = false; + main.show_error(err_msg); + } +// ============================================================================ +// Game Load/Unload and Palette Load Procedures +// ============================================================================ + public static int chr_size = 0; + public static int prg_size = 0; + private static byte trainer_array[]; // ??? + private static int chr_pages = 0; + private static int prg_pages = 0; +// ============================================================================ + public final void load_new_palette() throws IOException { + String path = "/resource/palettes/" + pal_name + ".pal"; + InputStream is = getClass().getResourceAsStream(path); + if(is == null) throw new IOException("\n\n" + path + "\n\n" + "File not found"); + for(int i = 0; i < 64; i++) { + int r = 0xFF & is.read(); + int g = 0xFF & is.read(); + int b = 0xFF & is.read(); + int x = (r + g + b) / 3; + r = (((r - x) * saturation) / 100) + x; + g = (((g - x) * saturation) / 100) + x; + b = (((b - x) * saturation) / 100) + x; + r = (r * brightness)/100; + g = (g * brightness)/100; + b = (b * brightness)/100; + if(r > 255) r = 255; if(r < 0) r = 0; + if(g > 255) g = 255; if(g < 0) g = 0; + if(b > 255) b = 255; if(b < 0) b = 0; + int entry = r << 16 | g << 8 | b; + if(hid_invc) entry ^= 0x00FFFFFF; + pal_1[i + 0x00] = entry; pal_1[i + 0x40] = 1 << 24 | entry; + pal_2[i + 0x00] = pal_2[i + 0x40] = entry >> 1 & 0x007F7F7F; + } + is.close(); + for(int i = 0; i < 32; i++) pal_lst[i] = pal_1[pal_ram[i]]; + } +// ============================================================================ + private final void load_and_test_nes_file() throws IOException { + String path = "/games/" + nes_name + ".nes"; + InputStream is = getClass().getResourceAsStream(path); + if(is == null) throw new IOException("\n\n" + path + "\n\n" + "File not found"); +// is.available() = header + prg_rom + chr_rom + memory = new byte[is.available() + (2048 + 8192) + (4096 + 8192)]; +// 2048 + 8192 = ram + sram +// 4096 + 8192 = name_table + chr_ram + is.read(memory, 0, is.available()); + is.close(); + if(!(memory.length >= 26627 && + memory[0] == 78 /* N */ && + memory[1] == 69 /* E */ && + memory[2] == 83 /* S */ && + memory[3] == 26)) throw new IOException("\n\n" + path + "\n\n" + "Wrong file format"); + prg_pages = memory[4]; + chr_pages = memory[5]; + byte sets = memory[6]; + mir_vert = (sets & 1) != 0; + bat_sram = (sets & 2) != 0; + trainer = (sets & 4) != 0; + mir_four = (sets & 8) != 0; + int map0 = sets >> 4 & 0x0F; + int map1 = memory[7] & 0xF0; + mapper = map0 | map1; + for(int i = 8; i < 16; i++) if(memory[i] != 0) mapper &= 0x0F; + switch(mapper) { + case 0: case 1: case 2: case 3: case 4: case 6: case 7: + case 8: case 11: case 15: /*case 69:*/ break; + default: + throw new IOException("\n\n" + "Mapper #" + mapper + "\n\n" + "Unsupported mapper"); + } + if(prg_pages == 0) throw new IOException("\n\n" + path + "\n\n" + "Wrong file format"); + prg_size = prg_pages * 16384; +// copy from memory[0] to memory[2048+8192] - резервируется память для ram и sram + System.arraycopy(memory, 0, memory, 2048 + 8192, memory.length - (2048 + 8192)); + nt_chr_addr = 10240 + 16; +// nt_chr_addr указывает на начало trainer в файле. Если trainer отсутствует, то на начало prg_rom + if(trainer) { + trainer_array = new byte[512]; + for(int i = 0; i < 512; i++) + trainer_array[i] = memory[0x1800 + i] = memory[nt_chr_addr++]; + } else { + trainer_array = null; + for(int i = 0; i < 512; i++) + memory[0x1800 + i] = 0; + } +// переносит prg_rom в memory[2048+8192] + System.arraycopy(memory, nt_chr_addr, memory, 2048 + 8192, prg_size); + nt_chr_addr += prg_size; +// nt_chr_addr указывает на начало chr_rom + chr_size = chr_pages * 8192; + if(chr_size == 0) { +// 4096 (name_table) + 8192 (chr_ram) + nt_chr_size = 4096 + 8192; + } else { +// 4096 (name_table) + chr_size (chr_rom) + nt_chr_size = 4096 + chr_size; +// копирует chr_rom в memory[2048+8192+prg_rom+4096] + System.arraycopy(memory, nt_chr_addr, memory, nt_chr_addr + 4096, chr_size); + + } + } +// ============================================================================ + protected final void unload_curr_rom() { + if(auto_sload) SaveState(0); + emulThreadControl(ACT_STOP); + memory = null; + System.gc(); + } +// ============================================================================ +// [+] CPU Reset, Save/Load state +// ============================================================================ + private int A = 0, X = 0, Y = 0, S = 0, P = 0, PC = 0; + private int cpu_ticks = 0, loc_pc, bank_base; +// ============================================================================ + private final void reset_cpu() { + emulThreadControl(ACT_STOP); + PC = (char)(memory[cpu_bank[7] + 0xFFFC] & 0xFF | memory[cpu_bank[7] + 0xFFFD] << 8); + P = 0x04; S = 0xFF; + emulThreadControl(ACT_RESUME); + } +// ============================================================================ + private final void SaveState_cpu(ByteArrayOutputStream state) { + state.write(A & 0xFF); + state.write(X & 0xFF); + state.write(Y & 0xFF); + state.write(S & 0xFF); + state.write(P & 0xFF); + state.write(PC >> 0 & 0xFF); + state.write(PC >> 8 & 0xFF); + } +// ============================================================================ + private final void LoadState_cpu(ByteArrayInputStream state) { + A = state.read() & 0xFF; + X = state.read() & 0xFF; + Y = state.read() & 0xFF; + S = state.read() & 0xFF; + P = state.read() & 0xFF; + PC = state.read() << 0 & 0x00FF; + PC |= state.read() << 8 & 0xFF00; + } +// ============================================================================ +// [+] Emulate One NES Frame in loop +// ============================================================================ + public final int STAT_SIZE = 32; + public final int STAT_RATE = 20; + public final int MIN_FPS = 25; + public final int T_STATES = 1137; +// ============================================================================ + public final void run() { + try { + boolean hsync_enab = hsync_used && !fastvideo; + // -------------------------------------------------------------------- + int jp_ad_value[][] = new int[5][STAT_SIZE]; + int jp_ad_times[][] = new int[5][STAT_SIZE]; + int free_pos_id[] = new int[5]; + int adr_to_skip[] = new int[5]; + boolean count_jumps = false; + // -------------------------------------------------------------------- + for(int i = 0; i < 32; i++) pal_lst[i] = pal_1[pal_ram[i]]; + int VFrames = -1; + int hit_line = -1; + // -------------------------------------------------------------------- + int mem_bank[] = cpu_bank; + byte mem[] = memory; + int INC8[] = new int[256]; + int DEC8[] = new int[256]; + for(int i = 0; i < 256; i++) { + INC8[i] = (i + 1) & 0xFF; + DEC8[i] = (i - 1) & 0xFF; + } + // -------------------------------------------------------------------- + loc_pc = PC + (bank_base = mem_bank[PC >> 13]); + int sz = (2 - (P & 0x02)) | (P & 0x80); + int v = P >> 6 & 0x01; + int c = P & 0x01; + int i = P & 0x04; + int d = P & 0x08; + cpu_ticks = 0; + // -------------------------------------------------------------------- + while(!threadMustStop) { + long f_start = System.currentTimeMillis(); + boolean rendering = VFrames % frameskip == 0; + if(fastvideo) { + if((hit_line = (spr_ram[0] & 0xFF) + 1) > 239) hit_line = -1; + } else { + if((ppuContr2 & 0x18) != 0) ppuV = ppuT; + hit_line = get_spr0_hitline(); + } + play_apu(); + for(int scanline = 0; scanline < 262; scanline++) { + switch(scanline) { + case 261: ppuStatus &= 0x3F; break; + case 241: ppuStatus |= 0x80; break; + case 242: + if((ppuContr1 & 0x80) != 0) { + PC = loc_pc - bank_base; + mem[0x0100 + S] = (byte)(PC >> 8); + S = DEC8[S]; + mem[0x0100 + S] = (byte)PC; + S = DEC8[S]; + mem[0x0100 + S] = (byte)(c | (sz != 0 ? 0 : 2) | i | d | v << 6 | (sz & 0x80)); + S = DEC8[S]; + PC = (char)(mem[mem_bank[7] + 0xFFFA] & 0xFF | mem[mem_bank[7] + 0xFFFB] << 8); + loc_pc = PC + (bank_base = mem_bank[PC >> 13]); + cpu_ticks -= 70; + } + break; + } + if(fastvideo) { + if(scanline == hit_line + 1 && (ppuContr2 & 0x18) != 0) ppuStatus |= 0x40; + if(scanline < 240) { + if(scanline > hit_line) { + cpu_ticks += T_STATES * (240 - scanline - 1); + scanline = 240; + } else + if(scanline < hit_line) { + cpu_ticks += T_STATES * (hit_line - scanline - 1); + scanline = hit_line; + } + } else + if(scanline > 241 && scanline < 260) { + cpu_ticks += T_STATES * (260 - scanline - 1); + scanline = 260; + } + } +cpu_lab: for(cpu_ticks += T_STATES; cpu_ticks > 0;) { + switch(mem[loc_pc++]) { +// ---------------------------------------------------------------------------- + case 0x00000000: // INS_BRK, ADR_IMP + PC = (loc_pc - bank_base) + 1; + mem[0x0100 + S] = (byte)(PC >> 8); + S = DEC8[S]; + mem[0x0100 + S] = (byte)PC; + S = DEC8[S]; + P = c | (sz != 0 ? 0 : 2) | i | d | v << 6 | (sz & 0x80); + mem[0x0100 + S] = (byte)(P | 0x10); + S = DEC8[S]; + PC = (char)(mem[mem_bank[7] + 0xFFFE] & 0xFF | mem[mem_bank[7] + 0xFFFF] << 8); + loc_pc = PC + (bank_base = mem_bank[PC >> 13]); + i = 4; + cpu_ticks -= 70; + continue; +// ---------------------------------------------------------------------------- + case 0x00000001: // INS_ORA, ADR_INDX + { int V0 = mem[loc_pc++] + X & 0xFF; + A |= cpuReadMem((char)(mem[V0] & 0xFF | mem[++V0] << 8)); + sz = A; + cpu_ticks -= 60; + } continue; +// ---------------------------------------------------------------------------- + case 0x00000005: // INS_ORA, ADR_ZP + A |= mem[mem[loc_pc++] & 0xFF] & 0xFF; + sz = A; + cpu_ticks -= 30; + continue; +// ---------------------------------------------------------------------------- + case 0x00000006: // INS_ASL, ADR_ZP + { int V0 = mem[loc_pc++] & 0xFF; + int V1 = mem[V0] & 0xFF; + c = V1 >> 7; + mem[V0] = (byte)(sz = V1 << 1 & 0xFF); + cpu_ticks -= 50; + } continue; +// ---------------------------------------------------------------------------- + case 0x00000008: // INS_PHP, ADR_IMP + P = c | (sz != 0 ? 0 : 2) | i | d | v << 6 | (sz & 0x80); + mem[0x0100 + S] = (byte)(P | 0x10); + S = DEC8[S]; + cpu_ticks -= 30; + continue; +// ---------------------------------------------------------------------------- + case 0x00000009: // INS_ORA, ADR_IMM + A |= mem[loc_pc++] & 0xFF; + sz = A; + cpu_ticks -= 20; + continue; +// ---------------------------------------------------------------------------- + case 0x0000000A: // INS_ASL, ADR_ACC + c = A >> 7; + sz = A = A << 1 & 0xFF; + cpu_ticks -= 20; + continue; +// ---------------------------------------------------------------------------- + case 0x0000000D: // INS_ORA, ADR_ABS + A |= cpuReadMem((char)(mem[loc_pc++] & 0xFF | mem[loc_pc++] << 8)); + sz = A; + cpu_ticks -= 40; + continue; +// ---------------------------------------------------------------------------- + case 0x0000000E: // INS_ASL, ADR_ABS + { int V0 = (char)(mem[loc_pc++] & 0xFF | mem[loc_pc++] << 8); + int V1 = cpuReadMem(V0); + c = V1 >> 7; + cpuWriteMem(V0, sz = V1 << 1 & 0xFF); + cpu_ticks -= 60; + } continue; +// ---------------------------------------------------------------------------- + case 0x00000010: // INS_BPL, ADR_REL + if(sz >> 7 == 0) { + loc_pc += mem[loc_pc] + 1; + if(loc_pc == adr_to_skip[3]) { cpu_ticks = 0; break cpu_lab; } + cpu_ticks -= 30; // + if(count_jumps) { + for(int index = 0; index < jp_ad_value[3].length; index++) { + if(jp_ad_value[3][index] == loc_pc) { + jp_ad_times[3][index]++; continue cpu_lab; + } + } + if(free_pos_id[3] < jp_ad_value[3].length) { + jp_ad_value[3][free_pos_id[3]] = loc_pc; + jp_ad_times[3][free_pos_id[3]++]++; + } + } + } else { + loc_pc++; + cpu_ticks -= 20; + } + continue; +// ---------------------------------------------------------------------------- + case 0x00000011: // INS_ORA, ADR_INDY + { int V0 = mem[loc_pc++] & 0xFF; + A |= cpuReadMem((char)((mem[V0] & 0xFF | mem[++V0] << 8) + Y)); + sz = A; + cpu_ticks -= 50; // + } continue; +// ---------------------------------------------------------------------------- + case 0x00000015: // INS_ORA, ADR_ZPX + A |= mem[mem[loc_pc++] + X & 0xFF] & 0xFF; + sz = A; + cpu_ticks -= 40; + continue; +// ---------------------------------------------------------------------------- + case 0x00000016: // INS_ASL, ADR_ZPX + { int V0 = mem[loc_pc++] + X & 0xFF; + int V1 = mem[V0] & 0xFF; + c = V1 >> 7; + mem[V0] = (byte)(sz = V1 << 1 & 0xFF); + cpu_ticks -= 60; + } continue; +// ---------------------------------------------------------------------------- + case 0x00000018: // INS_CLC, ADR_IMP + c = 0; + cpu_ticks -= 20; + continue; +// ---------------------------------------------------------------------------- + case 0x00000019: // INS_ORA, ADR_ABSY + A |= cpuReadMem((char)((mem[loc_pc++] & 0xFF | mem[loc_pc++] << 8) + Y)); + sz = A; + cpu_ticks -= 40; // + continue; +// ---------------------------------------------------------------------------- + case 0x0000001D: // INS_ORA, ADR_ABSX + A |= cpuReadMem((char)((mem[loc_pc++] & 0xFF | mem[loc_pc++] << 8) + X)); + sz = A; + cpu_ticks -= 40; // + continue; +// ---------------------------------------------------------------------------- + case 0x0000001E: // INS_ASL, ADR_ABSX + { int V0 = (char)((mem[loc_pc++] & 0xFF | mem[loc_pc++] << 8) + X); + int V1 = cpuReadMem(V0); + c = V1 >> 7; + cpuWriteMem(V0, sz = V1 << 1 & 0xFF); + cpu_ticks -= 70; + } continue; +// ---------------------------------------------------------------------------- + case 0x00000020: // INS_JSR, ADR_ABS + PC = (loc_pc - bank_base) + 1; + mem[0x0100 + S] = (byte)(PC >> 8); + S = DEC8[S]; + mem[0x0100 + S] = (byte)PC; + S = DEC8[S]; + PC = (char)(mem[loc_pc] & 0xFF | mem[loc_pc + 1] << 8); + loc_pc = PC + (bank_base = mem_bank[PC >> 13]); + cpu_ticks -= 60; + continue; +// ---------------------------------------------------------------------------- + case 0x00000021: // INS_AND, ADR_INDX + { int V0 = mem[loc_pc++] + X & 0xFF; + A &= cpuReadMem((char)(mem[V0] & 0xFF | mem[++V0] << 8)); + sz = A; + cpu_ticks -= 60; + } continue; +// ---------------------------------------------------------------------------- + case 0x00000024: // INS_BIT, ADR_ZP + { int V0 = mem[mem[loc_pc++] & 0xFF] & 0xFF; + v = V0 >> 6 & 1; + byte V1 = mem[loc_pc]; + sz = V1 == 16 || V1 == 48 ? V0 : A & V0; + cpu_ticks -= 30; + } continue; +// ---------------------------------------------------------------------------- + case 0x00000025: // INS_AND, ADR_ZP + A &= mem[mem[loc_pc++] & 0xFF]; + sz = A; + cpu_ticks -= 30; + continue; +// ---------------------------------------------------------------------------- + case 0x00000026: // INS_ROL, ADR_ZP + { int V0 = mem[loc_pc++] & 0xFF; + int V1 = mem[V0] & 0xFF; + V1 = V1 << 1 | c; + c = V1 >> 8; + mem[V0] = (byte)(sz = V1 & 0xFF); + cpu_ticks -= 50; + } continue; +// ---------------------------------------------------------------------------- + case 0x00000028: // INS_PLP, ADR_IMP + S = INC8[S]; + P = mem[0x0100 + S] & 0xFF; + sz = (2 - (P & 0x02)) | (P & 0x80); + v = P >> 6 & 0x01; + c = P & 0x01; + i = P & 0x04; + d = P & 0x08; + cpu_ticks -= 40; + continue; +// ---------------------------------------------------------------------------- + case 0x00000029: // INS_AND, ADR_IMM + A &= mem[loc_pc++]; + sz = A; + cpu_ticks -= 20; + continue; +// ---------------------------------------------------------------------------- + case 0x0000002A: // INS_ROL, ADR_ACC + A = A << 1 | c; + c = A >> 8; + A &= 0xFF; + sz = A; + cpu_ticks -= 20; + continue; +// ---------------------------------------------------------------------------- + case 0x0000002C: // INS_BIT, ADR_ABS + { int V0 = cpuReadMem((char)(mem[loc_pc++] & 0xFF | mem[loc_pc++] << 8)); + v = V0 >> 6 & 1; + byte V1 = mem[loc_pc]; + sz = V1 == 16 || V1 == 48 ? V0 : A & V0; + cpu_ticks -= 40; + } continue; +// ---------------------------------------------------------------------------- + case 0x0000002D: // INS_AND, ADR_ABS + A &= cpuReadMem((char)(mem[loc_pc++] & 0xFF | mem[loc_pc++] << 8)); + sz = A; + cpu_ticks -= 40; + continue; +// ---------------------------------------------------------------------------- + case 0x0000002E: // INS_ROL, ADR_ABS + { int V0 = (char)(mem[loc_pc++] & 0xFF | mem[loc_pc++] << 8); + int V1 = cpuReadMem(V0); + V1 = V1 << 1 | c; + c = V1 >> 8; + cpuWriteMem(V0, sz = V1 & 0xFF); + cpu_ticks -= 60; + } continue; +// ---------------------------------------------------------------------------- + case 0x00000030: // INS_BMI, ADR_REL + if(sz >> 7 != 0) { + loc_pc += mem[loc_pc] + 1; + cpu_ticks -= 30; // + } else { + loc_pc++; + cpu_ticks -= 20; + } + continue; +// ---------------------------------------------------------------------------- + case 0x00000031: // INS_AND, ADR_INDY + { int V0 = mem[loc_pc++] & 0xFF; + A &= cpuReadMem((char)((mem[V0] & 0xFF | mem[++V0] << 8) + Y)); + sz = A; + cpu_ticks -= 50; // + } continue; +// ---------------------------------------------------------------------------- + case 0x00000035: // INS_AND, ADR_ZPX + A &= mem[mem[loc_pc++] + X & 0xFF]; + sz = A; + cpu_ticks -= 40; + continue; +// ---------------------------------------------------------------------------- + case 0x00000036: // INS_ROL, ADR_ZPX + { int V0 = mem[loc_pc++] + X & 0xFF; + int V1 = mem[V0] & 0xFF; + V1 = V1 << 1 | c; + c = V1 >> 8; + mem[V0] = (byte)(sz = V1 & 0xFF); + cpu_ticks -= 60; + } continue; +// ---------------------------------------------------------------------------- + case 0x00000038: // INS_SEC, ADR_IMP + c = 1; + cpu_ticks -= 20; + continue; +// ---------------------------------------------------------------------------- + case 0x00000039: // INS_AND, ADR_ABSY + A &= cpuReadMem((char)((mem[loc_pc++] & 0xFF | mem[loc_pc++] << 8) + Y)); + sz = A; + cpu_ticks -= 40; // + continue; +// ---------------------------------------------------------------------------- + case 0x0000003D: // INS_AND, ADR_ABSX + A &= cpuReadMem((char)((mem[loc_pc++] & 0xFF | mem[loc_pc++] << 8) + X)); + sz = A; + cpu_ticks -= 40; // + continue; +// ---------------------------------------------------------------------------- + case 0x0000003E: // INS_ROL, ADR_ABSX + { int V0 = (char)((mem[loc_pc++] & 0xFF | mem[loc_pc++] << 8) + X); + int V1 = cpuReadMem(V0); + V1 = V1 << 1 | c; + c = V1 >> 8; + cpuWriteMem(V0, sz = V1 & 0xFF); + cpu_ticks -= 70; + } continue; +// ---------------------------------------------------------------------------- + case 0x00000040: // INS_RTI, ADR_IMP + S = INC8[S]; + P = mem[0x0100 + S] & 0xFF; + sz = (2 - (P & 0x02)) | (P & 0x80); + v = P >> 6 & 0x01; + c = P & 0x01; + i = P & 0x04; + S = INC8[S]; + PC = mem[0x0100 + S] & 0xFF; + S = INC8[S]; + PC |= (mem[0x0100 + S] & 0xFF) << 8; + loc_pc = PC + (bank_base = mem_bank[PC >> 13]); + cpu_ticks -= 60; + continue; +// ---------------------------------------------------------------------------- + case 0x00000041: // INS_EOR, ADR_INDX + { int V0 = mem[loc_pc++] + X & 0xFF; + A ^= cpuReadMem((char)(mem[V0] & 0xFF | mem[++V0] << 8)); + sz = A; + cpu_ticks -= 60; + } continue; +// ---------------------------------------------------------------------------- + case 0x00000045: // INS_EOR, ADR_ZP + A ^= mem[mem[loc_pc++] & 0xFF] & 0xFF; + sz = A; + cpu_ticks -= 30; + continue; +// ---------------------------------------------------------------------------- + case 0x00000046: // INS_LSR, ADR_ZP + { int V0 = mem[loc_pc++] & 0xFF; + int V1 = mem[V0] & 0xFF; + c = V1 & 1; + mem[V0] = (byte)(sz = V1 >> 1); + cpu_ticks -= 50; + } continue; +// ---------------------------------------------------------------------------- + case 0x00000048: // INS_PHA, ADR_IMP + mem[0x0100 + S] = (byte)A; + S = DEC8[S]; + cpu_ticks -= 30; + continue; +// ---------------------------------------------------------------------------- + case 0x00000049: // INS_EOR, ADR_IMM + A ^= mem[loc_pc++] & 0xFF; + sz = A; + cpu_ticks -= 20; + continue; +// ---------------------------------------------------------------------------- + case 0x0000004A: // INS_LSR, ADR_ACC + c = A & 0x01; + A >>= 1; + sz = A; + cpu_ticks -= 20; + continue; +// ---------------------------------------------------------------------------- + case 0x0000004C: // INS_JMP, ADR_ABS + { int V0 = loc_pc - 1; + PC = (char)(mem[loc_pc] & 0xFF | mem[loc_pc + 1] << 8); + loc_pc = PC + (bank_base = mem_bank[PC >> 13]); + if(loc_pc == adr_to_skip[0] || loc_pc == V0) { + cpu_ticks = 0; break cpu_lab; + } + cpu_ticks -= 30; + if(count_jumps) { + for(int index = 0; index < jp_ad_value[0].length; index++) { + if(jp_ad_value[0][index] == loc_pc) { + jp_ad_times[0][index]++; continue cpu_lab; + } + } + if(free_pos_id[0] < jp_ad_value[0].length) { + jp_ad_value[0][free_pos_id[0]] = loc_pc; + jp_ad_times[0][free_pos_id[0]++]++; + } + } + } continue; +// ---------------------------------------------------------------------------- + case 0x0000004D: // INS_EOR, ADR_ABS + A ^= cpuReadMem((char)(mem[loc_pc++] & 0xFF | mem[loc_pc++] << 8)); + sz = A; + cpu_ticks -= 40; + continue; +// ---------------------------------------------------------------------------- + case 0x0000004E: // INS_LSR, ADR_ABS + { int V0 = (char)(mem[loc_pc++] & 0xFF | mem[loc_pc++] << 8); + int V1 = cpuReadMem(V0); + c = V1 & 1; + cpuWriteMem(V0, sz = V1 >> 1); + cpu_ticks -= 60; + } continue; +// ---------------------------------------------------------------------------- + case 0x00000050: // INS_BVC, ADR_REL + { if(v == 0) { + loc_pc += mem[loc_pc] + 1; + if(loc_pc == adr_to_skip[4]) { cpu_ticks = 0; break cpu_lab; } + cpu_ticks -= 30; // + if(count_jumps) { + for(int index = 0; index < jp_ad_value[4].length; index++) { + if(jp_ad_value[4][index] == loc_pc) { + jp_ad_times[4][index]++; continue cpu_lab; + } + } + if(free_pos_id[4] < jp_ad_value[4].length) { + jp_ad_value[4][free_pos_id[4]] = loc_pc; + jp_ad_times[4][free_pos_id[4]++]++; + } + } + } else { + loc_pc++; + cpu_ticks -= 20; + } + } continue; +// ---------------------------------------------------------------------------- + case 0x00000051: // INS_EOR, ADR_INDY + { int V0 = mem[loc_pc++] & 0xFF; + A ^= cpuReadMem((char)((mem[V0] & 0xFF | mem[++V0] << 8) + Y)); + sz = A; + cpu_ticks -= 50; // + } continue; +// ---------------------------------------------------------------------------- + case 0x00000055: // INS_EOR, ADR_ZPX + A ^= mem[mem[loc_pc++] + X & 0xFF] & 0xFF; + sz = A; + cpu_ticks -= 40; + continue; +// ---------------------------------------------------------------------------- + case 0x00000056: // INS_LSR, ADR_ZPX + { int V0 = mem[loc_pc++] + X & 0xFF; + int V1 = mem[V0] & 0xFF; + c = V1 & 1; + mem[V0] = (byte)(sz = V1 >> 1); + cpu_ticks -= 60; + } continue; +// ---------------------------------------------------------------------------- + case 0x00000058: // INS_CLI, ADR_IMP + i = 0; + cpu_ticks -= 20; + continue; +// ---------------------------------------------------------------------------- + case 0x00000059: // INS_EOR, ADR_ABSY + A ^= cpuReadMem((char)((mem[loc_pc++] & 0xFF | mem[loc_pc++] << 8) + Y)); + sz = A; + cpu_ticks -= 40; // + continue; +// ---------------------------------------------------------------------------- + case 0x0000005D: // INS_EOR, ADR_ABSX + A ^= cpuReadMem((char)((mem[loc_pc++] & 0xFF | mem[loc_pc++] << 8) + X)); + sz = A; + cpu_ticks -= 40; // + continue; +// ---------------------------------------------------------------------------- + case 0x0000005E: // INS_LSR, ADR_ABSX + { int V0 = (char)((mem[loc_pc++] & 0xFF | mem[loc_pc++] << 8) + X); + int V1 = cpuReadMem(V0); + c = V1 & 1; + cpuWriteMem(V0, sz = V1 >> 1); + cpu_ticks -= 70; + } continue; +// ---------------------------------------------------------------------------- + case 0x00000060: // INS_RTS, ADR_IMP + S = INC8[S]; + PC = mem[0x0100 + S] & 0xFF; + S = INC8[S]; + PC |= (mem[0x0100 + S] & 0xFF) << 8; + PC++; + loc_pc = PC + (bank_base = mem_bank[PC >> 13]); + cpu_ticks -= 60; + continue; +// ---------------------------------------------------------------------------- + case 0x00000061: // INS_ADC, ADR_INDX + { int V0 = mem[loc_pc++] + X & 0xFF; + int V1 = cpuReadMem((char)(mem[V0] & 0xFF | mem[++V0] << 8)); + int V2 = A + V1 + c; + v = (~(A ^ V1) & (A ^ V2)) >> 7 & 1; + c = V2 >> 8; + A = V2 & 0xFF; + sz = A; + cpu_ticks -= 60; + } continue; +// ---------------------------------------------------------------------------- + case 0x00000065: // INS_ADC, ADR_ZP + { int V0 = mem[mem[loc_pc++] & 0xFF] & 0xFF; + int V1 = A + V0 + c; + v = (~(A ^ V0) & (A ^ V1)) >> 7 & 1; + c = V1 >> 8; + sz = A = V1 & 0xFF; + cpu_ticks -= 30; + } continue; +// ---------------------------------------------------------------------------- + case 0x00000066: // INS_ROR, ADR_ZP + { int V0 = mem[loc_pc++] & 0xFF; + int V1 = mem[V0] & 0xFF; + int V2 = c << 7; + c = V1 & 1; + mem[V0] = (byte)(sz = V1 >> 1 | V2); + cpu_ticks -= 50; + } continue; +// ---------------------------------------------------------------------------- + case 0x00000068: // INS_PLA, ADR_IMP + S = INC8[S]; + sz = A = mem[0x0100 + S] & 0xFF; + cpu_ticks -= 40; + continue; +// ---------------------------------------------------------------------------- + case 0x00000069: // INS_ADC, ADR_IMM + { int V0 = mem[loc_pc++] & 0xFF; + int V1 = A + V0 + c; + v = (~(A ^ V0) & (A ^ V1)) >> 7 & 1; + c = V1 >> 8; + sz = A = V1 & 0xFF; + cpu_ticks -= 20; + } continue; +// ---------------------------------------------------------------------------- + case 0x0000006A: // INS_ROR, ADR_ACC + { int V0 = c; + c = A & 1; + sz = A = A >> 1 | V0 << 7; + cpu_ticks -= 20; + } continue; +// ---------------------------------------------------------------------------- + case 0x0000006C: // INS_JMP, ADR_IND + { int V0 = (char)(mem[loc_pc++] & 0xFF | mem[loc_pc++] << 8); + if((V0 & 0x00FF) == 0xFF) + PC = cpuReadMem(V0) | cpuReadMem(V0 & 0xFF00) << 8; + else + PC = cpuReadMem(V0) | cpuReadMem(V0 + 1) << 8; + loc_pc = PC + (bank_base = mem_bank[PC >> 13]); + cpu_ticks -= 50; + } continue; +// ---------------------------------------------------------------------------- + case 0x0000006D: // INS_ADC, ADR_ABS + { int V0 = cpuReadMem((char)(mem[loc_pc++] & 0xFF | mem[loc_pc++] << 8)); + int V1 = A + V0 + c; + v = (~(A ^ V0) & (A ^ V1)) >> 7 & 1; + c = V1 >> 8; + sz = A = V1 & 0xFF; + cpu_ticks -= 40; + } continue; +// ---------------------------------------------------------------------------- + case 0x0000006E: // INS_ROR, ADR_ABS + { int V0 = (char)(mem[loc_pc++] & 0xFF | mem[loc_pc++] << 8); + int V1 = cpuReadMem(V0); + int V2 = c << 7; + c = V1 & 1; + cpuWriteMem(V0, sz = V1 >> 1 | V2); + cpu_ticks -= 60; + } continue; +// ---------------------------------------------------------------------------- + case 0x00000070: // INS_BVS, ADR_REL + if(v != 0) { + loc_pc += mem[loc_pc] + 1; + cpu_ticks -= 30; // + } else { + loc_pc++; + cpu_ticks -= 20; + } + continue; +// ---------------------------------------------------------------------------- + case 0x00000071: // INS_ADC, ADR_INDY + { int V0 = mem[loc_pc++] & 0xFF; + int V1 = cpuReadMem((char)((mem[V0] & 0xFF | mem[++V0] << 8) + Y)); + int V2 = A + V1 + c; + v = (~(A ^ V1) & (A ^ V2)) >> 7 & 1; + c = V2 >> 8; + sz = A = V2 & 0xFF; + cpu_ticks -= 50; // + } continue; +// ---------------------------------------------------------------------------- + case 0x00000075: // INS_ADC, ADR_ZPX + { int V0 = mem[mem[loc_pc++] + X & 0xFF] & 0xFF; + int V1 = A + V0 + c; + v = (~(A ^ V0) & (A ^ V1)) >> 7 & 1; + c = V1 >> 8; + sz = A = V1 & 0xFF; + cpu_ticks -= 40; + } continue; +// ---------------------------------------------------------------------------- + case 0x00000076: // INS_ROR, ADR_ZPX + { int V0 = mem[loc_pc++] + X & 0xFF; + int V1 = mem[V0] & 0xFF; + int V2 = c << 7; + c = V1 & 1; + mem[V0] = (byte)(sz = V1 >> 1 | V2); + cpu_ticks -= 60; + } continue; +// ---------------------------------------------------------------------------- + case 0x00000078: // INS_SEI, ADR_IMP + i = 4; + cpu_ticks -= 20; + continue; +// ---------------------------------------------------------------------------- + case 0x00000079: // INS_ADC, ADR_ABSY + { int V0 = cpuReadMem((char)((mem[loc_pc++] & 0xFF | mem[loc_pc++] << 8) + Y)); + int V1 = A + V0 + c; + v = (~(A ^ V0) & (A ^ V1)) >> 7 & 1; + c = V1 >> 8; + sz = A = V1 & 0xFF; + cpu_ticks -= 40; // + } continue; +// ---------------------------------------------------------------------------- + case 0x0000007D: // INS_ADC, ADR_ABSX + { int V0 = cpuReadMem((char)((mem[loc_pc++] & 0xFF | mem[loc_pc++] << 8) + X)); + int V1 = A + V0 + c; + v = (~(A ^ V0) & (A ^ V1)) >> 7 & 1; + c = V1 >> 8; + sz = A = V1 & 0xFF; + cpu_ticks -= 40; // + } continue; +// ---------------------------------------------------------------------------- + case 0x0000007E: // INS_ROR, ADR_ABSX + { int V0 = (char)((mem[loc_pc++] & 0xFF | mem[loc_pc++] << 8) + X); + int V1 = cpuReadMem(V0); + int V2 = c << 7; + c = V1 & 1; + cpuWriteMem(V0, sz = V1 >> 1 | V2); + cpu_ticks -= 70; + } continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFF81: // INS_STA, ADR_INDX + { int V0 = mem[loc_pc++] + X & 0xFF; + cpuWriteMem((char)(mem[V0] & 0xFF | mem[++V0] << 8), A); + cpu_ticks -= 60; + } continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFF84: // INS_STY, ADR_ZP + mem[mem[loc_pc++] & 0xFF] = (byte)Y; + cpu_ticks -= 30; + continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFF85: // INS_STA, ADR_ZP + mem[mem[loc_pc++] & 0xFF] = (byte)A; + cpu_ticks -= 30; + continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFF86: // INS_STX, ADR_ZP + mem[mem[loc_pc++] & 0xFF] = (byte)X; + cpu_ticks -= 30; + continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFF88: // INS_DEY, ADR_IMP + sz = Y = DEC8[Y]; + cpu_ticks -= 20; + continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFF8A: // INS_TXA, ADR_IMP + sz = A = X; + cpu_ticks -= 20; + continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFF8C: // INS_STY, ADR_ABS + cpuWriteMem((char)(mem[loc_pc++] & 0xFF | mem[loc_pc++] << 8), Y); + cpu_ticks -= 40; + continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFF8D: // INS_STA, ADR_ABS + cpuWriteMem((char)(mem[loc_pc++] & 0xFF | mem[loc_pc++] << 8), A); + cpu_ticks -= 40; + continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFF8E: // INS_STX, ADR_ABS + cpuWriteMem((char)(mem[loc_pc++] & 0xFF | mem[loc_pc++] << 8), X); + cpu_ticks -= 40; + continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFF90: // INS_BCC, ADR_REL + if(c == 0) { + loc_pc += mem[loc_pc] + 1; + cpu_ticks -= 30; // + } else { + loc_pc++; + cpu_ticks -= 20; + } + continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFF91: // INS_STA, ADR_INDY + { int V0 = mem[loc_pc++] & 0xFF; + cpuWriteMem((char)((mem[V0] & 0xFF | mem[++V0] << 8) + Y), A); + cpu_ticks -= 60; + } continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFF94: // INS_STY, ADR_ZPX + mem[mem[loc_pc++] + X & 0xFF] = (byte)Y; + cpu_ticks -= 40; + continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFF95: // INS_STA, ADR_ZPX + mem[mem[loc_pc++] + X & 0xFF] = (byte)A; + cpu_ticks -= 40; + continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFF96: // INS_STX, ADR_ZPY + mem[mem[loc_pc++] + Y & 0xFF] = (byte)X; + cpu_ticks -= 40; + continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFF98: // INS_TYA, ADR_IMP + sz = A = Y; + cpu_ticks -= 20; + continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFF99: // INS_STA, ADR_ABSY + cpuWriteMem((char)((mem[loc_pc++] & 0xFF | mem[loc_pc++] << 8) + Y), A); + cpu_ticks -= 50; + continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFF9A: // INS_TXS, ADR_IMP + S = X; + cpu_ticks -= 20; + continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFF9D: // INS_STA, ADR_ABSX + cpuWriteMem((char)((mem[loc_pc++] & 0xFF | mem[loc_pc++] << 8) + X), A); + cpu_ticks -= 50; + continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFA0: // INS_LDY, ADR_IMM + sz = Y = mem[loc_pc++] & 0xFF; + cpu_ticks -= 20; + continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFA1: // INS_LDA, ADR_INDX + { int V0 = mem[loc_pc++] + X & 0xFF; + sz = A = cpuReadMem((char)(mem[V0] & 0xFF | mem[++V0] << 8)); + cpu_ticks -= 60; + } continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFA2: // INS_LDX, ADR_IMM + sz = X = mem[loc_pc++] & 0xFF; + cpu_ticks -= 20; + continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFA4: // INS_LDY, ADR_ZP + sz = Y = mem[mem[loc_pc++] & 0xFF] & 0xFF; + cpu_ticks -= 30; + continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFA5: // INS_LDA, ADR_ZP + sz = A = mem[mem[loc_pc++] & 0xFF] & 0xFF; + cpu_ticks -= 30; + continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFA6: // INS_LDX, ADR_ZP + sz = X = mem[mem[loc_pc++] & 0xFF] & 0xFF; + cpu_ticks -= 30; + continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFA8: // INS_TAY, ADR_IMP + sz = Y = A; + cpu_ticks -= 20; + continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFA9: // INS_LDA, ADR_IMM + sz = A = mem[loc_pc++] & 0xFF; + cpu_ticks -= 20; + continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFAA: // INS_TAX, ADR_IMP + sz = X = A; + cpu_ticks -= 20; + continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFAC: // INS_LDY, ADR_ABS + sz = Y = cpuReadMem((char)(mem[loc_pc++] & 0xFF | mem[loc_pc++] << 8)); + cpu_ticks -= 40; + continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFAD: // INS_LDA, ADR_ABS + sz = A = cpuReadMem((char)(mem[loc_pc++] & 0xFF | mem[loc_pc++] << 8)); + cpu_ticks -= 40; + continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFAE: // INS_LDX, ADR_ABS + sz = X = cpuReadMem((char)(mem[loc_pc++] & 0xFF | mem[loc_pc++] << 8)); + cpu_ticks -= 40; + continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFB0: // INS_BCS, ADR_REL + if(c != 0) { + loc_pc += mem[loc_pc] + 1; + cpu_ticks -= 30; // + } else { + loc_pc++; + cpu_ticks -= 20; + } + continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFB1: // INS_LDA, ADR_INDY + { int V0 = mem[loc_pc++] & 0xFF; + sz = A = cpuReadMem((char)((mem[V0] & 0xFF | mem[++V0] << 8) + Y)); + cpu_ticks -= 50; // + } continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFB4: // INS_LDY, ADR_ZPX + sz = Y = mem[mem[loc_pc++] + X & 0xFF] & 0xFF; + cpu_ticks -= 40; + continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFB5: // INS_LDA, ADR_ZPX + sz = A = mem[mem[loc_pc++] + X & 0xFF] & 0xFF; + cpu_ticks -= 40; + continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFB6: // INS_LDX, ADR_ZPY + sz = X = mem[mem[loc_pc++] + Y & 0xFF] & 0xFF; + cpu_ticks -= 40; + continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFB8: // INS_CLV, ADR_IMP + v = 0; + cpu_ticks -= 20; + continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFB9: // INS_LDA, ADR_ABSY + sz = A = cpuReadMem((char)((mem[loc_pc++] & 0xFF | mem[loc_pc++] << 8) + Y)); + cpu_ticks -= 40; // + continue; +// ---------------------------------------------------------------------------- + case 0XFFFFFFBA: // INS_TSX, ADR_IMP + sz = X = S; + cpu_ticks -= 20; + continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFBC: // INS_LDY, ADR_ABSX + sz = Y = cpuReadMem((char)((mem[loc_pc++] & 0xFF | mem[loc_pc++] << 8) + X)); + cpu_ticks -= 40; // + continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFBD: // INS_LDA, ADR_ABSX + sz = A = cpuReadMem((char)((mem[loc_pc++] & 0xFF | mem[loc_pc++] << 8) + X)); + cpu_ticks -= 40; // + continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFBE: // INS_LDX, ADR_ABSY + sz = X = cpuReadMem((char)((mem[loc_pc++] & 0xFF | mem[loc_pc++] << 8) + Y)); + cpu_ticks -= 40; // + continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFC0: // INS_CPY, ADR_IMM + { int V0 = Y - (mem[loc_pc++] & 0xFF); + c = V0 >= 0 ? 1 : 0; + sz = V0 & 0xFF; + cpu_ticks -= 20; + } continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFC1: // INS_CMP, ADR_INDX + { int V0 = mem[loc_pc++] + X & 0xFF; + V0 = A - cpuReadMem((char)(mem[V0] & 0xFF | mem[++V0] << 8)); + c = V0 >= 0 ? 1 : 0; + sz = V0 & 0xFF; + cpu_ticks -= 60; + } continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFC4: // INS_CPY, ADR_ZP + { int V0 = Y - (mem[mem[loc_pc++] & 0xFF] & 0xFF); + c = V0 >= 0 ? 1 : 0; + sz = V0 & 0xFF; + cpu_ticks -= 30; + } continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFC5: // INS_CMP, ADR_ZP + { int V0 = A - (mem[mem[loc_pc++] & 0xFF] & 0xFF); + c = V0 >= 0 ? 1 : 0; + sz = V0 & 0xFF; + cpu_ticks -= 30; + } continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFC6: // INS_DEC, ADR_ZP + { int V0 = mem[loc_pc++] & 0xFF; + sz = --mem[V0] & 0xFF; + cpu_ticks -= 50; + } continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFC8: // INS_INY, ADR_IMP + sz = Y = INC8[Y]; + cpu_ticks -= 20; + continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFC9: // INS_CMP, ADR_IMM + { int V0 = A - (mem[loc_pc++] & 0xFF); + c = V0 >= 0 ? 1 : 0; + sz = V0 & 0xFF; + cpu_ticks -= 20; + } continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFCA: // INS_DEX, ADR_IMP + sz = X = DEC8[X]; + cpu_ticks -= 20; + continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFCC: // INS_CPY, ADR_ABS + { int V0 = Y - cpuReadMem((char)(mem[loc_pc++] & 0xFF | mem[loc_pc++] << 8)); + c = V0 >= 0 ? 1 : 0; + sz = V0 & 0xFF; + cpu_ticks -= 40; + } continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFCD: // INS_CMP, ADR_ABS + { int V0 = A - cpuReadMem((char)(mem[loc_pc++] & 0xFF | mem[loc_pc++] << 8)); + c = V0 >= 0 ? 1 : 0; + sz = V0 & 0xFF; + cpu_ticks -= 40; + } continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFCE: // INS_DEC, ADR_ABS + { int V0 = (char)(mem[loc_pc++] & 0xFF | mem[loc_pc++] << 8); + cpuWriteMem(V0, sz = DEC8[cpuReadMem(V0)]); + cpu_ticks -= 60; + } continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFD0: // INS_BNE, ADR_REL + { if(sz != 0) { + int V0 = mem[loc_pc++]; + loc_pc += V0; + cpu_ticks -= 30; // + if(V0 == -3) { // Fix for some games like Lode Runner + switch(mem[loc_pc]) { + case 0xFFFFFFCA: // -3, INS_DEX, ADR_IMP + { int V2 = cpu_ticks / 50; + if(X < V2) V2 = X; X -= V2; cpu_ticks -= V2 * 50; + if(X == 0) { sz = X; cpu_ticks += 10; loc_pc -= V0; } + } continue; + case 0xFFFFFF88: // -3, INS_DEY, ADR_IMP + { int V2 = cpu_ticks / 50; + if(Y < V2) V2 = Y; Y -= V2; cpu_ticks -= V2 * 50; + if(Y == 0) { sz = Y; cpu_ticks += 10; loc_pc -= V0; } + } continue; + } + } + if(loc_pc == adr_to_skip[1]) { cpu_ticks = 0; break cpu_lab; } + if(count_jumps) { + for(int index = 0; index < jp_ad_value[1].length; index++) { + if(jp_ad_value[1][index] == loc_pc) { + jp_ad_times[1][index]++; continue cpu_lab; + } + } + if(free_pos_id[1] < jp_ad_value[1].length) { + jp_ad_value[1][free_pos_id[1]] = loc_pc; + jp_ad_times[1][free_pos_id[1]++]++; + } + } + } else { + loc_pc++; + cpu_ticks -= 20; + } + } continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFD1: // INS_CMP, ADR_INDY + { int V0 = mem[loc_pc++] & 0xFF; + V0 = A - cpuReadMem((char)((mem[V0] & 0xFF | mem[++V0] << 8) + Y)); + c = V0 >= 0 ? 1 : 0; + sz = V0 & 0xFF; + cpu_ticks -= 50; // + } continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFD5: // INS_CMP, ADR_ZPX + { int V0 = A - (mem[mem[loc_pc++] + X & 0xFF] & 0xFF); + c = V0 >= 0 ? 1 : 0; + sz = V0 & 0xFF; + cpu_ticks -= 40; + } continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFD6: // INS_DEC, ADR_ZPX + { int V0 = mem[loc_pc++] + X & 0xFF; + sz = --mem[V0] & 0xFF; + cpu_ticks -= 60; + } continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFD8: // INS_CLD, ADR_IMP + d = 0; + cpu_ticks -= 20; + continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFD9: // INS_CMP, ADR_ABSY + { int V0 = A - cpuReadMem((char)((mem[loc_pc++] & 0xFF | mem[loc_pc++] << 8) + Y)); + c = V0 >= 0 ? 1 : 0; + sz = V0 & 0xFF; + cpu_ticks -= 40; // + } continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFDD: // INS_CMP, ADR_ABSX + { int V0 = A - cpuReadMem((char)((mem[loc_pc++] & 0xFF | mem[loc_pc++] << 8) + X)); + c = V0 >= 0 ? 1 : 0; + sz = V0 & 0xFF; + cpu_ticks -= 40; // + } continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFDE: // INS_DEC, ADR_ABSX + { int V0 = (char)((mem[loc_pc++] & 0xFF | mem[loc_pc++] << 8) + X); + cpuWriteMem(V0, sz = DEC8[cpuReadMem(V0)]); + cpu_ticks -= 70; + } continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFE0: // INS_CPX, ADR_IMM + { int V0 = X - (mem[loc_pc++] & 0xFF); + c = V0 >= 0 ? 1 : 0; + sz = V0 & 0xFF; + cpu_ticks -= 20; + } continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFE1: // INS_SBC, ADR_INDX + { int V0 = mem[loc_pc++] + X & 0xFF; + int V1 = cpuReadMem((char)(mem[V0] & 0xFF | mem[++V0] << 8)); + int V2 = A - V1 - (1 - c); + v = ((A ^ V1) & (A ^ V2)) >> 7; + c = V2 >= 0 ? 1 : 0; + sz = A = V2 & 0xFF; + cpu_ticks -= 60; + } continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFE4: // INS_CPX, ADR_ZP + { int V0 = X - (mem[mem[loc_pc++] & 0xFF] & 0xFF); + c = V0 >= 0 ? 1 : 0; + sz = V0 & 0xFF; + cpu_ticks -= 30; + } continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFE5: // INS_SBC, ADR_ZP + { int V0 = mem[mem[loc_pc++] & 0xFF] & 0xFF; + int V1 = A - V0 - (1 - c); + v = ((A ^ V0) & (A ^ V1)) >> 7; + c = V1 >= 0 ? 1 : 0; + sz = A = V1 & 0xFF; + cpu_ticks -= 30; + } continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFE6: // INS_INC, ADR_ZP + { int V0 = mem[loc_pc++] & 0xFF; + sz = ++mem[V0] & 0xFF; + cpu_ticks -= 50; + } continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFE8: // INS_INX, ADR_IMP + sz = X = INC8[X]; + cpu_ticks -= 20; + continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFE9: // INS_SBC, ADR_IMM + { int V0 = mem[loc_pc++] & 0xFF; + int V1 = A - V0 - (1 - c); + v = ((A ^ V0) & (A ^ V1)) >> 7; + c = V1 >= 0 ? 1 : 0; + sz = A = V1 & 0xFF; + cpu_ticks -= 20; + } continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFEA: // INS_NOP, ADR_IMP + cpu_ticks -= 20; + continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFEC: // INS_CPX, ADR_ABS + { int V0 = X - cpuReadMem((char)(mem[loc_pc++] & 0xFF | mem[loc_pc++] << 8)); + c = V0 >= 0 ? 1 : 0; + sz = V0 & 0xFF; + cpu_ticks -= 40; + } continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFED: // INS_SBC, ADR_ABS + { int V0 = cpuReadMem((char)(mem[loc_pc++] & 0xFF | mem[loc_pc++] << 8)); + int V1 = A - V0 - (1 - c); + v = ((A ^ V0) & (A ^ V1)) >> 7; + c = V1 >= 0 ? 1 : 0; + sz = A = V1 & 0xFF; + cpu_ticks -= 40; + } continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFEE: // INS_INC, ADR_ABS + { int V0 = (char)(mem[loc_pc++] & 0xFF | mem[loc_pc++] << 8); + cpuWriteMem(V0, sz = INC8[cpuReadMem(V0)]); + cpu_ticks -= 60; + } continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFF0: // INS_BEQ, ADR_REL + { if(sz == 0) { + loc_pc += mem[loc_pc] + 1; + if(loc_pc == adr_to_skip[2]) { cpu_ticks = 0; break cpu_lab; } + cpu_ticks -= 30; // + if(count_jumps) { + for(int index = 0; index < jp_ad_value[2].length; index++) { + if(jp_ad_value[2][index] == loc_pc) { + jp_ad_times[2][index]++; continue cpu_lab; + } + } + if(free_pos_id[2] < jp_ad_value[2].length) { + jp_ad_value[2][free_pos_id[2]] = loc_pc; + jp_ad_times[2][free_pos_id[2]++]++; + } + } + } else { + loc_pc++; + cpu_ticks -= 20; + } + } continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFF1: // INS_SBC, ADR_INDY + { int V0 = mem[loc_pc++] & 0xFF; + int V1 = cpuReadMem((char)((mem[V0] & 0xFF | mem[++V0] << 8) + Y)); + int V2 = A - V1 - (1 - c); + v = ((A ^ V1) & (A ^ V2)) >> 7; + c = V2 >= 0 ? 1 : 0; + sz = A = V2 & 0xFF; + cpu_ticks -= 50; // + } continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFF5: // INS_SBC, ADR_ZPX + { int V0 = mem[mem[loc_pc++] + X & 0xFF] & 0xFF; + int V1 = A - V0 - (1 - c); + v = ((A ^ V0) & (A ^ V1)) >> 7; + c = V1 >= 0 ? 1 : 0; + sz = A = V1 & 0xFF; + cpu_ticks -= 40; + } continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFF6: // INS_INC, ADR_ZPX + { int V0 = mem[loc_pc++] + X & 0xFF; + sz = ++mem[V0] & 0xFF; + cpu_ticks -= 60; + } continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFF8: // INS_SED, ADR_IMP + d = 1; // ??? + cpu_ticks -= 20; + continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFF9: // INS_SBC, ADR_ABSY + { int V0 = cpuReadMem((char)((mem[loc_pc++] & 0xFF | mem[loc_pc++] << 8) + Y)); + int V1 = A - V0 - (1 - c); + v = ((A ^ V0) & (A ^ V1)) >> 7; + c = V1 >= 0 ? 1 : 0; + sz = A = V1 & 0xFF; + cpu_ticks -= 40; // + } continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFFD: // INS_SBC, ADR_ABSX + { int V0 = cpuReadMem((char)((mem[loc_pc++] & 0xFF | mem[loc_pc++] << 8) + X)); + int V1 = A - V0 - (1 - c); + v = ((A ^ V0) & (A ^ V1)) >> 7; + c = V1 >= 0 ? 1 : 0; + sz = A = V1 & 0xFF; + cpu_ticks -= 40; // + } continue; +// ---------------------------------------------------------------------------- + case 0xFFFFFFFE: // INS_INC, ADR_ABSX + { int V0 = (char)((mem[loc_pc++] & 0xFF | mem[loc_pc++] << 8) + X); + cpuWriteMem(V0, sz = INC8[cpuReadMem(V0)]); + cpu_ticks -= 70; + } continue; +// ---------------------------------------------------------------------------- + default: + print_osd("CPU failed!"); +// ---------------------------------------------------------------------------- + } + } + if(hsync_enab && Map_HSync(scanline) == i) { + PC = loc_pc - bank_base; + mem[0x0100 + S] = (byte)(PC >> 8); + S = DEC8[S]; + mem[0x0100 + S] = (byte)PC; + S = DEC8[S]; + mem[0x0100 + S] = (byte)(c | (sz != 0 ? 0 : 2) | i | d | v << 6 | (sz & 0x80)); + S = DEC8[S]; + PC = (char)(mem[mem_bank[7] + 0xFFFE] & 0xFF | mem[mem_bank[7] + 0xFFFF] << 8); + loc_pc = PC + (bank_base = mem_bank[PC >> 13]); + i = 4; + cpu_ticks += 70; + } + if(rendering && fastvideo && scanline == 240 && (ppuContr2 & 0x18) != 0) { + switch(vmode) { + case SCALE_LQ: render_fscr_vmode_0(); break; + case SCALE_HQ: render_fscr_vmode_1(); break; + case DYNAMIC : render_fscr_vmode23(); break; + case STATIC : render_fscr_vmode23(); break; + } + } + if(fastvideo || scanline > 239) continue; + if(emuscroff && rendering) { + switch(vmode) { + case SCALE_LQ: clear_line_vmode_0(scanline); break; + case SCALE_HQ: clear_line_vmode_1(scanline); break; + case DYNAMIC : clear_line_vmode23(scanline); break; + case STATIC : clear_line_vmode23(scanline); break; + } + } + if((ppuContr2 & 0x18) == 0) continue; + if(rendering) { + ppuV &= 0xFBE0; ppuV |= ppuT & 0x041F; + ppu_v_x[scanline] = ppuV << 3 | ppuX; + switch(vmode) { + case SCALE_LQ: render_line_vmode_0(scanline); break; + case SCALE_HQ: render_line_vmode_1(scanline); break; + case DYNAMIC : render_line_vmode23(scanline); break; + case STATIC : render_line_vmode23(scanline); break; + } + if((ppuV & 0x7000) == 0x7000) { + ppuV &= 0x8FFF; + if((ppuV & 0x03E0) == 0x03A0) { ppuV ^= 0x0800; ppuV &= 0xFC1F; continue; } + if((ppuV & 0x03E0) == 0x03E0) ppuV &= 0xFC1F; else ppuV += 0x0020; + } else + ppuV += 0x1000; + } + if(scanline == hit_line) ppuStatus |= 0x40; + } + if(rendering) draw_to_display(VFrames); + VFrames++; + if(turbo_a) j1_state ^= 0x01; + if(turbo_b) j1_state ^= 0x02; + if(cpu_accel > 0 && VFrames % STAT_RATE == 0) { + if(count_jumps == false) { + if((cpu_accel > 1 && fps < MIN_FPS) || VFrames == 0) { + jp_ad_value = null; jp_ad_value = new int[5][STAT_SIZE]; + jp_ad_times = null; jp_ad_times = new int[5][STAT_SIZE]; + free_pos_id = null; free_pos_id = new int[5]; + adr_to_skip = null; adr_to_skip = new int[5]; + count_jumps = true; + } + } else { + int count_all[] = new int[adr_to_skip.length]; + int max_count = 0; + for(int n = 0; n < adr_to_skip.length; n++) { + count_all[n]++; for(int m = 0; m < jp_ad_times[n].length; m++) count_all[n] += jp_ad_times[n][m]; + if(count_all[n] > count_all[max_count]) max_count = n; + } + int max_index = 0; + for(int m = 0; m < jp_ad_value[max_count].length; m++) + if(jp_ad_times[max_count][m] > jp_ad_times[max_count][max_index]) max_index = m; + int is_found = (jp_ad_times[max_count][max_index] * 100) / count_all[max_count]; + if(is_found > 50) adr_to_skip[max_count] = jp_ad_value[max_count][max_index]; + count_jumps = false; + } + } + int f_time = (int)(System.currentTimeMillis() - f_start); + int w_time = f_time < frame_del ? frame_del - f_time : 0; + try { Thread.currentThread().sleep(w_time); } + catch( InterruptedException e ){ } + } + P = c | (sz != 0 ? 0 : 2) | i | d | v << 6 | (sz & 0x80); + PC = loc_pc - bank_base; + } catch(Exception e) { e.printStackTrace(); } + } +// ============================================================================ +// [+] NES Memory Read/Write Manager +// ============================================================================ + private static final int cpuReadMem(int addr) { + byte loc_mem[] = memory; + switch(addr >> 13) { + // read from 2K memory in area 0x0000-0x1FFFF + case 0: + return loc_mem[addr & 0x07FF] & 0xFF; + // read from PPU registers in area 0x2000-0x3FFF + case 1: + switch(addr & 0x07) { + case 0x02: + ppuSwitch = false; + int i = ppuStatus; + ppuStatus &= 0x7F; + ppuLatch = i & 0xE0 | ppuLatch & 0x1F; + return ppuLatch; + case 0x04: + return spr_ram[ppuSprAddr] & 0xFF; + case 0x07: + ppuLatch = ppuL2007; + addr = ppuV & 0x3FFF; + ppuV += addr_add; + if(addr >= 0x3000) { + if(addr >= 0x3F00) return pal_ram[addr & 0x1F]; + addr &= 0xEFFF; + } + ppuL2007 = loc_mem[nt_chr_addr + ppu_bank[addr >> 10] + (addr & 0x03FF)] & 0xFF; + return ppuLatch; + } + return ppuLatch; + // read from Joystick registers in area 0x4000-0x5FFF + case 2: + switch(addr) { + default: + return addr >> 8; + case 0x4015: + return 0 + | (sq1_len_cou > 0 ? 0x01 : 0) + | (sq2_len_cou > 0 ? 0x02 : 0) + | (tri_len_cou > 0 ? 0x04 : 0); + case 0x4016: + return (j1_value >>= 1) & 0x01; + } + // read from 8K memory in area 0x6000-0x7FFF + case 3: // map_sram - ? + return map_sram ? loc_mem[addr - 0x5800] & 0xFF : 0x40; + } + // read from 32K banked PRG memory in area 0x8000-0xFFFF + return loc_mem[cpu_bank[addr >> 13] + addr] & 0xFF; + } + +// ============================================================================ + private final void cpuWriteMem(int addr, int value) { + byte loc_mem[] = memory; + switch(addr >> 13) { + // write to 2K memory in area 0x0000-0x1FFFF + case 0: + loc_mem[addr & 0x07FF] = (byte)value; + return; + // write to PPU registers in area 0x2000-0x3FFF + case 1: + ppuLatch = value; + switch(addr & 0x07) { + default: + case 0x02: return; + case 0x00: + ppuContr1 = value; + bg_pat_tab = (value & 0x10) == 0 ? 0 : 4; + sp_pat_tab = (value & 0x08) == 0 ? 0 : 4; + addr_add = (value & 0x04) == 0 ? 1 : 32; + ppuT &= 0xF3FF; + ppuT |= (value & 0x03) << 10; + return; + case 0x01: + ppuContr2 = value; + return; + case 0x03: + ppuSprAddr = value; + return; + case 0x04: + spr_ram[ppuSprAddr++] = (byte)value; + ppuSprAddr &= 0xFF; + return; + case 0x05: + ppuSwitch = !ppuSwitch; + if(ppuSwitch) { + ppuX = value & 0x07; + ppuT &= 0xFFE0; + ppuT |= value >> 3; + } else { + ppuT &= 0x8C1F; + ppuT |= (value & 0xF8) << 2; + ppuT |= (value & 0x07) << 12; + } + return; + case 0x06: + ppuSwitch = !ppuSwitch; + if(ppuSwitch) { + ppuT &= 0xFF; + ppuT |= (value & 0x3F) << 8; + } else { + ppuT &= 0xFF00; + ppuT |= value; + ppuV = ppuT; + } + return; + case 0x07: + addr = ppuV & 0x3FFF; + ppuV += addr_add; + break; + } + if(addr >= 0x3F00) { + value &= 0x3F; + addr &= 0x1F; + if((addr & 0x03) != 0) { + pal_ram[addr] = (byte)value; + pal_lst[addr] = pal_1[value]; + } + if((addr & 0x0F) == 0) { + value |= 0x40; + for(int i = 0; i < 32; i += 4) { + pal_ram[i] = (byte)value; + pal_lst[i] = pal_1[value]; + } + } + return; + } + if(addr >= 0x3000) addr &= 0xEFFF; addr = ppu_bank[addr >> 10] + (addr & 0x03FF); + if(addr < 4096) { loc_mem[nt_chr_addr + addr] = (byte)value; return; } + if(chr_size != 0 ) return; + loc_mem[nt_chr_addr + addr] = (byte)value; if((addr & 0x08) != 0) { + int i = addr - 4096; i = i >> 1 & 0xFFFFFFF8 | i & 0x07; + dec_tiles[i] = (char)(conv8to16[0][loc_mem[nt_chr_addr + (addr ^ 0x08)] & 0xFF] | conv8to16[1][value]); + } + return; + // write to APU registers, DMA, Joystick registers, Mapper in area 0x4000-0x5FFF + case 2: + if(addr < 0x4018) { + switch(addr & 0xFF) { + default: + return; + case 0x01: + apureg[0x1] = value; + sq1_swp_shi = value & 0x07; + return; + case 0x05: + apureg[0x5] = value; + sq2_swp_shi = value & 0x07; + return; + case 0x02: + apureg[0x2] = value; + sq1_frq_val = (sq1_frq_val & 0x0700) | value; + return; + case 0x06: + apureg[0x6] = value; + sq2_frq_val = (sq2_frq_val & 0x0700) | value; + return; + case 0x0A: + apureg[0xA] = value; + tri_frq_val = (tri_frq_val & 0x0700) | value; + return; + case 0x03: + apureg[0x3] = value; + sq1_env_vol = 0xF; + sq1_len_cou = V_LENGTHS[value >> 3]; + sq1_frq_val = (value & 0x07) << 8 | (sq1_frq_val & 0x00FF); + return; + case 0x07: + apureg[0x7] = value; + sq2_env_vol = 0xF; + sq2_len_cou = V_LENGTHS[value >> 3]; + sq2_frq_val = (value & 0x07) << 8 | (sq2_frq_val & 0x00FF); + return; + case 0x0B: + apureg[0xB] = value; + tri_len_cou = V_LENGTHS[value >> 3]; + tri_frq_val = (value & 0x07) << 8 | (tri_frq_val & 0x00FF); + return; + case 0x0F: + apureg[0xF] = value; + noi_env_vol = 0xF; + noi_len_cou = V_LENGTHS[value >> 3]; + return; + case 0x08: + apureg[0x8] = value; + if((value & 0x80) != 0) tri_lin_cou = value & 0x7F; + return; + case 0x00: + case 0x04: + case 0x09: + case 0x0C: + case 0x0D: + case 0x0E: + apureg[addr & 0xF] = value; + return; + case 0x15: + apu_ctrl = value; + return; + case 0x10: + case 0x11: + case 0x12: + case 0x13: + return; + case 0x14: + int taddr = value << 8; + switch(value >> 5) { + case 0: taddr &= 0x07FF; break; + case 1: + case 2: return; + case 3: taddr -= 0x5800; break; + case 4: + case 5: + case 6: + case 7: taddr += cpu_bank[taddr >> 13]; break; + } + System.arraycopy(loc_mem, taddr, spr_ram, 0, 256); + cpu_ticks -= 5120; + return; + case 0x16: + if((value & 0x01) == 0) j1_value = j1_state << 1; + return; + case 0x17: + return; + } + } else { + MapWR4000(addr, value); + return; + } + // write to 8K SRAM memory in area 0x6000-0x7FFF + case 3: // map_sram - ? + if(map_sram) loc_mem[addr - 0x5800] = (byte)value; + return; + // write to Mapper in area 0x8000-0xFFFF + default: + PC = (loc_pc - bank_base) & 0xFFFF; + MapperWrite(addr, value); + loc_pc = PC + (bank_base = cpu_bank[PC >> 13]); + return; + } + } +// ============================================================================ +// NES Picture Processing Unit PPU2C02 +// ============================================================================ + private static final int NES_SH = 240; + private static final int NES_SW = 256; +// ----------------------------------- main ppu registers and memory areas ---- + private static int ppuContr1 = 0; + private static int ppuContr2 = 0; + private static int ppuStatus = 0; + private static int ppuSprAddr = 0; + private static byte spr_ram[] = new byte[0x0100]; + private static byte pal_ram[] = new byte[0x0020]; +// -------------------------------------- internal ppu rendering variables ---- + private static int bg_pat_tab = 0; + private static int sp_pat_tab = 0; + private static int ppuT = 0; + private static int ppuV = 0; + private static int ppuX = 0; +// ------------------------------------------------ internal ppu variables ---- + private static boolean ppuSwitch = false; + private static int ppuLatch = 0; + private static int ppuL2007 = 0; + private static int addr_add = 1; +// ---------------------------------------------- additional ppu variables ---- + private static char conv8to16[][] = new char[2][256]; + private static char dec_tiles[]; + private static int ppu_v_x[] = new int[240]; + private static int nt_chr_addr = 0; + private static int nt_chr_size = 0; +// ---------------------------------------------- create additional tables ---- + static { + for(int b = 0; b < 256; b++) { + conv8to16[0][b] = (char)(b & 0x01 | (b & 0x02) << 1 | + (b & 0x04) << 2 | (b & 0x08) << 3 | + (b & 0x10) << 4 | (b & 0x20) << 5 | + (b & 0x40) << 6 | (b & 0x80) << 7); + conv8to16[1][b] = (char)(conv8to16[0][b] << 1); + } + } +// ============================================================================ + private final void reset_ppu() { +// очистка области name_table (4096 байта) + for(int i = 0; i < 4096; i++) memory[nt_chr_addr + i] = 0; +// очистка памяти спрайтов (256 байт) + for(int i = 0; i < spr_ram.length; i++) spr_ram[i] = 0; + for(int i = 0; i < 8; i++) { +// инициализация начальных ссылок на банки chr памяти + ppu_bank[i] = 0x0400 * i + 4096; + dec_bank[i] = 0x0200 * i; + } + if( mir_four) setMirroringFourScrn(); + else + if(!mir_vert) setMirroringHorizont(); + else + if( mir_vert) setMirroringVertical(); + createDecodedTiles(); + ppuContr1 = ppuContr2 = ppuStatus = ppuSprAddr = 0; + bg_pat_tab = sp_pat_tab = 0; + ppuLatch = ppuL2007 = 0; + ppuT = ppuV = ppuX = 0; + ppuSwitch = false; + addr_add = 1; + } +// ============================================================================ + private final void createDecodedTiles() { + dec_tiles = null; dec_tiles = new char[(nt_chr_size - 4096) / 2]; + for(int addr = 4096; addr < nt_chr_size; addr++) + if((addr & 0x08) == 0) { + int B0 = memory[nt_chr_addr + addr] & 0xFF; + int B1 = memory[nt_chr_addr + (addr | 0x08)] & 0xFF; + int i = addr - 4096; i = i >> 1 & 0xFFFFFFF8 | i & 0x07; + dec_tiles[i] = (char)(conv8to16[0][B0] | conv8to16[1][B1]); + } + + } +// ============================================================================ + private final void SaveState_ppu(ByteArrayOutputStream state) { + state.write(ppuContr1 & 0xFF ); + state.write(ppuContr2 & 0xFF ); + state.write(ppuStatus & 0xFF ); + state.write(ppuSprAddr & 0xFF ); + state.write(bg_pat_tab & 0xFF ); + state.write(sp_pat_tab & 0xFF ); + state.write(ppuLatch & 0xFF ); + state.write(ppuL2007 & 0xFF ); + state.write(addr_add & 0xFF ); + state.write(ppuSwitch ? 1 : 0 ); + state.write(ppuX & 0xFF ); + state.write(ppuV >> 0 & 0xFF ); + state.write(ppuV >> 8 & 0xFF ); + state.write(ppuT >> 0 & 0xFF ); + state.write(ppuT >> 8 & 0xFF ); +// запись содержимого памяти спрайтов и памяти палитры + state.write(spr_ram, 0, spr_ram.length); + state.write(pal_ram, 0, pal_ram.length); +// запись ссылок на банки для chr_mem и name_table + for(int i = 0; i < ppu_bank.length; i++) { + state.write((ppu_bank[i] >> 0x00) & 0xFF); + state.write((ppu_bank[i] >> 0x08) & 0xFF); + state.write((ppu_bank[i] >> 0x10) & 0xFF); + state.write((ppu_bank[i] >> 0x18) & 0xFF); + } +// запись ссылок на банки для decoded_tiles + for(int i = 0; i < dec_bank.length; i++) { + state.write((dec_bank[i] >> 0x00) & 0xFF); + state.write((dec_bank[i] >> 0x08) & 0xFF); + state.write((dec_bank[i] >> 0x10) & 0xFF); + state.write((dec_bank[i] >> 0x18) & 0xFF); + } +// запись содержимого памяти name_table + state.write(memory, nt_chr_addr, 4096); + if(chr_size == 0) +// запись содержимого памяти chr_ram + state.write(memory, nt_chr_addr + 4096, 8192); + } +// ============================================================================ + private final void LoadState_ppu(ByteArrayInputStream state) { + ppuContr1 = state.read() & 0xFF; + ppuContr2 = state.read() & 0xFF; + ppuStatus = state.read() & 0xFF; + ppuSprAddr = state.read() & 0xFF; + bg_pat_tab = state.read() & 0xFF; + sp_pat_tab = state.read() & 0xFF; + ppuLatch = state.read() & 0xFF; + ppuL2007 = state.read() & 0xFF; + addr_add = state.read() & 0xFF; + ppuSwitch = state.read() != 0; + ppuX = state.read() & 0xFF; + ppuV = state.read() << 0 & 0x00FF; + ppuV |= state.read() << 8 & 0xFF00; + ppuT = state.read() << 0 & 0x00FF; + ppuT |= state.read() << 8 & 0xFF00; +// чтение содержимого памяти спрайтов и памяти палитры + int a = state.read(spr_ram, 0, spr_ram.length); + int b = state.read(pal_ram, 0, pal_ram.length); +// чтение ссылок на банки для chr_mem и name_table + for(int i = 0; i < ppu_bank.length; i++) { + ppu_bank[i] = (state.read() & 0xFF) << 0x00; + ppu_bank[i] |= (state.read() & 0xFF) << 0x08; + ppu_bank[i] |= (state.read() & 0xFF) << 0x10; + ppu_bank[i] |= (state.read() & 0xFF) << 0x18; + } +// чтение ссылок на банки для decoded_tiles + for(int i = 0; i < dec_bank.length; i++) { + dec_bank[i] = (state.read() & 0xFF) << 0x00; + dec_bank[i] |= (state.read() & 0xFF) << 0x08; + dec_bank[i] |= (state.read() & 0xFF) << 0x10; + dec_bank[i] |= (state.read() & 0xFF) << 0x18; + } +// чтение содержимого памяти name_table + int c = state.read(memory, nt_chr_addr, 4096); + if(chr_size == 0) { +// чтение содержимого памяти chr_ram, декодирование тайлов + int d = state.read(memory, nt_chr_addr + 4096, 8192); + createDecodedTiles(); + } + } +// ============================================================================ + private static final int get_spr0_hitline() { + char loc_dec_tiles[] = dec_tiles; + int loc_dec_bank[] = dec_bank; + byte loc_spr_ram[] = spr_ram; + int spr_h = (ppuContr1 & 0x20) == 0 ? 8 : 16; + int sp_tile_numb = loc_spr_ram[1] & 0xFF; + int sp_dec_tile_addr = spr_h == 16 ? + loc_dec_bank[((sp_tile_numb & 1) << 2) + (sp_tile_numb >> 6)] + ((sp_tile_numb & 0x3E) << 3) : + loc_dec_bank[sp_pat_tab + (sp_tile_numb >> 6)] + ((sp_tile_numb & 0x3F) << 3); + int spr_y = (loc_spr_ram[0] & 0xFF) + 1; + int spr_x = (loc_spr_ram[3] & 0xFF); + int max_h = (spr_y + spr_h > 240) ? 240 - spr_y : spr_h; + for(int sp_vline = 0; sp_vline < max_h; sp_vline++) { + int tile_line = (loc_spr_ram[2] & 0x80) != 0 ? spr_h - sp_vline - 1 : sp_vline; + if(loc_dec_tiles[sp_dec_tile_addr + tile_line] != 0) { + int scanline = spr_y + sp_vline; + if(accuS0hit) { + boolean bg_not_clipped = (ppuContr2 & 0x02) != 0; + int tile_x = -(ppu_v_x[scanline] & 0x07); + int ppu_v = ppu_v_x[scanline] >> 3; + int loc_ppu_bank[] = ppu_bank; + int nt_addr = nt_chr_addr + loc_ppu_bank[8 + (ppu_v >> 10 & 0x03)]; + int voffset = ppu_v >> 12 & 0x07; + byte loc_mem[] = memory; + do { + int bg_tile_numb = loc_mem[nt_addr + (ppu_v & 0x03FF)] & 0xFF; + int bg_dec_tile_addr = loc_dec_bank[bg_pat_tab + (bg_tile_numb >> 6)] + ((bg_tile_numb & 0x3F) << 3) + voffset; + int last_x = tile_x < 0 ? 0 : tile_x; + int col_word = loc_dec_tiles[bg_dec_tile_addr]; + for(int x = tile_x + 7; x >= last_x; x--) { + if(x >= spr_x && x <= spr_x + 7) + if((x > 7 || bg_not_clipped) && (col_word & 0x03) != 0) return scanline; + col_word >>= 2; + } + if((tile_x += 8) > 255) break; + if((++ppu_v & 0x01) == 0) { + if((ppu_v & 0x03) == 0) { + if((ppu_v & 0x1F) == 0) { + ppu_v = (ppu_v -= 32) ^ 0x0400; + nt_addr = nt_chr_addr + loc_ppu_bank[8 + (ppu_v >> 10 & 0x03)]; + } + } + } + } while(true); + } else return scanline; + } + } + return -1; + } +// ============================================================================ +// [+] Видеорендеринг, основанный на Scanline (Fast rendering = Disabled) +// ============================================================================ + private static final void render_line_vmode_0(int scanline) { + if(scanline_visible[scanline]) { + boolean loc_ver_line_vis[] = ver_line_visible; + char loc_dec_tiles[] = dec_tiles; + int loc_scry_addr[] = scry_addr; + int loc_scrx_addr[] = scrx_addr; + int loc_dec_bank[] = dec_bank; + int loc_pal_lst[] = pal_lst; + int loc_screen[] = screen; + byte loc_mem[] = memory; + if((ppuContr2 & 0x08) != 0) { + boolean bg_not_clipped = (ppuContr2 & 0x02) != 0; + int loc_ppu_bank[] = ppu_bank; + int ppu_v = ppuV; + int voffset = ppu_v >> 12 & 0x07; + int nt_addr = loc_ppu_bank[8 + (ppu_v >> 10 & 0x03)]; + int nt_cnum = ppu_v & 0x02 | ppu_v >> 4 & 0x04; + int nt_color_byte = loc_mem[nt_chr_addr + (nt_addr + 960 + (ppu_v >> 4 & 0x38) + (ppu_v >> 2 & 0x07))] & 0xFF; + int nt_color = (nt_color_byte >> nt_cnum & 0x03) << 2; + int tile_x = -ppuX; + do { + int tile_numb = loc_mem[nt_chr_addr + nt_addr + (ppu_v & 0x03FF)] & 0xFF; + int dec_tile_addr = loc_dec_bank[bg_pat_tab + (tile_numb >> 6)] + ((tile_numb & 0x3F) << 3) + voffset; + int last_x = tile_x < 0 ? 0 : tile_x; + int col_word = loc_dec_tiles[dec_tile_addr]; + for(int x = tile_x + 7; x >= last_x; x--) { + if(x < 256) if(loc_ver_line_vis[x]) { + if(bg_not_clipped || x > 7) + loc_screen[loc_scry_addr[scanline] + loc_scrx_addr[x]] = loc_pal_lst[nt_color | col_word & 0x03]; + else + loc_screen[loc_scry_addr[scanline] + loc_scrx_addr[x]] = 0; + } + col_word >>= 2; + } + if((tile_x += 8) > 255) break; + if((++ppu_v & 0x01) == 0) { + if((ppu_v & 0x03) == 0) { + if((ppu_v & 0x1F) == 0) { + ppu_v = (ppu_v -= 32) ^ 0x0400; + nt_addr = loc_ppu_bank[8 + (ppu_v >> 10 & 0x03)]; + } + nt_color_byte = loc_mem[nt_chr_addr + nt_addr + 960 + (ppu_v >> 4 & 0x38) + (ppu_v >> 2 & 0x07)] & 0xFF; + nt_cnum -= 2; + } else + nt_cnum += 2; + nt_color = (nt_color_byte >> nt_cnum & 0x03) << 2; + } + } while(true); + } + if((ppuContr2 & 0x10) != 0) { + boolean sp_not_clipped = (ppuContr2 & 0x04) != 0; + byte loc_spr_ram[] = spr_ram; + byte spr_h = (byte)((ppuContr1 & 0x20) == 0 ? 8 : 16); + for(int spr_addr = 0; spr_addr < 256; spr_addr += 4) { + int sp_vline = scanline - ((loc_spr_ram[spr_addr] & 0xFF) + 1); + if(sp_vline < 0 || sp_vline >= spr_h) continue; + int tile_numb = loc_spr_ram[spr_addr + 1] & 0xFF; + int sp_color = loc_spr_ram[spr_addr + 2] & 0xFF; + int sp_tilex = loc_spr_ram[spr_addr + 3] & 0xFF; + boolean flipv = (sp_color & 0x80) != 0; + boolean fliph = (sp_color & 0x40) != 0; + boolean ontop = (sp_color & 0x20) == 0; + sp_color = (sp_color & 0x03) << 2 | 0x10; + int dec_tile_addr = spr_h == 16 ? + loc_dec_bank[((tile_numb & 1) << 2) + (tile_numb >> 6)] + ((tile_numb & 0x3E) << 3) : + loc_dec_bank[sp_pat_tab + (tile_numb >> 6)] + ((tile_numb & 0x3F) << 3); + int col_word = loc_dec_tiles[dec_tile_addr | (flipv ? spr_h - 1 - sp_vline : sp_vline)]; + int tile_x = fliph ? sp_tilex : sp_tilex + 7; + for(int i = 0; i < 8; i++) { + if((tile_x > 7 || sp_not_clipped) && tile_x < 256) { + if(loc_ver_line_vis[tile_x]) { + int scr_a = loc_scry_addr[scanline] + loc_scrx_addr[tile_x]; + int pixel = loc_screen[scr_a] >> 24; + if((pixel & 0x02) == 0) { + int color = col_word & 0x03; + if(color != 0) { + if(ontop || pixel != 0) + loc_screen[scr_a] = loc_pal_lst[color | sp_color] | 0x02000000; + else + loc_screen[scr_a] |= 0x02000000; + } + } + } + } + col_word >>= 2; + if(fliph) tile_x++; else tile_x--; + } + } + } + } + } +// ============================================================================ + private static final void clear_line_vmode_0(int scanline){ + if(scanline_visible[scanline]) { + boolean loc_ver_line_vis[] = ver_line_visible; + int loc_scry_addr[] = scry_addr; + int loc_scrx_addr[] = scrx_addr; + int loc_screen[] = screen; + for(int x = 0; x < 256; x++) if(loc_ver_line_vis[x]) + loc_screen[loc_scry_addr[scanline] + loc_scrx_addr[x]] = 0; + } + } +// ============================================================================ + private static final void render_line_vmode_1(int scanline) { + if(scanline_visible[scanline]) { + boolean loc_ver_line_vis[] = ver_line_visible; + char loc_dec_tiles[] = dec_tiles; + byte loc_vbuffer[][] = vbuffer; + int loc_dec_bank[] = dec_bank; + byte loc_pal_ram[] = pal_ram; + byte loc_mem[] = memory; + if((ppuContr2 & 0x08) != 0) { + boolean bg_not_clipped = (ppuContr2 & 0x02) != 0; + int loc_ppu_bank[] = ppu_bank; + int ppu_v = ppuV; + int voffset = ppu_v >> 12 & 0x07; + int nt_addr = loc_ppu_bank[8 + (ppu_v >> 10 & 0x03)]; + int nt_cnum = ppu_v & 0x02 | ppu_v >> 4 & 0x04; + int nt_color_byte = loc_mem[nt_chr_addr + (nt_addr + 960 + (ppu_v >> 4 & 0x38) + (ppu_v >> 2 & 0x07))] & 0xFF; + int nt_color = (nt_color_byte >> nt_cnum & 0x03) << 2; + int tile_x = -ppuX; + do { + int tile_numb = loc_mem[nt_chr_addr + nt_addr + (ppu_v & 0x03FF)] & 0xFF; + int dec_tile_addr = loc_dec_bank[bg_pat_tab + (tile_numb >> 6)] + ((tile_numb & 0x3F) << 3) + voffset; + int last_x = tile_x < 0 ? 0 : tile_x; + int col_word = loc_dec_tiles[dec_tile_addr]; + for(int x = tile_x + 7; x >= last_x; x--) { + if(x < 256) if(loc_ver_line_vis[x]) { + if(bg_not_clipped || x > 7) + loc_vbuffer[scanline][x] = loc_pal_ram[nt_color | col_word & 0x03]; + else + loc_vbuffer[scanline][x] = 0x3F; + } + col_word >>= 2; + } + if((tile_x += 8) > 255) break; + if((++ppu_v & 0x01) == 0) { + if((ppu_v & 0x03) == 0) { + if((ppu_v & 0x1F) == 0) { + ppu_v = (ppu_v -= 32) ^ 0x0400; + nt_addr = loc_ppu_bank[8 + (ppu_v >> 10 & 0x03)]; + } + nt_color_byte = loc_mem[nt_chr_addr + nt_addr + 960 + (ppu_v >> 4 & 0x38) + (ppu_v >> 2 & 0x07)] & 0xFF; + nt_cnum -= 2; + } else + nt_cnum += 2; + nt_color = (nt_color_byte >> nt_cnum & 0x03) << 2; + } + } while(true); + } + if((ppuContr2 & 0x10) != 0) { + boolean sp_not_clipped = (ppuContr2 & 0x04) != 0; + byte loc_spr_ram[] = spr_ram; + boolean solid[] = new boolean[256]; + byte spr_h = (byte)((ppuContr1 & 0x20) == 0 ? 8 : 16); + for(int spr_addr = 0; spr_addr < 256; spr_addr += 4) { + int sp_vline = scanline - ((loc_spr_ram[spr_addr] & 0xFF) + 1); + if(sp_vline < 0 || sp_vline >= spr_h) continue; + int tile_numb = loc_spr_ram[spr_addr + 1] & 0xFF; + int sp_color = loc_spr_ram[spr_addr + 2] & 0xFF; + int sp_tilex = loc_spr_ram[spr_addr + 3] & 0xFF; + boolean flipv = (sp_color & 0x80) != 0; + boolean fliph = (sp_color & 0x40) != 0; + boolean ontop = (sp_color & 0x20) == 0; + sp_color = (sp_color & 0x03) << 2 | 0x10; + int dec_tile_addr = spr_h == 16 ? + loc_dec_bank[((tile_numb & 1) << 2) + (tile_numb >> 6)] + ((tile_numb & 0x3E) << 3) : + loc_dec_bank[sp_pat_tab + (tile_numb >> 6)] + ((tile_numb & 0x3F) << 3); + int col_word = loc_dec_tiles[dec_tile_addr | (flipv ? spr_h - 1 - sp_vline : sp_vline)]; + int tile_x = fliph ? sp_tilex : sp_tilex + 7; + for(int i = 0; i < 8; i++) { + if((tile_x > 7 || sp_not_clipped) && tile_x < 256) { + if(loc_ver_line_vis[tile_x] && !solid[tile_x]) { + int color = col_word & 0x03; + if(color != 0) { + if(ontop || (loc_vbuffer[scanline][tile_x] & 0x40) != 0) + loc_vbuffer[scanline][tile_x] = loc_pal_ram[color | sp_color]; + solid[tile_x] = true; + } + } + } + col_word >>= 2; + if(fliph) tile_x++; else tile_x--; + } + } + } + } + } +// ============================================================================ + private static final void clear_line_vmode_1(int scanline) { + if(scanline_visible[scanline]) { + byte loc_vbuffer[][] = vbuffer; + for(int x = 0; x < 256; x++) loc_vbuffer[scanline][x] = 0x3F; + } + } +// ============================================================================ + private static final void render_line_vmode23(int scanline) { + if(scanline >= scr_y_fst && scanline <= scr_y_lst) { + char loc_dec_tiles[] = dec_tiles; + int loc_dec_bank[] = dec_bank; + int loc_pal_lst[] = pal_lst; + int loc_screen[] = screen; + byte loc_mem[] = memory; + int scr_a = 0; + if((ppuContr2 & 0x08) != 0) { + boolean bg_not_clipped = (ppuContr2 & 0x02) != 0; + int loc_ppu_bank[] = ppu_bank; + int ppu_v = ppuV; + int voffset = ppu_v >> 12 & 0x07; + int nt_addr = loc_ppu_bank[8 + (ppu_v >> 10 & 0x03)]; + int nt_cnum = ppu_v & 0x02 | ppu_v >> 4 & 0x04; + int nt_color_byte = loc_mem[nt_chr_addr + (nt_addr + 960 + (ppu_v >> 4 & 0x38) + (ppu_v >> 2 & 0x07))] & 0xFF; + int nt_color = (nt_color_byte >> nt_cnum & 0x03) << 2; + int tile_x = -ppuX; + do { + int tile_numb = loc_mem[nt_chr_addr + nt_addr + (ppu_v & 0x03FF)] & 0xFF; + int dec_tile_addr = loc_dec_bank[bg_pat_tab + (tile_numb >> 6)] + ((tile_numb & 0x3F) << 3) + voffset; + int last_x = tile_x < 0 ? 0 : tile_x; + int col_word = loc_dec_tiles[dec_tile_addr]; + for(int x = tile_x + 7; x >= last_x; x--) { + if(x >= scr_x_fst && x <= scr_x_lst) { + switch(rmode){ + case 0: scr_a = (scanline - scr_y_fst) * PW + (x - scr_x_fst); break; + case 1: scr_a = (x - scr_x_fst) * PH + (PH - 1 - (scanline - scr_y_fst)); break; + case 2: scr_a = (PW - 1 - (x - scr_x_fst)) * PH + (scanline - scr_y_fst); break; + } + loc_screen[scr_a] = (x > 7 || bg_not_clipped) ? loc_pal_lst[nt_color | col_word & 0x03] : 0; + } + col_word >>= 2; + } + if((tile_x += 8) > 255) break; + if((++ppu_v & 0x01) == 0) { + if((ppu_v & 0x03) == 0) { + if((ppu_v & 0x1F) == 0) { + ppu_v = (ppu_v -= 32) ^ 0x0400; + nt_addr = loc_ppu_bank[8 + (ppu_v >> 10 & 0x03)]; + } + nt_color_byte = loc_mem[nt_chr_addr + nt_addr + 960 + (ppu_v >> 4 & 0x38) + (ppu_v >> 2 & 0x07)] & 0xFF; + nt_cnum -= 2; + } else + nt_cnum += 2; + nt_color = (nt_color_byte >> nt_cnum & 0x03) << 2; + } + } while(true); + } + if((ppuContr2 & 0x10) != 0) { + boolean sp_not_clipped = (ppuContr2 & 0x04) != 0; + byte loc_spr_ram[] = spr_ram; + byte spr_h = (byte)((ppuContr1 & 0x20) == 0 ? 8 : 16); + for(int spr_addr = 0; spr_addr < 256; spr_addr += 4) { + int sp_vline = scanline - ((loc_spr_ram[spr_addr] & 0xFF) + 1); + if(sp_vline < 0 || sp_vline >= spr_h) continue; + int tile_numb = loc_spr_ram[spr_addr + 1] & 0xFF; + int sp_color = loc_spr_ram[spr_addr + 2] & 0xFF; + int sp_tilex = loc_spr_ram[spr_addr + 3] & 0xFF; + boolean flipv = (sp_color & 0x80) != 0; + boolean fliph = (sp_color & 0x40) != 0; + boolean ontop = (sp_color & 0x20) == 0; + sp_color = (sp_color & 0x03) << 2 | 0x10; + int dec_tile_addr = spr_h == 16 ? + loc_dec_bank[((tile_numb & 1) << 2) + (tile_numb >> 6)] + ((tile_numb & 0x3E) << 3) : + loc_dec_bank[sp_pat_tab + (tile_numb >> 6)] + ((tile_numb & 0x3F) << 3); + int col_word = loc_dec_tiles[dec_tile_addr | (flipv ? spr_h - 1 - sp_vline : sp_vline)]; + int tile_x = fliph ? sp_tilex : sp_tilex + 7; + for(int i = 0; i < 8; i++) { + if(tile_x > 7 || sp_not_clipped) { + if(tile_x >= scr_x_fst && tile_x <= scr_x_lst) { + switch(rmode){ + case 0: scr_a = (scanline - scr_y_fst) * PW + (tile_x - scr_x_fst); break; + case 1: scr_a = (tile_x - scr_x_fst) * PH + (PH - 1 - (scanline - scr_y_fst)); break; + case 2: scr_a = (PW - 1 - (tile_x - scr_x_fst)) * PH + (scanline - scr_y_fst); break; + } + int pixel = loc_screen[scr_a] >> 24; + if((pixel & 0x02) == 0) { + int color = col_word & 0x03; + if(color != 0) { + if(ontop || pixel != 0) + loc_screen[scr_a] = loc_pal_lst[color | sp_color] | 0x02000000; + else + loc_screen[scr_a] |= 0x02000000; + } + } + } else + if(spr_addr == 0) ppuStatus |= 0x40; + } + col_word >>= 2; + if(fliph) tile_x++; else tile_x--; + } + } + } + } + } +// ============================================================================ + private static final void clear_line_vmode23(int scanline) { + if(scanline >= scr_y_fst && scanline <= scr_y_lst) { + int loc_screen[] = screen; + int scr_a = 0; + for(int x = scr_x_fst; x <= scr_x_lst; x++) { + switch(rmode){ + case 0: scr_a = (scanline - scr_y_fst) * PW + (x - scr_x_fst); break; + case 1: scr_a = (x - scr_x_fst) * PH + (PH - 1 - (scanline - scr_y_fst)); break; + case 2: scr_a = (PW - 1 - (x - scr_x_fst)) * PH + (scanline - scr_y_fst); break; + } + loc_screen[scr_a] = 0; + } + } + } +// ============================================================================ +// [?] Видеорендеринг, основанный на Fullscreen (Fast rendering = Enabled) +// ============================================================================ + private static final void render_fscr_vmode_0() { + boolean loc_ver_line_vis[] = ver_line_visible; + boolean loc_scanline_vis[] = scanline_visible; + char loc_dec_tiles[] = dec_tiles; + int loc_scry_addr[] = scry_addr; + int loc_scrx_addr[] = scrx_addr; + int loc_dec_bank[] = dec_bank; + int loc_pal_lst[] = pal_lst; + byte loc_spr_ram[] = spr_ram; + int loc_screen[] = screen; + if((ppuContr2 & 0x08) != 0) { + int loc_ppu_bank[] = ppu_bank; + byte loc_mem[] = memory; + ppuV = ppuT; + for(int scanline = 0; scanline < 240;) { + int ppu_v = ppuV; + int voffset = ppu_v >> 12 & 0x07; + int nt_addr = loc_ppu_bank[8 + (ppu_v >> 10 & 0x03)]; + int tile_ad = nt_addr + (ppu_v & 0x03FF); + int nt_cnum = ppu_v & 0x02 | ppu_v >> 4 & 0x04; + int nt_color_byte = loc_mem[nt_chr_addr + (nt_addr + 960 + (ppu_v >> 4 & 0x38) + (ppu_v >> 2 & 0x07))] & 0xFF; + int nt_color = (nt_color_byte >> nt_cnum & 0x03) << 2; + int tile_x = -ppuX; + do { + int tile_numb_a = loc_mem[nt_chr_addr + tile_ad] & 0xFF; + int dec_tile_addr_a = loc_dec_bank[bg_pat_tab + (tile_numb_a >> 6)] + ((tile_numb_a & 0x3F) << 3) + voffset; + int last_y = (scanline + 8) - voffset; + int last_x = tile_x < 0 ? 0 : tile_x; + int y; + for(y = scanline; y < last_y;) { + try { + if(loc_scanline_vis[y]) { + int col_word = loc_dec_tiles[dec_tile_addr_a]; + for(int x = tile_x + 7; x >= last_x; x--) { + if(x < 256) if(loc_ver_line_vis[x]) + loc_screen[loc_scry_addr[y] + loc_scrx_addr[x]] = loc_pal_lst[nt_color | col_word & 0x03]; + col_word >>= 2; + } + } + } catch(ArrayIndexOutOfBoundsException e) {} + dec_tile_addr_a++; + y++; + } + if((ppu_v & 0x20) == 0) { + int tile_numb_b = loc_mem[nt_chr_addr + (tile_ad | 0x20)] & 0xFF; + int dec_tile_addr_b = loc_dec_bank[bg_pat_tab + (tile_numb_b >> 6)] + ((tile_numb_b & 0x3F) << 3); + for(last_y += 8; y < last_y;) { + try { + if(loc_scanline_vis[y]) { + int col_word = loc_dec_tiles[dec_tile_addr_b]; + for(int x = tile_x + 7; x >= last_x; x--) { + if(x < 256) if(loc_ver_line_vis[x]) + loc_screen[loc_scry_addr[y] + loc_scrx_addr[x]] = loc_pal_lst[nt_color | col_word & 0x03]; + col_word >>= 2; + } + } + } catch(ArrayIndexOutOfBoundsException e) {} + dec_tile_addr_b++; + y++; + } + } + if((tile_x += 8) >= 256) break; + tile_ad++; + ppu_v++; + if((ppu_v & 0x01) == 0) { + if((ppu_v & 0x03) == 0) { + if((ppu_v & 0x1F) == 0) { + ppu_v = (ppu_v -= 32) ^ 0x0400; + nt_addr = loc_ppu_bank[8 + (ppu_v >> 10 & 0x03)]; + tile_ad = nt_addr + (ppu_v & 0x03FF); + } + nt_color_byte = loc_mem[nt_chr_addr + (nt_addr + 960 + (ppu_v >> 4 & 0x38) + (ppu_v >> 2 & 0x07))] & 0xFF; + nt_cnum -= 2; + } else + nt_cnum += 2; + nt_color = (nt_color_byte >> nt_cnum & 0x03) << 2; + } + } while(true); + int line_add = (ppu_v & 0x20) == 0 ? 16 - voffset : 8 - voffset; + scanline += line_add; + while(line_add > 0) { + if((ppuV & 0x7000) == 0x7000) { + ppuV &= 0x8FFF; + if((ppuV & 0x03E0) == 0x03A0) { ppuV ^= 0x0800; ppuV &= 0xFC1F; } + else + if((ppuV & 0x03E0) == 0x03E0) ppuV &= 0xFC1F; else ppuV += 0x0020; + } else + ppuV += 0x1000; + line_add--; + } + } + } + if((ppuContr2 & 0x10) != 0) { + byte spr_h = (byte)((ppuContr1 & 0x20) == 0 ? 8 : 16); + L0: for(int spr_addr = 252; spr_addr >= 0; spr_addr -= 4) { + int sp_vline = (loc_spr_ram[spr_addr] & 0xFF) + 1; + if(sp_vline > 239) continue; + int tile_numb = loc_spr_ram[spr_addr + 1] & 0xFF; + int sp_color = loc_spr_ram[spr_addr + 2] & 0xFF; + int sp_tilex = loc_spr_ram[spr_addr + 3] & 0xFF; + boolean flipv = (sp_color & 0x80) != 0; + boolean fliph = (sp_color & 0x40) != 0; + boolean ontop = (sp_color & 0x20) == 0; + sp_color = (sp_color & 0x03) << 2 | 0x10; + int dec_tile_addr; + if(spr_h == 16) + dec_tile_addr = loc_dec_bank[((tile_numb & 0x01) << 2) + (tile_numb >> 6)] + ((tile_numb & 0x3E) << 3); + else + dec_tile_addr = loc_dec_bank[sp_pat_tab + (tile_numb >> 6)] + ((tile_numb & 0x3F) << 3); + int left_lines = spr_h; + int y = sp_vline; + try { + do { + if(left_lines == 0) continue L0; + if(loc_scanline_vis[y]) { + int col_word = loc_dec_tiles[dec_tile_addr | (flipv ? left_lines - 1 : spr_h - left_lines)]; + int tile_x = fliph ? sp_tilex : sp_tilex + 7; + for(int i = 0; i < 8; i++) { + if(tile_x < 256) if(loc_ver_line_vis[tile_x]) { + int color = col_word & 0x03; + if(color != 0 && (ontop || (loc_screen[loc_scry_addr[y] + loc_scrx_addr[tile_x]] >> 24) != 0)) + loc_screen[loc_scry_addr[y] + loc_scrx_addr[tile_x]] = loc_pal_lst[color | sp_color]; + } + col_word >>= 2; + if(fliph) tile_x++; else tile_x--; + } + } + left_lines--; + y++; + } while(true); + } catch(ArrayIndexOutOfBoundsException e) {} + } + } + + } +// ============================================================================ + private static final void render_fscr_vmode_1() { + boolean loc_ver_line_vis[] = ver_line_visible; + boolean loc_scanline_vis[] = scanline_visible; + char loc_dec_tiles[] = dec_tiles; + int loc_dec_bank[] = dec_bank; + byte loc_vbuffer[][] = vbuffer; + byte loc_pal_ram[] = pal_ram; + byte loc_spr_ram[] = spr_ram; + if((ppuContr2 & 0x08) != 0) { + int loc_ppu_bank[] = ppu_bank; + byte loc_mem[] = memory; + ppuV = ppuT; + for(int scanline = 0; scanline < 240;) { + int ppu_v = ppuV; + int voffset = ppu_v >> 12 & 0x07; + int nt_addr = loc_ppu_bank[8 + (ppu_v >> 10 & 0x03)]; + int tile_ad = nt_addr + (ppu_v & 0x03FF); + int nt_cnum = ppu_v & 0x02 | ppu_v >> 4 & 0x04; + int nt_color_byte = loc_mem[nt_chr_addr + (nt_addr + 960 + (ppu_v >> 4 & 0x38) + (ppu_v >> 2 & 0x07))] & 0xFF; + int nt_color = (nt_color_byte >> nt_cnum & 0x03) << 2; + int tile_x = -ppuX; + do { + int tile_numb_a = loc_mem[nt_chr_addr + tile_ad] & 0xFF; + int dec_tile_addr_a = loc_dec_bank[bg_pat_tab + (tile_numb_a >> 6)] + ((tile_numb_a & 0x3F) << 3) + voffset; + int last_y = (scanline + 8) - voffset; + int last_x = tile_x < 0 ? 0 : tile_x; + int y; + for(y = scanline; y < last_y;) { + try { + if(loc_scanline_vis[y]) { + int col_word = loc_dec_tiles[dec_tile_addr_a]; + for(int x = tile_x + 7; x >= last_x; x--) { + if(x < 256) if(loc_ver_line_vis[x]) + loc_vbuffer[y][x] = loc_pal_ram[nt_color | col_word & 0x03]; + col_word >>= 2; + } + } + } catch(ArrayIndexOutOfBoundsException e) {} + dec_tile_addr_a++; + y++; + } + if((ppu_v & 0x20) == 0) { + int tile_numb_b = loc_mem[nt_chr_addr + (tile_ad | 0x20)] & 0xFF; + int dec_tile_addr_b = loc_dec_bank[bg_pat_tab + (tile_numb_b >> 6)] + ((tile_numb_b & 0x3F) << 3); + for(last_y += 8; y < last_y;) { + try { + if(loc_scanline_vis[y]) { + int col_word = loc_dec_tiles[dec_tile_addr_b]; + for(int x = tile_x + 7; x >= last_x; x--) { + if(x < 256) if(loc_ver_line_vis[x]) + loc_vbuffer[y][x] = loc_pal_ram[nt_color | col_word & 0x03]; + col_word >>= 2; + } + } + } catch(ArrayIndexOutOfBoundsException e) {} + dec_tile_addr_b++; + y++; + } + } + if((tile_x += 8) >= 256) break; + tile_ad++; + ppu_v++; + if((ppu_v & 0x01) == 0) { + if((ppu_v & 0x03) == 0) { + if((ppu_v & 0x1F) == 0) { + ppu_v = (ppu_v -= 32) ^ 0x0400; + nt_addr = loc_ppu_bank[8 + (ppu_v >> 10 & 0x03)]; + tile_ad = nt_addr + (ppu_v & 0x03FF); + } + nt_color_byte = loc_mem[nt_chr_addr + (nt_addr + 960 + (ppu_v >> 4 & 0x38) + (ppu_v >> 2 & 0x07))] & 0xFF; + nt_cnum -= 2; + } else + nt_cnum += 2; + nt_color = (nt_color_byte >> nt_cnum & 0x03) << 2; + } + } while(true); + int line_add = (ppu_v & 0x20) == 0 ? 16 - voffset : 8 - voffset; + scanline += line_add; + while(line_add > 0) { + if((ppuV & 0x7000) == 0x7000) { + ppuV &= 0x8FFF; + if((ppuV & 0x03E0) == 0x03A0) { ppuV ^= 0x0800; ppuV &= 0xFC1F; } + else + if((ppuV & 0x03E0) == 0x03E0) ppuV &= 0xFC1F; else ppuV += 0x0020; + } else + ppuV += 0x1000; + line_add--; + } + } + } + if((ppuContr2 & 0x10) != 0) { + byte spr_h = (byte)((ppuContr1 & 0x20) == 0 ? 8 : 16); + L0: for(int spr_addr = 252; spr_addr >= 0; spr_addr -= 4) { + int sp_vline = (loc_spr_ram[spr_addr] & 0xFF) + 1; + if(sp_vline > 239) continue; + int tile_numb = loc_spr_ram[spr_addr + 1] & 0xFF; + int sp_color = loc_spr_ram[spr_addr + 2] & 0xFF; + int sp_tilex = loc_spr_ram[spr_addr + 3] & 0xFF; + boolean flipv = (sp_color & 0x80) != 0; + boolean fliph = (sp_color & 0x40) != 0; + boolean ontop = (sp_color & 0x20) == 0; + sp_color = (sp_color & 0x03) << 2 | 0x10; + int dec_tile_addr; + if(spr_h == 16) + dec_tile_addr = loc_dec_bank[((tile_numb & 0x01) << 2) + (tile_numb >> 6)] + ((tile_numb & 0x3E) << 3); + else + dec_tile_addr = loc_dec_bank[sp_pat_tab + (tile_numb >> 6)] + ((tile_numb & 0x3F) << 3); + int left_lines = spr_h; + int y = sp_vline; + try { + do { + if(left_lines == 0) continue L0; + if(loc_scanline_vis[y]) { + int col_word = loc_dec_tiles[dec_tile_addr | (flipv ? left_lines - 1 : spr_h - left_lines)]; + int tile_x = fliph ? sp_tilex : sp_tilex + 7; + for(int i = 0; i < 8; i++) { + if(tile_x < 256) if(loc_ver_line_vis[tile_x]) { + int color = col_word & 0x03; + if(color != 0 && (ontop || (loc_vbuffer[y][tile_x] & 0x40) == 0)) + loc_vbuffer[y][tile_x] = loc_pal_ram[color | sp_color]; + } + col_word >>= 2; + if(fliph) tile_x++; else tile_x--; + } + } + left_lines--; + y++; + } while(true); + } catch(ArrayIndexOutOfBoundsException e) {} + } + } + + } +// ============================================================================ + private static final void render_fscr_vmode23() { + char loc_dec_tiles[] = dec_tiles; + int loc_dec_bank[] = dec_bank; + int loc_pal_lst[] = pal_lst; + byte loc_spr_ram[] = spr_ram; + int loc_screen[] = screen; + int scr_a = 0; + if((ppuContr2 & 0x08) != 0) { + int loc_ppu_bank[] = ppu_bank; + byte loc_mem[] = memory; + ppuV = ppuT; + for(int scanline = 0; scanline < 240;) { + int ppu_v = ppuV; + int voffset = ppu_v >> 12 & 0x07; + int nt_addr = loc_ppu_bank[8 + (ppu_v >> 10 & 0x03)]; + int tile_ad = nt_addr + (ppu_v & 0x03FF); + int nt_cnum = ppu_v & 0x02 | ppu_v >> 4 & 0x04; + int nt_color_byte = loc_mem[nt_chr_addr + (nt_addr + 960 + (ppu_v >> 4 & 0x38) + (ppu_v >> 2 & 0x07))] & 0xFF; + int nt_color = (nt_color_byte >> nt_cnum & 0x03) << 2; + int tile_x = -ppuX; + do { + int tile_numb_a = loc_mem[nt_chr_addr + tile_ad] & 0xFF; + int dec_tile_addr_a = loc_dec_bank[bg_pat_tab + (tile_numb_a >> 6)] + ((tile_numb_a & 0x3F) << 3) + voffset; + int last_y = (scanline + 8) - voffset; + int last_x = tile_x < 0 ? 0 : tile_x; + int y; + for(y = scanline; y < last_y;) { + try { + if(y >= scr_y_fst && y <= scr_y_lst) { + int col_word = loc_dec_tiles[dec_tile_addr_a]; + for(int x = tile_x + 7; x >= last_x; x--) { + if(x >= scr_x_fst && x <= scr_x_lst) { + switch(rmode){ + case 0: scr_a = (y - scr_y_fst) * PW + (x - scr_x_fst); break; + case 1: scr_a = (x - scr_x_fst) * PH + (PH - 1 - (y - scr_y_fst)); break; + case 2: scr_a = (PW - 1 - (x - scr_x_fst)) * PH + (y - scr_y_fst); break; + } + loc_screen[scr_a] = loc_pal_lst[nt_color | col_word & 0x03]; + } + col_word >>= 2; + } + } + } catch(ArrayIndexOutOfBoundsException e) {} + dec_tile_addr_a++; + y++; + } + if((ppu_v & 0x20) == 0) { + int tile_numb_b = loc_mem[nt_chr_addr + (tile_ad | 0x20)] & 0xFF; + int dec_tile_addr_b = loc_dec_bank[bg_pat_tab + (tile_numb_b >> 6)] + ((tile_numb_b & 0x3F) << 3); + for(last_y += 8; y < last_y;) { + try { + if(y >= scr_y_fst && y <= scr_y_lst) { + int col_word = loc_dec_tiles[dec_tile_addr_b]; + for(int x = tile_x + 7; x >= last_x; x--) { + if(x >= scr_x_fst && x <= scr_x_lst) { + switch(rmode){ + case 0: scr_a = (y - scr_y_fst) * PW + (x - scr_x_fst); break; + case 1: scr_a = (x - scr_x_fst) * PH + (PH - 1 - (y - scr_y_fst)); break; + case 2: scr_a = (PW - 1 - (x - scr_x_fst)) * PH + (y - scr_y_fst); break; + } + loc_screen[scr_a] = loc_pal_lst[nt_color | col_word & 0x03]; + } + col_word >>= 2; + } + } + } catch(ArrayIndexOutOfBoundsException e) {} + dec_tile_addr_b++; + y++; + } + } + if((tile_x += 8) >= 256) break; + tile_ad++; + ppu_v++; + if((ppu_v & 0x01) == 0) { + if((ppu_v & 0x03) == 0) { + if((ppu_v & 0x1F) == 0) { + ppu_v = (ppu_v -= 32) ^ 0x0400; + nt_addr = loc_ppu_bank[8 + (ppu_v >> 10 & 0x03)]; + tile_ad = nt_addr + (ppu_v & 0x03FF); + } + nt_color_byte = loc_mem[nt_chr_addr + (nt_addr + 960 + (ppu_v >> 4 & 0x38) + (ppu_v >> 2 & 0x07))] & 0xFF; + nt_cnum -= 2; + } else + nt_cnum += 2; + nt_color = (nt_color_byte >> nt_cnum & 0x03) << 2; + } + } while(true); + int line_add = (ppu_v & 0x20) == 0 ? 16 - voffset : 8 - voffset; + scanline += line_add; + while(line_add > 0) { + if((ppuV & 0x7000) == 0x7000) { + ppuV &= 0x8FFF; + if((ppuV & 0x03E0) == 0x03A0) { ppuV ^= 0x0800; ppuV &= 0xFC1F; } + else + if((ppuV & 0x03E0) == 0x03E0) ppuV &= 0xFC1F; else ppuV += 0x0020; + } else + ppuV += 0x1000; + line_add--; + } + } + } + if((ppuContr2 & 0x10) != 0) { + byte spr_h = (byte)((ppuContr1 & 0x20) == 0 ? 8 : 16); + L0: for(int spr_addr = 252; spr_addr >= 0; spr_addr -= 4) { + int sp_vline = (loc_spr_ram[spr_addr] & 0xFF) + 1; + if(sp_vline > 239) continue; + int tile_numb = loc_spr_ram[spr_addr + 1] & 0xFF; + int sp_color = loc_spr_ram[spr_addr + 2] & 0xFF; + int sp_tilex = loc_spr_ram[spr_addr + 3] & 0xFF; + boolean flipv = (sp_color & 0x80) != 0; + boolean fliph = (sp_color & 0x40) != 0; + boolean ontop = (sp_color & 0x20) == 0; + sp_color = (sp_color & 0x03) << 2 | 0x10; + int dec_tile_addr; + if(spr_h == 16) + dec_tile_addr = loc_dec_bank[((tile_numb & 0x01) << 2) + (tile_numb >> 6)] + ((tile_numb & 0x3E) << 3); + else + dec_tile_addr = loc_dec_bank[sp_pat_tab + (tile_numb >> 6)] + ((tile_numb & 0x3F) << 3); + int left_lines = spr_h; + int y = sp_vline; + try { + do { + if(left_lines == 0) continue L0; + if(y >= scr_y_fst && y <= scr_y_lst) { + int col_word = loc_dec_tiles[dec_tile_addr | (flipv ? left_lines - 1 : spr_h - left_lines)]; + int tile_x = fliph ? sp_tilex : sp_tilex + 7; + for(int i = 0; i < 8; i++) { + if(tile_x >= scr_x_fst && tile_x <= scr_x_lst) { + switch(rmode){ + case 0: scr_a = (y - scr_y_fst) * PW + (tile_x - scr_x_fst); break; + case 1: scr_a = (tile_x - scr_x_fst) * PH + (PH - 1 - (y - scr_y_fst)); break; + case 2: scr_a = (PW - 1 - (tile_x - scr_x_fst)) * PH + (y - scr_y_fst); break; + } + int color = col_word & 0x03; + if(color != 0 && (ontop || (loc_screen[scr_a] >> 24) != 0)) + loc_screen[scr_a] = loc_pal_lst[color | sp_color]; + } + col_word >>= 2; + if(fliph) tile_x++; else tile_x--; + } + } + left_lines--; + y++; + } while(true); + } catch(ArrayIndexOutOfBoundsException e) {} + } + } + } +// ============================================================================ +// [-] NES CPU memory manager +// ============================================================================ + private static int cpu_bank[] = new int[8]; +// ============================================================================ + private static final void setCPUBanks(int bank8, int bankA, int bankC, int bankE) { + setCPUAddress(4, bank8 << 13); + setCPUAddress(5, bankA << 13); + setCPUAddress(6, bankC << 13); + setCPUAddress(7, bankE << 13); + } +// ============================================================================ +// private static final void setCPUBank6(int bank) { +// setCPUAddress(3, bank << 13); +// } +// ============================================================================ + private static final void setCPUBank8(int bank) { + setCPUAddress(4, bank << 13); + } +// ============================================================================ + private static final void setCPUBankA(int bank) { + setCPUAddress(5, bank << 13); + } +// ============================================================================ + private static final void setCPUBankC(int bank) { + setCPUAddress(6, bank << 13); + } +// ============================================================================ + private static final void setCPUBankE(int bank) { + setCPUAddress(7, bank << 13); + } +// ============================================================================ + private static final void setCPUAddress(int area, int addr) { + addr &= prg_size - 1; + cpu_bank[area] = (addr - (area << 13)) + 10240; + } +// ============================================================================ +// [-] NES PPU memory manager +// ============================================================================ + private static int ppu_bank[] = new int[8 + 4]; + private static int dec_bank[] = new int[8]; +// ============================================================================ + private static final void setMirroring(int m0, int m1, int m2, int m3) { + ppu_bank[0x08] = m0 << 10; + ppu_bank[0x09] = m1 << 10; + ppu_bank[0x0A] = m2 << 10; + ppu_bank[0x0B] = m3 << 10; + } +// ============================================================================ + private static final void setMirroringVertical() { + setMirroring(0, 1, 0, 1); + } +// ============================================================================ + private static final void setMirroringHorizont() { + setMirroring(0, 0, 1, 1); + } +// ============================================================================ + private static final void setMirroringOneScrLo() { + setMirroring(0, 0, 0, 0); + } +// ============================================================================ + private static final void setMirroringOneScrHi() { + setMirroring(1, 1, 1, 1); + } +// ============================================================================ + private static final void setMirroringFourScrn() { + setMirroring(0, 1, 2, 3); + } +// ============================================================================ + private static final void setPPUBanks(int b0, int b1, int b2, int b3, int b4, int b5, int b6, int b7) { + setPPUBankAddress(0, b0 << 10); + setPPUBankAddress(1, b1 << 10); + setPPUBankAddress(2, b2 << 10); + setPPUBankAddress(3, b3 << 10); + setPPUBankAddress(4, b4 << 10); + setPPUBankAddress(5, b5 << 10); + setPPUBankAddress(6, b6 << 10); + setPPUBankAddress(7, b7 << 10); + } +// ============================================================================ + private static final void setPPUBank0(int bank) { + setPPUBankAddress(0, bank << 10); + } +// ============================================================================ + private static final void setPPUBank1(int bank) { + setPPUBankAddress(1, bank << 10); + } +// ============================================================================ + private static final void setPPUBank2(int bank) { + setPPUBankAddress(2, bank << 10); + } +// ============================================================================ + private static final void setPPUBank3(int bank) { + setPPUBankAddress(3, bank << 10); + } +// ============================================================================ + private static final void setPPUBank4(int bank) { + setPPUBankAddress(4, bank << 10); + } +// ============================================================================ + private static final void setPPUBank5(int bank) { + setPPUBankAddress(5, bank << 10); + } +// ============================================================================ + private static final void setPPUBank6(int bank) { + setPPUBankAddress(6, bank << 10); + } +// ============================================================================ + private static final void setPPUBank7(int bank) { + setPPUBankAddress(7, bank << 10); + } +// ============================================================================ + private static final void setPPUBankAddress(int area, int addr) { + if(chr_size == 0) return; + addr &= chr_size - 1; + ppu_bank[area] = 4096 + addr; + dec_bank[area] = addr >> 1; + } +// ????????==================================================================== + private static final void setVRAMBank(int area, int page) { + page &= 0x07; + ppu_bank[area] = 4096 + (page << 10); + dec_bank[area] = page << 9; + } +// ============================================================================ +// NES Memory Mappers Emulation +// ============================================================================ + public static boolean mir_four; + public static boolean mir_vert; + public static boolean bat_sram; + public static boolean trainer; + public static int mapper; +// ------------------------------------------------ MMC1 Special variables ---- + private static int MMC1_Reg[] = new int[8]; + private static int MMC1_Counter ; + private static int MMC1_Bits ; +// ------------------------------------------------ MMC3 Special variables ---- + private static boolean MMC3_PPU_Swap; + private static boolean MMC3_CPU_Swap; + private static int MMC3_Command; + private static int chr01; + private static int chr23; + private static int chr4 ; + private static int chr5 ; + private static int chr6 ; + private static int chr7 ; + private static int prg0 ; + private static int prg1 ; +// ----------------------------------------------- Other mappers variables ---- + private static boolean map_irq_enabled; + private static int map_irq_counter; + private static int map_irq_c_latch; + private static int map_command; + private static boolean map_sram; + private static boolean hsync_used; + private static int chr_n; + private static int prg_n; + +// ============================================================================ + private final void SaveState_map(ByteArrayOutputStream state) { + state.write(map_irq_enabled ? 1: 0 ); + state.write(map_irq_counter >> 0 & 0xFF ); + state.write(map_irq_counter >> 8 & 0xFF ); + state.write(map_irq_c_latch >> 0 & 0xFF ); + state.write(map_irq_c_latch >> 8 & 0xFF ); + state.write(map_command & 0xFF ); + state.write(map_sram ? 1: 0 ); + switch(mapper) { + case 1: + state.write(MMC1_Reg[0] & 0xFF ); + state.write(MMC1_Reg[1] & 0xFF ); + state.write(MMC1_Reg[2] & 0xFF ); + state.write(MMC1_Reg[3] & 0xFF ); + state.write(MMC1_Counter & 0xFF ); + state.write(MMC1_Bits & 0xFF ); + break; + case 4: + state.write(MMC3_PPU_Swap ? 1: 0 ); + state.write(MMC3_CPU_Swap ? 1: 0 ); + state.write(MMC3_Command & 0xFF ); + state.write(chr01 & 0xFF ); + state.write(chr23 & 0xFF ); + state.write(chr4 & 0xFF ); + state.write(chr5 & 0xFF ); + state.write(chr6 & 0xFF ); + state.write(chr7 & 0xFF ); + state.write(prg0 & 0xFF ); + state.write(prg1 & 0xFF ); + break; + } + } +// ============================================================================ + private final void LoadState_map(ByteArrayInputStream state) { + map_irq_enabled = state.read() != 0; + map_irq_counter = state.read() << 0 & 0x00FF; + map_irq_counter |= state.read() << 8 & 0xFF00; + map_irq_c_latch = state.read() << 0 & 0x00FF; + map_irq_c_latch |= state.read() << 8 & 0xFF00; + map_command = state.read() & 0xFF; + map_sram = state.read() != 0; + switch(mapper) { + case 1: + MMC1_Reg[0] = state.read() & 0xFF; + MMC1_Reg[1] = state.read() & 0xFF; + MMC1_Reg[2] = state.read() & 0xFF; + MMC1_Reg[3] = state.read() & 0xFF; + MMC1_Counter = state.read() & 0xFF; + MMC1_Bits = state.read() & 0xFF; + break; + case 4: + MMC3_PPU_Swap = state.read() != 0; + MMC3_CPU_Swap = state.read() != 0; + MMC3_Command = state.read() & 0xFF; + chr01 = state.read() & 0xFF; + chr23 = state.read() & 0xFF; + chr4 = state.read() & 0xFF; + chr5 = state.read() & 0xFF; + chr6 = state.read() & 0xFF; + chr7 = state.read() & 0xFF; + prg0 = state.read() & 0xFF; + prg1 = state.read() & 0xFF; + break; + } + } +// ============================================================================ + private static final void reset_map() { + map_irq_enabled = false; + map_irq_counter = 0; + map_irq_c_latch = 0; + map_sram = bat_sram; // ??? + hsync_used = false; + prg_n = prg_size >> 13; + chr_n = chr_size >> 10; + switch(mapper) { + default: + break; + case 0: // [+] mapper #000 + if(prg_n > 2) + setCPUBanks(0, 1, 2, 3); + else + if(prg_n > 1) + setCPUBanks(0, 1, 0, 1); + else + setCPUBanks(0, 0, 0, 0); + if(chr_n > 0) + setPPUBanks(0, 1, 2, 3, 4, 5, 6, 7); + break; + case 1: // [-] mapper #001 + MMC1_Counter = 0x00; + MMC1_Bits = 0x00; + MMC1_Reg[0] = 0x0C; + MMC1_Reg[1] = 0x00; + MMC1_Reg[2] = 0x00; + MMC1_Reg[3] = 0x00; + setCPUBanks(0, 1, prg_n - 2, prg_n - 1); + setPPUBanks(0, 1, 2, 3, 4, 5, 6, 7); + break; + case 2: // [+] mapper #002 + setCPUBanks(0, 1, prg_n - 2, prg_n - 1); + break; + case 3: // [+] mapper #003 + setPPUBanks(0, 1, 2, 3, 4, 5, 6, 7); + if(prg_n > 2) + setCPUBanks(0, 1, 2, 3); + else + setCPUBanks(0, 1, 0, 1); + break; + case 4: // [?] mapper #004 + hsync_used = true; + if(chr_n > 0) { + chr01 = 0; chr23 = 2; + chr4 = 4; chr5 = 5; + chr6 = 6; chr7 = 7; + MMC3_set_PPU_banks(); + } else + chr01 = chr23 = chr4 = chr5 = chr6 = chr7 = 0; // ??? + setPPUBanks(0, 1, 2, 3, 4, 5, 6, 7); // ??? + prg0 = 0; + prg1 = 1; + MMC3_set_CPU_banks(); + MMC3_PPU_Swap = false; + MMC3_CPU_Swap = false; + MMC3_Command = 0; + break; + case 6: // [?] mapper #006 + hsync_used = true; + setCPUBanks(0, 1, 14, 15); + if(chr_n > 0) { + setPPUBanks(0, 1, 2, 3, 4, 5, 6, 7); + } else { + setVRAMBank(0, 0); // ??? + setVRAMBank(0, 1); + setVRAMBank(0, 2); + setVRAMBank(0, 3); + setVRAMBank(0, 4); + setVRAMBank(0, 5); + setVRAMBank(0, 6); + setVRAMBank(0, 7); + } + break; + case 7: // [+] mapper #007 + setCPUBanks(0, 1, 2, 3); + break; + case 8: // [-] mapper #008 + setPPUBanks(0, 1, 2, 3, 4, 5, 6, 7); + setCPUBanks(0, 1, 2, 3); + break; + case 11: // [?] mapper #011 + setPPUBanks(0, 1, 2, 3, 4, 5, 6, 7); + setCPUBanks(0, 1, 2, 3); + setMirroringVertical(); // ??? + break; + case 15: // [-] mapper #015 + setCPUBanks(0, 1, 2, 3); + break; +// case 69: // [-] mapper #069 +// hsync_used = true; +// map_sram = true; +// setCPUBanks(0, 1, prg_n - 2, prg_n - 1); +// if(chr_n > 0) setPPUBanks(0, 1, 2, 3, 4, 5, 6, 7); +// break; + } + } +// ============================================================================ + private static final void MapperWrite(int addr, int value) { + switch(mapper) { + default: return; +// ============================================================================ + case 1: // [?] mapper #001 - возможно проблемы с battery-backed (zelda) + if((value & 0x80) != 0) { + MMC1_Reg[0] |= 0x0C; + MMC1_Bits = MMC1_Reg[(addr >> 13) & 0x03]; + MMC1_Counter = 0x05; + } else { + MMC1_Bits |= (value & 0x01) << MMC1_Counter; + MMC1_Counter++; + } + if(MMC1_Counter == 5) { + MMC1_Reg[(addr >> 13) & 0x03] = MMC1_Bits; + MMC1_Bits = MMC1_Counter = 0; + int MMC1_Swap256K = prg_n == 64 ? (MMC1_Reg[1] & 0x10) << 1 : 0; + switch(MMC1_Reg[0] & 0x03) { + default: setMirroringOneScrLo(); break; + case 2: setMirroringVertical(); break; + case 3: setMirroringHorizont(); break; + } + if((MMC1_Reg[0] & 0x08) == 0) { + int prg_bank = ((MMC1_Reg[3] & 0x0F) << 2) + MMC1_Swap256K; + setCPUBanks(prg_bank + 0, prg_bank + 1, prg_bank + 2, prg_bank + 3); + } else { + int prg_bank = ((MMC1_Reg[3] & 0x0F) << 1) + MMC1_Swap256K; + if((MMC1_Reg[0] & 0x04) != 0) + setCPUBanks(prg_bank + 0, prg_bank + 1, prg_n - 2, prg_n - 1); + else + setCPUBanks(0, 1, prg_bank + 0, prg_bank + 1); + } + if(chr_n != 0) { + if ((MMC1_Reg[0] & 0x10) != 0) { + int chr_bnk0 = (MMC1_Reg[1] & 0x1F) << 2; + int chr_bnk1 = (MMC1_Reg[2] & 0x1F) << 2; + setPPUBanks(chr_bnk0 + 0, chr_bnk0 + 1, chr_bnk0 + 2, chr_bnk0 + 3, + chr_bnk1 + 0, chr_bnk1 + 1, chr_bnk1 + 2, chr_bnk1 + 3); + } else { + int chr_bank = (MMC1_Reg[1] & 0x1F) << 2; + setPPUBanks(chr_bank + 0, chr_bank + 1, chr_bank + 2, chr_bank + 3, + chr_bank + 4, chr_bank + 5, chr_bank + 6, chr_bank + 7); + } + } + } + return; +// ============================================================================ + case 2: // [+] mapper #002 + { int prg_bank = (value & (prg_n - 1)) << 1; + setCPUBanks(prg_bank + 0, prg_bank + 1, prg_n - 2, prg_n - 1); + } return; +// ============================================================================ + case 3: // [+] mapper #003 + { int chr_bank = (value & (chr_n >> 1) - 1) << 3; + setPPUBanks(chr_bank + 0, chr_bank + 1, chr_bank + 2, chr_bank + 3, + chr_bank + 4, chr_bank + 5, chr_bank + 6, chr_bank + 7); + } return; +// ============================================================================ + case 4: // [+] mapper #004 + switch(addr & 0xE001) { + case 0x8000: + MMC3_PPU_Swap = (value & 0x80) != 0; + MMC3_CPU_Swap = (value & 0x40) != 0; + MMC3_Command = value & 0x07; + MMC3_set_PPU_banks(); + MMC3_set_CPU_banks(); + return; + case 0x8001: + switch(MMC3_Command) { + case 0: chr01 = value & 0xFE; + MMC3_set_PPU_banks(); return; + case 1: chr23 = value & 0xFE; + MMC3_set_PPU_banks(); return; + case 2: chr4 = value; + MMC3_set_PPU_banks(); return; + case 3: chr5 = value; + MMC3_set_PPU_banks(); return; + case 4: chr6 = value; + MMC3_set_PPU_banks(); return; + case 5: chr7 = value; + MMC3_set_PPU_banks(); return; + case 6: prg0 = value; + MMC3_set_CPU_banks(); return; + case 7: prg1 = value; + MMC3_set_CPU_banks(); return; + } + return; + case 0xA000: + if(!mir_four) + if((value & 0x01) != 0) + setMirroringHorizont(); + else + setMirroringVertical(); + return; + case 0xA001: + map_sram = (value & 0x80) == 0x80; + return; + case 0xC000: map_irq_counter = value; return; + case 0xC001: map_irq_c_latch = value; return; + case 0xE000: map_irq_enabled = false; return; + case 0xE001: map_irq_enabled = true ; return; + } + return; +// ============================================================================ + case 6: // [-] mapper #006 - не тестировался + { int prg_bank = (value & 0x3C) >> 1; + int chr_bank = (value & 0x03) << 3; + setCPUBank8(prg_bank + 0); + setCPUBankA(prg_bank + 1); + setVRAMBank(0, chr_bank + 0); // <- скорее всего не будет работать + setVRAMBank(1, chr_bank + 1); + setVRAMBank(2, chr_bank + 2); + setVRAMBank(3, chr_bank + 3); + setVRAMBank(4, chr_bank + 4); + setVRAMBank(5, chr_bank + 5); + setVRAMBank(6, chr_bank + 6); + setVRAMBank(7, chr_bank + 7); + } return; +// ============================================================================ + case 7: // [-] mapper #007 - часть игр не работают или работают с ошибками (тактировка) + { int prg_bank = (value & 0x07) << 2; + setCPUBanks(prg_bank + 0, prg_bank + 1, prg_bank + 2, prg_bank + 3); + if((value & 0x10) != 0) + setMirroringOneScrHi(); + else + setMirroringOneScrLo(); + } return; +// ============================================================================ + case 8: // [-] mapper #008 - не тестировался + { int chr_bank = (value & 0xF8) >> 0; + int prg_bank = (value & 0x07) << 1; + setCPUBank8(prg_bank + 0); + setCPUBankA(prg_bank + 1); + setPPUBanks(chr_bank + 0, chr_bank + 1, chr_bank + 2, chr_bank + 3, + chr_bank + 4, chr_bank + 5, chr_bank + 6, chr_bank + 7); + } return; +// ============================================================================ + case 11: // [-] mapper #011 - не тестировался + { int prg_bank = (value & 0x01) << 2; + int chr_bank = (value & 0x70) >> 1; + setCPUBanks(prg_bank + 0, prg_bank + 1, prg_bank + 2, prg_bank + 3); + setPPUBanks(chr_bank + 0, chr_bank + 1, chr_bank + 2, chr_bank + 3, + chr_bank + 4, chr_bank + 5, chr_bank + 6, chr_bank + 7); + } return; +// ============================================================================ + case 15: // [-] mapper #015 - не тестировался + { int prg_bank = (value & 0x3F) << 1; + switch(addr) { + case 0x8000: + if((value & 0x80) != 0) + setCPUBanks(prg_bank + 1, prg_bank + 0, prg_bank + 3, prg_bank + 2); + else + setCPUBanks(prg_bank + 0, prg_bank + 1, prg_bank + 2, prg_bank + 3); + if((value & 0x40) != 0) setMirroringHorizont(); else setMirroringVertical(); + return; + case 0x8001: + if((value & 0x80) != 0) { + setCPUBankC(prg_bank + 1); setCPUBankE(prg_bank + 0); + } else { + setCPUBankC(prg_bank + 0); setCPUBankE(prg_bank + 1); + } + return; + case 0x8002: + if((value & 0x80) != 0) + setCPUBanks(prg_bank + 1, prg_bank + 1, prg_bank + 1, prg_bank + 1); + else + setCPUBanks(prg_bank + 0, prg_bank + 0, prg_bank + 0, prg_bank + 0); + return; + case 0x8003: + if((value & 0x80) != 0) { + setCPUBankC(prg_bank + 1); setCPUBankE(prg_bank + 0); + } else { + setCPUBankC(prg_bank + 0); setCPUBankE(prg_bank + 1); + } + if((value & 0x40) != 0) setMirroringHorizont(); else setMirroringVertical(); + return; + } + } return; +// ============================================================================ +// case 69: +// { switch(addr & 0xE000) { +// case 0x8000: map_command = value; return; +// case 0xA000: +// { switch(map_command & 0x0F) { +// case 0x00: setPPUBank0(value); return; +// case 0x01: setPPUBank1(value); return; +// case 0x02: setPPUBank2(value); return; +// case 0x03: setPPUBank3(value); return; +// case 0x04: setPPUBank4(value); return; +// case 0x05: setPPUBank5(value); return; +// case 0x06: setPPUBank6(value); return; +// case 0x07: setPPUBank7(value); return; +// case 0x08: +// switch(value & 0xC0) { +// case 0x00: +// case 0x80: +// setCPUBank6(value); return; +// case 0xC0: +// // активизация sram +// } +// case 0x09: setCPUBank8(value); return; +// case 0x0A: setCPUBankA(value); return; +// case 0x0B: setCPUBankC(value); return; +// case 0x0C: +// switch(value &= 0x03) { +// case 0: setMirroringVertical(); return; +// case 1: setMirroringHorizont(); return; +// case 2: setMirroringOneScrLo(); return; +// case 3: setMirroringOneScrHi(); return; +// } +// case 0x0D: map_irq_enabled = value != 0; return; +// case 0x0E: map_irq_counter = (map_irq_counter & 0xFF00) | value; return; +// case 0x0F: map_irq_counter = (map_irq_counter & 0x00FF) | value << 8; return; +// } +// } return; +// case 0xC000: +// case 0xE000: +// return; +// } +// } return; +// ============================================================================ + } + } +// ============================================================================ + private static final void MapWR4000(int addr, int value) { + switch(mapper) { + default: + return; + case 6: // [-] mapper #006 - не тестировался + { switch(addr) { + case 0x42FE: + if((value & 0x10) != 0) + setMirroringOneScrHi(); + else + setMirroringOneScrLo(); + return; + case 0x42FF: + if((value & 0x10) != 0) + setMirroringHorizont(); + else + setMirroringVertical(); + return; + case 0x4501: + map_irq_enabled = false; + return; + case 0x4502: + map_irq_counter = map_irq_counter & 0xFF00 | value & 0xFF; + return; + case 0x4503: + map_irq_counter = map_irq_counter & 0x00FF | value << 8; + map_irq_enabled = true; + return; + } + } return; + } + } +// ============================================================================ + private static final int Map_HSync(int scanline) { + switch(mapper) { + case 4: // [?] mapper #004 + if(map_irq_enabled && scanline < 240 && (ppuContr2 & 0x18) != 0 && --map_irq_counter < 0) { + map_irq_counter = map_irq_c_latch; + return 0; + } + break; + case 6: // [-] mapper #006 - не тестировался + if(map_irq_enabled) { + map_irq_counter += 133; + if(map_irq_counter >= 0xFFFF) { + map_irq_counter = 0; + return 0; + } + } + break; + case 69: // [-] mapper #069 - не тестировался + if(map_irq_enabled) { + if(map_irq_counter <= 133) { + map_irq_counter = 0; + return 0; + } else + map_irq_counter -= 133; + } + break; + } + return -1; + } +// ============================================================================ + private static final void MMC3_set_CPU_banks() { + if(MMC3_CPU_Swap) + setCPUBanks(prg_n - 2, prg1, prg0, prg_n - 1); + else + setCPUBanks(prg0, prg1, prg_n - 2, prg_n - 1); + } +// ============================================================================ + private static final void MMC3_set_PPU_banks() { + if(chr_n != 0) { + if(MMC3_PPU_Swap) + setPPUBanks(chr4, chr5, chr6, chr7, chr01, chr01 + 1, chr23, chr23 + 1); + else + setPPUBanks(chr01, chr01 + 1, chr23, chr23 + 1, chr4, chr5, chr6, chr7); + } else { + if(MMC3_PPU_Swap) { + setVRAMBank(0, chr4); + setVRAMBank(1, chr5); + setVRAMBank(2, chr6); + setVRAMBank(3, chr7); + setVRAMBank(4, chr01 + 0); + setVRAMBank(5, chr01 + 1); + setVRAMBank(6, chr23 + 0); + setVRAMBank(7, chr23 + 1); + } else { + setVRAMBank(0, chr01 + 0); + setVRAMBank(1, chr01 + 1); + setVRAMBank(2, chr23 + 0); + setVRAMBank(3, chr23 + 1); + setVRAMBank(4, chr4); + setVRAMBank(5, chr5); + setVRAMBank(6, chr6); + setVRAMBank(7, chr7); + } + } + } +// ============================================================================ +// NES Small Screen Interface +// ============================================================================ + private static boolean ver_line_visible[] = new boolean[NES_SW]; + private static boolean scanline_visible[] = new boolean[NES_SH]; + private static int pal_1[] = new int[64 * 2]; + private static int pal_2[] = new int[64 * 2]; + private static int pal_lst[] = new int[32]; + private static byte vbuffer[][]; + private static int SH = 0, SW = 0; + private static int PH = 0, PW = 0; + private static int ys_tab[][]; + private static int xs_tab[][]; + private static int screen[]; + private static int scry_addr[]; + private static int scrx_addr[]; + private static int pic_line_len, pic_x_pos, pic_y_pos, pic_w, pic_h; +// ---------------------------------------------------------------------------- + private static int scr_y_fst = 0, scr_y_lst = 0, new_y = scr_y_fst; + private static int scr_x_fst = 0, scr_x_lst = 0, new_x = scr_x_fst; + private static int ly[] = new int[64]; + private static int lx[] = new int[64]; + private static int l_scroll_x = 0, l_scroll_y = 0; +// ---------------------------------------------------------------------------- + private static final int SCALE_LQ = 0; + private static final int SCALE_HQ = 1; + private static final int DYNAMIC = 2; + private static final int STATIC = 3; + private static final int DISABLED = 4; +// ============================================================================ +// [+] Set camera mode and init screen drawing +// ============================================================================ + public synchronized final void set_camera_mode() { + emulThreadControl(ACT_STOP); + ys_tab = null; xs_tab = null; scry_addr = null; scrx_addr = null; + screen = null; vbuffer = null; System.gc(); + setFullScreenMode(vmode <= SCALE_HQ ? (full_scr || hid_fscr) : true); + SH = getHeight(); SW = getWidth(); + graphics.setColor(0x40, 0x40, 0x40); + graphics.fillRect(0x0, 0x0, SW, SH); + if(vmode <= SCALE_HQ && !full_scr) { + PW = (rmode == 0) ? (SW > NES_SW ? NES_SW : SW) : (SH > NES_SW ? NES_SW : SH); + PH = (NES_SH * PW) / NES_SW; + } else { + PW = (rmode == 0) ? (SW > NES_SW ? NES_SW : SW) : (SH > NES_SW ? NES_SW : SH); + PH = (rmode == 0) ? (SH > NES_SH ? NES_SH : SH) : (SW > NES_SH ? NES_SH : SW); + } + if(rmode == 0) { + pic_x_pos = (SW - PW) / 2; pic_y_pos = (SH - PH) / 2; + pic_line_len = PW; pic_w = PW; pic_h = PH; + } else { + pic_x_pos = (SW - PH) / 2; pic_y_pos = (SH - PW) / 2; + pic_line_len = PH; pic_w = PH; pic_h = PW; + } + if(vmode <= SCALE_HQ) { + ys_tab = new int[2][PH]; xs_tab = new int[2][PW]; + int sv = (NES_SH * 100) / PH; + int sh = (NES_SW * 100) / PW; + for(int i = 0; i < PH; i++) { + ys_tab[0][i] = (i * sv) / 100; + ys_tab[1][i] = (i * sv + 50) / 100; + } + for(int i = 0; i < PW; i++) { + xs_tab[0][i] = (i * sh) / 100; + xs_tab[1][i] = (i * sh + 50) / 100; + } + } + graphics.setFont(Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, OSD_FONTS[osd_font])); + screen = new int[PW * PH]; + for(int i = 0; i < scanline_visible.length; i++) scanline_visible[i] = (vmode >= DYNAMIC); + for(int i = 0; i < ver_line_visible.length; i++) ver_line_visible[i] = (vmode >= DYNAMIC); + switch(vmode) { + case SCALE_LQ: + scry_addr = new int[NES_SH]; + scrx_addr = new int[NES_SW]; + for(int i = 0; i < PH; i++) { + scanline_visible[ys_tab[0][i]] = true; + if(rmode == 0) scry_addr[ys_tab[0][i]] = i * PW; + if(rmode == 1) scry_addr[ys_tab[0][i]] = PH - 1 - i; + if(rmode == 2) scry_addr[ys_tab[0][i]] = i; + } + for(int i = 0; i < PW; i++) { + ver_line_visible[xs_tab[0][i]] = true; + if(rmode == 0) scrx_addr[xs_tab[0][i]] = i; + if(rmode == 1) scrx_addr[xs_tab[0][i]] = i * PH; + if(rmode == 2) scrx_addr[xs_tab[0][i]] = (PW - 1 - i) * PH; + } + break; + case SCALE_HQ: + for(int i = 0; i < PH; i++) { + scanline_visible[ys_tab[0][i]] = true; + scanline_visible[ys_tab[1][i]] = true; + } + for(int i = 0; i < PW; i++) { + ver_line_visible[xs_tab[0][i]] = true; + ver_line_visible[xs_tab[1][i]] = true; + } + vbuffer = new byte[NES_SH][NES_SW]; + for(int y = 0; y < NES_SH; y++) for(int x = 0; x < NES_SW; x++) vbuffer[y][x] = 0x3F; + break; + case DYNAMIC: + case STATIC: + draw_to_display(0); + break; + } + if(ntsc_mode) for(int i = 0; i < 8; i++) scanline_visible[i] = scanline_visible[NES_SH - 1 - i] = false; + emulThreadControl(ACT_RESUME); + } +// ============================================================================ +// [?] Draw screen to mobile phone display +// ============================================================================ + private final void draw_to_display(int vframes) { + int loc_screen[] = screen; + L0: switch(vmode) { + case SCALE_LQ: + break L0; + case SCALE_HQ: + { byte loc_vbuffer[][] = vbuffer; + int loc_ys_tab[][] = ys_tab; + int loc_xs_tab[][] = xs_tab; + int loc_pal_2[] = pal_2; + int c1, c2; + switch(rmode) { + case 0: + for(int y = 0; y < PH; y++) + for(int x = 0; x < PW; x++) { + c1 = loc_pal_2[loc_vbuffer[loc_ys_tab[0][y]][loc_xs_tab[0][x]]]; + c2 = loc_pal_2[loc_vbuffer[loc_ys_tab[1][y]][loc_xs_tab[1][x]]]; + loc_screen[y * PW + x ] = c1 + c2; + } + break L0; + case 1: + for(int y = 0; y < PH; y++) + for(int x = 0; x < PW; x++) { + c1 = loc_pal_2[loc_vbuffer[loc_ys_tab[0][y]][loc_xs_tab[0][x]]]; + c2 = loc_pal_2[loc_vbuffer[loc_ys_tab[1][y]][loc_xs_tab[1][x]]]; + loc_screen[x * PH + (PH - 1 - y) ] = c1 + c2; + } + break L0; + case 2: + for(int y = 0; y < PH; y++) + for(int x = 0; x < PW; x++) { + c1 = loc_pal_2[loc_vbuffer[loc_ys_tab[0][y]][loc_xs_tab[0][x]]]; + c2 = loc_pal_2[loc_vbuffer[loc_ys_tab[1][y]][loc_xs_tab[1][x]]]; + loc_screen[(PW - 1 - x) * PH + y ] = c1 + c2; + } + break L0; + } + } break L0; + case DYNAMIC: + { byte loc_spr_ram[] = spr_ram; + if((vframes % (REACTIONS[reaction] / frameskip) ) == 0) { + int nes_y_lst = ntsc_mode ? NES_SH - 16 - 1 : NES_SH - 1; // ... NES_SH - 8 - 1 ... + int nes_y_fst = ntsc_mode ? 8 : 0; + int nes_x_lst = NES_SW - 1; + int nes_x_fst = 0; + int Summ_Y = 0, Summ_X = 0, spr_num = 0; + for(int i = 0; i < 64; i++) { + int y = (0xFF & loc_spr_ram[i * 4]) + 1; + int x = (0xFF & loc_spr_ram[i * 4 + 3]); + if(y < 240) { + if(y != ly[i] || x != lx[i]) { + Summ_Y = Summ_Y + y; + Summ_X = Summ_X + x; + spr_num++; + } + ly[i] = y; + lx[i] = x; + } + } + if(spr_num > 0) { + Summ_Y = Summ_Y / spr_num; + Summ_X = Summ_X / spr_num; + } else { + Summ_Y = NES_SH / 2; + Summ_X = NES_SW / 2; + } + new_x = Summ_X - (PW / 2); + new_y = Summ_Y - (PH / 2); + if(new_x > (nes_x_lst - PW)) new_x = nes_x_lst - PW; + if(new_y > (nes_y_lst - PH)) new_y = nes_y_lst - PH; + if(new_x < nes_x_fst) new_x = nes_x_fst; + if(new_y < nes_y_fst) new_y = nes_y_fst; + } + if(scr_x_fst < new_x) scr_x_fst += frameskip; + if(scr_x_fst > new_x) scr_x_fst -= frameskip; + if(scr_y_fst < new_y) scr_y_fst += frameskip; + if(scr_y_fst > new_y) scr_y_fst -= frameskip; + scr_x_lst = scr_x_fst + PW - 1; + scr_y_lst = scr_y_fst + PH - 1; + } break L0; + case STATIC: + { if((vframes % (REACTIONS[reaction] / frameskip) ) == 0) { + int nes_y_lst = ntsc_mode ? NES_SH - 16 - 1 : NES_SH - 1; + int nes_y_fst = ntsc_mode ? 8 : 0; + int nes_x_lst = NES_SW - 1; + int nes_x_fst = 0; + int Summ_Y = NES_SH / 2; + int Summ_X = NES_SW / 2; + int scroll_x = (ppuT & 0x001F) << 3 | ppuX; + int scroll_y = (ppuT & 0x03E0) >> 2 | (ppuT & 0x7000) >> 12; + int d_scroll_x = scroll_x - l_scroll_x; + int d_scroll_y = scroll_y - l_scroll_y; + if(d_scroll_x > 0) Summ_X = (Summ_X + NES_SW - 1) / 2; + if(d_scroll_y > 0) Summ_Y = (Summ_Y + NES_SH - 1) / 2; + if(d_scroll_x < 0) Summ_X = (Summ_X + 0) / 2; + if(d_scroll_y < 0) Summ_Y = (Summ_Y + 0) / 2; + l_scroll_x = scroll_x; + l_scroll_y = scroll_y; + new_x = Summ_X - (PW / 2); + new_y = Summ_Y - (PH / 2); + if(new_x > (nes_x_lst - PW)) new_x = nes_x_lst - PW; + if(new_y > (nes_y_lst - PH)) new_y = nes_y_lst - PH; + if(new_x < nes_x_fst) new_x = nes_x_fst; + if(new_y < nes_y_fst) new_y = nes_y_fst; + } + if(scr_x_fst < new_x) scr_x_fst += frameskip; + if(scr_x_fst > new_x) scr_x_fst -= frameskip; + if(scr_y_fst < new_y) scr_y_fst += frameskip; + if(scr_y_fst > new_y) scr_y_fst -= frameskip; + scr_x_lst = scr_x_fst + PW - 1; + scr_y_lst = scr_y_fst + PH - 1; + } break L0; + case DISABLED: + graphics.setColor(64, 64, 64); + graphics.fillRect(0, 0, SW, SH); + draw_osd(); + flushGraphics(0, 0, SW, SH); + return; + } + graphics.drawRGB(loc_screen, 0, pic_line_len, pic_x_pos, pic_y_pos, pic_w, pic_h, false); + draw_osd(); + flushGraphics(); + } +// ============================================================================ +// [-] OSD Procedures and variables +// ============================================================================ + private long lasttime = System.currentTimeMillis(), osdstart = 0, fps = 0; + private String osd_text = ""; +// ============================================================================ + private final void print_osd(String text) { + osdstart = System.currentTimeMillis(); + osd_text = text; + } +// ============================================================================ + private final void draw_osd() { + if(osd_enable && (System.currentTimeMillis() - osdstart) < osd_time) { + graphics.setColor(OSD_COLOR[osd_colour]); + graphics.drawString(osd_text, pic_x_pos + 1, pic_y_pos + 1, 0); + return; + } + if(fps_enable) { + long currtime = System.currentTimeMillis(); + long framtime = (currtime - lasttime) / frameskip; + lasttime = currtime; + fps = 1020 / framtime; // 1020 = 17 * 60; + graphics.setColor(fps < 25 ? 0x00FF0000 : (fps < 50 ? 0x00FFFF00 : 0x0000FF00)); + graphics.drawString("FPS: " + fps, pic_x_pos + 1, pic_y_pos + 1, 0); + } + } +// ============================================================================ +// +// ============================================================================ + private static boolean turbo_a = false; + private static boolean turbo_b = false; + private static int j1_state = 0; + private static int j1_value = 0; +// ============================================================================ + protected final void keyPressed(int keyCode) { + switch(keyCode) { + case -1: // 4-way select up + j1_state |= getJoyKeyMask(0x00, true); return; + case -2: // 4-way select down + j1_state |= getJoyKeyMask(0x01, true); return; + case -3: // 4-way select left + j1_state |= getJoyKeyMask(0x02, true); return; + case -4: // 4-way select right + j1_state |= getJoyKeyMask(0x03, true); return; + case -5: // 4-way select press + j1_state |= getJoyKeyMask(0x04, true); return; + case 42: // [*] + j1_state |= getJoyKeyMask(0x05, true); return; + case 35: // [#] + j1_state |= getJoyKeyMask(0x06, true); return; + case 48: // [0] + j1_state |= getJoyKeyMask(0x07, true); return; + case 49: // [1] + j1_state |= getJoyKeyMask(0x08, true); return; + case 50: // [2] + j1_state |= getJoyKeyMask(0x09, true); return; + case 51: // [3] + j1_state |= getJoyKeyMask(0x0A, true); return; + case 52: // [4] + j1_state |= getJoyKeyMask(0x0B, true); return; + case 53: // [5] + j1_state |= getJoyKeyMask(0x0C, true); return; + case 54: // [6] + j1_state |= getJoyKeyMask(0x0D, true); return; + case 55: // [7] + j1_state |= getJoyKeyMask(0x0E, true); return; + case 56: // [8] + j1_state |= getJoyKeyMask(0x0F, true); return; + case 57: // [9] + j1_state |= getJoyKeyMask(0x10, true); return; + case -8: // C key (Clear) + j1_state |= getJoyKeyMask(0x11, true); return; + case -13: // Special gaming key A + j1_state |= getJoyKeyMask(0x13, true); return; + case -14: // Special gaming key B + j1_state |= getJoyKeyMask(0x14, true); return; + case -36: // Volume+ (Zoom+) key + j1_state |= getJoyKeyMask(0x15, true); return; + case -37: // Volume- (Zoom-) key + j1_state |= getJoyKeyMask(0x16, true); return; + } + } +// ============================================================================ + protected final void keyReleased(int keyCode) { + switch(keyCode) { + // default : return; + case -1: // 4-way select up + j1_state &= ~getJoyKeyMask(0x00, false); return; + case -2: // 4-way select down + j1_state &= ~getJoyKeyMask(0x01, false); return; + case -3: // 4-way select left + j1_state &= ~getJoyKeyMask(0x02, false); return; + case -4: // 4-way select right + j1_state &= ~getJoyKeyMask(0x03, false); return; + case -5: // 4-way select press + j1_state &= ~getJoyKeyMask(0x04, false); return; + case 42: // [*] + j1_state &= ~getJoyKeyMask(0x05, false); return; + case 35: // [#] + j1_state &= ~getJoyKeyMask(0x06, false); return; + case 48: // [0] + j1_state &= ~getJoyKeyMask(0x07, false); return; + case 49: // [1] + j1_state &= ~getJoyKeyMask(0x08, false); return; + case 50: // [2] + j1_state &= ~getJoyKeyMask(0x09, false); return; + case 51: // [3] + j1_state &= ~getJoyKeyMask(0x0A, false); return; + case 52: // [4] + j1_state &= ~getJoyKeyMask(0x0B, false); return; + case 53: // [5] + j1_state &= ~getJoyKeyMask(0x0C, false); return; + case 54: // [6] + j1_state &= ~getJoyKeyMask(0x0D, false); return; + case 55: // [7] + j1_state &= ~getJoyKeyMask(0x0E, false); return; + case 56: // [8] + j1_state &= ~getJoyKeyMask(0x0F, false); return; + case 57: // [9] + j1_state &= ~getJoyKeyMask(0x10, false); return; + case -8: // C key (Clear) + j1_state &= ~getJoyKeyMask(0x11, false); return; + case -13: // Special gaming key A + j1_state &= ~getJoyKeyMask(0x13, false); return; + case -14: // Special gaming key B + j1_state &= ~getJoyKeyMask(0x14, false); return; + case -36: // Volume+ (Zoom+) key + j1_state &= ~getJoyKeyMask(0x15, false); return; + case -37: // Volume- (Zoom-) key + j1_state &= ~getJoyKeyMask(0x16, false); return; + } + } +// ============================================================================ + private final int getJoyKeyMask(int key_map_index, boolean press) { + switch(keys_map[rmode][key_map_index]) { + case 0x0: return 0x00; // Not defined + case 0x1: return 0x80; // Right + case 0x2: return 0x40; // Left + case 0x3: return 0x20; // Down + case 0x4: return 0x10; // Up + case 0x5: return 0x04; // Select + case 0x6: return 0x08; // Start + case 0x7: turbo_a = (press && turboen_a); return 0x01; // A (jump) + case 0x8: turbo_b = (press && turboen_b); return 0x02; // B (fire) + } + if(press) { + if(keys_map[rmode][key_map_index] == 0x9) { + if(++vmode > STATIC) vmode = SCALE_LQ; + set_camera_mode(); + print_osd("Camera: " + CAMERA[vmode]); + } else + if(keys_map[rmode][key_map_index] == 0xA) { + full_scr = !full_scr; + if(vmode == DISABLED) hid_fscr = full_scr; + set_camera_mode(); + print_osd("Scale: " + (full_scr ? "Fullscreen" : "Windowed")); + } else + if(keys_map[rmode][key_map_index] == 0xB) { + if(++rmode > 2) rmode = 0; + if(vmode == DISABLED && rmode == 0) hid_invc = !hid_invc; + set_camera_mode(); + print_osd("Rotation"); + } + SaveSettings(); + } + return 0; + } +// ============================================================================ +// [-] Midi device control for APU 2A03 +// ============================================================================ + private static byte tone_note[] = new byte[2048]; + private static int volum[] = new int[0x10]; + private static int tones[] = new int[0x10]; + private static MIDIControl midi = null; + private static Player player = null; +// ---------------------------------------------------------------------------- + private static final byte PACKED_NOTES[][] = { + { 1, 127}, { 1, 127}, { 1, 127}, { 1, 127}, { 1, 127}, { 1, 127}, { 1, 127}, { 1, 127}, + { 1, 127}, { 1, 125}, { 1, 123}, { 1, 122}, { 1, 120}, { 1, 119}, { 1, 118}, { 1, 117}, + { 1, 116}, { 1, 115}, { 1, 114}, { 1, 113}, { 1, 112}, { 2, 111}, { 1, 110}, { 1, 109}, + { 2, 108}, { 2, 107}, { 1, 106}, { 2, 105}, { 2, 104}, { 2, 103}, { 2, 102}, { 3, 101}, + { 2, 100}, { 3, 99}, { 3, 98}, { 2, 97}, { 4, 96}, { 3, 95}, { 3, 94}, { 4, 93}, + { 4, 92}, { 4, 91}, { 4, 90}, { 5, 89}, { 5, 88}, { 5, 87}, { 6, 86}, { 5, 85}, + { 7, 84}, { 6, 83}, { 7, 82}, { 7, 81}, { 8, 80}, { 8, 79}, { 9, 78}, { 9, 77}, + { 10, 76}, { 11, 75}, { 11, 74}, { 11, 73}, { 13, 72}, { 13, 71}, { 13, 70}, { 15, 69}, + { 16, 68}, { 16, 67}, { 18, 66}, { 18, 65}, { 20, 64}, { 21, 63}, { 22, 62}, { 23, 61}, + { 25, 60}, { 26, 59}, { 27, 58}, { 30, 57}, { 31, 56}, { 33, 55}, { 35, 54}, { 37, 53}, + { 39, 52}, { 42, 51}, { 44, 50}, { 46, 49}, { 50, 48}, { 52, 47}, { 55, 46}, { 59, 45}, + { 62, 44}, { 66, 43}, { 70, 42}, { 74, 41}, { 79, 40}, { 83, 39}, { 88, 38}, { 93, 37}, + { 99, 36}, {104, 35}, {111, 34}, { 73, 33} + }; + private static final int MIDI_VOL[] = { + 0x00, 0x20, 0x2E, 0x38, 0x41, 0x49, 0x50, 0x56, 0x5C, 0x62, 0x67, 0x6C, 0x71, 0x76, 0x7A, 0x7F + }; +// ---------------------------------------------------------------------------- + private static final int PROGRAM_CHANGE = 0xC0; + private static final int NOTE_ON = 0x90; + private static final int NOTE_OFF = 0x80; +// ---------------------------------------------------------------------------- + private static final int CHAN_SQUARE1 = 0x00; + private static final int CHAN_SQUARE2 = 0x01; + private static final int CHAN_TRIANGLE = 0x02; +// ============================================================================ + private static final boolean OpenMidi() { + try{ + player = Manager.createPlayer(Manager.MIDI_DEVICE_LOCATOR); + player.prefetch(); + midi = (MIDIControl)player.getControl("javax.microedition.media.control.MIDIControl"); + } catch (Exception e) {} + return midi != null; + } +// ============================================================================ + private static final void SelectInstrument(int channel, int patch) { + midi.shortMidiEvent(PROGRAM_CHANGE | channel, patch, 0); + } +// ============================================================================ + private static final void ToneOn(int channel, int tone, int volume) { + midi.shortMidiEvent(NOTE_ON | channel, tone, volume); + } +// ============================================================================ + private static final void ToneOff(int channel, int tone) { + midi.shortMidiEvent(NOTE_OFF | channel, tone, 0); + } +// ============================================================================ +// [?] pAPU 2A03 engine +// ============================================================================ + private static int apureg[] = new int[0x10]; + private static int apu_ctrl = 0; + private static int divider4 = 0; + private static int divider2 = 0; +// ---------------------------------------------------------------------------- + private static int sq1_env_vol = 0; + private static int sq1_env_dec = 0; + private static int sq1_len_cou = 0; + private static int sq1_frq_val = 0; + private static int sq1_frq_lmt = 0; + private static int sq1_swp_dec = 0; + private static int sq1_swp_shi = 0; +// ---------------------------------------------------------------------------- + private static int sq2_env_vol = 0; + private static int sq2_env_dec = 0; + private static int sq2_len_cou = 0; + private static int sq2_frq_val = 0; + private static int sq2_frq_lmt = 0; + private static int sq2_swp_dec = 0; + private static int sq2_swp_shi = 0; +// ---------------------------------------------------------------------------- + private static int tri_len_cou = 0; + private static int tri_lin_cou = 0; + private static int tri_frq_val = 0; +// ---------------------------------------------------------------------------- + private static int noi_env_vol = 0; + private static int noi_env_dec = 0; + private static int noi_len_cou = 0; +// ---------------------------------------------------------------------------- + private static final byte V_LENGTHS[] = { + 0x05, 0x7F, 0x0A, 0x01, 0x13, 0x02, 0x28, 0x03, + 0x50, 0x04, 0x1E, 0x05, 0x07, 0x06, 0x0D, 0x07, + 0x06, 0x08, 0x0C, 0x09, 0x18, 0x0A, 0x30, 0x0B, + 0x60, 0x0C, 0x24, 0x0D, 0x08, 0x0E, 0x10, 0x0F + }; + private static final int FRQ_LIMIT[] = { + 0x3FF, 0x555, 0x666, 0x71C, 0x787, 0x7C1, 0x7E0, 0x7F0 + }; +// ============================================================================ + private final void open_apu() { + if(!midi_device) { + midi_device = OpenMidi(); + int index = 0; + for(int i = 0; i < 100; i++) + for(int j = 0; j < PACKED_NOTES[i][0]; j++) tone_note[index++] = PACKED_NOTES[i][1]; + } + if(midi_device && apu_enabl) { + SelectInstrument(CHAN_SQUARE1 , apu_sqr_patch); + SelectInstrument(CHAN_SQUARE2 , apu_sqr_patch); + SelectInstrument(CHAN_TRIANGLE, apu_tri_patch); + } + } +// ============================================================================ + private final void close_apu() { + if(midi_device && apu_enabl) { + StopTone(CHAN_SQUARE1 ); + StopTone(CHAN_SQUARE2 ); + StopTone(CHAN_TRIANGLE); + } + } +// ============================================================================ + private final void reset_apu() { + for(int reg = 0; reg < 16; reg++) cpuWriteMem(0x4000 + reg, 0); + cpuWriteMem(0x4015, 0); + } +// ============================================================================ + private final void play_apu() { + if(midi_device && apu_enabl) { + for(divider4 = 0; divider4 < 4; divider4++) { + divider2 = divider4 & 0x01; + if(sq1_enabl) PlaySquare1(); + if(sq2_enabl) PlaySquare2(); + if(tri_enabl) PlayTriangl(); + } + } + } +// ============================================================================ + private final void PlaySquare1() { + if((apu_ctrl & 0x01) != 0 && sq1_len_cou > 0) { + int vol_dec = apureg[0] & 0x0F; + if(--sq1_env_dec < 0) { + sq1_env_dec = vol_dec; + if(--sq1_env_vol < 0) + sq1_env_vol = (apureg[0] & 0x20) != 0 ? 0x0F : 0x00; + } + int vol = (apureg[0] & 0x10) != 0 ? vol_dec : sq1_env_vol; + if(sq1_frq_val < 8 || sq1_frq_val > FRQ_LIMIT[sq1_swp_shi]) { + StopTone(CHAN_SQUARE1); + return; + } else { + if((apureg[1] & 0x80) != 0 && sq1_swp_shi != 0) { + if(divider2 == 0) sq1_swp_dec--; + if(sq1_swp_dec < 0) { + sq1_swp_dec = (apureg[1] & 0x70) >> 4; + if((apureg[1] & 0x08) == 0) + sq1_frq_val -= sq1_frq_val >> sq1_swp_shi; + else + sq1_frq_val += sq1_frq_val >> sq1_swp_shi; + } + } + } + if((apureg[0] & 0x20) == 0 && divider4 == 0) sq1_len_cou--; + PlayTone(CHAN_SQUARE1, tone_note[sq1_frq_val], vol); + } else { + StopTone(CHAN_SQUARE1); + } + } +// ============================================================================ + private final void PlaySquare2() { + if((apu_ctrl & 0x02) != 0 && sq2_len_cou > 0) { + int vol_dec = apureg[4] & 0x0F; + if(--sq2_env_dec < 0) { + sq2_env_dec = vol_dec; + if(--sq2_env_vol < 0) + sq2_env_vol = (apureg[4] & 0x20) != 0 ? 0x0F : 0x00; + } + int vol = (apureg[4] & 0x10) != 0 ? vol_dec : sq2_env_vol; + if(sq2_frq_val < 8 || sq2_frq_val > FRQ_LIMIT[sq2_swp_shi]) { + StopTone(CHAN_SQUARE2); + return; + } else { + if((apureg[5] & 0x80) != 0 && sq2_swp_shi != 0) { + if(divider2 == 0) sq2_swp_dec--; + if(sq2_swp_dec < 0) { + sq2_swp_dec = (apureg[5] & 0x70) >> 4; + if((apureg[5] & 0x08) != 0) + sq2_frq_val -= sq2_frq_val >> sq2_swp_shi; + else + sq2_frq_val += sq2_frq_val >> sq2_swp_shi; + } + } + } + if((apureg[4] & 0x20) == 0 && divider4 == 0) sq2_len_cou--; + PlayTone(CHAN_SQUARE2, tone_note[sq2_frq_val], vol); + } else { + StopTone(CHAN_SQUARE2); + } + } +// ============================================================================ + private final void PlayTriangl() { + if((apu_ctrl & 0x04) != 0 && tri_len_cou > 0 && tri_lin_cou > 0) { + if((apureg[8] & 0x80) != 0) { // ??? + if(divider4 == 0) tri_len_cou--; // ??? + } else // ??? + tri_lin_cou--; // ??? + if(tri_frq_val >= 8) PlayTone(CHAN_TRIANGLE, tone_note[tri_frq_val] - 24, 0xF); // !!! + } else { + StopTone(CHAN_TRIANGLE); + } + } +// ============================================================================ + private final void PlayTone(int chan, int tone, int volume) { + if(tone != tones[chan] && tones[chan] != 0) ToneOff(chan, tones[chan]); + if(tone != tones[chan] || volume != volum[chan]) { + ToneOn(chan, tone, MIDI_VOL[volume]); + volum[chan] = volume; + tones[chan] = tone; + } + } +// ============================================================================ + private final void StopTone(int chan) { + if(tones[chan] != 0) { + ToneOff(chan, tones[chan]); + volum[chan] = 0; + tones[chan] = 0; + } + } +// ============================================================================ +} + + +// То, что желательно сделать: +// -------------------------------------- +// ! WARRING: Необходимо реализовать многоязыковую систему перевода фраз и сообщений; +// ? проверить все мапперы на работоспособность с разными играми; +// ? оптимизировать мапперы по скорости (навести порядок в коде); +// ! Mario: неправильное начало игры - проверить правильность загрузки рома (ром испорчен); +// - APU: максимально ускорить эмуляцию, возможно добавить каналы NOISE и/или DMC; +// - APU: на некоторых играх не играет Triangle, присутствует завывание звука - проблемы с Lenght counter/Liniar counter/Write apu regs +// - APU > Square > Sweep unit - иногда звучит как-то грязно, нужно проверить тайминг/фазы смещения частоты/Write apu regs +// ! вынести пути к файлам *.nes и *.pal в отдельные константы для быстрого управления; +// - подкорректировать и оптимизировать код в соответствии с рекомендациями SE; +// ! устранить глюк с обработкой кнопок, не вызывающих keyReleased(); +// ! реализовать режим Gameplay, в котором горячими клавишами будет настраиваться геймплей (центр экрана, палитра, яркость, цветность и т.д.) + + +// То, что необходимо сделать: +// --------------------------------------- +// ! Dynamic/Static: некорректная работа при Lines=224; +// ! разобраться с системой ограничения fps (дает неправильное ограничение либо вообще не работает); +// - эмуль вылетает, если не удалось запустить игру (не обрабатывается java.lang.OutOfMemoryError); +// ? ограничение имен файлов игр в 32 - n символов; +// - провести эксперименты и выявить, где Static идентификатор замедляет систему, а где ускоряет; +// - ввести поддержку battery-backet sram; +// ! оптимизировать алгоритм улучшенного определения Sprite 0 Hit; +// - добавить: if(ch>128) ch +=0x350; // Перевод из Win1251 в Unicode, это если хочется видеть русские строки +// - Lion King III - 4 маппер - происходит неправильная синхронизация + + +// 20.07.2006 ======================================================================================================================= +// ! оффициальный релиз: Nescube 0.4 demo; + +// ??.08.2006 - ??.09.2006 подробная история утеряна: +// + написан новый движок эмуляции; +// + написан новый интерфейс эмулятора; +// + оптимизация для сокращения до минимума используемой памяти; +// + добавлена Error message для обработки исключительных ситуаций; +// + введена эмуляция APU через MIDI устройство телефона; +// + добавлены режимы повернутого на 90 и 270 градусов экрана; +// + добавлены режим Full screen для видеорежимов Scale LQ и Scale HQ; +// + введена возможность управления цветностью и яркостью палитры цветов; +// + введена технология FPS limit (Settings > Engine > FPS Limit); +// + добавлена возможность настройки эмуляции APU (Settings > Sound) +// + переписана по другому типу обработка клавиатуры телефона; +// + добавлена возможность программирования клавиш телефона (Settings > Control); +// ! эмуляция APU работает только на телефонах, поддерживающих элемент MMAPI - MIDIControl; +// ! промежуточная сборка для теста: Nescube 1.0 - Nescube 1.0 beta 4; + +// 29.09.2006 ======================================================================================================================= +// + исправлена работа Fast rendering (теперь работает аналогично vNes); +// + добавлен пункт настройки Settings > Engine > Finding loop; +// ! промежуточная сборка для теста: Nescube 1.0 beta 5; + +// 30.09.2006 ======================================================================================================================= +// + введена функция Default для разделов настроек Settings; +// + инициализатор видеоподсистемы переписан с целью нормальной работы на дисплее 320x240; +// ! корректная работа видеоподсистемы гарантирована на дисплеях: 160х128, 220х176, 320х240; +// ? функция Default для настроек Settings > Control не работает правильно; +// ? видеорежимы Dynamic и Static работают не так, как задумано при Lines in frame = 224; + +// 01.10.2006 +// + исправлена и доделана функция Default для настроек Settings > Control; +// + исправлено BG and SP Clipping (отображается поле черным цветом); +// + убрано ненужное определение 0-го спрайта в реримах Scale LQ и Scale HQ; +// + исправлены "ляпы" с флагом SP Clipping в прорисовке спрайтов (вместо sp_not_clipped было bg_not_clipped); +// + добавлены видеорежимы для Dynamic & Static при активном Fast rendering; +// ! при Fast rendering BG and SP Clipping используется в старой упрощенной форме (возможно будет убрано вообще); +// ? обнаружено общее уменьшение FPS начиная с beta 5 (для Bombermen с max 63 до max 51); +// ? обнаружены неточности в работе Error message; + +// 09.10.2006 +// + новая ревизия исходников, подчищены некоторые места в коде; +// + убрано лишнее ppuT &= 0x8FFF в алгоритме записи значения в регостр PPU 0x2005; +// ! малейшие изменения в алгоритмах формирования картинки NES сильно влияют на FPS; +// ! причину уменьшения FPS (01.10.2006) так и не удалось установить; +// ? обнаружена неккоректная (во всех видеорежимах, кроме Scale HQ) работа алгоритма Sprite 0 hit; +// ? иногда ошибка при переключении видеорежима (конфликт когда одновременно выполняется прорисовка и переключение режима); + +// 11.10.2006 +// + подправлена (временное решение) случайная ошибка при переключении видеорежимов; +// + исправлена неверная прорисовка правого края экрана NES при скролиловании Background; +// + добавлена опция Settings > Engine > Scale LQ mode, позволяющая выбрать одну из двух таблиц масштабирования; +// + добавлена опция Settings > Engine > CPU t-states in line, задающая количество тактов на строку кадра; +// + опции Settings > Sound > Triangle/Square patch теперь работают через TextField путем ввода чисел 0-127; + +// 14.10.2006 +// + подчищен и оптимизирован по размеру код видеорежимов, при активном Fast rendering BG and SP Clipping не обрабатывается; +// + реализована система единократного вызова из главного цикла эмуляции процедуры эмуляции APU, подчищен код APU; + +// 26.10.2006 +// + введена синхронизация потока эмуляции с другими потоками, "устранена" случайная ошибка при переключении видеорежимов; + +// 13.11.2006 +// + написана (но не тестирована) процедура LoadSettings(), которая грузит общие (или для выбранной игры) настройки; +// + добавлена опция Settings > Other > Keep settings, подкорректирована группа About; +// ! сделать загрузку настроек: при создании движка, сразу после загрузки игры; +// ! сделать запись настроек: при выборе Save в коммандном меню настроек; + +// 14.11.2006 +// + дописана запись и загрузка настроек (13.11.2006), требуется тщательная отладка; +// + массив настроек клавиш теперь вместо int имеет тип byte; + +// 26.11.2006 +// + исправлены проблемы записи и загрузки настроек, отлажена работа на реальном телефоне (14.11.2006); +// + добавлена запись настроек при их изменении горячими клавишами; +// + добавлено определение поддержки звука (немного коряво) и блокирование меню Sound при недоступности звука; +// ! навести порядок в процедурах определения поддержки/инициализации/открытия/закрытия apu, возможно переписать код apu; +// ! сделать запоминание позиции селектора в списке файлов игр для более быстрой навигации; + +// 04.12.2006 +// + в меню Open game указатель автоматически становится на текущую запущеную игру (ускорение навигации, 26.11.2006); +// + исправлен переход Settings > Other > Keep sets из Disabled в Enabled (терялись индивидуальные настройки игры); +// ! необходимо тщательно проработать систему Error Message, чтобы определиться со следующим пунктом; +// ? при срабатывании Error Message эмулятор должен переходить в режим незапущенной игры (nes_name = null); +// ? обнаружена (после изминений 26.11.2006) неправильная работа apu; + +// 05.12.2006 +// + исправлена неработоспособность apu (04.12.2006), наведен порядок в процедурах (26.11.2006); +// ! оффициальный урезанный промежуточный релиз: Nescube 1.1 beta; + +// 08.12.2006 ======================================================================================================================= +// + добавлена цветная подсветка FPS (красный - низкий, желтый - приемлемый, зеленый - нормальный уровень FPS); +// + в эмуляции CPU выражения типа a[i] & 0xFF | (a[i+1] & 0xFF) << 8 заменены на (char)(a[i] & 0xFF | a[i+1] << 8) что типа быстрее; +// + в процедуре cpuReadMem() убран addr &= 0xFFFF, т.к. исправлена адресация с ...+X(+Y) и теперь адрес стабильно 16-ти битный; +// + переписана, оптимизирована по скорости и размеру процедура check_spr0_hits(), не всегда верно работает; +// ? в старой процедуре check_spr0_hits() вычисляется sp_color но нигде ниже по сути не используется; + +// 09.12.2006 +// + исправлена ошибка в check_spr0_hits(), процедура оптимизирована и нормально работает; +// + процедуры видеорендеринга оптимизорованы по скорости за счет локальных ссылок на глобальные массивы (+1-5 fps); +// ! навести порядок в коде процедур видеорендеринга, выбрать общие для всех процедур имена переменных; + +// 11.12.2006 +// + процедура draw_to_display() оптимизорована по скорости за счет локальных ссылок на глобальные массивы (+1-3 fps); +// + процедуры cpuReadMem() и cpuWriteMem() оптимизорована по скорости за счет локальных ссылок на глобальные массивы; +// + введена опция Settings > Engine > Detect S0 mode, задающая режим работы процедуры check_spr0_hits(); +// + check_spr0_hits() наново переписана и ускорена за счет исключения бесполезных вызовов после установки ppuStatus |= 0x40; + +// 12.12.2006 +// + исправлен случайно сделанный (11.12.2006) глючек со строчной синхронизацией мапперов; +// + добавлен новый видеорежим Smart LQ при Fast rendering = false, выбран оптимальный алгоритм; +// + расширен список режимов Settings > Display > Camera type, произведены соответствующие корректировки в коде; +// ? необходимо полномасштабное тестирование Smart LQ с выявлением положительных и отрицательных моментов; + +// 14.12.2006 +// + режим Smart LQ заменен более широкой технологией Smart scanline drawing, Settings > Display > Camera type вернут обратно; +// + изменена (всвязи с первым пунктом) пририсовка FPS, теперь значение меньше скачет и отображает более реальное значение; +// + наново переписан раздел настроек Settings > Engine, переименованы часть технологий; +// ! технология Smart scanline drawing работает совместно с Scale LQ, Scale HQ при !fastvideo; + +// 18.12.2006 +// + добавлен класс, отображающий заставку программы с ожиданием нажатия клавиши; +// + исправлен древний баг java.lang.ArithmeticException в алгоритме Finding loop, вешавший эмуль на некоторых играх; +// ! разобраться с алгоритмом Turbo A/B, неудовлетворительная работа Turbo B (fire); + +// 19.12.2006 +// + подчищен код эмуляции CPU, удалены старые закомментированные фрагменты кода; +// + в коде эмуляции CPU проведены замены: mem[V0 + 1] -> mem[++V0], X/Y + 1 -> ++X/Y, X/Y - 1 -> --X/Y; +// + добавлен код для нахождения самых популярных попарных комманд (для будущего выполнения 2-х комманд за раз); +// ! разобраться с флагом d флагового регистра CPU, возможно он нигде не используется; +// ? обнаружено, что демки многих игр работают не так, как на PC-эмуле, что-то не так с временными задержками; +// ? на некоторых играх при сбросе несколько первых комманд распознаются как недокументированные; + +// 21.12.2006 +// + подчищен код эмуляции cpu, убраны ненужные ... & 0xFF в операциях типа A &= ... ; +// + добавлены таблички INC8 и DEC8 для быстрых 8-ми битных операций инкремента/декремента; +// + добавлена возможность чтения данных из регистра ppu 0x2004 (память спрайтов); +// + добавлена возможность чтения из регистра ppu 0x2007 значений палитры цветов; + +// 23.12.2006 +// + внедрена технология Smart CPU Acceleration (SCA), в основе которой лежит технология Finding loop; +// + всвязи с внедрением технологии Smart CPU Acceleration переписан код эмуляции момманд JSR, BNE, BEQ, BPL, BVC; +// + проведены тесты, отлажена внедренная технология, подчищен код потока эмуляции; +// ? если fps игры колеблется около 25, то постоянно запускается статистический анализатор технологии SCA; +// ? иногда fps низкий, но достаточный чтобы не запускался анализатор SCA, хотя если его запустить вручную, то fps получается выше; + +// 25.12.2006 - 26.12.2006 +// + вернута обратно отключенная в бете (05.12.2006) эмуляция звука, подправлена процедура open_apu() (убрат ... && apu_enabl); +// + исправлен старый глюк системы Error Message (04.12.2006) при котором эмуль не реагировал на OK в диалоге Error Message; +// + тексты сообщений об ошибках переписаны в новой, более стандартизированной и короткой форме; +// + исправлено ошибочное появление Resume после сохранения настроек при отсутствии загруженной игры; +// ! обратить внимание на правильную работу engine_ok, возможно улучшить алгоритм; + +// 03.01.2007 +// + наведен порядок в коде процедур видеорендеринга, выбраны общие для всех процедур имена переменных (09.12.2006); +// + откорректирован текст в About > Emulator; +// + убрана технология Smart scanline drawing, т.к. прирост fps не оправдывает затраты и плохое качество картинки; +// + добавлена "очистка" строк кадра перед их прорисовкой для повышения совместимости (Settings > Engine > Emulate disabled screen); + +// 04.01.2007 +// + переменные loc_pc, bank_base, cpu_ticks и переменные-флаги перенесены внутрь процедуры run() потока эмуляции; +// + конвертирование тайлов ускорено за счет использования таблиц conv8to16[][]; +// + сокращено и ускорено условие вызова IRQ, соответственно доработана процедура Map_HSync(); +// + добавлен Emulation thread control для удобного и правильного управления потоком эмуляции, старые алгоритмы удалены; +// + порог срабатывания статистического анализатора технологии SCA снижен до 22 fps; +// + полномасштабно подчищен исходный код эмулятора; +// ! после внедрения Emulation thread control нужна соответствующая коррекция работы apu для устранения ненужных звуков; +// ? в коде вызова NMI и IRQ используется cpu_ticks += 7 вместо cpu_ticks -= 7, но так стабильнее работает синхронизация маппера 4; + +// 05.01.2007 +// + оптимизирована по алгоритму и размеру процедура сброса ppu, декодирование тайлов вынесено в отдельную процедуру; +// + реализовано сохранение/загрузка состояния узлов nes, кроме мапперов, задействованы Save state и Load state; +// + обновлено и переписано меню настроек Settings > Other; +// + добавлена возможность автоматической загрузки состояния игры (Settings > Other > Game features > Auto-load state); +// ? есть подозрение, что верхние 4 Кб из выделяемых под name_table 8 Кб не используется; + +// 12.01.2007 - 14.01.2007 +// + добавлена автоматическая запись состояния для Auto-load state (замораживание игровой ситуации до следующего сеанса); +// + ускорены процедуры разделов CPU memory manager и PPU memory manager; +// + добавлен контроль пересечения cpu границы банка памяти (Settings > Engine > CPU emulation > Checking the bank border); +// + тип комманды Resume изменен на Command.BACK, что задействует клавишу возврата; +// + добавлены скрытые Scale LQ/HQ видеорежимы, работающие в full screen с сохранением пропорций экрана (hid_fscr); +// + добавлен скрытый режим загрузки палитры цветов, в котором цвета палитры инвертируются; + +// 15.01.2007 +// + переписан алгоритм детекта 0-го спрайта, теперь во всех видеорежимах 0-й спрайт детектится одинаково; +// + всвязи с первым пунктом подчищен код видеорежимов и главный цикл эмуляции; +// + всвязи с первым пунктом убран Settings > Engine > Video rendering > Alternative Scale LQ, т.к. от него уже нет пользы; +// + добавлена DMA задержка 512 тактов для более корректной работы 7-го маппера; +// ! всвязи с предыдущим пунктом возможно визуальное замедление скорости работы игр; +// ! детект 0-го спрайта работает по упрощенному алгоритму (не учитывается BG); + +// 16.01.2007 - 17.01.2007 +// + контроль пересечения границы банка преобразован в автоматическое перевычисление loc_pc после вызова записи в маппер; +// + всвязи с первым пунктом Settings > Engine > CPU emulation > Checking the bank border удалено; +// + введено использование долей такта cpu, что повышает точность тактировки, CPU t-states in line задается в процентах; +// + проверена и откорректирована тактировка комманд cpu, наведен порядок в коде эмуляции cpu; +// ! в игрушках на 7-м маппере наблюдается подергивание картинки, поэкспериментировать с тактировкой; + +// 19.01.2007 - 20.01.2007 +// + выделяемая для name_table память сокращена до нормального своего размера - 4096 байт (05.01.2007); +// + стандартизирована запись кода мапперов, код большинства мапперов переписан и улучшен; +// + наново переписан маппер 1, теперь он проще и обладает лучшей совместимостью, поддерживает картриджи до prg_rom = 512K; + +// 22.01.2007 +// + подобрана тактировка, обеспечивающая максимально правильное без дрожжания воспроизведение картинки игры; +// + убрана за ненадобностью опция Settings > Engine > CPU t-states, откорректировано Settings > Display > Rotate screen; +// + опция Settings > Engine > CPU emulation трансформирована в Smart CPU acceleration с более широким выбором алгоритма; +// + опция Settings > Engine > FPS limit трансформирована в Frame delay, позволяющий вносить задержку в эмуляцию кадра; +// + подчищены исходники эмулятора, проведена небольшая оптимизация в классе интерфейса; + +// 23.01.2007 - 03.02.2007 +// + для Fast fullscreen rendering откорректирован просчет времени эмуляции n-го количества строк: +// + подчищены процедуры мапперов, добавлено сохранение/восстановление состояния маппера; +// + наново переписана процедура чтения списка имен файлов из файлов-списков, работает без ограничений, новое имя с новой строки; +// + добавлено аккуратное определение строки, на которой выполняется Sprite 0 Hit; +// + доработан класс заставки, поддерживается отсутствие титульной картинки (облегченный режим); + +// 04.02.2007 - 18.02.2007 +// + написание документации к программе; +// + оффициальный выход полной версии эмулятора Nescube 1.2 full version! diff --git a/src/nescube.java b/src/nescube.java new file mode 100644 index 0000000..f5e7208 --- /dev/null +++ b/src/nescube.java @@ -0,0 +1,603 @@ +// ============================================================================ +// Nescube v1.1 beta [05.12.2006] nes gui class by Dr. Lion/RSM +// ============================================================================ +import javax.microedition.midlet.*; +import javax.microedition.lcdui.*; +import java.lang.Runtime.*; +import java.io.*; + +public class nescube extends MIDlet implements CommandListener { +// ============================================================================ +// [+] NES GUI Class Variables +// ============================================================================ + private List OpenGame, MainMenu, Settings, AboutInf; + private Form FrmDispl, FrmContr, FrmEngin, FrmSound, FrmOther, FrmAbout; + private Alert AlertMsg; + private ChoiceGroup choisegroupitem; + private TextField textfield; + private Gauge gauge; +// ---------------------------------------------------------------------------- + private Display display; + private engine nes; + private boolean emu_config; + private Displayable temp_disp = null; +// ============================================================================ +// [+] NES GUI Class Constants +// ============================================================================ + private final String PHONE_KEYS[] = { + "Joystick [UP]", "Joystick [DOWN]", "Joystick [LEFT]", "Joystick [RIGHT]", "Joystick [PRESS]", "Key [*]", "Key [#]", + "Key [0]", "Key [1]", "Key [2]", "Key [3]", "Key [4]", "Key [5]", "Key [6]", "Key [7]", "Key [8]", "Key [9]", + "Key [C] (Clear)", "Special gaming key A", "Special gaming key B", "Volume+ (Zoom+) key", "Volume- (Zoom-) key" + }; +// ---------------------------------------------------------------------------- + private final String CONTROL[] = { + "Not defined", + "Right", "Left", "Down", "Up","Select", "Start", "A (jump)", "B (fire)", + "Switch camera", "Scale mode", "Rotate", + }; +// ---------------------------------------------------------------------------- + private final String path_mainmenu = "/resource/icons/mainmenu"; + private final String path_settings = "/resource/icons/settings"; + private final String path_about = "/resource/icons/about"; + private final String path_other = "/resource/icons/other"; +// ============================================================================ +// [+] NES GUI commands for soft keys +// ============================================================================ + private Command minimize = new Command("Minimize", Command.SCREEN, 2); + private Command sstate = new Command("Save state", Command.SCREEN, 2); + private Command lstate = new Command("Load state", Command.SCREEN, 3); + private Command config = new Command("Settings", Command.SCREEN, 1); + private Command defaul = new Command("Default", Command.SCREEN, 1); + private Command resume = new Command("Resume", Command.BACK, 1); + private Command cancel = new Command("Cancel", Command.BACK, 1); + private Command reset = new Command("Reset", Command.SCREEN, 4); + private Command mmenu = new Command("Menu", Command.SCREEN, 5); + private Command save = new Command("Save", Command.SCREEN, 1); + private Command ok = new Command("OK", Command.SCREEN, 1); +// ============================================================================ +// [+] NES GUI Class Constructor +// ============================================================================ + public nescube() { + display = Display.getDisplay(this); + nes = new engine(this); + nes.addCommand(config); + nes.addCommand(sstate); + nes.addCommand(lstate); + nes.addCommand(reset ); + nes.addCommand(mmenu ); + nes.setCommandListener(this); + temp_disp = new demo(this); + } +// ============================================================================ +// [+] NES Emulator main access points +// ============================================================================ + public void startApp() { + if(temp_disp == null) ShowMainMenu(); + else display.setCurrent(temp_disp); + } + public void pauseApp() { + temp_disp = display.getCurrent(); + } + public void destroyApp(boolean unconditional) { + nes.unload_curr_rom(); + notifyDestroyed(); + } + public void returnFromDemo() { + temp_disp = null; startApp(); + } +// ============================================================================ +// [+] NES GUI show menu objects +// ============================================================================ + private void ShowMainMenu() { + String sArray[] = { "Open game", "Settings", "About", "Exit" }; + Image iArray[] = new Image[4]; + try { + iArray[0] = Image.createImage(path_mainmenu + "/open.png"); + iArray[1] = Image.createImage(path_mainmenu + "/settings.png"); + iArray[2] = Image.createImage(path_mainmenu + "/about.png"); + iArray[3] = Image.createImage(path_mainmenu + "/exit.png"); + } catch (Exception e) {} + MainMenu = new List("Main menu", Choice.IMPLICIT, sArray, iArray); + if(nes.engine_ok && nes.nes_name != null) MainMenu.addCommand(resume); // ??? + MainMenu.addCommand(minimize); + MainMenu.setCommandListener(this); + OpenGame = Settings = AboutInf = null; + FrmDispl = FrmContr = FrmEngin = FrmSound = FrmOther = null; + FrmAbout = null; + System.gc(); + display.setCurrent(MainMenu); + emu_config = false; + } +// ============================================================================ + private void ShowOpenGame() { + String sArray[] = GetFileList("/games/nes_list.txt"); + Image iArray[] = new Image[sArray.length]; + Image cart_img = null; + try { + cart_img = Image.createImage(path_other + "/cart.png"); + } catch (Exception e) {} + for(int i = 0; i < iArray.length; i++) iArray[i] = cart_img; + OpenGame = new List("Select game", Choice.IMPLICIT, sArray, iArray); + if(nes.nes_name != null) + for(int i = 0; i < OpenGame.size(); i++) + if(nes.nes_name.equals(OpenGame.getString(i))) OpenGame.setSelectedIndex(i, true); + OpenGame.addCommand(cancel); + OpenGame.setCommandListener(this); + MainMenu = Settings = AboutInf = null; + FrmDispl = FrmContr = FrmEngin = FrmSound = FrmOther = null; + FrmAbout = null; + System.gc(); + display.setCurrent(OpenGame); + } +// ============================================================================ + private void ShowSettings() { + String sArray[] = { "Display", "Control", "Engine", "Sound", "Other" }; + Image iArray[] = new Image[5]; + try { + iArray[0] = Image.createImage(path_settings + "/display.png"); + iArray[1] = Image.createImage(path_settings + "/control.png"); + iArray[2] = Image.createImage(path_settings + "/engine.png"); + iArray[3] = Image.createImage(path_settings + "/sound.png"); + iArray[4] = Image.createImage(path_settings + "/other.png"); + } catch (Exception e) {} + Settings = new List("Settings", Choice.IMPLICIT, sArray, iArray); + Settings.addCommand(cancel); + Settings.setCommandListener(this); + MainMenu = OpenGame = AboutInf = null; + FrmDispl = FrmContr = FrmEngin = FrmSound = FrmOther = null; + FrmAbout = null; + System.gc(); + display.setCurrent(Settings); + } +// ============================================================================ + private void ShowAbout() { + String sArray[] = { "Cartridge", "Emulator", "System" }; + Image iArray[] = new Image[3]; + try { + iArray[0] = Image.createImage(path_about + "/rom.png"); + iArray[1] = Image.createImage(path_about + "/help.png"); + iArray[2] = Image.createImage(path_about + "/system.png"); + } catch (Exception e) {} + AboutInf = new List("About", Choice.IMPLICIT, sArray, iArray); + AboutInf.addCommand(cancel); + AboutInf.setCommandListener(this); + MainMenu = OpenGame = Settings = null; + FrmDispl = FrmContr = FrmEngin = FrmSound = FrmOther = null; + FrmAbout = null; + System.gc(); + display.setCurrent(AboutInf); + } +// ============================================================================ + private void ShowDisplay(boolean def) { + FrmDispl = new Form("Display sets"); + { String sArray[] = nes.CAMERA; + choisegroupitem = new ChoiceGroup("Camera type", ChoiceGroup.EXCLUSIVE, sArray, null); + choisegroupitem.setSelectedIndex(def ? nes.def_vmode : nes.vmode, true); + } FrmDispl.append(choisegroupitem); + { String sArray[] = { "Disabled", "Enabled" }; + choisegroupitem = new ChoiceGroup("Fullscreen scale", ChoiceGroup.EXCLUSIVE, sArray, null); + choisegroupitem.setSelectedIndex((def ? nes.def_full_scr : nes.full_scr) ? 1 : 0, true); + } FrmDispl.append(choisegroupitem); + { String sArray[] = { "Fast", "Normal", "Slow" }; + choisegroupitem = new ChoiceGroup("Camera reaction", ChoiceGroup.EXCLUSIVE, sArray, null); + choisegroupitem.setSelectedIndex(def ? nes.def_reaction : nes.reaction, true); + } FrmDispl.append(choisegroupitem); + { String sArray[] = { "Not rotate", "Clockwise", "Counterclockwise" }; + choisegroupitem = new ChoiceGroup("Rotate screen", ChoiceGroup.EXCLUSIVE, sArray, null); + choisegroupitem.setSelectedIndex(def ? nes.def_rmode : nes.rmode, true); + } FrmDispl.append(choisegroupitem); + { String sArray[] = GetFileList("/resource/palettes/pal_list.txt"); + Image iArray[] = new Image[sArray.length]; + Image pal_img = null; + try { + pal_img = Image.createImage(path_other + "/palette.png"); + } catch (Exception e) {} + for(int i = 0; i < iArray.length; i++) iArray[i] = pal_img; + choisegroupitem = new ChoiceGroup("Palette", ChoiceGroup.EXCLUSIVE, sArray, iArray); + String pal_name = def ? nes.def_pal_name : nes.pal_name; + for(int i = 0; i < choisegroupitem.size(); i++) + if(pal_name.equals(choisegroupitem.getString(i))) choisegroupitem.setSelectedIndex(i, true); + } FrmDispl.append(choisegroupitem); + { gauge = new Gauge("Saturation, %", true, 200, 100); + gauge.setValue(def ? nes.def_saturation : nes.saturation); + } FrmDispl.append(gauge); + { gauge = new Gauge("Brightness, %", true, 200, 100); + gauge.setValue(def ? nes.def_brightness : nes.brightness); + } FrmDispl.append(gauge); + FrmDispl.addCommand(save); FrmDispl.addCommand(cancel); FrmDispl.addCommand(defaul); + FrmDispl.setCommandListener(this); + MainMenu = OpenGame = Settings = AboutInf = null; + FrmContr = FrmEngin = FrmSound = FrmOther = null; + FrmAbout = null; + System.gc(); + display.setCurrent(FrmDispl); + } +// ============================================================================ + private void ShowControl(boolean def) { + FrmContr = new Form("Control sets"); + { String sArray[] = { "Turbo A (Jump)", "Turbo B (Fire)" }; + choisegroupitem = new ChoiceGroup("Turbo buttons", ChoiceGroup.MULTIPLE, sArray, null); + boolean selected[] = { def ? nes.def_turboen_a : nes.turboen_a, def ? nes.def_turboen_b : nes.turboen_b }; + choisegroupitem.setSelectedFlags(selected); + } FrmContr.append(choisegroupitem); + for(int i = 0; i < PHONE_KEYS.length; i++) { + choisegroupitem = new ChoiceGroup(PHONE_KEYS[i], ChoiceGroup.EXCLUSIVE, CONTROL, null); + choisegroupitem.setSelectedIndex(def ? nes.def_keys_map[nes.rmode][i] : nes.keys_map[nes.rmode][i], true); + FrmContr.append(choisegroupitem); + } + FrmContr.addCommand(save); FrmContr.addCommand(cancel); FrmContr.addCommand(defaul); + FrmContr.setCommandListener(this); + MainMenu = OpenGame = Settings = AboutInf = null; + FrmDispl = FrmEngin = FrmSound = FrmOther = null; + FrmAbout = null; + System.gc(); + display.setCurrent(FrmContr); + } +// ============================================================================ + private void ShowEngine(boolean def) { + FrmEngin = new Form("Engine sets"); + { gauge = new Gauge("Skip frames", true, 8, 4); + gauge.setValue((def ? nes.def_frameskip : nes.frameskip) - 1); + } FrmEngin.append(gauge); + { String sArray[] = { "Fast fullscreen rendering", "Accurate Sprite 0 detect", "Emulate disabled screen", "224 lines in frame" }; + choisegroupitem = new ChoiceGroup("Video rendering", ChoiceGroup.MULTIPLE, sArray, null); + boolean selected[] = { + def ? nes.def_fastvideo : nes.fastvideo, def ? nes.def_accuS0hit : nes.accuS0hit, + def ? nes.def_emuscroff : nes.emuscroff, def ? nes.def_ntsc_mode : nes.ntsc_mode + }; + choisegroupitem.setSelectedFlags(selected); + } FrmEngin.append(choisegroupitem); + { String sArray[] = { "Restart on low FPS", "Without restart", "Disabled" }; + choisegroupitem = new ChoiceGroup("Smart CPU acceleration", ChoiceGroup.EXCLUSIVE, sArray, null); + choisegroupitem.setSelectedIndex(2 - (def ? nes.def_cpu_accel : nes.cpu_accel), true); + } FrmEngin.append(choisegroupitem); + { gauge = new Gauge("Frame delay", true, 20, 0); + gauge.setValue(def ? nes.def_frame_del : nes.frame_del); + } FrmEngin.append(gauge); + FrmEngin.addCommand(save); FrmEngin.addCommand(cancel); FrmEngin.addCommand(defaul); + FrmEngin.setCommandListener(this); + MainMenu = OpenGame = Settings = AboutInf = null; + FrmDispl = FrmContr = FrmSound = FrmOther = null; + FrmAbout = null; + System.gc(); + display.setCurrent(FrmEngin); + } +// ============================================================================ + private void ShowSound(boolean def) { + if(nes.midi_device) { + FrmSound = new Form("Sound sets"); + { String sArray[] = { "Disabled", "Enabled" }; + choisegroupitem = new ChoiceGroup("APU emulation", ChoiceGroup.EXCLUSIVE, sArray, null); + choisegroupitem.setSelectedIndex((def ? nes.def_apu_enabl : nes.apu_enabl) ? 1 : 0, true); + } FrmSound.append(choisegroupitem); + { String sArray[] = { "Square 1", "Square 2", "Triangle" }; + choisegroupitem = new ChoiceGroup("APU channels", ChoiceGroup.MULTIPLE, sArray, null); + boolean selected[] = { + def ? nes.def_sq1_enabl : nes.sq1_enabl, + def ? nes.def_sq2_enabl : nes.sq2_enabl, + def ? nes.def_tri_enabl : nes.tri_enabl + }; + choisegroupitem.setSelectedFlags(selected); + } FrmSound.append(choisegroupitem); + { textfield = new TextField("Triangle patch", String.valueOf(def ? nes.def_apu_tri_patch : nes.apu_tri_patch), 3, TextField.NUMERIC); + } FrmSound.append(textfield); + { textfield = new TextField("Square patch", String.valueOf(def ? nes.def_apu_sqr_patch : nes.apu_sqr_patch), 3, TextField.NUMERIC); + } FrmSound.append(textfield); + FrmSound.addCommand(save); FrmSound.addCommand(cancel); FrmSound.addCommand(defaul); + FrmSound.setCommandListener(this); + MainMenu = OpenGame = Settings = AboutInf = null; + FrmDispl = FrmContr = FrmEngin = FrmOther = null; + FrmAbout = null; + System.gc(); + display.setCurrent(FrmSound); + } else show_error("The sound output is not supported on this device"); + } +// ============================================================================ + private void ShowOther(boolean def) { + FrmOther = new Form("Other sets"); + { String sArray[] = { "Enable OSD", "Show FPS" }; + choisegroupitem = new ChoiceGroup("Video features", ChoiceGroup.MULTIPLE, sArray, null); + boolean selected[] = { + def ? nes.def_osd_enable : nes.osd_enable, def ? nes.def_fps_enable : nes.fps_enable + }; + choisegroupitem.setSelectedFlags(selected); + } FrmOther.append(choisegroupitem); + { String sArray[] = { "White", "Yellow", "Cyan", "Blue" }; + choisegroupitem = new ChoiceGroup("OSD colour", ChoiceGroup.EXCLUSIVE, sArray, null); + choisegroupitem.setSelectedIndex(def ? nes.def_osd_colour : nes.osd_colour, true); + } FrmOther.append(choisegroupitem); + { String sArray[] = { "Small", "Medium", "Large" }; + choisegroupitem = new ChoiceGroup("OSD size", ChoiceGroup.EXCLUSIVE, sArray, null); + choisegroupitem.setSelectedIndex(def ? nes.def_osd_font : nes.osd_font, true); + } FrmOther.append(choisegroupitem); + { String sArray[] = { "Keep game settings", "Unceasing play" }; + choisegroupitem = new ChoiceGroup("Game features", ChoiceGroup.MULTIPLE, sArray, null); + boolean selected[] = { + def ? nes.def_keep_sets : nes.keep_sets, def ? nes.def_auto_sload : nes.auto_sload + }; + choisegroupitem.setSelectedFlags(selected); + } FrmOther.append(choisegroupitem); + FrmOther.addCommand(save); FrmOther.addCommand(cancel); FrmOther.addCommand(defaul); + FrmOther.setCommandListener(this); + MainMenu = OpenGame = Settings = AboutInf = null; + FrmDispl = FrmContr = FrmEngin = FrmSound = null; + FrmAbout = null; + System.gc(); + display.setCurrent(FrmOther); + } +// ============================================================================ + private void ShowInformation(int index) { + String title = ""; + String text = ""; + switch (index) { + case 0: + title = "Cartridge info"; + if(nes.engine_ok && nes.nes_name != null) { // ???? + String mirroring = ""; + if(nes.mir_four) mirroring = "Four screen"; + else + if(!nes.mir_vert) mirroring = "Horizontal"; + else + if(nes.mir_vert) mirroring = "Vertical"; + text += "Cartridge: " + nes.nes_name + "\n"; + text += "Mirroring: " + mirroring + "\n"; + text += "PRG-ROM: " + nes.prg_size / 1024 + "K" + "\n"; + if(nes.chr_size != 0) + text += "CHR-ROM: " + nes.chr_size / 1024 + "K" + "\n"; + text += "Mapper: " + nes.mapper + "\n"; + text += "Save SRAM: " + (nes.bat_sram ? "Yes" : "No"); + } else + text = "There is no cartridge! You must go to \"Open game\" and select the game!"; + break; + case 1: + title = "About emulator"; + text += "Nescube v1.2" + "\n\n"; + text += "Nintendo Entertainment System emulator for mobile phones with J2ME. "; + text += "Suppurted mappers: 0, 1, 2, 3, 4, 6, 7, 8, 11, 15" + "\n\n"; + text += "Main idea, code, gfx:" + "\n"; + text += "Dr.Lion/RSM" + "\n\n"; + text += "mail: lion_rsm@mail.ru" + "\n"; + text += "web: http://rsm.pud.ru" + "\n"; + text += "icq: 347279524" + "\n\n"; + text += "Real Soft Makers 2007"; + break; + case 2: + Runtime r = Runtime.getRuntime(); + long total = (r.totalMemory() + 512) / 1024; + long free = (r.freeMemory() + 512) / 1024; + title = "System info"; + text += "J2ME platform: " + System.getProperty("microedition.platform") + "\n"; + text += "Total memory: " + total + "K\n"; + text += "Used memory: " + (total - free) + "K\n"; + break; + + + } + StringItem stringitem = new StringItem(null, text); + stringitem.setFont(Font.getFont(Font.FACE_MONOSPACE, Font.STYLE_PLAIN, Font.SIZE_SMALL)); + FrmAbout = new Form(title); + FrmAbout.append(stringitem); + FrmAbout.addCommand(ok); + FrmAbout.setCommandListener(this); + MainMenu = OpenGame = Settings = AboutInf = null; + FrmDispl = FrmContr = FrmEngin = FrmSound = FrmOther = null; + System.gc(); + display.setCurrent(FrmAbout); + } +// ============================================================================ + private void ShowNes() { + OpenGame = MainMenu = Settings = AboutInf = null; + FrmDispl = FrmContr = FrmEngin = FrmSound = FrmOther = null; + FrmAbout = null; + System.gc(); + if(nes.engine_ok) display.setCurrent(nes); + emu_config = true; + } +// ============================================================================ +// [+] Put variables to NES hardware emulator +// ============================================================================ + private void SaveSetsDisplay() { + choisegroupitem = (ChoiceGroup)(FrmDispl.get(0)); nes.vmode = choisegroupitem.getSelectedIndex(); + choisegroupitem = (ChoiceGroup)(FrmDispl.get(1)); nes.full_scr = choisegroupitem.getSelectedIndex() == 1; + choisegroupitem = (ChoiceGroup)(FrmDispl.get(2)); nes.reaction = choisegroupitem.getSelectedIndex(); + choisegroupitem = (ChoiceGroup)(FrmDispl.get(3)); nes.rmode = choisegroupitem.getSelectedIndex(); + choisegroupitem = (ChoiceGroup)(FrmDispl.get(4)); nes.pal_name = choisegroupitem.getString(choisegroupitem.getSelectedIndex()); + gauge = (Gauge)(FrmDispl.get(5)); nes.saturation = gauge.getValue(); + gauge = (Gauge)(FrmDispl.get(6)); nes.brightness = gauge.getValue(); + nes.apply_settings(); + if(nes.engine_ok) if(emu_config) ShowNes(); else ShowMainMenu(); + } +// ============================================================================ + private void SaveSetsControl() { + boolean selected[] = null; + choisegroupitem = (ChoiceGroup)(FrmContr.get(0)); selected = new boolean[choisegroupitem.size()]; + choisegroupitem.getSelectedFlags(selected); + nes.turboen_a = selected[0]; + nes.turboen_b = selected[1]; + for(int i = 0; i < PHONE_KEYS.length; i++) { + choisegroupitem = (ChoiceGroup)(FrmContr.get(1 + i)); + nes.keys_map[nes.rmode][i] = (byte)choisegroupitem.getSelectedIndex(); + } + nes.apply_settings(); + if(nes.engine_ok) if(emu_config) ShowNes(); else ShowMainMenu(); + } +// ============================================================================ + private void SaveSetsEngine() { + boolean selected[] = null; + gauge = (Gauge)(FrmEngin.get(0)); nes.frameskip = gauge.getValue() + 1; + choisegroupitem = (ChoiceGroup)(FrmEngin.get(1)); selected = new boolean[choisegroupitem.size()]; + choisegroupitem.getSelectedFlags(selected); + nes.fastvideo = selected[0]; + nes.accuS0hit = selected[1]; + nes.emuscroff = selected[2]; + nes.ntsc_mode = selected[3]; + choisegroupitem = (ChoiceGroup)(FrmEngin.get(2)); nes.cpu_accel = 2 - choisegroupitem.getSelectedIndex(); + gauge = (Gauge)(FrmEngin.get(3)); nes.frame_del = gauge.getValue(); + nes.apply_settings(); + if(nes.engine_ok) if(emu_config) ShowNes(); else ShowMainMenu(); + } +// ============================================================================ + private void SaveSetsSound() { + boolean selected[] = null; + choisegroupitem = (ChoiceGroup)(FrmSound.get(0)); nes.apu_enabl = choisegroupitem.getSelectedIndex() == 1; + choisegroupitem = (ChoiceGroup)(FrmSound.get(1)); selected = new boolean[choisegroupitem.size()]; + choisegroupitem.getSelectedFlags(selected); + nes.sq1_enabl = selected[0]; + nes.sq2_enabl = selected[1]; + nes.tri_enabl = selected[2]; + textfield = (TextField)(FrmSound.get(2)); nes.apu_tri_patch = Integer.parseInt(textfield.getString()); + textfield = (TextField)(FrmSound.get(3)); nes.apu_sqr_patch = Integer.parseInt(textfield.getString()); + nes.apply_settings(); + if(nes.engine_ok) if(emu_config) ShowNes(); else ShowMainMenu(); + } +// ============================================================================ + private void SaveSetsOther() { + boolean selected[] = null; + choisegroupitem = (ChoiceGroup)(FrmOther.get(0)); selected = new boolean[choisegroupitem.size()]; + choisegroupitem.getSelectedFlags(selected); + nes.osd_enable = selected[0]; + nes.fps_enable = selected[1]; + choisegroupitem = (ChoiceGroup)(FrmOther.get(1)); nes.osd_colour = choisegroupitem.getSelectedIndex(); + choisegroupitem = (ChoiceGroup)(FrmOther.get(2)); nes.osd_font = choisegroupitem.getSelectedIndex(); + choisegroupitem = (ChoiceGroup)(FrmOther.get(3)); selected = new boolean[choisegroupitem.size()]; + choisegroupitem.getSelectedFlags(selected); + nes.keep_sets = selected[0]; + nes.auto_sload = selected[1]; + nes.apply_settings(); + if(nes.engine_ok) if(emu_config) ShowNes(); else ShowMainMenu(); + } +// ============================================================================ +// [+] NES GUI other procedures +// ============================================================================ + private String[] GetFileList(String listfile) { + String FileList[] = null; InputStream is = null; int i, k; + try { +// проверка на наличие файла-списка + is = this.getClass().getResourceAsStream(listfile); + if(is == null) throw new IOException("\n\n" + listfile + "\n\n" + "File not found"); +// подсчет количества имен с файле-списке + for(i = k = 0; k != -1; k = is.read()) if(k > 32) { i++; while((k = is.read()) > 31) {} } + if(i == 0) throw new IOException("\n\n" + listfile +"\n\n" + "Wrong file format"); +// чтение имен из файла-списка с их размещением в массиве + is = this.getClass().getResourceAsStream(listfile); + FileList = new String[i]; + for(i = k = 0; k != -1; k = is.read()) + if(k > 32) { + StringBuffer sb = new StringBuffer(); + do sb.append((char)k); while((k = is.read()) > 31); + FileList[i++] = new String(sb); + } + } catch (Exception e) { show_error(e.toString()); } + return FileList; + } +// ============================================================================ + public void show_error(String err_msg) { + AlertMsg = null; + AlertMsg = new Alert("Error message", err_msg + "!", null, AlertType.ERROR); + AlertMsg.setTimeout(Alert.FOREVER); + AlertMsg.addCommand(ok); + AlertMsg.setCommandListener(this); + display.setCurrent(AlertMsg); + } +// ============================================================================ +// NES GUI Processor +// ============================================================================ + public void commandAction(Command c, Displayable d) { + if(d.equals(nes)) { + if(c == config) ShowSettings(); + if(c == sstate) nes.SaveState(0); + if(c == lstate) nes.LoadState(0); + if(c == reset ) nes.reset_system(); + if(c == mmenu ) ShowMainMenu(); + } else +// ---------------------------------------------------------------------------- + if(d.equals(MainMenu)) { + if(c == List.SELECT_COMMAND) { + switch (MainMenu.getSelectedIndex()) { + case 0: ShowOpenGame(); + break; + case 1: ShowSettings(); + break; + case 2: ShowAbout(); + break; + case 3: destroyApp(false); + break; + } + } + if(c == minimize) { + temp_disp = display.getCurrent(); + display.setCurrent(null); + } + if(c == resume) ShowNes(); + } else +// ---------------------------------------------------------------------------- + if(d.equals(OpenGame)) { + if(c == List.SELECT_COMMAND) { + nes.start_emulation(OpenGame.getString(OpenGame.getSelectedIndex())); + ShowNes(); + } + if(c == cancel) ShowMainMenu(); + } else +// ---------------------------------------------------------------------------- + if(d.equals(Settings)) { + if(c == List.SELECT_COMMAND) { + switch (Settings.getSelectedIndex()) { + case 0: ShowDisplay(false); break; + case 1: ShowControl(false); break; + case 2: ShowEngine(false); break; + case 3: ShowSound(false); break; + case 4: ShowOther(false); break; + } + } + if(c == cancel) + if(emu_config) ShowNes(); else ShowMainMenu(); + } else +// ---------------------------------------------------------------------------- + if(d.equals(FrmDispl)) { + if(c == save ) SaveSetsDisplay(); + if(c == cancel) ShowSettings(); + if(c == defaul) ShowDisplay(true); + } else +// ---------------------------------------------------------------------------- + if(d.equals(FrmContr)) { + if(c == save ) SaveSetsControl(); + if(c == cancel) ShowSettings(); + if(c == defaul) ShowControl(true); + } else +// ---------------------------------------------------------------------------- + if(d.equals(FrmEngin)) { + if(c == save ) SaveSetsEngine(); + if(c == cancel) ShowSettings(); + if(c == defaul) ShowEngine(true); + } else +// ---------------------------------------------------------------------------- + if(d.equals(FrmSound)) { + if(c == save ) SaveSetsSound(); + if(c == cancel) ShowSettings(); + if(c == defaul) ShowSound(true); + } else +// ---------------------------------------------------------------------------- + if(d.equals(FrmOther)) { + if(c == save ) SaveSetsOther(); + if(c == cancel) ShowSettings(); + if(c == defaul) ShowOther(true); + } else +// ---------------------------------------------------------------------------- + if(d.equals(AboutInf)) { + if(c == List.SELECT_COMMAND) + ShowInformation(AboutInf.getSelectedIndex()); + if(c == cancel) ShowMainMenu(); + } else +// ---------------------------------------------------------------------------- + if(d.equals(FrmAbout)) { + if(c == ok) ShowAbout(); + } else +// ---------------------------------------------------------------------------- + if(d.equals(AlertMsg)) { + if(c == ok) { + ShowMainMenu(); + } + } + } +// ============================================================================ +}