From 938609818fb459cca1825b8f964b52f9f38c2e74 Mon Sep 17 00:00:00 2001 From: byuu <byuu@byuu.org> Date: Tue, 7 Dec 2004 01:12:32 +0000 Subject: [PATCH] Update to bsnes v002 ir9 release. [No changelog available] --- bsnes/Makefile | 34 + bsnes/base.h | 332 ++++++++ bsnes/bsnes.exe | Bin 0 -> 233472 bytes bsnes/cc.bat | 2 + bsnes/clean.bat | 1 + bsnes/cpu/d65816.cpp | 337 ++++++++ bsnes/cpu/g65816.cpp | 234 ++++++ bsnes/cpu/g65816.h | 69 ++ bsnes/cpu/g65816_ops.cpp | 161 ++++ bsnes/cpu/g65816_ops_adc.cpp | 345 ++++++++ bsnes/cpu/g65816_ops_and.cpp | 335 ++++++++ bsnes/cpu/g65816_ops_cmp.cpp | 321 ++++++++ bsnes/cpu/g65816_ops_eor.cpp | 335 ++++++++ bsnes/cpu/g65816_ops_incdec.cpp | 355 +++++++++ bsnes/cpu/g65816_ops_lda.cpp | 335 ++++++++ bsnes/cpu/g65816_ops_misc.cpp | 1294 +++++++++++++++++++++++++++++++ bsnes/cpu/g65816_ops_ora.cpp | 335 ++++++++ bsnes/cpu/g65816_ops_pc.cpp | 223 ++++++ bsnes/cpu/g65816_ops_sbc.cpp | 345 ++++++++ bsnes/cpu/g65816_ops_shift.cpp | 595 ++++++++++++++ bsnes/cpu/g65816_ops_sta.cpp | 288 +++++++ bsnes/cpu/g65816_ops_stack.cpp | 243 ++++++ bsnes/d65816.obj | Bin 0 -> 84902 bytes bsnes/demo.smc | Bin 0 -> 131584 bytes bsnes/demo.srm | Bin 0 -> 8192 bytes bsnes/demo_mode1.smc | Bin 0 -> 131584 bytes bsnes/demo_mode1.srm | Bin 0 -> 8192 bytes bsnes/demo_mode4.smc | Bin 0 -> 131584 bytes bsnes/demo_mode4.srm | Bin 0 -> 8192 bytes bsnes/g65816.obj | Bin 0 -> 289767 bytes bsnes/gui.obj | Bin 0 -> 67128 bytes bsnes/libstr.obj | Bin 0 -> 38996 bytes bsnes/main.cpp | 97 +++ bsnes/main.h | 34 + bsnes/main.obj | Bin 0 -> 11653 bytes bsnes/mem/memory.cpp | 516 ++++++++++++ bsnes/memory.obj | Bin 0 -> 11196 bytes bsnes/misc/libstr.cpp | 393 ++++++++++ bsnes/misc/libstr_math.cpp | 181 +++++ bsnes/misc/libstr_replace.cpp | 82 ++ bsnes/misc/libstr_split.cpp | 41 + bsnes/misc/libstr_sprintf.cpp | 130 ++++ bsnes/misc/libvlist.cpp | 55 ++ bsnes/mmio.obj | Bin 0 -> 81207 bytes bsnes/ppu/mmio.cpp | 198 +++++ bsnes/ppu/ppu.cpp | 476 ++++++++++++ bsnes/ppu/ppu_addsub.cpp | 60 ++ bsnes/ppu/ppu_cache.cpp | 49 ++ bsnes/ppu/ppu_dma.cpp | 485 ++++++++++++ bsnes/ppu/ppu_joypad.cpp | 67 ++ bsnes/ppu/ppu_misc.cpp | 15 + bsnes/ppu/ppu_mode7.cpp | 74 ++ bsnes/ppu/ppu_muldiv.cpp | 57 ++ bsnes/ppu/ppu_oam.cpp | 86 ++ bsnes/ppu/ppu_palette.cpp | 36 + bsnes/ppu/ppu_render.cpp | 753 ++++++++++++++++++ bsnes/ppu/ppu_render_mode7f.cpp | 74 ++ bsnes/ppu/ppu_render_mode7i.cpp | 66 ++ bsnes/ppu/ppu_screen.cpp | 92 +++ bsnes/ppu/ppu_scroll.cpp | 46 ++ bsnes/ppu/ppu_timing.cpp | 160 ++++ bsnes/ppu/ppu_vram.cpp | 131 ++++ bsnes/ppu/ppu_window.cpp | 121 +++ bsnes/ppu/ppu_wram.cpp | 24 + bsnes/qc.bat | 3 + bsnes/timing.obj | Bin 0 -> 3162 bytes bsnes/timing/timing.cpp | 103 +++ bsnes/timing/timing.h | 23 + bsnes/win/gui.cpp | 434 +++++++++++ bsnes/win/gui_bgtoggle.cpp | 149 ++++ bsnes/win/gui_bp.cpp | 131 ++++ bsnes/win/gui_cpu.cpp | 208 +++++ bsnes/win/gui_mem.cpp | 252 ++++++ bsnes/win/render.cpp | 269 +++++++ bsnes/win/render_modes.cpp | 303 ++++++++ 75 files changed, 12993 insertions(+) create mode 100644 bsnes/Makefile create mode 100644 bsnes/base.h create mode 100644 bsnes/bsnes.exe create mode 100644 bsnes/cc.bat create mode 100644 bsnes/clean.bat create mode 100644 bsnes/cpu/d65816.cpp create mode 100644 bsnes/cpu/g65816.cpp create mode 100644 bsnes/cpu/g65816.h create mode 100644 bsnes/cpu/g65816_ops.cpp create mode 100644 bsnes/cpu/g65816_ops_adc.cpp create mode 100644 bsnes/cpu/g65816_ops_and.cpp create mode 100644 bsnes/cpu/g65816_ops_cmp.cpp create mode 100644 bsnes/cpu/g65816_ops_eor.cpp create mode 100644 bsnes/cpu/g65816_ops_incdec.cpp create mode 100644 bsnes/cpu/g65816_ops_lda.cpp create mode 100644 bsnes/cpu/g65816_ops_misc.cpp create mode 100644 bsnes/cpu/g65816_ops_ora.cpp create mode 100644 bsnes/cpu/g65816_ops_pc.cpp create mode 100644 bsnes/cpu/g65816_ops_sbc.cpp create mode 100644 bsnes/cpu/g65816_ops_shift.cpp create mode 100644 bsnes/cpu/g65816_ops_sta.cpp create mode 100644 bsnes/cpu/g65816_ops_stack.cpp create mode 100644 bsnes/d65816.obj create mode 100644 bsnes/demo.smc create mode 100644 bsnes/demo.srm create mode 100644 bsnes/demo_mode1.smc create mode 100644 bsnes/demo_mode1.srm create mode 100644 bsnes/demo_mode4.smc create mode 100644 bsnes/demo_mode4.srm create mode 100644 bsnes/g65816.obj create mode 100644 bsnes/gui.obj create mode 100644 bsnes/libstr.obj create mode 100644 bsnes/main.cpp create mode 100644 bsnes/main.h create mode 100644 bsnes/main.obj create mode 100644 bsnes/mem/memory.cpp create mode 100644 bsnes/memory.obj create mode 100644 bsnes/misc/libstr.cpp create mode 100644 bsnes/misc/libstr_math.cpp create mode 100644 bsnes/misc/libstr_replace.cpp create mode 100644 bsnes/misc/libstr_split.cpp create mode 100644 bsnes/misc/libstr_sprintf.cpp create mode 100644 bsnes/misc/libvlist.cpp create mode 100644 bsnes/mmio.obj create mode 100644 bsnes/ppu/mmio.cpp create mode 100644 bsnes/ppu/ppu.cpp create mode 100644 bsnes/ppu/ppu_addsub.cpp create mode 100644 bsnes/ppu/ppu_cache.cpp create mode 100644 bsnes/ppu/ppu_dma.cpp create mode 100644 bsnes/ppu/ppu_joypad.cpp create mode 100644 bsnes/ppu/ppu_misc.cpp create mode 100644 bsnes/ppu/ppu_mode7.cpp create mode 100644 bsnes/ppu/ppu_muldiv.cpp create mode 100644 bsnes/ppu/ppu_oam.cpp create mode 100644 bsnes/ppu/ppu_palette.cpp create mode 100644 bsnes/ppu/ppu_render.cpp create mode 100644 bsnes/ppu/ppu_render_mode7f.cpp create mode 100644 bsnes/ppu/ppu_render_mode7i.cpp create mode 100644 bsnes/ppu/ppu_screen.cpp create mode 100644 bsnes/ppu/ppu_scroll.cpp create mode 100644 bsnes/ppu/ppu_timing.cpp create mode 100644 bsnes/ppu/ppu_vram.cpp create mode 100644 bsnes/ppu/ppu_window.cpp create mode 100644 bsnes/ppu/ppu_wram.cpp create mode 100644 bsnes/qc.bat create mode 100644 bsnes/timing.obj create mode 100644 bsnes/timing/timing.cpp create mode 100644 bsnes/timing/timing.h create mode 100644 bsnes/win/gui.cpp create mode 100644 bsnes/win/gui_bgtoggle.cpp create mode 100644 bsnes/win/gui_bp.cpp create mode 100644 bsnes/win/gui_cpu.cpp create mode 100644 bsnes/win/gui_mem.cpp create mode 100644 bsnes/win/render.cpp create mode 100644 bsnes/win/render_modes.cpp diff --git a/bsnes/Makefile b/bsnes/Makefile new file mode 100644 index 00000000..c037c5e1 --- /dev/null +++ b/bsnes/Makefile @@ -0,0 +1,34 @@ +CC = cl +CFLAGS = /nologo /O2 +OBJS = main.obj timing.obj g65816.obj d65816.obj memory.obj mmio.obj gui.obj libstr.obj +LIBS = kernel32.lib user32.lib gdi32.lib comdlg32.lib ddraw.lib + +all: $(OBJS) + $(CC) /Febsnes.exe $(CFLAGS) $(OBJS) $(LIBS) + +clean: + del *.obj + +main.obj: main.cpp main.h + $(CC) $(CFLAGS) /c main.cpp + +timing.obj: timing/timing.cpp timing/timing.h + $(CC) $(CFLAGS) /c timing/timing.cpp + +g65816.obj: cpu/g65816*.cpp cpu/g65816.h + $(CC) $(CFLAGS) /c cpu/g65816.cpp + +d65816.obj: cpu/d65816.cpp + $(CC) $(CFLAGS) /c cpu/d65816.cpp + +memory.obj: mem/memory.cpp + $(CC) $(CFLAGS) /c mem/memory.cpp + +mmio.obj: ppu/mmio.cpp ppu/ppu*.cpp + $(CC) $(CFLAGS) /c ppu/mmio.cpp + +gui.obj: win/gui*.cpp win/render*.cpp + $(CC) $(CFLAGS) /c win/gui.cpp + +libstr.obj: misc/libstr.cpp + $(CC) $(CFLAGS) /c misc/libstr.cpp diff --git a/bsnes/base.h b/bsnes/base.h new file mode 100644 index 00000000..32dbfd6a --- /dev/null +++ b/bsnes/base.h @@ -0,0 +1,332 @@ +//#define PUBLIC_DOMAIN + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> + +#define null 0xffffffff +typedef unsigned char byte; +typedef unsigned short word; +typedef unsigned long ulong; +typedef void (*vfunc)(void); + +#define SH_2 1 +#define SH_4 2 +#define SH_8 3 +#define SH_16 4 +#define SH_32 5 +#define SH_64 6 +#define SH_128 7 +#define SH_256 8 + +/************************* + *** general functions *** + ************************/ + +void alert(char *s, ...); + +/************************ + *** joypad functions *** + ***********************/ + +void UpdateJoypad(void); + +typedef struct { + byte a, b, x, y; + byte l, r; + byte select, start; + byte up, down, left, right; + + byte read_pos; +}joypad_state; + +/****************** + *** deprecated *** + *****************/ + +typedef struct { + char rom_name[4096], sram_name[4096]; + ulong sram_save_tick_count; +}emustate; + +/*********************** + *** video functions *** + **********************/ + +void video_setmode(bool fullscreen, word width, word height); +void video_setsnesmode(void); + +//global export: render +typedef struct { + word width, height; + word snes_width, snes_height; + bool fullscreen; + bool show_menu; + + byte frame_skip; + byte frame_count; + + bool bg1_enabled[3], bg2_enabled[3], bg3_enabled[3], bg4_enabled[3], oam_enabled[5]; +}videostate; + +/*************************** + *** debugging functions *** + **************************/ + +#define DEBUGMODE_NOROM 0 +#define DEBUGMODE_DISABLED 1 +#define DEBUGMODE_WAIT 2 +#define DEBUGMODE_RUN 3 +#define DEBUGMODE_STEP 4 +#define DEBUGMODE_PROCEED 5 + +#define DEBUG_BGENABLED_ALL 0 +#define DEBUG_BGENABLED_PRI0 1 +#define DEBUG_BGENABLED_PRI1 2 +#define DEBUG_BGENABLED_PRI2 3 +#define DEBUG_BGENABLED_PRI3 4 + +void debug_set_state(byte state); +#define debug_get_state() debugger.mode +void dprintf(char *s, ...); +void debug_refresh_mem(void); +void debug_refresh_bp(void); +void debug_update_cycles(void); + +typedef struct { + byte mode; + + bool trace_enabled; + ulong mem_ptr; //position of wram ptr for debug window + bool disas_op; + bool refresh_mem; + bool refresh_bp; + + FILE *trace_fp; +}debugstate; + +/********************* + *** ppu functions *** + ********************/ + +#define BG1 0x00 +#define BG2 0x01 +#define BG3 0x02 +#define BG4 0x03 +#define OAM 0x04 +#define SS_BG1 0x80 +#define SS_BG2 0x81 +#define SS_BG3 0x82 +#define SS_BG4 0x83 +#define SS_OAM 0x84 +#define BACK 0x05 + +#define CLIPMODE_IN 0 +#define CLIPMODE_OUT 1 + +#define WINDOWMASK_OR 0 +#define WINDOWMASK_AND 1 +#define WINDOWMASK_XOR 2 +#define WINDOWMASK_XNOR 3 + +#define COLORMODE_ADD 0 +#define COLORMODE_SUB 1 + +typedef struct { + word *screen; //internal buffer used to render 512x448 screen + byte *vram; //65536 bytes + byte *cgram; //512 bytes + byte *oam; //544 bytes mirrored as 1024 bytes (512-543 are mirrored to 544-1023) + word *light_table; //16 * 32768 -- applies display_brightness to bgr555 color data +//ppu.mosaic_table[ppu.mosaic_size][x] == (x / (ppu.mosaic_size + 1)) * (ppu.mosaic_size + 1) + word *mosaic_table[16]; + + ulong ppu_cycles, ppu_prev_cycles; //used in ppu_update_scanline() + + bool display_disable; //$2100 bit 7 + byte display_brightness; //$2100 bits 0-3 + + byte visible_scanlines; //$2133 bit 2 (NTSC/PAL mode) -- value = 224 or 240 + byte toggle_visible_scanlines; //do not allow change midframe + + bool interlace; + byte interlace_frame; //0 or 1, used to alternate between scanlines rendered + + bool bg_enabled[5]; + bool ss_bg_enabled[5]; + byte mosaic_size; + bool mosaic_enabled[4]; + word bg_tilemap_loc[4]; + byte bg_tile_size[4]; + byte bg_tilemap_size[4]; + word bg_tiledata_loc[4]; + word oam_tiledata_loc; + byte bg_priority_mode; + byte oam_base_size; + byte oam_name_sel; + byte bg_mode; + + word hline_pos; //0-255: inside vblank, 256-339: outside vblank + word vline_pos; //0-223: inside vblank, 224-261: outside vblank, 180 cycles/scanline + bool irq_triggered; //for $4211 read + bool virq_triggered; //prevent recursive calling + bool hirq_triggered; //prevent recursive calling + word vram_write_pos; //holds value at $2116 + word vram_inc_size; //amount to increment vram_write_pos by + byte vram_inc_reg; //increment on 2118 (0) or 2119 (1) + word cgram_write_pos; //holds value at $2121 (multiplied by 2) + word oam_write_pos; //holds value at $2102/$2103 (multiplied by 2) + ulong wram_write_pos; //holds value at $2181-$2183 + + word bg_hscroll_pos[4]; //$210d-$2114 + word bg_vscroll_pos[4]; + word m7hofs, m7vofs; + + byte mul_a, mul_b; + word div_a; + byte div_b; + word r_4214, r_4216; + + word smul_a; + byte smul_b; + ulong smul_r; + + bool bg_window1_enabled[5]; + bool bg_window2_enabled[5]; + byte bg_window1_clipmode[5]; + byte bg_window2_clipmode[5]; + + byte window1_left, window1_right; + byte window2_left, window2_right; + + bool bg_windowing_enabled[5]; + bool ss_bg_windowing_enabled[5]; + byte bg_window_mask[5]; + + bool color_window1_enabled, color_window2_enabled; + bool color_window1_clipmode, color_window2_clipmode; + byte color_window_mask; + byte color_mask, ss_color_mask; + byte addsub_mode; + byte color_mode, color_halve; + bool bg_color_enabled[6]; + byte color_r, color_g, color_b; + + byte toggle_active_hdma_channels; + byte active_hdma_channels; + word hdma_scanlines_remaining[8]; + word hdma_index_pointer[8]; + bool hdma_completed[8]; + + bool vcounter_enabled, hcounter_enabled; + word hirq_pos, virq_pos; + + bool auto_joypad_read; + byte joypad_strobe_value; + + byte latch_toggle; + word latch_vpos, latch_hpos; + + word m7a, m7b, m7c, m7d, m7x, m7y; + + bool mode7_vflip, mode7_hflip; +}ppustate; + +/********************* + *** cpu functions *** + ********************/ + +#define MEMSPEED_SLOWROM 0 +#define MEMSPEED_FASTROM 1 + +#define PF_N 0x80 +#define PF_V 0x40 +#define PF_M 0x20 +#define PF_X 0x10 +#define PF_D 0x08 +#define PF_I 0x04 +#define PF_Z 0x02 +#define PF_C 0x01 + +#define BP_OFF 0 +#define BP_READ 1 +#define BP_WRITE 2 +#define BP_EXEC 4 +#define BP_VAL 8 + +/************************ + *** memory functions *** + ***********************/ + +#define MEMMAP_HIROM 0x01 +#define MEMMAP_LOROM 0x02 + +#define MEMACCESS_NORMAL 0 +#define MEMACCESS_CPU 1 +#define MEMACCESS_DEBUGGER 1 + +enum { + MEMMODE_NONE = 0, //(no address translation) + MEMMODE_DP, //dp + MEMMODE_DPX, //dp,x + MEMMODE_DPY, //dp,y + MEMMODE_IDP, //(dp) + MEMMODE_IDPX, //(dp,x) + MEMMODE_IDPY, //(dp),y + MEMMODE_ILDP, //[dp] + MEMMODE_ILDPY, //[dp],y + MEMMODE_ADDR, //addr + MEMMODE_ADDRX, //addr,x + MEMMODE_ADDRY, //addr,y + MEMMODE_IADDRX, //(addr,x) + MEMMODE_ILADDR, //[addr] + MEMMODE_LONG, //long + MEMMODE_LONGX, //long, x + MEMMODE_SR, //sr,s + MEMMODE_ISRY, //(sr,s),y +//exchanges pbr for dbr, disables address mirroring + MEMMODE_ADDR_PC, //addr + MEMMODE_IADDR_PC, //(addr) + MEMMODE_IADDRX_PC, //(addr,x) + MEMMODE_ILADDR_PC //[addr] +}; + +#define MEMSIZE_BYTE 1 +#define MEMSIZE_WORD 2 +#define MEMSIZE_LONG 3 + +/*********************** + *** misc. functions *** + **********************/ + +//main.cpp +void InitSNES(void); +extern vfunc RunSNES; +void RunSNES_NoDebug(void); +void RunSNES_Debug(void); + +//cpu/g65816_ops_stack.cpp +ulong g65816_stackread(byte size); +void g65816_stackwrite(byte size, ulong value); + +//cpu/d65816.cpp +void disas_g65816_op(void); + +//ppu/mmio.cpp +byte mmio_read(word addr); +void mmio_write(word addr, byte value); + +//ppu/ppu.cpp +void ppu_render_scanline(void); +void ppu_update_scanline(void); +byte oam_read(word addr); +void oam_write(word addr, byte value); +void PPUInit(byte first_time); + +//win/render.cpp +void UpdateDisplay(void); + +//misc/libstr.cpp +ulong strhex(char *str); +ulong strdec(char *str); diff --git a/bsnes/bsnes.exe b/bsnes/bsnes.exe new file mode 100644 index 0000000000000000000000000000000000000000..551d2a4a9a340a95fe3d19ca462e13ae619e0f61 GIT binary patch literal 233472 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 literal 0 HcmV?d00001 diff --git a/bsnes/cc.bat b/bsnes/cc.bat new file mode 100644 index 00000000..5a1afd6a --- /dev/null +++ b/bsnes/cc.bat @@ -0,0 +1,2 @@ +@nmake /NOLOGO +@pause \ No newline at end of file diff --git a/bsnes/clean.bat b/bsnes/clean.bat new file mode 100644 index 00000000..02d7e1df --- /dev/null +++ b/bsnes/clean.bat @@ -0,0 +1 @@ +@nmake /NOLOGO clean \ No newline at end of file diff --git a/bsnes/cpu/d65816.cpp b/bsnes/cpu/d65816.cpp new file mode 100644 index 00000000..35f12cf2 --- /dev/null +++ b/bsnes/cpu/d65816.cpp @@ -0,0 +1,337 @@ +#include "../base.h" +#include "g65816.h" +extern g65816 *gx816; + +ulong _disas_relb(byte addr) { + return gx816->regs.pc + (signed char)(addr + 2); +} + +ulong _disas_relw(word addr) { + return gx816->regs.pc + (signed short)(addr + 3); +} + +char __disas_op_str[256]; +void __disas_op(byte op, byte op0, byte op1, byte op2) { +char *s = (char*)__disas_op_str; + switch(op) { + case 0x00:sprintf(s, "brk #$%0.2x ", op0);break; + case 0x01:sprintf(s, "ora ($%0.2x,x) [$%0.6x]", op0, gx816->convert_offset(MEMMODE_IDPX, op0));break; + case 0x02:sprintf(s, "cop #$%0.2x ", op0);break; + case 0x03:sprintf(s, "ora $%0.2x,s [$%0.6x]", op0, gx816->convert_offset(MEMMODE_SR, op0));break; + case 0x04:sprintf(s, "tsb $%0.2x [$%0.6x]", op0, gx816->convert_offset(MEMMODE_DP, op0));break; + case 0x05:sprintf(s, "ora $%0.2x [$%0.6x]", op0, gx816->convert_offset(MEMMODE_DP, op0));break; + case 0x06:sprintf(s, "asl $%0.2x [$%0.6x]", op0, gx816->convert_offset(MEMMODE_DP, op0));break; + case 0x07:sprintf(s, "ora [$%0.2x] [$%0.6x]", op0, gx816->convert_offset(MEMMODE_ILDP, op0));break; + case 0x08:sprintf(s, "php ");break; + case 0x09: + if(gx816->regs.e == true || (gx816->regs.e == false && (gx816->regs.p & 0x20)))sprintf(s, "ora #$%0.2x ", op0); + else sprintf(s, "ora #$%0.4x ", op0|op1<<8);break; + case 0x0a:sprintf(s, "asl a ");break; + case 0x0b:sprintf(s, "phd ");break; + case 0x0c:sprintf(s, "tsb $%0.4x [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDR, (op0|op1<<8)));break; + case 0x0d:sprintf(s, "ora $%0.4x [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDR, (op0|op1<<8)));break; + case 0x0e:sprintf(s, "asl $%0.4x [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDR, (op0|op1<<8)));break; + case 0x0f:sprintf(s, "ora $%0.6x [$%0.6x]", op0|op1<<8|op2<<16, gx816->convert_offset(MEMMODE_LONG, (op0|op1<<8|op2<<16)));break; + case 0x10:sprintf(s, "bpl $%0.4x [$%0.6x]", _disas_relb(op0)&0xffff, _disas_relb(op0)&0xffffff);break; + case 0x11:sprintf(s, "ora ($%0.2x),y [$%0.6x]", op0, gx816->convert_offset(MEMMODE_IDPY, op0));break; + case 0x12:sprintf(s, "ora ($%0.2x) [$%0.6x]", op0, gx816->convert_offset(MEMMODE_IDP, op0));break; + case 0x13:sprintf(s, "ora ($%0.2x,s),y [$%0.6x]", op0, gx816->convert_offset(MEMMODE_ISRY, op0));break; + case 0x14:sprintf(s, "trb $%0.2x [$%0.6x]", op0, gx816->convert_offset(MEMMODE_DP, op0));break; + case 0x15:sprintf(s, "ora $%0.2x,x [$%0.6x]", op0, gx816->convert_offset(MEMMODE_DPX, op0));break; + case 0x16:sprintf(s, "asl $%0.2x,x [$%0.6x]", op0, gx816->convert_offset(MEMMODE_DPX, op0));break; + case 0x17:sprintf(s, "ora [$%0.2x],y [$%0.6x]", op0, gx816->convert_offset(MEMMODE_ILDPY, op0));break; + case 0x18:sprintf(s, "clc ");break; + case 0x19:sprintf(s, "ora $%0.4x,y [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDRY, (op0|op1<<8)));break; + case 0x1a:sprintf(s, "inc ");break; + case 0x1b:sprintf(s, "tcs ");break; + case 0x1c:sprintf(s, "trb $%0.4x [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDR, (op0|op1<<8)));break; + case 0x1d:sprintf(s, "ora $%0.4x,x [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDRX, (op0|op1<<8)));break; + case 0x1e:sprintf(s, "asl $%0.4x,x [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDRX, (op0|op1<<8)));break; + case 0x1f:sprintf(s, "ora $%0.6x,x [$%0.6x]", op0|op1<<8|op2<<16, gx816->convert_offset(MEMMODE_LONGX, (op0|op1<<8|op2<<16)));break; + case 0x20:sprintf(s, "jsr $%0.4x [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDR_PC, (op0|op1<<8)));break; + case 0x21:sprintf(s, "and ($%0.2x,x) [$%0.6x]", op0, gx816->convert_offset(MEMMODE_IDPX, op0));break; + case 0x22:sprintf(s, "jsl $%0.6x [$%0.6x]", op0|op1<<8|op2<<16, gx816->convert_offset(MEMMODE_LONG, (op0|op1<<8|op2<<16)));break; + case 0x23:sprintf(s, "and $%0.2x,s [$%0.6x]", op0, gx816->convert_offset(MEMMODE_SR, op0));break; + case 0x24:sprintf(s, "bit $%0.2x [$%0.6x]", op0, gx816->convert_offset(MEMMODE_DP, op0));break; + case 0x25:sprintf(s, "and $%0.2x [$%0.6x]", op0, gx816->convert_offset(MEMMODE_DP, op0));break; + case 0x26:sprintf(s, "rol $%0.2x [$%0.6x]", op0, gx816->convert_offset(MEMMODE_DP, op0));break; + case 0x27:sprintf(s, "and [$%0.2x] [$%0.6x]", op0, gx816->convert_offset(MEMMODE_ILDP, op0));break; + case 0x28:sprintf(s, "plp ");break; + case 0x29: + if(gx816->regs.e == true || (gx816->regs.e == false && (gx816->regs.p & 0x20)))sprintf(s, "and #$%0.2x ", op0); + else sprintf(s, "and #$%0.4x ", op0|op1<<8);break; + case 0x2a:sprintf(s, "rol a ");break; + case 0x2b:sprintf(s, "pld ");break; + case 0x2c:sprintf(s, "bit $%0.4x [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDR, (op0|op1<<8)));break; + case 0x2d:sprintf(s, "and $%0.4x [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDR, (op0|op1<<8)));break; + case 0x2e:sprintf(s, "rol $%0.4x [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDR, (op0|op1<<8)));break; + case 0x2f:sprintf(s, "and $%0.6x [$%0.6x]", op0|op1<<8|op2<<16, gx816->convert_offset(MEMMODE_LONG, (op0|op1<<8|op2<<16)));break; + case 0x30:sprintf(s, "bmi $%0.4x [$%0.6x]", _disas_relb(op0)&0xffff, _disas_relb(op0)&0xffffff);break; + case 0x31:sprintf(s, "and ($%0.2x),y [$%0.6x]", op0, gx816->convert_offset(MEMMODE_IDPY, op0));break; + case 0x32:sprintf(s, "and ($%0.2x) [$%0.6x]", op0, gx816->convert_offset(MEMMODE_IDP, op0));break; + case 0x33:sprintf(s, "and ($%0.2x,s),y [$%0.6x]", op0, gx816->convert_offset(MEMMODE_ISRY, op0));break; + case 0x34:sprintf(s, "bit $%0.2x,x [$%0.6x]", op0, gx816->convert_offset(MEMMODE_DPX, op0));break; + case 0x35:sprintf(s, "and $%0.2x,x [$%0.6x]", op0, gx816->convert_offset(MEMMODE_DPX, op0));break; + case 0x36:sprintf(s, "rol $%0.2x,x [$%0.6x]", op0, gx816->convert_offset(MEMMODE_DPX, op0));break; + case 0x37:sprintf(s, "and [$%0.2x],y [$%0.6x]", op0, gx816->convert_offset(MEMMODE_ILDPY, op0));break; + case 0x38:sprintf(s, "sec ");break; + case 0x39:sprintf(s, "and $%0.4x,y [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDRY, (op0|op1<<8)));break; + case 0x3a:sprintf(s, "dec ");break; + case 0x3b:sprintf(s, "tsc ");break; + case 0x3c:sprintf(s, "bit $%0.4x,x [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDRX, (op0|op1<<8)));break; + case 0x3d:sprintf(s, "and $%0.4x,x [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDRX, (op0|op1<<8)));break; + case 0x3e:sprintf(s, "rol $%0.4x,x [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDRX, (op0|op1<<8)));break; + case 0x3f:sprintf(s, "and $%0.6x,x [$%0.6x]", op0|op1<<8|op2<<16, gx816->convert_offset(MEMMODE_LONGX, (op0|op1<<8|op2<<16)));break; + case 0x40:sprintf(s, "rti ");break; + case 0x41:sprintf(s, "eor ($%0.2x,x) [$%0.6x]", op0, gx816->convert_offset(MEMMODE_IDPX, op0));break; + case 0x42:sprintf(s, "wdm ");break; + case 0x43:sprintf(s, "eor $%0.2x,s [$%0.6x]", op0, gx816->convert_offset(MEMMODE_SR, op0));break; + case 0x44:sprintf(s, "mvp $%0.2x,$%0.2x ", op1, op0);break; + case 0x45:sprintf(s, "eor $%0.2x [$%0.6x]", op0, gx816->convert_offset(MEMMODE_DP, op0));break; + case 0x46:sprintf(s, "lsr $%0.2x [$%0.6x]", op0, gx816->convert_offset(MEMMODE_DP, op0));break; + case 0x47:sprintf(s, "eor [$%0.2x] [$%0.6x]", op0, gx816->convert_offset(MEMMODE_ILDP, op0));break; + case 0x48:sprintf(s, "pha ");break; + case 0x49: + if(gx816->regs.e == true || (gx816->regs.e == false && (gx816->regs.p & 0x20)))sprintf(s, "eor #$%0.2x ", op0); + else sprintf(s, "eor #$%0.4x ", op0|op1<<8);break; + case 0x4a:sprintf(s, "lsr a ");break; + case 0x4b:sprintf(s, "phk ");break; + case 0x4c:sprintf(s, "jmp $%0.4x [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDR_PC, (op0|op1<<8)));break; + case 0x4d:sprintf(s, "eor $%0.4x [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDR, (op0|op1<<8)));break; + case 0x4e:sprintf(s, "lsr $%0.4x [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDR, (op0|op1<<8)));break; + case 0x4f:sprintf(s, "eor $%0.6x [$%0.6x]", op0|op1<<8|op2<<16, gx816->convert_offset(MEMMODE_LONG, (op0|op1<<8|op2<<16)));break; + case 0x50:sprintf(s, "bvc $%0.4x [$%0.6x]", _disas_relb(op0)&0xffff, _disas_relb(op0)&0xffffff);break; + case 0x51:sprintf(s, "eor ($%0.2x),y [$%0.6x]", op0, gx816->convert_offset(MEMMODE_IDPY, op0));break; + case 0x52:sprintf(s, "eor ($%0.2x) [$%0.6x]", op0, gx816->convert_offset(MEMMODE_IDP, op0));break; + case 0x53:sprintf(s, "eor ($%0.2x,s),y [$%0.6x]", op0, gx816->convert_offset(MEMMODE_ISRY, op0));break; + case 0x54:sprintf(s, "mvn $%0.2x,$%0.2x ", op1, op0);break; + case 0x55:sprintf(s, "eor $%0.2x,x [$%0.6x]", op0, gx816->convert_offset(MEMMODE_DPX, op0));break; + case 0x56:sprintf(s, "lsr $%0.2x,x [$%0.6x]", op0, gx816->convert_offset(MEMMODE_DPX, op0));break; + case 0x57:sprintf(s, "eor [$%0.2x],y [$%0.6x]", op0, gx816->convert_offset(MEMMODE_ILDPY, op0));break; + case 0x58:sprintf(s, "cli ");break; + case 0x59:sprintf(s, "eor $%0.4x,y [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDRY, (op0|op1<<8)));break; + case 0x5a:sprintf(s, "phy ");break; + case 0x5b:sprintf(s, "tcd ");break; + case 0x5c:sprintf(s, "jml $%0.6x [$%0.6x]", op0|op1<<8|op2<<16, gx816->convert_offset(MEMMODE_LONG, (op0|op1<<8|op2<<16)));break; + case 0x5d:sprintf(s, "eor $%0.4x,x [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDRX, (op0|op1<<8)));break; + case 0x5e:sprintf(s, "lsr $%0.4x,x [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDRX, (op0|op1<<8)));break; + case 0x5f:sprintf(s, "eor $%0.6x,x [$%0.6x]", op0|op1<<8|op2<<16, gx816->convert_offset(MEMMODE_LONGX, (op0|op1<<8|op2<<16)));break; + case 0x60:sprintf(s, "rts ");break; + case 0x61:sprintf(s, "adc ($%0.2x,x) [$%0.6x]", op0, gx816->convert_offset(MEMMODE_IDPX, op0));break; + case 0x62:sprintf(s, "per $%0.4x [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDR, (op0|op1<<8)));break; + case 0x63:sprintf(s, "adc $%0.2x,s [$%0.6x]", op0, gx816->convert_offset(MEMMODE_SR, op0));break; + case 0x64:sprintf(s, "stz $%0.2x [$%0.6x]", op0, gx816->convert_offset(MEMMODE_DP, op0));break; + case 0x65:sprintf(s, "adc $%0.2x [$%0.6x]", op0, gx816->convert_offset(MEMMODE_DP, op0));break; + case 0x66:sprintf(s, "ror $%0.2x [$%0.6x]", op0, gx816->convert_offset(MEMMODE_DP, op0));break; + case 0x67:sprintf(s, "adc [$%0.2x] [$%0.6x]", op0, gx816->convert_offset(MEMMODE_ILDP, op0));break; + case 0x68:sprintf(s, "pla ");break; + case 0x69: + if(gx816->regs.e == true || (gx816->regs.e == false && (gx816->regs.p & 0x20)))sprintf(s, "adc #$%0.2x ", op0); + else sprintf(s, "adc #$%0.4x ", op0|op1<<8);break; + case 0x6a:sprintf(s, "ror a ");break; + case 0x6b:sprintf(s, "rtl ");break; + case 0x6c:sprintf(s, "jmp ($%0.4x) [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_IADDR_PC, (op0|op1<<8)));break; + case 0x6d:sprintf(s, "adc $%0.4x [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDR, (op0|op1<<8)));break; + case 0x6e:sprintf(s, "ror $%0.4x [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDR, (op0|op1<<8)));break; + case 0x6f:sprintf(s, "adc $%0.6x [$%0.6x]", op0|op1<<8|op2<<16, gx816->convert_offset(MEMMODE_LONG, (op0|op1<<8|op2<<16)));break; + case 0x70:sprintf(s, "bvs $%0.4x [$%0.6x]", _disas_relb(op0)&0xffff, _disas_relb(op0)&0xffffff);break; + case 0x71:sprintf(s, "adc ($%0.2x),y [$%0.6x]", op0, gx816->convert_offset(MEMMODE_IDPY, op0));break; + case 0x72:sprintf(s, "adc ($%0.2x) [$%0.6x]", op0, gx816->convert_offset(MEMMODE_IDP, op0));break; + case 0x73:sprintf(s, "adc ($%0.2x,s),y [$%0.6x]", op0, gx816->convert_offset(MEMMODE_ISRY, op0));break; + case 0x74:sprintf(s, "stz $%0.2x,x [$%0.6x]", op0, gx816->convert_offset(MEMMODE_DPX, op0));break; + case 0x75:sprintf(s, "adc $%0.2x,x [$%0.6x]", op0, gx816->convert_offset(MEMMODE_DPX, op0));break; + case 0x76:sprintf(s, "ror $%0.2x,x [$%0.6x]", op0, gx816->convert_offset(MEMMODE_DPX, op0));break; + case 0x77:sprintf(s, "adc [$%0.2x],y [$%0.6x]", op0, gx816->convert_offset(MEMMODE_ILDPY, op0));break; + case 0x78:sprintf(s, "sei ");break; + case 0x79:sprintf(s, "adc $%0.4x,y [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDRY, (op0|op1<<8)));break; + case 0x7a:sprintf(s, "ply ");break; + case 0x7b:sprintf(s, "tdc ");break; + case 0x7c:sprintf(s, "jmp ($%0.4x,x) [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_IADDRX_PC, (op0|op1<<8)));break; + case 0x7d:sprintf(s, "adc $%0.4x,x [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDRX, (op0|op1<<8)));break; + case 0x7e:sprintf(s, "ror $%0.4x,x [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDRX, (op0|op1<<8)));break; + case 0x7f:sprintf(s, "adc $%0.6x,x [$%0.6x]", op0|op1<<8|op2<<16, gx816->convert_offset(MEMMODE_LONGX, (op0|op1<<8|op2<<16)));break; + case 0x80:sprintf(s, "bra $%0.4x [$%0.6x]", _disas_relb(op0)&0xffff, _disas_relb(op0)&0xffffff);break; + case 0x81:sprintf(s, "sta ($%0.2x,x) [$%0.6x]", op0, gx816->convert_offset(MEMMODE_IDPX, op0));break; + case 0x82:sprintf(s, "brl $%0.4x [$%0.6x]", _disas_relw(op0|op1<<8)&0xffff, _disas_relw(op0|op1<<8)&0xffffff);break; + case 0x83:sprintf(s, "sta $%0.2x,s [$%0.6x]", op0, gx816->convert_offset(MEMMODE_SR, op0));break; + case 0x84:sprintf(s, "sty $%0.2x [$%0.6x]", op0, gx816->convert_offset(MEMMODE_DP, op0));break; + case 0x85:sprintf(s, "sta $%0.2x [$%0.6x]", op0, gx816->convert_offset(MEMMODE_DP, op0));break; + case 0x86:sprintf(s, "stx $%0.2x [$%0.6x]", op0, gx816->convert_offset(MEMMODE_DP, op0));break; + case 0x87:sprintf(s, "sta [$%0.2x] [$%0.6x]", op0, gx816->convert_offset(MEMMODE_ILDP, op0));break; + case 0x88:sprintf(s, "dey ");break; + case 0x89: + if(gx816->regs.e == true || (gx816->regs.e == false && (gx816->regs.p & 0x20)))sprintf(s, "bit #$%0.2x ", op0); + else sprintf(s, "bit #$%0.4x ", op0|op1<<8);break; + case 0x8a:sprintf(s, "txa ");break; + case 0x8b:sprintf(s, "phb ");break; + case 0x8c:sprintf(s, "sty $%0.4x [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDR, (op0|op1<<8)));break; + case 0x8d:sprintf(s, "sta $%0.4x [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDR, (op0|op1<<8)));break; + case 0x8e:sprintf(s, "stx $%0.4x [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDR, (op0|op1<<8)));break; + case 0x8f:sprintf(s, "sta $%0.6x [$%0.6x]", op0|op1<<8|op2<<16, gx816->convert_offset(MEMMODE_LONG, (op0|op1<<8|op2<<16)));break; + case 0x90:sprintf(s, "bcc $%0.4x [$%0.6x]", _disas_relb(op0)&0xffff, _disas_relb(op0)&0xffffff);break; + case 0x91:sprintf(s, "sta ($%0.2x),y [$%0.6x]", op0, gx816->convert_offset(MEMMODE_IDPY, op0));break; + case 0x92:sprintf(s, "sta ($%0.2x) [$%0.6x]", op0, gx816->convert_offset(MEMMODE_IDP, op0));break; + case 0x93:sprintf(s, "sta ($%0.2x,s),y [$%0.6x]", op0, gx816->convert_offset(MEMMODE_ISRY, op0));break; + case 0x94:sprintf(s, "sty $%0.2x,x [$%0.6x]", op0, gx816->convert_offset(MEMMODE_DPX, op0));break; + case 0x95:sprintf(s, "sta $%0.2x,x [$%0.6x]", op0, gx816->convert_offset(MEMMODE_DPX, op0));break; + case 0x96:sprintf(s, "stx $%0.2x,y [$%0.6x]", op0, gx816->convert_offset(MEMMODE_DPY, op0));break; + case 0x97:sprintf(s, "sta [$%0.2x],y [$%0.6x]", op0, gx816->convert_offset(MEMMODE_ILDPY, op0));break; + case 0x98:sprintf(s, "tya ");break; + case 0x99:sprintf(s, "sta $%0.4x,y [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDRY, (op0|op1<<8)));break; + case 0x9a:sprintf(s, "txs ");break; + case 0x9b:sprintf(s, "txy ");break; + case 0x9c:sprintf(s, "stz $%0.4x [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDR, (op0|op1<<8)));break; + case 0x9d:sprintf(s, "sta $%0.4x,x [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDRX, (op0|op1<<8)));break; + case 0x9e:sprintf(s, "stz $%0.4x,x [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDRX, (op0|op1<<8)));break; + case 0x9f:sprintf(s, "sta $%0.6x,x [$%0.6x]", op0|op1<<8|op2<<16, gx816->convert_offset(MEMMODE_LONGX, (op0|op1<<8|op2<<16)));break; + case 0xa0: + if(gx816->regs.e == true || (gx816->regs.e == false && (gx816->regs.p & PF_X)))sprintf(s, "ldy #$%0.2x ", op0); + else sprintf(s, "ldy #$%0.4x ", op0|op1<<8);break; + case 0xa1:sprintf(s, "lda ($%0.2x,x) [$%0.6x]", op0, gx816->convert_offset(MEMMODE_IDPX, op0));break; + case 0xa2: + if(gx816->regs.e == true || (gx816->regs.e == false && (gx816->regs.p & PF_X)))sprintf(s, "ldx #$%0.2x ", op0); + else sprintf(s, "ldx #$%0.4x ", op0|op1<<8);break; + case 0xa3:sprintf(s, "lda $%0.2x,s [$%0.6x]", op0, gx816->convert_offset(MEMMODE_SR, op0));break; + case 0xa4:sprintf(s, "ldy $%0.2x [$%0.6x]", op0, gx816->convert_offset(MEMMODE_DP, op0));break; + case 0xa5:sprintf(s, "lda $%0.2x [$%0.6x]", op0, gx816->convert_offset(MEMMODE_DP, op0));break; + case 0xa6:sprintf(s, "ldx $%0.2x [$%0.6x]", op0, gx816->convert_offset(MEMMODE_DP, op0));break; + case 0xa7:sprintf(s, "lda [$%0.2x] [$%0.6x]", op0, gx816->convert_offset(MEMMODE_ILDP, op0));break; + case 0xa8:sprintf(s, "tay ");break; + case 0xa9: + if(gx816->regs.e == true || (gx816->regs.e == false && (gx816->regs.p & 0x20)))sprintf(s, "lda #$%0.2x ", op0); + else sprintf(s, "lda #$%0.4x ", op0|op1<<8);break; + case 0xaa:sprintf(s, "tax ");break; + case 0xab:sprintf(s, "plb ");break; + case 0xac:sprintf(s, "ldy $%0.4x [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDR, (op0|op1<<8)));break; + case 0xad:sprintf(s, "lda $%0.4x [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDR, (op0|op1<<8)));break; + case 0xae:sprintf(s, "ldx $%0.4x [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDR, (op0|op1<<8)));break; + case 0xaf:sprintf(s, "lda $%0.6x [$%0.6x]", op0|op1<<8|op2<<16, gx816->convert_offset(MEMMODE_LONG, (op0|op1<<8|op2<<16)));break; + case 0xb0:sprintf(s, "bcs $%0.4x [$%0.6x]", _disas_relb(op0)&0xffff, _disas_relb(op0)&0xffffff);break; + case 0xb1:sprintf(s, "lda ($%0.2x),y [$%0.6x]", op0, gx816->convert_offset(MEMMODE_IDPY, op0));break; + case 0xb2:sprintf(s, "lda ($%0.2x) [$%0.6x]", op0, gx816->convert_offset(MEMMODE_IDP, op0));break; + case 0xb3:sprintf(s, "lda ($%0.2x,s),y [$%0.6x]", op0, gx816->convert_offset(MEMMODE_ISRY, op0));break; + case 0xb4:sprintf(s, "ldy $%0.2x,x [$%0.6x]", op0, gx816->convert_offset(MEMMODE_DPX, op0));break; + case 0xb5:sprintf(s, "lda $%0.2x,x [$%0.6x]", op0, gx816->convert_offset(MEMMODE_DPX, op0));break; + case 0xb6:sprintf(s, "ldx $%0.2x,y [$%0.6x]", op0, gx816->convert_offset(MEMMODE_DPY, op0));break; + case 0xb7:sprintf(s, "lda [$%0.2x],y [$%0.6x]", op0, gx816->convert_offset(MEMMODE_ILDPY, op0));break; + case 0xb8:sprintf(s, "clv ");break; + case 0xb9:sprintf(s, "lda $%0.4x,y [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDRY, (op0|op1<<8)));break; + case 0xba:sprintf(s, "tsx ");break; + case 0xbb:sprintf(s, "tyx ");break; + case 0xbc:sprintf(s, "ldy $%0.4x,x [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDRX, (op0|op1<<8)));break; + case 0xbd:sprintf(s, "lda $%0.4x,x [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDRX, (op0|op1<<8)));break; + case 0xbe:sprintf(s, "ldx $%0.4x,y [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDRY, (op0|op1<<8)));break; + case 0xbf:sprintf(s, "lda $%0.6x,x [$%0.6x]", op0|op1<<8|op2<<16, gx816->convert_offset(MEMMODE_LONGX, (op0|op1<<8|op2<<16)));break; + case 0xc0: + if(gx816->regs.e == true || (gx816->regs.e == false && (gx816->regs.p & PF_X)))sprintf(s, "cpy #$%0.2x ", op0); + else sprintf(s, "cpy #$%0.4x ", op0|op1<<8);break; + case 0xc1:sprintf(s, "cmp ($%0.2x,x) [$%0.6x]", op0, gx816->convert_offset(MEMMODE_IDPX, op0));break; + case 0xc2:sprintf(s, "rep #$%0.2x ", op0);break; + case 0xc3:sprintf(s, "cmp $%0.2x,s [$%0.6x]", op0, gx816->convert_offset(MEMMODE_SR, op0));break; + case 0xc4:sprintf(s, "cpy $%0.2x [$%0.6x]", op0, gx816->convert_offset(MEMMODE_DP, op0));break; + case 0xc5:sprintf(s, "cmp $%0.2x [$%0.6x]", op0, gx816->convert_offset(MEMMODE_DP, op0));break; + case 0xc6:sprintf(s, "dec $%0.2x [$%0.6x]", op0, gx816->convert_offset(MEMMODE_DP, op0));break; + case 0xc7:sprintf(s, "cmp [$%0.2x] [$%0.6x]", op0, gx816->convert_offset(MEMMODE_ILDP, op0));break; + case 0xc8:sprintf(s, "iny ");break; + case 0xc9: + if(gx816->regs.e == true || (gx816->regs.e == false && (gx816->regs.p & 0x20)))sprintf(s, "cmp #$%0.2x ", op0); + else sprintf(s, "cmp #$%0.4x ", op0|op1<<8);break; + case 0xca:sprintf(s, "dex ");break; + case 0xcb:sprintf(s, "wai ");break; + case 0xcc:sprintf(s, "cpy $%0.4x [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDR, (op0|op1<<8)));break; + case 0xcd:sprintf(s, "cmp $%0.4x [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDR, (op0|op1<<8)));break; + case 0xce:sprintf(s, "dec $%0.4x [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDR, (op0|op1<<8)));break; + case 0xcf:sprintf(s, "cmp $%0.6x [$%0.6x]", op0|op1<<8|op2<<16, gx816->convert_offset(MEMMODE_LONG, (op0|op1<<8|op2<<16)));break; + case 0xd0:sprintf(s, "bne $%0.4x [$%0.6x]", _disas_relb(op0)&0xffff, _disas_relb(op0)&0xffffff);break; + case 0xd1:sprintf(s, "cmp ($%0.2x),y [$%0.6x]", op0, gx816->convert_offset(MEMMODE_IDPY, op0));break; + case 0xd2:sprintf(s, "cmp ($%0.2x) [$%0.6x]", op0, gx816->convert_offset(MEMMODE_IDP, op0));break; + case 0xd3:sprintf(s, "cmp ($%0.2x,s),y [$%0.6x]", op0, gx816->convert_offset(MEMMODE_ISRY, op0));break; + case 0xd4:sprintf(s, "pei ($%0.2x) [$%0.6x]", op0, gx816->convert_offset(MEMMODE_IDP, op0));break; + case 0xd5:sprintf(s, "cmp $%0.2x,x [$%0.6x]", op0, gx816->convert_offset(MEMMODE_DPX, op0));break; + case 0xd6:sprintf(s, "dec $%0.2x,x [$%0.6x]", op0, gx816->convert_offset(MEMMODE_DPX, op0));break; + case 0xd7:sprintf(s, "cmp [$%0.2x],y [$%0.6x]", op0, gx816->convert_offset(MEMMODE_ILDPY, op0));break; + case 0xd8:sprintf(s, "cld ");break; + case 0xd9:sprintf(s, "cmp $%0.4x,y [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDRY, (op0|op1<<8)));break; + case 0xda:sprintf(s, "phx ");break; + case 0xdb:sprintf(s, "stp ");break; + case 0xdc:sprintf(s, "jmp [$%0.4x] [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ILADDR_PC, (op0|op1<<8)));break; + case 0xdd:sprintf(s, "cmp $%0.4x,x [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDRX, (op0|op1<<8)));break; + case 0xde:sprintf(s, "dec $%0.4x,x [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDRX, (op0|op1<<8)));break; + case 0xdf:sprintf(s, "cmp $%0.6x,x [$%0.6x]", op0|op1<<8|op2<<16, gx816->convert_offset(MEMMODE_LONGX, (op0|op1<<8|op2<<16)));break; + case 0xe0: + if(gx816->regs.e == true || (gx816->regs.e == false && (gx816->regs.p & PF_X)))sprintf(s, "cpx #$%0.2x ", op0); + else sprintf(s, "cpx #$%0.4x ", op0|op1<<8);break; + case 0xe1:sprintf(s, "sbc ($%0.2x,x) [$%0.6x]", op0, gx816->convert_offset(MEMMODE_IDPX, op0));break; + case 0xe2:sprintf(s, "sep #$%0.2x ", op0);break; + case 0xe3:sprintf(s, "sbc $%0.2x,s [$%0.6x]", op0, gx816->convert_offset(MEMMODE_SR, op0));break; + case 0xe4:sprintf(s, "cpx $%0.2x [$%0.6x]", op0, gx816->convert_offset(MEMMODE_DP, op0));break; + case 0xe5:sprintf(s, "sbc $%0.2x [$%0.6x]", op0, gx816->convert_offset(MEMMODE_DP, op0));break; + case 0xe6:sprintf(s, "inc $%0.2x [$%0.6x]", op0, gx816->convert_offset(MEMMODE_DP, op0));break; + case 0xe7:sprintf(s, "sbc [$%0.2x] [$%0.6x]", op0, gx816->convert_offset(MEMMODE_ILDP, op0));break; + case 0xe8:sprintf(s, "inx ");break; + case 0xe9: + if(gx816->regs.e == true || (gx816->regs.e == false && (gx816->regs.p & 0x20)))sprintf(s, "sbc #$%0.2x ", op0); + else sprintf(s, "sbc #$%0.4x ", op0|op1<<8);break; + case 0xea:sprintf(s, "nop ");break; + case 0xeb:sprintf(s, "xba ");break; + case 0xec:sprintf(s, "cpx $%0.4x [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDR, (op0|op1<<8)));break; + case 0xed:sprintf(s, "sbc $%0.4x [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDR, (op0|op1<<8)));break; + case 0xee:sprintf(s, "inc $%0.4x [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDR, (op0|op1<<8)));break; + case 0xef:sprintf(s, "sbc $%0.6x [$%0.6x]", op0|op1<<8|op2<<16, gx816->convert_offset(MEMMODE_LONG, (op0|op1<<8|op2<<16)));break; + case 0xf0:sprintf(s, "beq $%0.4x [$%0.6x]", _disas_relb(op0)&0xffff, _disas_relb(op0)&0xffffff);break; + case 0xf1:sprintf(s, "sbc ($%0.2x),y [$%0.6x]", op0, gx816->convert_offset(MEMMODE_IDPY, op0));break; + case 0xf2:sprintf(s, "sbc ($%0.2x) [$%0.6x]", op0, gx816->convert_offset(MEMMODE_IDP, op0));break; + case 0xf3:sprintf(s, "sbc ($%0.2x,s),y [$%0.6x]", op0, gx816->convert_offset(MEMMODE_ISRY, op0));break; + case 0xf4:sprintf(s, "pea $%0.4x [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDR, (op0|op1<<8)));break; + case 0xf5:sprintf(s, "sbc $%0.2x,x [$%0.6x]", op0, gx816->convert_offset(MEMMODE_DPX, op0));break; + case 0xf6:sprintf(s, "inc $%0.2x,x [$%0.6x]", op0, gx816->convert_offset(MEMMODE_DPX, op0));break; + case 0xf7:sprintf(s, "sbc [$%0.2x],y [$%0.6x]", op0, gx816->convert_offset(MEMMODE_ILDPY, op0));break; + case 0xf8:sprintf(s, "sed ");break; + case 0xf9:sprintf(s, "sbc $%0.4x,y [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDRY, (op0|op1<<8)));break; + case 0xfa:sprintf(s, "plx ");break; + case 0xfb:sprintf(s, "xce ");break; + case 0xfc:sprintf(s, "jsr ($%0.4x,x) [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_IADDRX_PC, (op0|op1<<8)));break; + case 0xfd:sprintf(s, "sbc $%0.4x,x [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDRX, (op0|op1<<8)));break; + case 0xfe:sprintf(s, "inc $%0.4x,x [$%0.6x]", op0|op1<<8, gx816->convert_offset(MEMMODE_ADDRX, (op0|op1<<8)));break; + case 0xff:sprintf(s, "sbc $%0.6x,x [$%0.6x]", op0|op1<<8|op2<<16, gx816->convert_offset(MEMMODE_LONGX, (op0|op1<<8|op2<<16)));break; + } +} + +void disas_g65816_op(void) { +byte op = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, gx816->regs.pc, MEMACCESS_DEBUGGER); +byte op0 = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, gx816->regs.pc + 1, MEMACCESS_DEBUGGER), + op1 = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, gx816->regs.pc + 2, MEMACCESS_DEBUGGER), + op2 = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, gx816->regs.pc + 3, MEMACCESS_DEBUGGER); +char str0[256], str1[256], str2[256]; + + strcpy(__disas_op_str, "??? "); + __disas_op(op, op0, op1, op2); + + if(gx816->regs.e == true) { + sprintf(str1, "%c%c%c%c%c%c%c%c E", + (gx816->regs.p & 0x80)?'N':'n', + (gx816->regs.p & 0x40)?'V':'v', + (gx816->regs.p & 0x20)?'1':'0', + (gx816->regs.p & 0x10)?'B':'b', + (gx816->regs.p & 0x08)?'D':'d', + (gx816->regs.p & 0x04)?'I':'i', + (gx816->regs.p & 0x02)?'Z':'z', + (gx816->regs.p & 0x01)?'C':'c'); + } else { + sprintf(str1, "%c%c%c%c%c%c%c%c N", + (gx816->regs.p & 0x80)?'N':'n', + (gx816->regs.p & 0x40)?'V':'v', + (gx816->regs.p & 0x20)?'M':'m', + (gx816->regs.p & 0x10)?'X':'x', + (gx816->regs.p & 0x08)?'D':'d', + (gx816->regs.p & 0x04)?'I':'i', + (gx816->regs.p & 0x02)?'Z':'z', + (gx816->regs.p & 0x01)?'C':'c'); + } + + dprintf("%0.6x %s A:%0.4x X:%0.4x Y:%0.4x S:%0.4x D:%0.4x DB:%0.2x %s", gx816->regs.pc, __disas_op_str, + gx816->regs.a.w, gx816->regs.x, gx816->regs.y, gx816->regs.s, + gx816->regs.d, gx816->regs.db, str1); + + debug_update_cycles(); +} diff --git a/bsnes/cpu/g65816.cpp b/bsnes/cpu/g65816.cpp new file mode 100644 index 00000000..9d117b50 --- /dev/null +++ b/bsnes/cpu/g65816.cpp @@ -0,0 +1,234 @@ +#include "../base.h" +#include "../timing/timing.h" +#include "g65816.h" + +extern emustate emu_state; +extern debugstate debugger; +extern ppustate ppu; +extern snes_timer *snes_time; +g65816 *gx816; + +#include "g65816_ops.cpp" + +void g65816::LoadROM(void) { +FILE *fp; +ulong fsize; +word header_offset = 0; +word cksum, icksum; +byte t; +int i; + dprintf("* Loading [%s]...", emu_state.rom_name); + + fp = fopen(emu_state.rom_name, "rb"); + if(!fp)return; + + fseek(fp, 0, SEEK_END); + fsize = ftell(fp); + fseek(fp, 0, SEEK_SET); + + map = MEMMAP_LOROM; + + if((fsize & 0x000fff) == 0x000200)header_offset = 512; + + fseek(fp, 0x7fdc + header_offset, SEEK_SET); + cksum = fgetc(fp) | fgetc(fp) << 8; + icksum = fgetc(fp) | fgetc(fp) << 8; + if(cksum + icksum == 0xffff)map = MEMMAP_LOROM; + + fseek(fp, 0xffdc + header_offset, SEEK_SET); + cksum = fgetc(fp) | fgetc(fp) << 8; + icksum = fgetc(fp) | fgetc(fp) << 8; + if(cksum + icksum == 0xffff)map = MEMMAP_HIROM; + + fseek(fp, 0 + header_offset, SEEK_SET); + fsize -= header_offset; + + dprintf("* ROM detected as: %s", (map == MEMMAP_LOROM)?"LoROM":"HiROM"); + + InitializeROM(map); + + fread(rom, 1, fsize, fp); + + t = (mem_read(MEMMODE_NONE, MEMSIZE_BYTE, 0x00ffd8) & 7); + switch(t) { + case 0: sram_size = 0; break; + case 1: sram_size = 2 * 1024;break; + case 2: sram_size = 4 * 1024;break; + case 3: sram_size = 8 * 1024;break; + case 4: sram_size = 16 * 1024;break; + case 5: sram_size = 32 * 1024;break; + case 6: sram_size = 64 * 1024;break; + case 7: sram_size = 128 * 1024;break; + default:sram_size = 0; break; + } + dprintf("* SRAM Size: %dkb", sram_size / 1024); + +//pbr is loaded with 00, and 16-bit pc is loaded with reset vector at 0xfffc +//upon power on and at first reset + regs.pc = mem_read(MEMMODE_LONG, MEMSIZE_WORD, 0x00fffc); + + fclose(fp); + + if(sram_size != 0) { + fp = fopen(emu_state.sram_name, "rb"); + +//create sram file if it does not exist + if(!fp) { + fp = fopen(emu_state.sram_name, "wb"); + for(i=0;i<sram_size;i++) { + fputc(0, fp); + } + fclose(fp); + fp = fopen(emu_state.sram_name, "rb"); + } + fseek(fp, 0, SEEK_END); + fsize = ftell(fp); + +//fix sram size if it is smaller than actual size + if(fsize < sram_size) { + fclose(fp); + fp = fopen(emu_state.sram_name, "rb+wb"); + fseek(fp, 0, SEEK_END); + while(fsize < sram_size) { + fputc(0, fp); + fsize++; + } + fclose(fp); + fp = fopen(emu_state.sram_name, "rb"); + } + +//read file into sram + fseek(fp, 0, SEEK_SET); + fread(sram, 1, sram_size, fp); + fclose(fp); + } +} + +void g65816::PowerOn(byte first_time) { +//default register states + regs.a.w = regs.x = regs.y = 0x0000; + regs.d = 0x0000; + regs.s = 0x01ff; + regs.db = 0x00; + regs.p = 0x34; + regs.e = true; + snes_time->master_cycles = 0; + memory_speed = MEMSPEED_SLOWROM; + toggle_memory_speed = MEMSPEED_SLOWROM; + wai_interrupt_occurred = false; + + InitializeWRAM(0x00); + PPUInit(first_time); + UpdateDisplay(); +} + +void g65816::Reset(void) { + regs.x &= 0xff; + regs.y &= 0xff; + regs.s = 0x0100 | (regs.s & 0xff); + regs.d = 0x0000; + regs.db = 0x00; + regs.p = 0x34; + regs.e = true; + regs.pc = mem_read(MEMMODE_LONG, MEMSIZE_WORD, 0x00fffc); + snes_time->master_cycles = 0; + memory_speed = MEMSPEED_SLOWROM; + toggle_memory_speed = MEMSPEED_SLOWROM; + wai_interrupt_occurred = false; + + PPUInit(0); //0 blocks reallocating memory for vram, cgram, etc. + UpdateDisplay(); + + if(debug_get_state() == DEBUGMODE_WAIT)disas_g65816_op(); +} + +void g65816::InvokeIRQ(word addr) { + wai_interrupt_occurred = true; + snes_time->add_cpu_pcycles(1); + if(gx816->regs.e == true) { + snes_time->add_cpu_scycles(3); + g65816_stackwrite(MEMSIZE_WORD, gx816->regs.pc); + g65816_stackwrite(MEMSIZE_BYTE, gx816->regs.p); + } else { + snes_time->add_cpu_scycles(4); + g65816_stackwrite(MEMSIZE_LONG, gx816->regs.pc); + g65816_stackwrite(MEMSIZE_BYTE, gx816->regs.p); + } + snes_time->add_cpu_mcycles(2, addr); + snes_time->add_cpu_icycles(1); + gx816->regs.pc = gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, addr); + gx816->regs.p |= PF_I; + + snes_time->update_timer(); +} + +void g65816::Run(void) { +FILE *fp; +byte op; +word h; +int i; +static ulong sram_save_tick = 0; + op = mem_read(MEMMODE_NONE, MEMSIZE_BYTE, gx816->regs.pc); + if(regs.e == true)g65816_optbl_e[op](); + else switch((regs.p & 0x30)) { + case 0x30:g65816_optbl_MX[op]();break; + case 0x20:g65816_optbl_Mx[op]();break; + case 0x10:g65816_optbl_mX[op]();break; + case 0x00:g65816_optbl_mx[op]();break; + } + + h = snes_time->hscan_pos; + snes_time->update_timer(); + if(snes_time->hscan_pos >= WRAM_REFRESH_DOT_POS && h < WRAM_REFRESH_DOT_POS) { + snes_time->add_cpu_cycles(40); + snes_time->update_timer(); + } + ppu_update_scanline(); + + memory_speed = toggle_memory_speed; + + if(debug_get_state() == DEBUGMODE_DISABLED)return; + + for(i=0;i<16;i++) { + if(gx816->bp_list[i].flags & BP_EXEC) { + if(gx816->bp_list[i].offset == gx816->regs.pc) { + dprintf("* breakpoint %d hit -- exec access", i); + gx816->bp_list[i].hit_count++; + debug_set_state(DEBUGMODE_WAIT); + disas_g65816_op(); + debugger.refresh_bp = true; + } + } + } + +//see if we need to backup sram to a file yet... + if(sram_size != 0) { + sram_save_tick++; + if(sram_save_tick >= emu_state.sram_save_tick_count) { + sram_save_tick = 0; + fp = fopen(emu_state.sram_name, "wb"); + fwrite(sram, 1, sram_size, fp); + fclose(fp); + } + } +} + +g65816::g65816() { + rom = (byte*)malloc(0x600000); + wram = (byte*)malloc(0x020000); + sram = (byte*)malloc(0x0e0000); + for(int i=0;i<16;i++) { + bp_list[i].offset = 0; + bp_list[i].flags = BP_OFF; + bp_list[i].value = 0; + bp_list[i].hit_count = 0; + } + nmi_enabled = false; + nmi_pin = 0; +} + +g65816::~g65816() { + if(rom)free(rom); + if(wram)free(wram); + if(sram)free(sram); +} diff --git a/bsnes/cpu/g65816.h b/bsnes/cpu/g65816.h new file mode 100644 index 00000000..f1e83a1b --- /dev/null +++ b/bsnes/cpu/g65816.h @@ -0,0 +1,69 @@ +/* + regs.dc is not a real register. it is used to store the data counter + for reading from / writing to memory locations. it is also needed + to emulate slowrom / fastrom, as the speed varies depending on the + location where the memory was accessed. +*/ + +class g65816 { +public: +//cpu declarations + struct { + ulong pc, dc; + union { + byte b; + word w; + struct { byte l, h; }p; + }a; + word x, y, d, s; + byte db, p; + bool e; + }regs; + byte memory_speed; + byte toggle_memory_speed; + bool index_bank_crossed; + + byte nmi_pin; + bool nmi_enabled; + + bool wai_interrupt_occurred; + +//memory declarations + byte *wram, *sram, *rom; + ulong map; + ulong sram_size; +//debug declarations + struct { + ulong offset; + byte flags; + byte value; + ulong hit_count; + }bp_list[16]; + +//cpu functions (cpu/g65816.cpp) + void Run(void); + void LoadROM(void); + void PowerOn(byte first_time); + void Reset(void); + void InvokeIRQ(word addr); + +//memory functions (mem/memory.cpp) + void InitializeROM(byte memory_map); + void InitializeWRAM(byte value); + ulong mirror_offset(ulong offset); + ulong convert_offset(byte read_mode, ulong addr, bool mirror = true); + ulong adjust_base_offset(byte read_mode, ulong addr); + ulong read_indirect_address(byte read_mode, ulong addr); + ulong get_dc(byte read_mode, ulong addr); + byte mem_getbyte_direct(ulong addr, byte access_mode = MEMACCESS_NORMAL); + byte mem_getbyte(ulong addr, byte access_mode = MEMACCESS_NORMAL); + void mem_putbyte_direct(ulong addr, byte value, byte access_mode = MEMACCESS_NORMAL); + void mem_putbyte(ulong addr, byte value, byte access_mode = MEMACCESS_NORMAL); + ulong mem_read(byte read_mode, byte read_size, ulong addr, byte access_mode = MEMACCESS_NORMAL); + void mem_write(byte write_mode, byte write_size, ulong addr, ulong value, byte access_mode = MEMACCESS_NORMAL); + ulong rom_read(ulong addr, byte read_size); + void rom_write(ulong addr, ulong v, byte write_size); + + g65816(); + ~g65816(); +}; diff --git a/bsnes/cpu/g65816_ops.cpp b/bsnes/cpu/g65816_ops.cpp new file mode 100644 index 00000000..02e036da --- /dev/null +++ b/bsnes/cpu/g65816_ops.cpp @@ -0,0 +1,161 @@ +#define g65816_setn() gx816->regs.p |= PF_N +#define g65816_clrn() gx816->regs.p &= ~PF_N +#define g65816_setv() gx816->regs.p |= PF_V +#define g65816_clrv() gx816->regs.p &= ~PF_V +#define g65816_setm() gx816->regs.p |= PF_M +#define g65816_clrm() gx816->regs.p &= ~PF_M +#define g65816_setx() gx816->regs.p |= PF_X +#define g65816_clrx() gx816->regs.p &= ~PF_X +#define g65816_setd() gx816->regs.p |= PF_D +#define g65816_clrd() gx816->regs.p &= ~PF_D +#define g65816_seti() gx816->regs.p |= PF_I +#define g65816_clri() gx816->regs.p &= ~PF_I +#define g65816_setz() gx816->regs.p |= PF_Z +#define g65816_clrz() gx816->regs.p &= ~PF_Z +#define g65816_setc() gx816->regs.p |= PF_C +#define g65816_clrc() gx816->regs.p &= ~PF_C +#define g65816_testn(x) if(x)g65816_setn(); else g65816_clrn() +#define g65816_testv(x) if(x)g65816_setv(); else g65816_clrv() +#define g65816_testd(x) if(x)g65816_setd(); else g65816_clrd() +#define g65816_testi(x) if(x)g65816_seti(); else g65816_clri() +#define g65816_testz(x) if(x)g65816_setz(); else g65816_clrz() +#define g65816_testc(x) if(x)g65816_setc(); else g65816_clrc() + +//read opcode argument +#define g65816_prefetch(__size) ulong arg = gx816->mem_read(MEMMODE_NONE, __size, gx816->regs.pc + 1, MEMACCESS_CPU) +//get destination address, do not mirror +#define g65816_getaddr(__mode) ulong dest_addr = gx816->get_dc(__mode, arg) +//get indirect destination address + destination address, do not mirror +#define g65816_getiaddr(__mode) ulong base_addr = gx816->adjust_base_offset(__mode, arg);ulong dest_addr = gx816->read_indirect_address(__mode, base_addr) +//increment program counter, wrap around pbr +#define g65816_incpc(__n) gx816->regs.pc = (gx816->regs.pc & 0xff0000) | ((gx816->regs.pc + __n) & 0xffff) + +//opcode functions +#include "g65816_ops_adc.cpp" +#include "g65816_ops_and.cpp" +#include "g65816_ops_cmp.cpp" +#include "g65816_ops_eor.cpp" +#include "g65816_ops_lda.cpp" +#include "g65816_ops_ora.cpp" +#include "g65816_ops_sbc.cpp" +#include "g65816_ops_sta.cpp" +#include "g65816_ops_incdec.cpp" +#include "g65816_ops_shift.cpp" +#include "g65816_ops_stack.cpp" +#include "g65816_ops_pc.cpp" +#include "g65816_ops_misc.cpp" + +vfunc g65816_optbl_e[256] = { // g65816_optbl_e, g65816_optbl_e, g65816_optbl_e, + // ----------------------x0, ----------------------x1, ----------------------x2, ----------------------x3, -------- ----------------------x4, ----------------------x5, ----------------------x6, ----------------------x7, -------- ----------------------x8, ----------------------x9, ----------------------xa, ----------------------xb, -------- ----------------------xc, ----------------------xd, ----------------------xe, ----------------------xf, +/* 0x */ g65816_op_brke, g65816_op_ora_idpxb, g65816_op_cope, g65816_op_ora_srb, /* 0x */ g65816_op_tsb_dpb, g65816_op_ora_dpb, g65816_op_asl_dpb, g65816_op_ora_ildpb, /* 0x */ g65816_op_php, g65816_op_ora_constb, g65816_op_aslb, g65816_op_phd, /* 0x */ g65816_op_tsb_addrb, g65816_op_ora_addrb, g65816_op_asl_addrb, g65816_op_ora_longb, +/* 1x */ g65816_op_bpl, g65816_op_ora_idpyb, g65816_op_ora_idpb, g65816_op_ora_isryb, /* 1x */ g65816_op_trb_dpb, g65816_op_ora_dpxb, g65816_op_asl_dpxb, g65816_op_ora_ildpyb, /* 1x */ g65816_op_clc, g65816_op_ora_addryb, g65816_op_incb, g65816_op_tcse, /* 1x */ g65816_op_trb_addrb, g65816_op_ora_addrxb, g65816_op_asl_addrxb, g65816_op_ora_longxb, +/* 2x */ g65816_op_jsr_addr, g65816_op_and_idpxb, g65816_op_jsr_long, g65816_op_and_srb, /* 2x */ g65816_op_bit_dpb, g65816_op_and_dpb, g65816_op_rol_dpb, g65816_op_and_ildpb, /* 2x */ g65816_op_plp, g65816_op_and_constb, g65816_op_rolb, g65816_op_pld, /* 2x */ g65816_op_bit_addrb, g65816_op_and_addrb, g65816_op_rol_addrb, g65816_op_and_longb, +/* 3x */ g65816_op_bmi, g65816_op_and_idpyb, g65816_op_and_idpb, g65816_op_and_isryb, /* 3x */ g65816_op_bit_dpxb, g65816_op_and_dpxb, g65816_op_rol_dpxb, g65816_op_and_ildpyb, /* 3x */ g65816_op_sec, g65816_op_and_addryb, g65816_op_decb, g65816_op_tsce, /* 3x */ g65816_op_bit_addrxb, g65816_op_and_addrxb, g65816_op_rol_addrxb, g65816_op_and_longxb, + +/* 4x */ g65816_op_rtie, g65816_op_eor_idpxb, g65816_op_wdm, g65816_op_eor_srb, /* 4x */ g65816_op_mvp, g65816_op_eor_dpb, g65816_op_lsr_dpb, g65816_op_eor_ildpb, /* 4x */ g65816_op_phab, g65816_op_eor_constb, g65816_op_lsrb, g65816_op_phk, /* 4x */ g65816_op_jmp_addr, g65816_op_eor_addrb, g65816_op_lsr_addrb, g65816_op_eor_longb, +/* 5x */ g65816_op_bvc, g65816_op_eor_idpyb, g65816_op_eor_idpb, g65816_op_eor_isryb, /* 5x */ g65816_op_mvn, g65816_op_eor_dpxb, g65816_op_lsr_dpxb, g65816_op_eor_ildpyb, /* 5x */ g65816_op_cli, g65816_op_eor_addryb, g65816_op_phyb, g65816_op_tcd, /* 5x */ g65816_op_jmp_long, g65816_op_eor_addrxb, g65816_op_lsr_addrxb, g65816_op_eor_longxb, +/* 6x */ g65816_op_rts, g65816_op_adc_idpxb, g65816_op_per, g65816_op_adc_srb, /* 6x */ g65816_op_stz_dpb, g65816_op_adc_dpb, g65816_op_ror_dpb, g65816_op_adc_ildpb, /* 6x */ g65816_op_plab, g65816_op_adc_constb, g65816_op_rorb, g65816_op_rtl, /* 6x */ g65816_op_jmp_iaddr, g65816_op_adc_addrb, g65816_op_ror_addrb, g65816_op_adc_longb, +/* 7x */ g65816_op_bvs, g65816_op_adc_idpyb, g65816_op_adc_idpb, g65816_op_adc_isryb, /* 7x */ g65816_op_stz_dpxb, g65816_op_adc_dpxb, g65816_op_ror_dpxb, g65816_op_adc_ildpyb, /* 7x */ g65816_op_sei, g65816_op_adc_addryb, g65816_op_plyb, g65816_tdc, /* 7x */ g65816_op_jmp_iaddrx, g65816_op_adc_addrxb, g65816_op_ror_addrxb, g65816_op_adc_longxb, + +/* 8x */ g65816_op_bra, g65816_op_sta_idpxb, g65816_op_brl, g65816_op_sta_srb, /* 8x */ g65816_op_sty_dpb, g65816_op_sta_dpb, g65816_op_stx_dpb, g65816_op_sta_ildpb, /* 8x */ g65816_op_deyb, g65816_op_bit_constb, g65816_op_txab, g65816_op_phb, /* 8x */ g65816_op_sty_addrb, g65816_op_sta_addrb, g65816_op_stx_addrb, g65816_op_sta_longb, +/* 9x */ g65816_op_bcc, g65816_op_sta_idpyb, g65816_op_sta_idpb, g65816_op_sta_isryb, /* 9x */ g65816_op_sty_dpxb, g65816_op_sta_dpxb, g65816_op_stx_dpyb, g65816_op_sta_ildpyb, /* 9x */ g65816_op_tyab, g65816_op_sta_addryb, g65816_op_txsb, g65816_op_txyb, /* 9x */ g65816_op_stz_addrb, g65816_op_sta_addrxb, g65816_op_stz_addrxb, g65816_op_sta_longxb, +/* ax */ g65816_op_ldy_constb, g65816_op_lda_idpxb, g65816_op_ldx_constb, g65816_op_lda_srb, /* ax */ g65816_op_ldy_dpb, g65816_op_lda_dpb, g65816_op_ldx_dpb, g65816_op_lda_ildpb, /* ax */ g65816_op_tayb, g65816_op_lda_constb, g65816_op_taxb, g65816_op_plb, /* ax */ g65816_op_ldy_addrb, g65816_op_lda_addrb, g65816_op_ldx_addrb, g65816_op_lda_longb, +/* bx */ g65816_op_bcs, g65816_op_lda_idpyb, g65816_op_lda_idpb, g65816_op_lda_isryb, /* bx */ g65816_op_ldy_dpxb, g65816_op_lda_dpxb, g65816_op_ldx_dpyb, g65816_op_lda_ildpyb, /* bx */ g65816_op_clv, g65816_op_lda_addryb, g65816_op_tsxb, g65816_op_tyxb, /* bx */ g65816_op_ldy_addrxb, g65816_op_lda_addrxb, g65816_op_ldx_addryb, g65816_op_lda_longxb, + +/* cx */ g65816_op_cpy_constb, g65816_op_cmp_idpxb, g65816_op_rep, g65816_op_cmp_srb, /* cx */ g65816_op_cpy_dpb, g65816_op_cmp_dpb, g65816_op_dec_dpb, g65816_op_cmp_ildpb, /* cx */ g65816_op_inyb, g65816_op_cmp_constb, g65816_op_dexb, g65816_op_wai, /* cx */ g65816_op_cpy_addrb, g65816_op_cmp_addrb, g65816_op_dec_addrb, g65816_op_cmp_longb, +/* dx */ g65816_op_bne, g65816_op_cmp_idpyb, g65816_op_cmp_idpb, g65816_op_cmp_isryb, /* dx */ g65816_op_pei, g65816_op_cmp_dpxb, g65816_op_dec_dpxb, g65816_op_cmp_ildpyb, /* dx */ g65816_op_cld, g65816_op_cmp_addryb, g65816_op_phxb, g65816_op_stp, /* dx */ g65816_op_jmp_iladdr, g65816_op_cmp_addrxb, g65816_op_dec_addrxb, g65816_op_cmp_longxb, +/* ex */ g65816_op_cpx_constb, g65816_op_sbc_idpxb, g65816_op_sep, g65816_op_sbc_srb, /* ex */ g65816_op_cpx_dpb, g65816_op_sbc_dpb, g65816_op_inc_dpb, g65816_op_sbc_ildpb, /* ex */ g65816_op_inxb, g65816_op_sbc_constb, g65816_op_nop, g65816_op_xba, /* ex */ g65816_op_cpx_addrb, g65816_op_sbc_addrb, g65816_op_inc_addrb, g65816_op_sbc_longb, +/* fx */ g65816_op_beq, g65816_op_sbc_idpyb, g65816_op_sbc_idpb, g65816_op_sbc_isryb, /* fx */ g65816_op_pea, g65816_op_sbc_dpxb, g65816_op_inc_dpxb, g65816_op_sbc_ildpyb, /* fx */ g65816_op_sed, g65816_op_sbc_addryb, g65816_op_plxb, g65816_op_xce, /* fx */ g65816_op_jsr_iaddrx, g65816_op_sbc_addrxb, g65816_op_inc_addrxb, g65816_op_sbc_longxb +}; + +vfunc g65816_optbl_MX[256] = { // g65816_optbl_MX, g65816_optbl_MX, g65816_optbl_MX, + // ----------------------x0, ----------------------x1, ----------------------x2, ----------------------x3, -------- ----------------------x4, ----------------------x5, ----------------------x6, ----------------------x7, -------- ----------------------x8, ----------------------x9, ----------------------xa, ----------------------xb, -------- ----------------------xc, ----------------------xd, ----------------------xe, ----------------------xf, +/* 0x */ g65816_op_brkn, g65816_op_ora_idpxb, g65816_op_copn, g65816_op_ora_srb, /* 0x */ g65816_op_tsb_dpb, g65816_op_ora_dpb, g65816_op_asl_dpb, g65816_op_ora_ildpb, /* 0x */ g65816_op_php, g65816_op_ora_constb, g65816_op_aslb, g65816_op_phd, /* 0x */ g65816_op_tsb_addrb, g65816_op_ora_addrb, g65816_op_asl_addrb, g65816_op_ora_longb, +/* 1x */ g65816_op_bpl, g65816_op_ora_idpyb, g65816_op_ora_idpb, g65816_op_ora_isryb, /* 1x */ g65816_op_trb_dpb, g65816_op_ora_dpxb, g65816_op_asl_dpxb, g65816_op_ora_ildpyb, /* 1x */ g65816_op_clc, g65816_op_ora_addryb, g65816_op_incb, g65816_op_tcsn, /* 1x */ g65816_op_trb_addrb, g65816_op_ora_addrxb, g65816_op_asl_addrxb, g65816_op_ora_longxb, +/* 2x */ g65816_op_jsr_addr, g65816_op_and_idpxb, g65816_op_jsr_long, g65816_op_and_srb, /* 2x */ g65816_op_bit_dpb, g65816_op_and_dpb, g65816_op_rol_dpb, g65816_op_and_ildpb, /* 2x */ g65816_op_plp, g65816_op_and_constb, g65816_op_rolb, g65816_op_pld, /* 2x */ g65816_op_bit_addrb, g65816_op_and_addrb, g65816_op_rol_addrb, g65816_op_and_longb, +/* 3x */ g65816_op_bmi, g65816_op_and_idpyb, g65816_op_and_idpb, g65816_op_and_isryb, /* 3x */ g65816_op_bit_dpxb, g65816_op_and_dpxb, g65816_op_rol_dpxb, g65816_op_and_ildpyb, /* 3x */ g65816_op_sec, g65816_op_and_addryb, g65816_op_decb, g65816_op_tscn, /* 3x */ g65816_op_bit_addrxb, g65816_op_and_addrxb, g65816_op_rol_addrxb, g65816_op_and_longxb, + +/* 4x */ g65816_op_rtin, g65816_op_eor_idpxb, g65816_op_wdm, g65816_op_eor_srb, /* 4x */ g65816_op_mvp, g65816_op_eor_dpb, g65816_op_lsr_dpb, g65816_op_eor_ildpb, /* 4x */ g65816_op_phab, g65816_op_eor_constb, g65816_op_lsrb, g65816_op_phk, /* 4x */ g65816_op_jmp_addr, g65816_op_eor_addrb, g65816_op_lsr_addrb, g65816_op_eor_longb, +/* 5x */ g65816_op_bvc, g65816_op_eor_idpyb, g65816_op_eor_idpb, g65816_op_eor_isryb, /* 5x */ g65816_op_mvn, g65816_op_eor_dpxb, g65816_op_lsr_dpxb, g65816_op_eor_ildpyb, /* 5x */ g65816_op_cli, g65816_op_eor_addryb, g65816_op_phyb, g65816_op_tcd, /* 5x */ g65816_op_jmp_long, g65816_op_eor_addrxb, g65816_op_lsr_addrxb, g65816_op_eor_longxb, +/* 6x */ g65816_op_rts, g65816_op_adc_idpxb, g65816_op_per, g65816_op_adc_srb, /* 6x */ g65816_op_stz_dpb, g65816_op_adc_dpb, g65816_op_ror_dpb, g65816_op_adc_ildpb, /* 6x */ g65816_op_plab, g65816_op_adc_constb, g65816_op_rorb, g65816_op_rtl, /* 6x */ g65816_op_jmp_iaddr, g65816_op_adc_addrb, g65816_op_ror_addrb, g65816_op_adc_longb, +/* 7x */ g65816_op_bvs, g65816_op_adc_idpyb, g65816_op_adc_idpb, g65816_op_adc_isryb, /* 7x */ g65816_op_stz_dpxb, g65816_op_adc_dpxb, g65816_op_ror_dpxb, g65816_op_adc_ildpyb, /* 7x */ g65816_op_sei, g65816_op_adc_addryb, g65816_op_plyb, g65816_tdc, /* 7x */ g65816_op_jmp_iaddrx, g65816_op_adc_addrxb, g65816_op_ror_addrxb, g65816_op_adc_longxb, + +/* 8x */ g65816_op_bra, g65816_op_sta_idpxb, g65816_op_brl, g65816_op_sta_srb, /* 8x */ g65816_op_sty_dpb, g65816_op_sta_dpb, g65816_op_stx_dpb, g65816_op_sta_ildpb, /* 8x */ g65816_op_deyb, g65816_op_bit_constb, g65816_op_txab, g65816_op_phb, /* 8x */ g65816_op_sty_addrb, g65816_op_sta_addrb, g65816_op_stx_addrb, g65816_op_sta_longb, +/* 9x */ g65816_op_bcc, g65816_op_sta_idpyb, g65816_op_sta_idpb, g65816_op_sta_isryb, /* 9x */ g65816_op_sty_dpxb, g65816_op_sta_dpxb, g65816_op_stx_dpyb, g65816_op_sta_ildpyb, /* 9x */ g65816_op_tyab, g65816_op_sta_addryb, g65816_op_txsb, g65816_op_txyb, /* 9x */ g65816_op_stz_addrb, g65816_op_sta_addrxb, g65816_op_stz_addrxb, g65816_op_sta_longxb, +/* ax */ g65816_op_ldy_constb, g65816_op_lda_idpxb, g65816_op_ldx_constb, g65816_op_lda_srb, /* ax */ g65816_op_ldy_dpb, g65816_op_lda_dpb, g65816_op_ldx_dpb, g65816_op_lda_ildpb, /* ax */ g65816_op_tayb, g65816_op_lda_constb, g65816_op_taxb, g65816_op_plb, /* ax */ g65816_op_ldy_addrb, g65816_op_lda_addrb, g65816_op_ldx_addrb, g65816_op_lda_longb, +/* bx */ g65816_op_bcs, g65816_op_lda_idpyb, g65816_op_lda_idpb, g65816_op_lda_isryb, /* bx */ g65816_op_ldy_dpxb, g65816_op_lda_dpxb, g65816_op_ldx_dpyb, g65816_op_lda_ildpyb, /* bx */ g65816_op_clv, g65816_op_lda_addryb, g65816_op_tsxb, g65816_op_tyxb, /* bx */ g65816_op_ldy_addrxb, g65816_op_lda_addrxb, g65816_op_ldx_addryb, g65816_op_lda_longxb, + +/* cx */ g65816_op_cpy_constb, g65816_op_cmp_idpxb, g65816_op_rep, g65816_op_cmp_srb, /* cx */ g65816_op_cpy_dpb, g65816_op_cmp_dpb, g65816_op_dec_dpb, g65816_op_cmp_ildpb, /* cx */ g65816_op_inyb, g65816_op_cmp_constb, g65816_op_dexb, g65816_op_wai, /* cx */ g65816_op_cpy_addrb, g65816_op_cmp_addrb, g65816_op_dec_addrb, g65816_op_cmp_longb, +/* dx */ g65816_op_bne, g65816_op_cmp_idpyb, g65816_op_cmp_idpb, g65816_op_cmp_isryb, /* dx */ g65816_op_pei, g65816_op_cmp_dpxb, g65816_op_dec_dpxb, g65816_op_cmp_ildpyb, /* dx */ g65816_op_cld, g65816_op_cmp_addryb, g65816_op_phxb, g65816_op_stp, /* dx */ g65816_op_jmp_iladdr, g65816_op_cmp_addrxb, g65816_op_dec_addrxb, g65816_op_cmp_longxb, +/* ex */ g65816_op_cpx_constb, g65816_op_sbc_idpxb, g65816_op_sep, g65816_op_sbc_srb, /* ex */ g65816_op_cpx_dpb, g65816_op_sbc_dpb, g65816_op_inc_dpb, g65816_op_sbc_ildpb, /* ex */ g65816_op_inxb, g65816_op_sbc_constb, g65816_op_nop, g65816_op_xba, /* ex */ g65816_op_cpx_addrb, g65816_op_sbc_addrb, g65816_op_inc_addrb, g65816_op_sbc_longb, +/* fx */ g65816_op_beq, g65816_op_sbc_idpyb, g65816_op_sbc_idpb, g65816_op_sbc_isryb, /* fx */ g65816_op_pea, g65816_op_sbc_dpxb, g65816_op_inc_dpxb, g65816_op_sbc_ildpyb, /* fx */ g65816_op_sed, g65816_op_sbc_addryb, g65816_op_plxb, g65816_op_xce, /* fx */ g65816_op_jsr_iaddrx, g65816_op_sbc_addrxb, g65816_op_inc_addrxb, g65816_op_sbc_longxb +}; + +vfunc g65816_optbl_Mx[256] = { // g65816_optbl_Mx, g65816_optbl_Mx, g65816_optbl_Mx, + // ----------------------x0, ----------------------x1, ----------------------x2, ----------------------x3, -------- ----------------------x4, ----------------------x5, ----------------------x6, ----------------------x7, -------- ----------------------x8, ----------------------x9, ----------------------xa, ----------------------xb, -------- ----------------------xc, ----------------------xd, ----------------------xe, ----------------------xf, +/* 0x */ g65816_op_brkn, g65816_op_ora_idpxb, g65816_op_copn, g65816_op_ora_srb, /* 0x */ g65816_op_tsb_dpb, g65816_op_ora_dpb, g65816_op_asl_dpb, g65816_op_ora_ildpb, /* 0x */ g65816_op_php, g65816_op_ora_constb, g65816_op_aslb, g65816_op_phd, /* 0x */ g65816_op_tsb_addrb, g65816_op_ora_addrb, g65816_op_asl_addrb, g65816_op_ora_longb, +/* 1x */ g65816_op_bpl, g65816_op_ora_idpyb, g65816_op_ora_idpb, g65816_op_ora_isryb, /* 1x */ g65816_op_trb_dpb, g65816_op_ora_dpxb, g65816_op_asl_dpxb, g65816_op_ora_ildpyb, /* 1x */ g65816_op_clc, g65816_op_ora_addryb, g65816_op_incb, g65816_op_tcsn, /* 1x */ g65816_op_trb_addrb, g65816_op_ora_addrxb, g65816_op_asl_addrxb, g65816_op_ora_longxb, +/* 2x */ g65816_op_jsr_addr, g65816_op_and_idpxb, g65816_op_jsr_long, g65816_op_and_srb, /* 2x */ g65816_op_bit_dpb, g65816_op_and_dpb, g65816_op_rol_dpb, g65816_op_and_ildpb, /* 2x */ g65816_op_plp, g65816_op_and_constb, g65816_op_rolb, g65816_op_pld, /* 2x */ g65816_op_bit_addrb, g65816_op_and_addrb, g65816_op_rol_addrb, g65816_op_and_longb, +/* 3x */ g65816_op_bmi, g65816_op_and_idpyb, g65816_op_and_idpb, g65816_op_and_isryb, /* 3x */ g65816_op_bit_dpxb, g65816_op_and_dpxb, g65816_op_rol_dpxb, g65816_op_and_ildpyb, /* 3x */ g65816_op_sec, g65816_op_and_addryb, g65816_op_decb, g65816_op_tscn, /* 3x */ g65816_op_bit_addrxb, g65816_op_and_addrxb, g65816_op_rol_addrxb, g65816_op_and_longxb, + +/* 4x */ g65816_op_rtin, g65816_op_eor_idpxb, g65816_op_wdm, g65816_op_eor_srb, /* 4x */ g65816_op_mvp, g65816_op_eor_dpb, g65816_op_lsr_dpb, g65816_op_eor_ildpb, /* 4x */ g65816_op_phab, g65816_op_eor_constb, g65816_op_lsrb, g65816_op_phk, /* 4x */ g65816_op_jmp_addr, g65816_op_eor_addrb, g65816_op_lsr_addrb, g65816_op_eor_longb, +/* 5x */ g65816_op_bvc, g65816_op_eor_idpyb, g65816_op_eor_idpb, g65816_op_eor_isryb, /* 5x */ g65816_op_mvn, g65816_op_eor_dpxb, g65816_op_lsr_dpxb, g65816_op_eor_ildpyb, /* 5x */ g65816_op_cli, g65816_op_eor_addryb, g65816_op_phyw, g65816_op_tcd, /* 5x */ g65816_op_jmp_long, g65816_op_eor_addrxb, g65816_op_lsr_addrxb, g65816_op_eor_longxb, +/* 6x */ g65816_op_rts, g65816_op_adc_idpxb, g65816_op_per, g65816_op_adc_srb, /* 6x */ g65816_op_stz_dpb, g65816_op_adc_dpb, g65816_op_ror_dpb, g65816_op_adc_ildpb, /* 6x */ g65816_op_plab, g65816_op_adc_constb, g65816_op_rorb, g65816_op_rtl, /* 6x */ g65816_op_jmp_iaddr, g65816_op_adc_addrb, g65816_op_ror_addrb, g65816_op_adc_longb, +/* 7x */ g65816_op_bvs, g65816_op_adc_idpyb, g65816_op_adc_idpb, g65816_op_adc_isryb, /* 7x */ g65816_op_stz_dpxb, g65816_op_adc_dpxb, g65816_op_ror_dpxb, g65816_op_adc_ildpyb, /* 7x */ g65816_op_sei, g65816_op_adc_addryb, g65816_op_plyw, g65816_tdc, /* 7x */ g65816_op_jmp_iaddrx, g65816_op_adc_addrxb, g65816_op_ror_addrxb, g65816_op_adc_longxb, + +/* 8x */ g65816_op_bra, g65816_op_sta_idpxb, g65816_op_brl, g65816_op_sta_srb, /* 8x */ g65816_op_sty_dpw, g65816_op_sta_dpb, g65816_op_stx_dpw, g65816_op_sta_ildpb, /* 8x */ g65816_op_deyw, g65816_op_bit_constb, g65816_op_txab, g65816_op_phb, /* 8x */ g65816_op_sty_addrw, g65816_op_sta_addrb, g65816_op_stx_addrw, g65816_op_sta_longb, +/* 9x */ g65816_op_bcc, g65816_op_sta_idpyb, g65816_op_sta_idpb, g65816_op_sta_isryb, /* 9x */ g65816_op_sty_dpxw, g65816_op_sta_dpxb, g65816_op_stx_dpyw, g65816_op_sta_ildpyb, /* 9x */ g65816_op_tyab, g65816_op_sta_addryb, g65816_op_txsw, g65816_op_txyw, /* 9x */ g65816_op_stz_addrb, g65816_op_sta_addrxb, g65816_op_stz_addrxb, g65816_op_sta_longxb, +/* ax */ g65816_op_ldy_constw, g65816_op_lda_idpxb, g65816_op_ldx_constw, g65816_op_lda_srb, /* ax */ g65816_op_ldy_dpw, g65816_op_lda_dpb, g65816_op_ldx_dpw, g65816_op_lda_ildpb, /* ax */ g65816_op_tayw, g65816_op_lda_constb, g65816_op_taxw, g65816_op_plb, /* ax */ g65816_op_ldy_addrw, g65816_op_lda_addrb, g65816_op_ldx_addrw, g65816_op_lda_longb, +/* bx */ g65816_op_bcs, g65816_op_lda_idpyb, g65816_op_lda_idpb, g65816_op_lda_isryb, /* bx */ g65816_op_ldy_dpxw, g65816_op_lda_dpxb, g65816_op_ldx_dpyw, g65816_op_lda_ildpyb, /* bx */ g65816_op_clv, g65816_op_lda_addryb, g65816_op_tsxw, g65816_op_tyxw, /* bx */ g65816_op_ldy_addrxw, g65816_op_lda_addrxb, g65816_op_ldx_addryw, g65816_op_lda_longxb, + +/* cx */ g65816_op_cpy_constw, g65816_op_cmp_idpxb, g65816_op_rep, g65816_op_cmp_srb, /* cx */ g65816_op_cpy_dpw, g65816_op_cmp_dpb, g65816_op_dec_dpb, g65816_op_cmp_ildpb, /* cx */ g65816_op_inyw, g65816_op_cmp_constb, g65816_op_dexw, g65816_op_wai, /* cx */ g65816_op_cpy_addrw, g65816_op_cmp_addrb, g65816_op_dec_addrb, g65816_op_cmp_longb, +/* dx */ g65816_op_bne, g65816_op_cmp_idpyb, g65816_op_cmp_idpb, g65816_op_cmp_isryb, /* dx */ g65816_op_pei, g65816_op_cmp_dpxb, g65816_op_dec_dpxb, g65816_op_cmp_ildpyb, /* dx */ g65816_op_cld, g65816_op_cmp_addryb, g65816_op_phxw, g65816_op_stp, /* dx */ g65816_op_jmp_iladdr, g65816_op_cmp_addrxb, g65816_op_dec_addrxb, g65816_op_cmp_longxb, +/* ex */ g65816_op_cpx_constw, g65816_op_sbc_idpxb, g65816_op_sep, g65816_op_sbc_srb, /* ex */ g65816_op_cpx_dpw, g65816_op_sbc_dpb, g65816_op_inc_dpb, g65816_op_sbc_ildpb, /* ex */ g65816_op_inxw, g65816_op_sbc_constb, g65816_op_nop, g65816_op_xba, /* ex */ g65816_op_cpx_addrw, g65816_op_sbc_addrb, g65816_op_inc_addrb, g65816_op_sbc_longb, +/* fx */ g65816_op_beq, g65816_op_sbc_idpyb, g65816_op_sbc_idpb, g65816_op_sbc_isryb, /* fx */ g65816_op_pea, g65816_op_sbc_dpxb, g65816_op_inc_dpxb, g65816_op_sbc_ildpyb, /* fx */ g65816_op_sed, g65816_op_sbc_addryb, g65816_op_plxw, g65816_op_xce, /* fx */ g65816_op_jsr_iaddrx, g65816_op_sbc_addrxb, g65816_op_inc_addrxb, g65816_op_sbc_longxb +}; + +vfunc g65816_optbl_mX[256] = { // g65816_optbl_mX, g65816_optbl_mX, g65816_optbl_mX, + // ----------------------x0, ----------------------x1, ----------------------x2, ----------------------x3, -------- ----------------------x4, ----------------------x5, ----------------------x6, ----------------------x7, -------- ----------------------x8, ----------------------x9, ----------------------xa, ----------------------xb, -------- ----------------------xc, ----------------------xd, ----------------------xe, ----------------------xf, +/* 0x */ g65816_op_brkn, g65816_op_ora_idpxw, g65816_op_copn, g65816_op_ora_srw, /* 0x */ g65816_op_tsb_dpw, g65816_op_ora_dpw, g65816_op_asl_dpw, g65816_op_ora_ildpw, /* 0x */ g65816_op_php, g65816_op_ora_constw, g65816_op_aslw, g65816_op_phd, /* 0x */ g65816_op_tsb_addrw, g65816_op_ora_addrw, g65816_op_asl_addrw, g65816_op_ora_longw, +/* 1x */ g65816_op_bpl, g65816_op_ora_idpyw, g65816_op_ora_idpw, g65816_op_ora_isryw, /* 1x */ g65816_op_trb_dpw, g65816_op_ora_dpxw, g65816_op_asl_dpxw, g65816_op_ora_ildpyw, /* 1x */ g65816_op_clc, g65816_op_ora_addryw, g65816_op_incw, g65816_op_tcsn, /* 1x */ g65816_op_trb_addrw, g65816_op_ora_addrxw, g65816_op_asl_addrxw, g65816_op_ora_longxw, +/* 2x */ g65816_op_jsr_addr, g65816_op_and_idpxw, g65816_op_jsr_long, g65816_op_and_srw, /* 2x */ g65816_op_bit_dpw, g65816_op_and_dpw, g65816_op_rol_dpw, g65816_op_and_ildpw, /* 2x */ g65816_op_plp, g65816_op_and_constw, g65816_op_rolw, g65816_op_pld, /* 2x */ g65816_op_bit_addrw, g65816_op_and_addrw, g65816_op_rol_addrw, g65816_op_and_longw, +/* 3x */ g65816_op_bmi, g65816_op_and_idpyw, g65816_op_and_idpw, g65816_op_and_isryw, /* 3x */ g65816_op_bit_dpxw, g65816_op_and_dpxw, g65816_op_rol_dpxw, g65816_op_and_ildpyw, /* 3x */ g65816_op_sec, g65816_op_and_addryw, g65816_op_decw, g65816_op_tscn, /* 3x */ g65816_op_bit_addrxw, g65816_op_and_addrxw, g65816_op_rol_addrxw, g65816_op_and_longxw, + +/* 4x */ g65816_op_rtin, g65816_op_eor_idpxw, g65816_op_wdm, g65816_op_eor_srw, /* 4x */ g65816_op_mvp, g65816_op_eor_dpw, g65816_op_lsr_dpw, g65816_op_eor_ildpw, /* 4x */ g65816_op_phaw, g65816_op_eor_constw, g65816_op_lsrw, g65816_op_phk, /* 4x */ g65816_op_jmp_addr, g65816_op_eor_addrw, g65816_op_lsr_addrw, g65816_op_eor_longw, +/* 5x */ g65816_op_bvc, g65816_op_eor_idpyw, g65816_op_eor_idpw, g65816_op_eor_isryw, /* 5x */ g65816_op_mvn, g65816_op_eor_dpxw, g65816_op_lsr_dpxw, g65816_op_eor_ildpyw, /* 5x */ g65816_op_cli, g65816_op_eor_addryw, g65816_op_phyb, g65816_op_tcd, /* 5x */ g65816_op_jmp_long, g65816_op_eor_addrxw, g65816_op_lsr_addrxw, g65816_op_eor_longxw, +/* 6x */ g65816_op_rts, g65816_op_adc_idpxw, g65816_op_per, g65816_op_adc_srw, /* 6x */ g65816_op_stz_dpw, g65816_op_adc_dpw, g65816_op_ror_dpw, g65816_op_adc_ildpw, /* 6x */ g65816_op_plaw, g65816_op_adc_constw, g65816_op_rorw, g65816_op_rtl, /* 6x */ g65816_op_jmp_iaddr, g65816_op_adc_addrw, g65816_op_ror_addrw, g65816_op_adc_longw, +/* 7x */ g65816_op_bvs, g65816_op_adc_idpyw, g65816_op_adc_idpw, g65816_op_adc_isryw, /* 7x */ g65816_op_stz_dpxw, g65816_op_adc_dpxw, g65816_op_ror_dpxw, g65816_op_adc_ildpyw, /* 7x */ g65816_op_sei, g65816_op_adc_addryw, g65816_op_plyb, g65816_tdc, /* 7x */ g65816_op_jmp_iaddrx, g65816_op_adc_addrxw, g65816_op_ror_addrxw, g65816_op_adc_longxw, + +/* 8x */ g65816_op_bra, g65816_op_sta_idpxw, g65816_op_brl, g65816_op_sta_srw, /* 8x */ g65816_op_sty_dpb, g65816_op_sta_dpw, g65816_op_stx_dpb, g65816_op_sta_ildpw, /* 8x */ g65816_op_deyb, g65816_op_bit_constw, g65816_op_txaw, g65816_op_phb, /* 8x */ g65816_op_sty_addrb, g65816_op_sta_addrw, g65816_op_stx_addrb, g65816_op_sta_longw, +/* 9x */ g65816_op_bcc, g65816_op_sta_idpyw, g65816_op_sta_idpw, g65816_op_sta_isryw, /* 9x */ g65816_op_sty_dpxb, g65816_op_sta_dpxw, g65816_op_stx_dpyb, g65816_op_sta_ildpyw, /* 9x */ g65816_op_tyaw, g65816_op_sta_addryw, g65816_op_txsb, g65816_op_txyb, /* 9x */ g65816_op_stz_addrw, g65816_op_sta_addrxw, g65816_op_stz_addrxw, g65816_op_sta_longxw, +/* ax */ g65816_op_ldy_constb, g65816_op_lda_idpxw, g65816_op_ldx_constb, g65816_op_lda_srw, /* ax */ g65816_op_ldy_dpb, g65816_op_lda_dpw, g65816_op_ldx_dpb, g65816_op_lda_ildpw, /* ax */ g65816_op_tayb, g65816_op_lda_constw, g65816_op_taxb, g65816_op_plb, /* ax */ g65816_op_ldy_addrb, g65816_op_lda_addrw, g65816_op_ldx_addrb, g65816_op_lda_longw, +/* bx */ g65816_op_bcs, g65816_op_lda_idpyw, g65816_op_lda_idpw, g65816_op_lda_isryw, /* bx */ g65816_op_ldy_dpxb, g65816_op_lda_dpxw, g65816_op_ldx_dpyb, g65816_op_lda_ildpyw, /* bx */ g65816_op_clv, g65816_op_lda_addryw, g65816_op_tsxb, g65816_op_tyxb, /* bx */ g65816_op_ldy_addrxb, g65816_op_lda_addrxw, g65816_op_ldx_addryb, g65816_op_lda_longxw, + +/* cx */ g65816_op_cpy_constb, g65816_op_cmp_idpxw, g65816_op_rep, g65816_op_cmp_srw, /* cx */ g65816_op_cpy_dpb, g65816_op_cmp_dpw, g65816_op_dec_dpw, g65816_op_cmp_ildpw, /* cx */ g65816_op_inyb, g65816_op_cmp_constw, g65816_op_dexb, g65816_op_wai, /* cx */ g65816_op_cpy_addrb, g65816_op_cmp_addrw, g65816_op_dec_addrw, g65816_op_cmp_longw, +/* dx */ g65816_op_bne, g65816_op_cmp_idpyw, g65816_op_cmp_idpw, g65816_op_cmp_isryw, /* dx */ g65816_op_pei, g65816_op_cmp_dpxw, g65816_op_dec_dpxw, g65816_op_cmp_ildpyw, /* dx */ g65816_op_cld, g65816_op_cmp_addryw, g65816_op_phxb, g65816_op_stp, /* dx */ g65816_op_jmp_iladdr, g65816_op_cmp_addrxw, g65816_op_dec_addrxw, g65816_op_cmp_longxw, +/* ex */ g65816_op_cpx_constb, g65816_op_sbc_idpxw, g65816_op_sep, g65816_op_sbc_srw, /* ex */ g65816_op_cpx_dpb, g65816_op_sbc_dpw, g65816_op_inc_dpw, g65816_op_sbc_ildpw, /* ex */ g65816_op_inxb, g65816_op_sbc_constw, g65816_op_nop, g65816_op_xba, /* ex */ g65816_op_cpx_addrb, g65816_op_sbc_addrw, g65816_op_inc_addrw, g65816_op_sbc_longw, +/* fx */ g65816_op_beq, g65816_op_sbc_idpyw, g65816_op_sbc_idpw, g65816_op_sbc_isryw, /* fx */ g65816_op_pea, g65816_op_sbc_dpxw, g65816_op_inc_dpxw, g65816_op_sbc_ildpyw, /* fx */ g65816_op_sed, g65816_op_sbc_addryw, g65816_op_plxb, g65816_op_xce, /* fx */ g65816_op_jsr_iaddrx, g65816_op_sbc_addrxw, g65816_op_inc_addrxw, g65816_op_sbc_longxw +}; + +vfunc g65816_optbl_mx[256] = { // g65816_optbl_mx, g65816_optbl_mx, g65816_optbl_mx, + // ----------------------x0, ----------------------x1, ----------------------x2, ----------------------x3, -------- ----------------------x4, ----------------------x5, ----------------------x6, ----------------------x7, -------- ----------------------x8, ----------------------x9, ----------------------xa, ----------------------xb, -------- ----------------------xc, ----------------------xd, ----------------------xe, ----------------------xf, +/* 0x */ g65816_op_brkn, g65816_op_ora_idpxw, g65816_op_copn, g65816_op_ora_srw, /* 0x */ g65816_op_tsb_dpw, g65816_op_ora_dpw, g65816_op_asl_dpw, g65816_op_ora_ildpw, /* 0x */ g65816_op_php, g65816_op_ora_constw, g65816_op_aslw, g65816_op_phd, /* 0x */ g65816_op_tsb_addrw, g65816_op_ora_addrw, g65816_op_asl_addrw, g65816_op_ora_longw, +/* 1x */ g65816_op_bpl, g65816_op_ora_idpyw, g65816_op_ora_idpw, g65816_op_ora_isryw, /* 1x */ g65816_op_trb_dpw, g65816_op_ora_dpxw, g65816_op_asl_dpxw, g65816_op_ora_ildpyw, /* 1x */ g65816_op_clc, g65816_op_ora_addryw, g65816_op_incw, g65816_op_tcsn, /* 1x */ g65816_op_trb_addrw, g65816_op_ora_addrxw, g65816_op_asl_addrxw, g65816_op_ora_longxw, +/* 2x */ g65816_op_jsr_addr, g65816_op_and_idpxw, g65816_op_jsr_long, g65816_op_and_srw, /* 2x */ g65816_op_bit_dpw, g65816_op_and_dpw, g65816_op_rol_dpw, g65816_op_and_ildpw, /* 2x */ g65816_op_plp, g65816_op_and_constw, g65816_op_rolw, g65816_op_pld, /* 2x */ g65816_op_bit_addrw, g65816_op_and_addrw, g65816_op_rol_addrw, g65816_op_and_longw, +/* 3x */ g65816_op_bmi, g65816_op_and_idpyw, g65816_op_and_idpw, g65816_op_and_isryw, /* 3x */ g65816_op_bit_dpxw, g65816_op_and_dpxw, g65816_op_rol_dpxw, g65816_op_and_ildpyw, /* 3x */ g65816_op_sec, g65816_op_and_addryw, g65816_op_decw, g65816_op_tscn, /* 3x */ g65816_op_bit_addrxw, g65816_op_and_addrxw, g65816_op_rol_addrxw, g65816_op_and_longxw, + +/* 4x */ g65816_op_rtin, g65816_op_eor_idpxw, g65816_op_wdm, g65816_op_eor_srw, /* 4x */ g65816_op_mvp, g65816_op_eor_dpw, g65816_op_lsr_dpw, g65816_op_eor_ildpw, /* 4x */ g65816_op_phaw, g65816_op_eor_constw, g65816_op_lsrw, g65816_op_phk, /* 4x */ g65816_op_jmp_addr, g65816_op_eor_addrw, g65816_op_lsr_addrw, g65816_op_eor_longw, +/* 5x */ g65816_op_bvc, g65816_op_eor_idpyw, g65816_op_eor_idpw, g65816_op_eor_isryw, /* 5x */ g65816_op_mvn, g65816_op_eor_dpxw, g65816_op_lsr_dpxw, g65816_op_eor_ildpyw, /* 5x */ g65816_op_cli, g65816_op_eor_addryw, g65816_op_phyw, g65816_op_tcd, /* 5x */ g65816_op_jmp_long, g65816_op_eor_addrxw, g65816_op_lsr_addrxw, g65816_op_eor_longxw, +/* 6x */ g65816_op_rts, g65816_op_adc_idpxw, g65816_op_per, g65816_op_adc_srw, /* 6x */ g65816_op_stz_dpw, g65816_op_adc_dpw, g65816_op_ror_dpw, g65816_op_adc_ildpw, /* 6x */ g65816_op_plaw, g65816_op_adc_constw, g65816_op_rorw, g65816_op_rtl, /* 6x */ g65816_op_jmp_iaddr, g65816_op_adc_addrw, g65816_op_ror_addrw, g65816_op_adc_longw, +/* 7x */ g65816_op_bvs, g65816_op_adc_idpyw, g65816_op_adc_idpw, g65816_op_adc_isryw, /* 7x */ g65816_op_stz_dpxw, g65816_op_adc_dpxw, g65816_op_ror_dpxw, g65816_op_adc_ildpyw, /* 7x */ g65816_op_sei, g65816_op_adc_addryw, g65816_op_plyw, g65816_tdc, /* 7x */ g65816_op_jmp_iaddrx, g65816_op_adc_addrxw, g65816_op_ror_addrxw, g65816_op_adc_longxw, + +/* 8x */ g65816_op_bra, g65816_op_sta_idpxw, g65816_op_brl, g65816_op_sta_srw, /* 8x */ g65816_op_sty_dpw, g65816_op_sta_dpw, g65816_op_stx_dpw, g65816_op_sta_ildpw, /* 8x */ g65816_op_deyw, g65816_op_bit_constw, g65816_op_txaw, g65816_op_phb, /* 8x */ g65816_op_sty_addrw, g65816_op_sta_addrw, g65816_op_stx_addrw, g65816_op_sta_longw, +/* 9x */ g65816_op_bcc, g65816_op_sta_idpyw, g65816_op_sta_idpw, g65816_op_sta_isryw, /* 9x */ g65816_op_sty_dpxw, g65816_op_sta_dpxw, g65816_op_stx_dpyw, g65816_op_sta_ildpyw, /* 9x */ g65816_op_tyaw, g65816_op_sta_addryw, g65816_op_txsw, g65816_op_txyw, /* 9x */ g65816_op_stz_addrw, g65816_op_sta_addrxw, g65816_op_stz_addrxw, g65816_op_sta_longxw, +/* ax */ g65816_op_ldy_constw, g65816_op_lda_idpxw, g65816_op_ldx_constw, g65816_op_lda_srw, /* ax */ g65816_op_ldy_dpw, g65816_op_lda_dpw, g65816_op_ldx_dpw, g65816_op_lda_ildpw, /* ax */ g65816_op_tayw, g65816_op_lda_constw, g65816_op_taxw, g65816_op_plb, /* ax */ g65816_op_ldy_addrw, g65816_op_lda_addrw, g65816_op_ldx_addrw, g65816_op_lda_longw, +/* bx */ g65816_op_bcs, g65816_op_lda_idpyw, g65816_op_lda_idpw, g65816_op_lda_isryw, /* bx */ g65816_op_ldy_dpxw, g65816_op_lda_dpxw, g65816_op_ldx_dpyw, g65816_op_lda_ildpyw, /* bx */ g65816_op_clv, g65816_op_lda_addryw, g65816_op_tsxw, g65816_op_tyxw, /* bx */ g65816_op_ldy_addrxw, g65816_op_lda_addrxw, g65816_op_ldx_addryw, g65816_op_lda_longxw, + +/* cx */ g65816_op_cpy_constw, g65816_op_cmp_idpxw, g65816_op_rep, g65816_op_cmp_srw, /* cx */ g65816_op_cpy_dpw, g65816_op_cmp_dpw, g65816_op_dec_dpw, g65816_op_cmp_ildpw, /* cx */ g65816_op_inyw, g65816_op_cmp_constw, g65816_op_dexw, g65816_op_wai, /* cx */ g65816_op_cpy_addrw, g65816_op_cmp_addrw, g65816_op_dec_addrw, g65816_op_cmp_longw, +/* dx */ g65816_op_bne, g65816_op_cmp_idpyw, g65816_op_cmp_idpw, g65816_op_cmp_isryw, /* dx */ g65816_op_pei, g65816_op_cmp_dpxw, g65816_op_dec_dpxw, g65816_op_cmp_ildpyw, /* dx */ g65816_op_cld, g65816_op_cmp_addryw, g65816_op_phxw, g65816_op_stp, /* dx */ g65816_op_jmp_iladdr, g65816_op_cmp_addrxw, g65816_op_dec_addrxw, g65816_op_cmp_longxw, +/* ex */ g65816_op_cpx_constw, g65816_op_sbc_idpxw, g65816_op_sep, g65816_op_sbc_srw, /* ex */ g65816_op_cpx_dpw, g65816_op_sbc_dpw, g65816_op_inc_dpw, g65816_op_sbc_ildpw, /* ex */ g65816_op_inxw, g65816_op_sbc_constw, g65816_op_nop, g65816_op_xba, /* ex */ g65816_op_cpx_addrw, g65816_op_sbc_addrw, g65816_op_inc_addrw, g65816_op_sbc_longw, +/* fx */ g65816_op_beq, g65816_op_sbc_idpyw, g65816_op_sbc_idpw, g65816_op_sbc_isryw, /* fx */ g65816_op_pea, g65816_op_sbc_dpxw, g65816_op_inc_dpxw, g65816_op_sbc_ildpyw, /* fx */ g65816_op_sed, g65816_op_sbc_addryw, g65816_op_plxw, g65816_op_xce, /* fx */ g65816_op_jsr_iaddrx, g65816_op_sbc_addrxw, g65816_op_inc_addrxw, g65816_op_sbc_longxw +}; diff --git a/bsnes/cpu/g65816_ops_adc.cpp b/bsnes/cpu/g65816_ops_adc.cpp new file mode 100644 index 00000000..06134a76 --- /dev/null +++ b/bsnes/cpu/g65816_ops_adc.cpp @@ -0,0 +1,345 @@ +#define bcd_add_adjust_byte() \ + if(gx816->regs.p & PF_D) { \ + if(((r ) & 15) > 9)r += 6; \ + if(((r >> 4) & 15) > 9)r += 6 << 4; \ + } + +#define bcd_add_adjust_word() \ + if(gx816->regs.p & PF_D) { \ + if(((r ) & 15) > 9)r += 6; \ + if(((r >> 4) & 15) > 9)r += 6 << 4; \ + if(((r >> 8) & 15) > 9)r += 6 << 8; \ + if(((r >> 12) & 15) > 9)r += 6 << 12; \ + } + +#define g65816_if_adc_b() \ +byte c = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr); \ +int r = gx816->regs.a.b + c + (gx816->regs.p & PF_C); \ + bcd_add_adjust_byte() \ + g65816_testn(r & 0x80); \ + g65816_testv(~(gx816->regs.a.b ^ c) & (gx816->regs.a.b ^ (byte)r) & 0x80); \ + g65816_testz((byte)r == 0); \ + g65816_testc(r > 0xff); \ + gx816->regs.a.b = r + +#define g65816_if_adc_w() \ +word c = gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr); \ +int r = gx816->regs.a.w + c + (gx816->regs.p & PF_C); \ + bcd_add_adjust_word() \ + g65816_testn(r & 0x8000); \ + g65816_testv(~(gx816->regs.a.w ^ c) & (gx816->regs.a.w ^ (word)r) & 0x8000); \ + g65816_testz((word)r == 0); \ + g65816_testc(r > 0xffff); \ + gx816->regs.a.w = r + +void g65816_op_adc_addrb(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDR); + g65816_if_adc_b(); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(1, dest_addr); +} + +void g65816_op_adc_addrw(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDR); + g65816_if_adc_w(); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(2, dest_addr); +} + +void g65816_op_adc_addrxb(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDRX); + g65816_if_adc_b(); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION4); +} + +void g65816_op_adc_addrxw(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDRX); + g65816_if_adc_w(); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION4); +} + +void g65816_op_adc_dpb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DP); + g65816_if_adc_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_adc_dpw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DP); + g65816_if_adc_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_adc_idpb(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_IDP); + g65816_if_adc_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, base_addr); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_adc_idpw(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_IDP); + g65816_if_adc_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, base_addr); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_adc_ildpb(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_ILDP); + g65816_if_adc_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(3, base_addr); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_adc_ildpw(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_ILDP); + g65816_if_adc_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(3, base_addr); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_adc_longb(void) { +g65816_prefetch(3); +g65816_getaddr(MEMMODE_LONG); + g65816_if_adc_b(); + g65816_incpc(4); + snes_time->add_cpu_pcycles(4); + snes_time->add_cpu_mcycles(1, dest_addr); +} + +void g65816_op_adc_longw(void) { +g65816_prefetch(3); +g65816_getaddr(MEMMODE_LONG); + g65816_if_adc_w(); + g65816_incpc(4); + snes_time->add_cpu_pcycles(4); + snes_time->add_cpu_mcycles(2, dest_addr); +} + +void g65816_op_adc_longxb(void) { +g65816_prefetch(3); +g65816_getaddr(MEMMODE_LONGX); + g65816_if_adc_b(); + g65816_incpc(4); + snes_time->add_cpu_pcycles(4); + snes_time->add_cpu_mcycles(1, dest_addr); +} + +void g65816_op_adc_longxw(void) { +g65816_prefetch(3); +g65816_getaddr(MEMMODE_LONGX); + g65816_if_adc_w(); + g65816_incpc(4); + snes_time->add_cpu_pcycles(4); + snes_time->add_cpu_mcycles(2, dest_addr); +} + +void g65816_op_adc_addryb(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDRY); + g65816_if_adc_b(); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION4); +} + +void g65816_op_adc_addryw(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDRY); + g65816_if_adc_w(); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION4); +} + +void g65816_op_adc_dpxb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DPX); + g65816_if_adc_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_CONDITION2); +} + +void g65816_op_adc_dpxw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DPX); + g65816_if_adc_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_CONDITION2); +} + +void g65816_op_adc_idpxb(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_IDPX); + g65816_if_adc_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, base_addr); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_CONDITION2); +} + +void g65816_op_adc_idpxw(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_IDPX); + g65816_if_adc_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, base_addr); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_CONDITION2); +} + +void g65816_op_adc_idpyb(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_IDPY); + g65816_if_adc_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, base_addr); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2 | TIMING_CONDITION4); +} + +void g65816_op_adc_idpyw(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_IDPY); + g65816_if_adc_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, base_addr); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2 | TIMING_CONDITION4); +} + +void g65816_op_adc_ildpyb(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_ILDPY); + g65816_if_adc_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(3, base_addr); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_adc_ildpyw(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_ILDPY); + g65816_if_adc_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(3, base_addr); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_adc_srb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_SR); + g65816_if_adc_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_scycles(1); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_adc_srw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_SR); + g65816_if_adc_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_scycles(2); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_adc_isryb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_ISRY); + g65816_if_adc_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_scycles(2); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(2); +} + +void g65816_op_adc_isryw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_ISRY); + g65816_if_adc_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_scycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(2); +} + +void g65816_op_adc_constb(void) { +g65816_prefetch(1); +int r = gx816->regs.a.b + arg + (gx816->regs.p & PF_C); + bcd_add_adjust_byte() + g65816_testn(r & 0x80); + g65816_testv(~(gx816->regs.a.b ^ arg) & (gx816->regs.a.b ^ (byte)r) & 0x80); + g65816_testz((byte)r == 0); + g65816_testc(r > 0xff); + gx816->regs.a.b = r; + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); +} + +void g65816_op_adc_constw(void) { +g65816_prefetch(2); +int r = gx816->regs.a.w + arg + (gx816->regs.p & PF_C); + bcd_add_adjust_word() + g65816_testn(r & 0x8000); + g65816_testv(~(gx816->regs.a.w ^ arg) & (gx816->regs.a.w ^ (word)r) & 0x8000); + g65816_testz((word)r == 0); + g65816_testc(r > 0xffff); + gx816->regs.a.w = r; + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); +} diff --git a/bsnes/cpu/g65816_ops_and.cpp b/bsnes/cpu/g65816_ops_and.cpp new file mode 100644 index 00000000..4ea42256 --- /dev/null +++ b/bsnes/cpu/g65816_ops_and.cpp @@ -0,0 +1,335 @@ +#define g65816_flags_and_b() g65816_testn(gx816->regs.a.b & 0x80); g65816_testz(gx816->regs.a.b == 0) +#define g65816_flags_and_w() g65816_testn(gx816->regs.a.w & 0x8000); g65816_testz(gx816->regs.a.w == 0) + +#define g65816_inst_and_b() gx816->regs.a.b &= gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr) +#define g65816_inst_and_w() gx816->regs.a.w &= gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr) + +void g65816_op_and_addrb(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDR); + g65816_inst_and_b(); + g65816_flags_and_b(); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(1, dest_addr); +} + +void g65816_op_and_addrw(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDR); + g65816_inst_and_w(); + g65816_flags_and_w(); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(2, dest_addr); +} + +void g65816_op_and_addrxb(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDRX); + g65816_inst_and_b(); + g65816_flags_and_b(); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION4); +} + +void g65816_op_and_addrxw(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDRX); + g65816_inst_and_w(); + g65816_flags_and_w(); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION4); +} + +void g65816_op_and_dpb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DP); + g65816_inst_and_b(); + g65816_flags_and_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_and_dpw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DP); + g65816_inst_and_w(); + g65816_flags_and_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_and_idpb(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_IDP); + g65816_inst_and_b(); + g65816_flags_and_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, base_addr); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_and_idpw(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_IDP); + g65816_inst_and_w(); + g65816_flags_and_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, base_addr); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_and_ildpb(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_ILDP); + g65816_inst_and_b(); + g65816_flags_and_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(3, base_addr); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_and_ildpw(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_ILDP); + g65816_inst_and_w(); + g65816_flags_and_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(3, base_addr); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_and_longb(void) { +g65816_prefetch(3); +g65816_getaddr(MEMMODE_LONG); + g65816_inst_and_b(); + g65816_flags_and_b(); + g65816_incpc(4); + snes_time->add_cpu_pcycles(4); + snes_time->add_cpu_mcycles(1, dest_addr); +} + +void g65816_op_and_longw(void) { +g65816_prefetch(3); +g65816_getaddr(MEMMODE_LONG); + g65816_inst_and_w(); + g65816_flags_and_w(); + g65816_incpc(4); + snes_time->add_cpu_pcycles(4); + snes_time->add_cpu_mcycles(2, dest_addr); +} + +void g65816_op_and_longxb(void) { +g65816_prefetch(3); +g65816_getaddr(MEMMODE_LONGX); + g65816_inst_and_b(); + g65816_flags_and_b(); + g65816_incpc(4); + snes_time->add_cpu_pcycles(4); + snes_time->add_cpu_mcycles(1, dest_addr); +} + +void g65816_op_and_longxw(void) { +g65816_prefetch(3); +g65816_getaddr(MEMMODE_LONGX); + g65816_inst_and_w(); + g65816_flags_and_w(); + g65816_incpc(4); + snes_time->add_cpu_pcycles(4); + snes_time->add_cpu_mcycles(2, dest_addr); +} + +void g65816_op_and_addryb(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDRY); + g65816_inst_and_b(); + g65816_flags_and_b(); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION4); +} + +void g65816_op_and_addryw(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDRY); + g65816_inst_and_w(); + g65816_flags_and_w(); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION4); +} + +void g65816_op_and_dpxb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DPX); + g65816_inst_and_b(); + g65816_flags_and_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_CONDITION2); +} + +void g65816_op_and_dpxw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DPX); + g65816_inst_and_w(); + g65816_flags_and_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_CONDITION2); +} + +void g65816_op_and_idpxb(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_IDPX); + g65816_inst_and_b(); + g65816_flags_and_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, base_addr); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_CONDITION2); +} + +void g65816_op_and_idpxw(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_IDPX); + g65816_inst_and_w(); + g65816_flags_and_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, base_addr); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_CONDITION2); +} + +void g65816_op_and_idpyb(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_IDPY); + g65816_inst_and_b(); + g65816_flags_and_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, base_addr); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2 | TIMING_CONDITION4); +} + +void g65816_op_and_idpyw(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_IDPY); + g65816_inst_and_w(); + g65816_flags_and_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, base_addr); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2 | TIMING_CONDITION4); +} + +void g65816_op_and_ildpyb(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_ILDPY); + g65816_inst_and_b(); + g65816_flags_and_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(3, base_addr); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_and_ildpyw(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_ILDPY); + g65816_inst_and_w(); + g65816_flags_and_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(3, base_addr); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_and_srb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_SR); + g65816_inst_and_b(); + g65816_flags_and_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_scycles(1); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_and_srw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_SR); + g65816_inst_and_w(); + g65816_flags_and_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_scycles(2); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_and_isryb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_ISRY); + g65816_inst_and_b(); + g65816_flags_and_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_scycles(2); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(2); +} + +void g65816_op_and_isryw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_ISRY); + g65816_inst_and_w(); + g65816_flags_and_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_scycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(2); +} + +void g65816_op_and_constb(void) { +g65816_prefetch(1); + gx816->regs.a.b &= arg; + g65816_flags_and_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); +} + +void g65816_op_and_constw(void) { +g65816_prefetch(2); + gx816->regs.a.w &= arg; + g65816_flags_and_w(); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); +} diff --git a/bsnes/cpu/g65816_ops_cmp.cpp b/bsnes/cpu/g65816_ops_cmp.cpp new file mode 100644 index 00000000..9eabc22a --- /dev/null +++ b/bsnes/cpu/g65816_ops_cmp.cpp @@ -0,0 +1,321 @@ +#define g65816_flags_cmp_b() \ +byte c = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr); \ +int r = gx816->regs.a.b - c; \ + g65816_testn(r & 0x80); \ + g65816_testz((byte)r == 0); \ + g65816_testc(r >= 0) + +#define g65816_flags_cmp_w() \ +word c = gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr); \ +int r = gx816->regs.a.w - c; \ + g65816_testn(r & 0x8000); \ + g65816_testz((word)r == 0); \ + g65816_testc(r >= 0) + +void g65816_op_cmp_addrb(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDR); + g65816_flags_cmp_b(); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(1, dest_addr); +} + +void g65816_op_cmp_addrw(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDR); + g65816_flags_cmp_w(); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(2, dest_addr); +} + +void g65816_op_cmp_addrxb(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDRX); + g65816_flags_cmp_b(); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION4); +} + +void g65816_op_cmp_addrxw(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDRX); + g65816_flags_cmp_w(); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION4); +} + +void g65816_op_cmp_dpb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DP); + g65816_flags_cmp_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_cmp_dpw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DP); + g65816_flags_cmp_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_cmp_idpb(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_IDP); + g65816_flags_cmp_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, base_addr); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_cmp_idpw(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_IDP); + g65816_flags_cmp_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, base_addr); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_cmp_ildpb(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_ILDP); + g65816_flags_cmp_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(3, base_addr); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_cmp_ildpw(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_ILDP); + g65816_flags_cmp_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(3, base_addr); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_cmp_longb(void) { +g65816_prefetch(3); +g65816_getaddr(MEMMODE_LONG); + g65816_flags_cmp_b(); + g65816_incpc(4); + snes_time->add_cpu_pcycles(4); + snes_time->add_cpu_mcycles(1, dest_addr); +} + +void g65816_op_cmp_longw(void) { +g65816_prefetch(3); +g65816_getaddr(MEMMODE_LONG); + g65816_flags_cmp_w(); + g65816_incpc(4); + snes_time->add_cpu_pcycles(4); + snes_time->add_cpu_mcycles(2, dest_addr); +} + +void g65816_op_cmp_longxb(void) { +g65816_prefetch(3); +g65816_getaddr(MEMMODE_LONGX); + g65816_flags_cmp_b(); + g65816_incpc(4); + snes_time->add_cpu_pcycles(4); + snes_time->add_cpu_mcycles(1, dest_addr); +} + +void g65816_op_cmp_longxw(void) { +g65816_prefetch(3); +g65816_getaddr(MEMMODE_LONGX); + g65816_flags_cmp_w(); + g65816_incpc(4); + snes_time->add_cpu_pcycles(4); + snes_time->add_cpu_mcycles(2, dest_addr); +} + +void g65816_op_cmp_addryb(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDRY); + g65816_flags_cmp_b(); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION4); +} + +void g65816_op_cmp_addryw(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDRY); + g65816_flags_cmp_w(); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION4); +} + +void g65816_op_cmp_dpxb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DPX); + g65816_flags_cmp_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_CONDITION2); +} + +void g65816_op_cmp_dpxw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DPX); + g65816_flags_cmp_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_CONDITION2); +} + +void g65816_op_cmp_idpxb(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_IDPX); + g65816_flags_cmp_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, base_addr); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_CONDITION2); +} + +void g65816_op_cmp_idpxw(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_IDPX); + g65816_flags_cmp_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, base_addr); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_CONDITION2); +} + +void g65816_op_cmp_idpyb(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_IDPY); + g65816_flags_cmp_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, base_addr); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2 | TIMING_CONDITION4); +} + +void g65816_op_cmp_idpyw(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_IDPY); + g65816_flags_cmp_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, base_addr); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2 | TIMING_CONDITION4); +} + +void g65816_op_cmp_ildpyb(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_ILDPY); + g65816_flags_cmp_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(3, base_addr); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_cmp_ildpyw(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_ILDPY); + g65816_flags_cmp_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(3, base_addr); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_cmp_srb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_SR); + g65816_flags_cmp_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_scycles(1); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_cmp_srw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_SR); + g65816_flags_cmp_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_scycles(2); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_cmp_isryb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_ISRY); + g65816_flags_cmp_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_scycles(2); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(2); +} + +void g65816_op_cmp_isryw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_ISRY); + g65816_flags_cmp_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_scycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(2); +} + +void g65816_op_cmp_constb(void) { +g65816_prefetch(1); +int r; + r = gx816->regs.a.b - arg; + g65816_testn(r & 0x80); + g65816_testz((byte)r == 0); + g65816_testc(r >= 0); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); +} + +void g65816_op_cmp_constw(void) { +g65816_prefetch(2); +int r; + r = gx816->regs.a.w - arg; + g65816_testn(r & 0x8000); + g65816_testz((word)r == 0); + g65816_testc(r >= 0); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); +} diff --git a/bsnes/cpu/g65816_ops_eor.cpp b/bsnes/cpu/g65816_ops_eor.cpp new file mode 100644 index 00000000..80932ee4 --- /dev/null +++ b/bsnes/cpu/g65816_ops_eor.cpp @@ -0,0 +1,335 @@ +#define g65816_flags_eor_b() g65816_testn(gx816->regs.a.b & 0x80); g65816_testz(gx816->regs.a.b == 0) +#define g65816_flags_eor_w() g65816_testn(gx816->regs.a.w & 0x8000); g65816_testz(gx816->regs.a.w == 0) + +#define g65816_inst_eor_b() gx816->regs.a.b ^= gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr) +#define g65816_inst_eor_w() gx816->regs.a.w ^= gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr) + +void g65816_op_eor_addrb(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDR); + g65816_inst_eor_b(); + g65816_flags_eor_b(); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(1, dest_addr); +} + +void g65816_op_eor_addrw(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDR); + g65816_inst_eor_w(); + g65816_flags_eor_w(); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(2, dest_addr); +} + +void g65816_op_eor_addrxb(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDRX); + g65816_inst_eor_b(); + g65816_flags_eor_b(); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION4); +} + +void g65816_op_eor_addrxw(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDRX); + g65816_inst_eor_w(); + g65816_flags_eor_w(); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION4); +} + +void g65816_op_eor_dpb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DP); + g65816_inst_eor_b(); + g65816_flags_eor_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_eor_dpw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DP); + g65816_inst_eor_w(); + g65816_flags_eor_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_eor_idpb(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_IDP); + g65816_inst_eor_b(); + g65816_flags_eor_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, base_addr); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_eor_idpw(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_IDP); + g65816_inst_eor_w(); + g65816_flags_eor_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, base_addr); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_eor_ildpb(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_ILDP); + g65816_inst_eor_b(); + g65816_flags_eor_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(3, base_addr); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_eor_ildpw(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_ILDP); + g65816_inst_eor_w(); + g65816_flags_eor_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(3, base_addr); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_eor_longb(void) { +g65816_prefetch(3); +g65816_getaddr(MEMMODE_LONG); + g65816_inst_eor_b(); + g65816_flags_eor_b(); + g65816_incpc(4); + snes_time->add_cpu_pcycles(4); + snes_time->add_cpu_mcycles(1, dest_addr); +} + +void g65816_op_eor_longw(void) { +g65816_prefetch(3); +g65816_getaddr(MEMMODE_LONG); + g65816_inst_eor_w(); + g65816_flags_eor_w(); + g65816_incpc(4); + snes_time->add_cpu_pcycles(4); + snes_time->add_cpu_mcycles(2, dest_addr); +} + +void g65816_op_eor_longxb(void) { +g65816_prefetch(3); +g65816_getaddr(MEMMODE_LONGX); + g65816_inst_eor_b(); + g65816_flags_eor_b(); + g65816_incpc(4); + snes_time->add_cpu_pcycles(4); + snes_time->add_cpu_mcycles(1, dest_addr); +} + +void g65816_op_eor_longxw(void) { +g65816_prefetch(3); +g65816_getaddr(MEMMODE_LONGX); + g65816_inst_eor_w(); + g65816_flags_eor_w(); + g65816_incpc(4); + snes_time->add_cpu_pcycles(4); + snes_time->add_cpu_mcycles(2, dest_addr); +} + +void g65816_op_eor_addryb(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDRY); + g65816_inst_eor_b(); + g65816_flags_eor_b(); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION4); +} + +void g65816_op_eor_addryw(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDRY); + g65816_inst_eor_w(); + g65816_flags_eor_w(); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION4); +} + +void g65816_op_eor_dpxb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DPX); + g65816_inst_eor_b(); + g65816_flags_eor_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_CONDITION2); +} + +void g65816_op_eor_dpxw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DPX); + g65816_inst_eor_w(); + g65816_flags_eor_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_CONDITION2); +} + +void g65816_op_eor_idpxb(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_IDPX); + g65816_inst_eor_b(); + g65816_flags_eor_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, base_addr); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_CONDITION2); +} + +void g65816_op_eor_idpxw(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_IDPX); + g65816_inst_eor_w(); + g65816_flags_eor_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, base_addr); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_CONDITION2); +} + +void g65816_op_eor_idpyb(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_IDPY); + g65816_inst_eor_b(); + g65816_flags_eor_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, base_addr); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2 | TIMING_CONDITION4); +} + +void g65816_op_eor_idpyw(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_IDPY); + g65816_inst_eor_w(); + g65816_flags_eor_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, base_addr); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2 | TIMING_CONDITION4); +} + +void g65816_op_eor_ildpyb(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_ILDPY); + g65816_inst_eor_b(); + g65816_flags_eor_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(3, base_addr); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_eor_ildpyw(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_ILDPY); + g65816_inst_eor_w(); + g65816_flags_eor_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(3, base_addr); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_eor_srb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_SR); + g65816_inst_eor_b(); + g65816_flags_eor_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_scycles(1); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_eor_srw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_SR); + g65816_inst_eor_w(); + g65816_flags_eor_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_scycles(2); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_eor_isryb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_ISRY); + g65816_inst_eor_b(); + g65816_flags_eor_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_scycles(2); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(2); +} + +void g65816_op_eor_isryw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_ISRY); + g65816_inst_eor_w(); + g65816_flags_eor_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_scycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(2); +} + +void g65816_op_eor_constb(void) { +g65816_prefetch(1); + gx816->regs.a.b ^= arg; + g65816_flags_eor_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); +} + +void g65816_op_eor_constw(void) { +g65816_prefetch(2); + gx816->regs.a.w ^= arg; + g65816_flags_eor_w(); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); +} diff --git a/bsnes/cpu/g65816_ops_incdec.cpp b/bsnes/cpu/g65816_ops_incdec.cpp new file mode 100644 index 00000000..5abaa9c7 --- /dev/null +++ b/bsnes/cpu/g65816_ops_incdec.cpp @@ -0,0 +1,355 @@ +/*********** + *** inc *** + **********/ + +void g65816_op_incb(void) { + gx816->regs.a.b++; + g65816_testn(gx816->regs.a.b & 0x80); + g65816_testz(gx816->regs.a.b == 0); + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_incw(void) { + gx816->regs.a.w++; + g65816_testn(gx816->regs.a.w & 0x8000); + g65816_testz(gx816->regs.a.w == 0); + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_inc_addrb(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDR); +byte m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr); + m++; + gx816->mem_write(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr, m); + g65816_testn(m & 0x80); + g65816_testz(m == 0); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_inc_addrw(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDR); +word m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr); + m++; + gx816->mem_write(MEMMODE_NONE, MEMSIZE_WORD, dest_addr, m); + g65816_testn(m & 0x8000); + g65816_testz(m == 0); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(4, dest_addr); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_inc_addrxb(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDRX); +byte m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr); + m++; + gx816->mem_write(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr, m); + g65816_testn(m & 0x80); + g65816_testz(m == 0); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(2); +} + +void g65816_op_inc_addrxw(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDRX); +word m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr); + m++; + gx816->mem_write(MEMMODE_NONE, MEMSIZE_WORD, dest_addr, m); + g65816_testn(m & 0x8000); + g65816_testz(m == 0); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(4, dest_addr); + snes_time->add_cpu_icycles(2); +} + +void g65816_op_inc_dpb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DP); +byte m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr); + m++; + gx816->mem_write(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr, m); + g65816_testn(m & 0x80); + g65816_testz(m == 0); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_REGD); +} + +void g65816_op_inc_dpw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DP); +word m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr); + m++; + gx816->mem_write(MEMMODE_NONE, MEMSIZE_WORD, dest_addr, m); + g65816_testn(m & 0x8000); + g65816_testz(m == 0); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(4, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_REGD); +} + +void g65816_op_inc_dpxb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DPX); +byte m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr); + m++; + gx816->mem_write(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr, m); + g65816_testn(m & 0x80); + g65816_testz(m == 0); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(2, TIMING_REGD); +} + +void g65816_op_inc_dpxw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DPX); +word m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr); + m++; + gx816->mem_write(MEMMODE_NONE, MEMSIZE_WORD, dest_addr, m); + g65816_testn(m & 0x8000); + g65816_testz(m == 0); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(4, dest_addr); + snes_time->add_cpu_icycles(2, TIMING_REGD); +} + +/*********** + *** inx *** + **********/ + +void g65816_op_inxb(void) { + gx816->regs.x = (gx816->regs.x + 1) & 0xff; + g65816_testn(gx816->regs.x & 0x80); + g65816_testz((gx816->regs.x & 0xff) == 0); + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_inxw(void) { + gx816->regs.x += 1; + g65816_testn(gx816->regs.x & 0x8000); + g65816_testz(gx816->regs.x == 0); + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(1); +} + +/*********** + *** iny *** + **********/ + +void g65816_op_inyb(void) { + gx816->regs.y = (gx816->regs.y + 1) & 0xff; + g65816_testn(gx816->regs.y & 0x80); + g65816_testz((gx816->regs.y & 0xff) == 0); + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_inyw(void) { + gx816->regs.y += 1; + g65816_testn(gx816->regs.y & 0x8000); + g65816_testz(gx816->regs.y == 0); + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(1); +} + +/*********** + *** dec *** + **********/ + +void g65816_op_decb(void) { + gx816->regs.a.b--; + g65816_testn(gx816->regs.a.b & 0x80); + g65816_testz(gx816->regs.a.b == 0); + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_decw(void) { + gx816->regs.a.w--; + g65816_testn(gx816->regs.a.w & 0x8000); + g65816_testz(gx816->regs.a.w == 0); + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_dec_addrb(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDR); +byte m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr); + m--; + gx816->mem_write(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr, m); + g65816_testn(m & 0x80); + g65816_testz(m == 0); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_dec_addrw(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDR); +word m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr); + m--; + gx816->mem_write(MEMMODE_NONE, MEMSIZE_WORD, dest_addr, m); + g65816_testn(m & 0x8000); + g65816_testz(m == 0); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(4, dest_addr); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_dec_addrxb(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDRX); +byte m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr); + m--; + gx816->mem_write(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr, m); + g65816_testn(m & 0x80); + g65816_testz(m == 0); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(2); +} + +void g65816_op_dec_addrxw(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDRX); +word m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr); + m--; + gx816->mem_write(MEMMODE_NONE, MEMSIZE_WORD, dest_addr, m); + g65816_testn(m & 0x8000); + g65816_testz(m == 0); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(4, dest_addr); + snes_time->add_cpu_icycles(2); +} + +void g65816_op_dec_dpb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DP); +byte m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr); + m--; + gx816->mem_write(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr, m); + g65816_testn(m & 0x80); + g65816_testz(m == 0); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_REGD); +} + +void g65816_op_dec_dpw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DP); +word m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr); + m--; + gx816->mem_write(MEMMODE_NONE, MEMSIZE_WORD, dest_addr, m); + g65816_testn(m & 0x8000); + g65816_testz(m == 0); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(4, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_REGD); +} + +void g65816_op_dec_dpxb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DPX); +byte m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr); + m--; + gx816->mem_write(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr, m); + g65816_testn(m & 0x80); + g65816_testz(m == 0); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(2, TIMING_REGD); +} + +void g65816_op_dec_dpxw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DPX); +word m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr); + m--; + gx816->mem_write(MEMMODE_NONE, MEMSIZE_WORD, dest_addr, m); + g65816_testn(m & 0x8000); + g65816_testz(m == 0); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(4, dest_addr); + snes_time->add_cpu_icycles(2, TIMING_REGD); +} + +/*********** + *** dex *** + **********/ + +void g65816_op_dexb(void) { + gx816->regs.x = (gx816->regs.x - 1) & 0xff; + g65816_testn(gx816->regs.x & 0x80); + g65816_testz((gx816->regs.x & 0xff) == 0); + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_dexw(void) { + gx816->regs.x -= 1; + g65816_testn(gx816->regs.x & 0x8000); + g65816_testz(gx816->regs.x == 0); + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(1); +} + +/*********** + *** dey *** + **********/ + +void g65816_op_deyb(void) { + gx816->regs.y = (gx816->regs.y - 1) & 0xff; + g65816_testn(gx816->regs.y & 0x80); + g65816_testz((gx816->regs.y & 0xff) == 0); + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_deyw(void) { + gx816->regs.y -= 1; + g65816_testn(gx816->regs.y & 0x8000); + g65816_testz(gx816->regs.y == 0); + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(1); +} diff --git a/bsnes/cpu/g65816_ops_lda.cpp b/bsnes/cpu/g65816_ops_lda.cpp new file mode 100644 index 00000000..289285cd --- /dev/null +++ b/bsnes/cpu/g65816_ops_lda.cpp @@ -0,0 +1,335 @@ +#define g65816_flags_lda_b() g65816_testn(gx816->regs.a.b & 0x80); g65816_testz(gx816->regs.a.b == 0) +#define g65816_flags_lda_w() g65816_testn(gx816->regs.a.w & 0x8000); g65816_testz(gx816->regs.a.w == 0) + +#define g65816_set_lda_b() gx816->regs.a.b = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr) +#define g65816_set_lda_w() gx816->regs.a.w = gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr) + +void g65816_op_lda_addrb(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDR); + g65816_set_lda_b(); + g65816_flags_lda_b(); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(1, dest_addr); +} + +void g65816_op_lda_addrw(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDR); + g65816_set_lda_w(); + g65816_flags_lda_w(); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(2, dest_addr); +} + +void g65816_op_lda_addrxb(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDRX); + g65816_set_lda_b(); + g65816_flags_lda_b(); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION4); +} + +void g65816_op_lda_addrxw(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDRX); + g65816_set_lda_w(); + g65816_flags_lda_w(); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION4); +} + +void g65816_op_lda_dpb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DP); + g65816_set_lda_b(); + g65816_flags_lda_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_lda_dpw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DP); + g65816_set_lda_w(); + g65816_flags_lda_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_lda_idpb(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_IDP); + g65816_set_lda_b(); + g65816_flags_lda_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, base_addr); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_lda_idpw(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_IDP); + g65816_set_lda_w(); + g65816_flags_lda_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, base_addr); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_lda_ildpb(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_ILDP); + g65816_set_lda_b(); + g65816_flags_lda_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(3, base_addr); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_lda_ildpw(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_ILDP); + g65816_set_lda_w(); + g65816_flags_lda_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(3, base_addr); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_lda_longb(void) { +g65816_prefetch(3); +g65816_getaddr(MEMMODE_LONG); + g65816_set_lda_b(); + g65816_flags_lda_b(); + g65816_incpc(4); + snes_time->add_cpu_pcycles(4); + snes_time->add_cpu_mcycles(1, dest_addr); +} + +void g65816_op_lda_longw(void) { +g65816_prefetch(3); +g65816_getaddr(MEMMODE_LONG); + g65816_set_lda_w(); + g65816_flags_lda_w(); + g65816_incpc(4); + snes_time->add_cpu_pcycles(4); + snes_time->add_cpu_mcycles(2, dest_addr); +} + +void g65816_op_lda_longxb(void) { +g65816_prefetch(3); +g65816_getaddr(MEMMODE_LONGX); + g65816_set_lda_b(); + g65816_flags_lda_b(); + g65816_incpc(4); + snes_time->add_cpu_pcycles(4); + snes_time->add_cpu_mcycles(1, dest_addr); +} + +void g65816_op_lda_longxw(void) { +g65816_prefetch(3); +g65816_getaddr(MEMMODE_LONGX); + g65816_set_lda_w(); + g65816_flags_lda_w(); + g65816_incpc(4); + snes_time->add_cpu_pcycles(4); + snes_time->add_cpu_mcycles(2, dest_addr); +} + +void g65816_op_lda_addryb(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDRY); + g65816_set_lda_b(); + g65816_flags_lda_b(); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION4); +} + +void g65816_op_lda_addryw(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDRY); + g65816_set_lda_w(); + g65816_flags_lda_w(); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION4); +} + +void g65816_op_lda_dpxb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DPX); + g65816_set_lda_b(); + g65816_flags_lda_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_CONDITION2); +} + +void g65816_op_lda_dpxw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DPX); + g65816_set_lda_w(); + g65816_flags_lda_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_CONDITION2); +} + +void g65816_op_lda_idpxb(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_IDPX); + g65816_set_lda_b(); + g65816_flags_lda_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, base_addr); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_CONDITION2); +} + +void g65816_op_lda_idpxw(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_IDPX); + g65816_set_lda_w(); + g65816_flags_lda_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, base_addr); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_CONDITION2); +} + +void g65816_op_lda_idpyb(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_IDPY); + g65816_set_lda_b(); + g65816_flags_lda_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, base_addr); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2 | TIMING_CONDITION4); +} + +void g65816_op_lda_idpyw(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_IDPY); + g65816_set_lda_w(); + g65816_flags_lda_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, base_addr); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2 | TIMING_CONDITION4); +} + +void g65816_op_lda_ildpyb(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_ILDPY); + g65816_set_lda_b(); + g65816_flags_lda_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(3, base_addr); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_lda_ildpyw(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_ILDPY); + g65816_set_lda_w(); + g65816_flags_lda_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(3, base_addr); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_lda_srb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_SR); + g65816_set_lda_b(); + g65816_flags_lda_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_scycles(1); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_lda_srw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_SR); + g65816_set_lda_w(); + g65816_flags_lda_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_scycles(2); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_lda_isryb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_ISRY); + g65816_set_lda_b(); + g65816_flags_lda_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_scycles(2); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(2); +} + +void g65816_op_lda_isryw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_ISRY); + g65816_set_lda_w(); + g65816_flags_lda_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_scycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(2); +} + +void g65816_op_lda_constb(void) { +g65816_prefetch(1); + gx816->regs.a.b = arg; + g65816_flags_lda_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); +} + +void g65816_op_lda_constw(void) { +g65816_prefetch(2); + gx816->regs.a.w = arg; + g65816_flags_lda_w(); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); +} diff --git a/bsnes/cpu/g65816_ops_misc.cpp b/bsnes/cpu/g65816_ops_misc.cpp new file mode 100644 index 00000000..6737a35f --- /dev/null +++ b/bsnes/cpu/g65816_ops_misc.cpp @@ -0,0 +1,1294 @@ +/*********** + *** bit *** + **********/ + +void g65816_op_bit_constb(void) { +g65816_prefetch(1); + g65816_testn(arg & 0x80); + g65816_testv(arg & 0x40); + g65816_testz((arg & gx816->regs.a.b) == 0); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); +} + +void g65816_op_bit_constw(void) { +g65816_prefetch(2); + g65816_testn(arg & 0x8000); + g65816_testv(arg & 0x4000); + g65816_testz((arg & gx816->regs.a.w) == 0); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); +} + +void g65816_op_bit_addrb(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDR); + arg = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr); + g65816_testn(arg & 0x80); + g65816_testv(arg & 0x40); + g65816_testz((arg & gx816->regs.a.b) == 0); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(1, dest_addr); +} + +void g65816_op_bit_addrw(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDR); + arg = gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr); + g65816_testn(arg & 0x8000); + g65816_testv(arg & 0x4000); + g65816_testz((arg & gx816->regs.a.w) == 0); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(2, dest_addr); +} + +void g65816_op_bit_addrxb(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDRX); + arg = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr); + g65816_testn(arg & 0x80); + g65816_testv(arg & 0x40); + g65816_testz((arg & gx816->regs.a.b) == 0); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION4); +} + +void g65816_op_bit_addrxw(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDRX); + arg = gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr); + g65816_testn(arg & 0x8000); + g65816_testv(arg & 0x4000); + g65816_testz((arg & gx816->regs.a.w) == 0); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION4); +} + +void g65816_op_bit_dpb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DP); + arg = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr); + g65816_testn(arg & 0x80); + g65816_testv(arg & 0x40); + g65816_testz((arg & gx816->regs.a.b) == 0); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_bit_dpw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DP); + arg = gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr); + g65816_testn(arg & 0x8000); + g65816_testv(arg & 0x4000); + g65816_testz((arg & gx816->regs.a.w) == 0); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_bit_dpxb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DPX); + arg = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr); + g65816_testn(arg & 0x80); + g65816_testv(arg & 0x40); + g65816_testz((arg & gx816->regs.a.b) == 0); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_CONDITION2); +} + +void g65816_op_bit_dpxw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DPX); + arg = gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr); + g65816_testn(arg & 0x8000); + g65816_testv(arg & 0x4000); + g65816_testz((arg & gx816->regs.a.w) == 0); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_CONDITION2); +} + +/*********** + *** cpx *** + **********/ + +void g65816_op_cpx_constb(void) { +g65816_prefetch(1); +int r = gx816->regs.x - arg; + g65816_testn(r & 0x80); + g65816_testz((byte)r == 0); + g65816_testc(r >= 0); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); +} + +void g65816_op_cpx_constw(void) { +g65816_prefetch(2); +int r = gx816->regs.x - arg; + g65816_testn(r & 0x8000); + g65816_testz((word)r == 0); + g65816_testc(r >= 0); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); +} + +void g65816_op_cpx_addrb(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDR); +int r = gx816->regs.x - gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr); + g65816_testn(r & 0x80); + g65816_testz((byte)r == 0); + g65816_testc(r >= 0); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(1, dest_addr); +} + +void g65816_op_cpx_addrw(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDR); +int r = gx816->regs.x - gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr); + g65816_testn(r & 0x8000); + g65816_testz((word)r == 0); + g65816_testc(r >= 0); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(2, dest_addr); +} + +void g65816_op_cpx_dpb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DP); +int r = gx816->regs.x - gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr); + g65816_testn(r & 0x80); + g65816_testz((byte)r == 0); + g65816_testc(r >= 0); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_cpx_dpw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DP); +int r = gx816->regs.x - gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr); + g65816_testn(r & 0x8000); + g65816_testz((word)r == 0); + g65816_testc(r >= 0); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +/*********** + *** cpy *** + **********/ + +void g65816_op_cpy_constb(void) { +g65816_prefetch(1); +int r = gx816->regs.y - arg; + g65816_testn(r & 0x80); + g65816_testz((byte)r == 0); + g65816_testc(r >= 0); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); +} + +void g65816_op_cpy_constw(void) { +g65816_prefetch(2); +int r = gx816->regs.y - arg; + g65816_testn(r & 0x8000); + g65816_testz((word)r == 0); + g65816_testc(r >= 0); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); +} + +void g65816_op_cpy_addrb(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDR); +int r = gx816->regs.y - gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr); + g65816_testn(r & 0x80); + g65816_testz((byte)r == 0); + g65816_testc(r >= 0); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(1, dest_addr); +} + +void g65816_op_cpy_addrw(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDR); +int r = gx816->regs.y - gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr); + g65816_testn(r & 0x8000); + g65816_testz((word)r == 0); + g65816_testc(r >= 0); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(2, dest_addr); +} + +void g65816_op_cpy_dpb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DP); +int r = gx816->regs.y - gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr); + g65816_testn(r & 0x80); + g65816_testz((byte)r == 0); + g65816_testc(r >= 0); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_cpy_dpw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DP); +int r = gx816->regs.y - gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr); + g65816_testn(r & 0x8000); + g65816_testz((word)r == 0); + g65816_testc(r >= 0); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +/*********** + *** ldx *** + **********/ + +void g65816_op_ldx_constb(void) { +g65816_prefetch(1); + gx816->regs.x = arg; + g65816_testn(gx816->regs.x & 0x80); + g65816_testz(gx816->regs.x == 0); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); +} + +void g65816_op_ldx_constw(void) { +g65816_prefetch(2); + gx816->regs.x = arg; + g65816_testn(gx816->regs.x & 0x8000); + g65816_testz(gx816->regs.x == 0); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); +} + +void g65816_op_ldx_addrb(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDR); + gx816->regs.x = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr); + g65816_testn(gx816->regs.x & 0x80); + g65816_testz(gx816->regs.x == 0); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(1, dest_addr); +} + +void g65816_op_ldx_addrw(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDR); + gx816->regs.x = gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr); + g65816_testn(gx816->regs.x & 0x8000); + g65816_testz(gx816->regs.x == 0); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(2, dest_addr); +} + +void g65816_op_ldx_addryb(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDRY); + gx816->regs.x = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr); + g65816_testn(gx816->regs.x & 0x80); + g65816_testz(gx816->regs.x == 0); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION4); +} + +void g65816_op_ldx_addryw(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDRY); + gx816->regs.x = gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr); + g65816_testn(gx816->regs.x & 0x8000); + g65816_testz(gx816->regs.x == 0); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION4); +} + +void g65816_op_ldx_dpb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DP); + gx816->regs.x = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr); + g65816_testn(gx816->regs.x & 0x80); + g65816_testz(gx816->regs.x == 0); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_ldx_dpw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DP); + gx816->regs.x = gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr); + g65816_testn(gx816->regs.x & 0x8000); + g65816_testz(gx816->regs.x == 0); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_ldx_dpyb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DPY); + gx816->regs.x = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr); + g65816_testn(gx816->regs.x & 0x80); + g65816_testz(gx816->regs.x == 0); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_CONDITION2); +} + +void g65816_op_ldx_dpyw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DPY); + gx816->regs.x = gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr); + g65816_testn(gx816->regs.x & 0x8000); + g65816_testz(gx816->regs.x == 0); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_CONDITION2); +} + +/*********** + *** ldy *** + **********/ + +void g65816_op_ldy_constb(void) { +g65816_prefetch(1); + gx816->regs.y = (gx816->regs.y & 0xff00) | arg; + g65816_testn(gx816->regs.y & 0x80); + g65816_testz(gx816->regs.y == 0); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); +} + +void g65816_op_ldy_constw(void) { +g65816_prefetch(2); + gx816->regs.y = arg; + g65816_testn(gx816->regs.y & 0x8000); + g65816_testz(gx816->regs.y == 0); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); +} + +void g65816_op_ldy_addrb(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDR); + gx816->regs.y = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr); + g65816_testn(gx816->regs.y & 0x80); + g65816_testz(gx816->regs.y == 0); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(1, dest_addr); +} + +void g65816_op_ldy_addrw(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDR); + gx816->regs.y = gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr); + g65816_testn(gx816->regs.y & 0x8000); + g65816_testz(gx816->regs.y == 0); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(2, dest_addr); +} + +void g65816_op_ldy_addrxb(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDRX); + gx816->regs.y = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr); + g65816_testn(gx816->regs.y & 0x80); + g65816_testz(gx816->regs.y == 0); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION4); +} + +void g65816_op_ldy_addrxw(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDRX); + gx816->regs.y = gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr); + g65816_testn(gx816->regs.y & 0x8000); + g65816_testz(gx816->regs.y == 0); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION4); +} + +void g65816_op_ldy_dpb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DP); + gx816->regs.y = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr); + g65816_testn(gx816->regs.y & 0x80); + g65816_testz(gx816->regs.y == 0); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_ldy_dpw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DP); + gx816->regs.y = gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr); + g65816_testn(gx816->regs.y & 0x8000); + g65816_testz(gx816->regs.y == 0); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_ldy_dpxb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DPX); + gx816->regs.y = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr); + g65816_testn(gx816->regs.y & 0x80); + g65816_testz(gx816->regs.y == 0); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_CONDITION2); +} + +void g65816_op_ldy_dpxw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DPX); + gx816->regs.y = gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr); + g65816_testn(gx816->regs.y & 0x8000); + g65816_testz(gx816->regs.y == 0); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_CONDITION2); +} + +/*********** + *** stx *** + **********/ + +void g65816_op_stx_addrb(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDR); + gx816->mem_write(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr, gx816->regs.x); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(1, dest_addr); +} + +void g65816_op_stx_addrw(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDR); + gx816->mem_write(MEMMODE_NONE, MEMSIZE_WORD, dest_addr, gx816->regs.x); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(2, dest_addr); +} + +void g65816_op_stx_dpb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DP); + gx816->mem_write(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr, gx816->regs.x); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_stx_dpw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DP); + gx816->mem_write(MEMMODE_NONE, MEMSIZE_WORD, dest_addr, gx816->regs.x); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_stx_dpyb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DPY); + gx816->mem_write(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr, gx816->regs.x); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_CONDITION2); +} + +void g65816_op_stx_dpyw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DPY); + gx816->mem_write(MEMMODE_NONE, MEMSIZE_WORD, dest_addr, gx816->regs.x); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_CONDITION2); +} + +/*********** + *** sty *** + **********/ + +void g65816_op_sty_addrb(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDR); + gx816->mem_write(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr, gx816->regs.y); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(1, dest_addr); +} + +void g65816_op_sty_addrw(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDR); + gx816->mem_write(MEMMODE_NONE, MEMSIZE_WORD, dest_addr, gx816->regs.y); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(2, dest_addr); +} + +void g65816_op_sty_dpb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DP); + gx816->mem_write(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr, gx816->regs.y); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_sty_dpw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DP); + gx816->mem_write(MEMMODE_NONE, MEMSIZE_WORD, dest_addr, gx816->regs.y); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_sty_dpxb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DPX); + gx816->mem_write(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr, gx816->regs.y); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_CONDITION2); +} + +void g65816_op_sty_dpxw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DPX); + gx816->mem_write(MEMMODE_NONE, MEMSIZE_WORD, dest_addr, gx816->regs.y); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_CONDITION2); +} + +/*********** + *** stz *** + **********/ + +void g65816_op_stz_addrb(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDR); + gx816->mem_write(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr, 0); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(1, dest_addr); +} + +void g65816_op_stz_addrw(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDR); + gx816->mem_write(MEMMODE_NONE, MEMSIZE_WORD, dest_addr, 0); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(2, dest_addr); +} + +void g65816_op_stz_addrxb(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDRX); + gx816->mem_write(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr, 0); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION4); +} + +void g65816_op_stz_addrxw(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDRX); + gx816->mem_write(MEMMODE_NONE, MEMSIZE_WORD, dest_addr, 0); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION4); +} + +void g65816_op_stz_dpb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DP); + gx816->mem_write(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr, 0); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_stz_dpw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DP); + gx816->mem_write(MEMMODE_NONE, MEMSIZE_WORD, dest_addr, 0); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_stz_dpxb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DPX); + gx816->mem_write(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr, 0); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_CONDITION2); +} + +void g65816_op_stz_dpxw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DPX); + gx816->mem_write(MEMMODE_NONE, MEMSIZE_WORD, dest_addr, 0); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_CONDITION2); +} + +/*********** + *** xba *** + **********/ + +void g65816_op_xba(void) { + gx816->regs.a.p.l ^= gx816->regs.a.p.h; + gx816->regs.a.p.h ^= gx816->regs.a.p.l; + gx816->regs.a.p.l ^= gx816->regs.a.p.h; + g65816_testn(gx816->regs.a.b & 0x80); + g65816_testz(gx816->regs.a.b == 0); + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(2); +} + +/*********** + *** trb *** + **********/ + +void g65816_op_trb_addrb(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDR); +byte m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr); + g65816_testz((m & gx816->regs.a.b) == 0); + m &= ~gx816->regs.a.b; + gx816->mem_write(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr, m); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_trb_addrw(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDR); +word m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr); + g65816_testz((m & gx816->regs.a.w) == 0); + m &= ~gx816->regs.a.w; + gx816->mem_write(MEMMODE_NONE, MEMSIZE_WORD, dest_addr, m); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(4, dest_addr); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_trb_dpb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DP); +byte m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr); + g65816_testz((m & gx816->regs.a.b) == 0); + m &= ~gx816->regs.a.b; + gx816->mem_write(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr, m); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_REGD); +} + +void g65816_op_trb_dpw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DP); +word m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr); + g65816_testz((m & gx816->regs.a.w) == 0); + m &= ~gx816->regs.a.w; + gx816->mem_write(MEMMODE_NONE, MEMSIZE_WORD, dest_addr, m); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(4, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_REGD); +} + +/*********** + *** tsb *** + **********/ + +void g65816_op_tsb_addrb(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDR); +byte m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr); + g65816_testz((m & gx816->regs.a.b) == 0); + m |= gx816->regs.a.b; + gx816->mem_write(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr, m); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_tsb_addrw(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDR); +word m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr); + g65816_testz((m & gx816->regs.a.w) == 0); + m |= gx816->regs.a.w; + gx816->mem_write(MEMMODE_NONE, MEMSIZE_WORD, dest_addr, m); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(4, dest_addr); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_tsb_dpb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DP); +byte m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr); + g65816_testz((m & gx816->regs.a.b) == 0); + m |= gx816->regs.a.b; + gx816->mem_write(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr, m); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_REGD); +} + +void g65816_op_tsb_dpw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DP); +word m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr); + g65816_testz((m & gx816->regs.a.w) == 0); + m |= gx816->regs.a.w; + gx816->mem_write(MEMMODE_NONE, MEMSIZE_WORD, dest_addr, m); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(4, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_REGD); +} + +/*********** + *** mvn *** + **********/ + +void g65816_op_mvn(void) { +g65816_prefetch(2); +ulong sp, dp; +byte m; + if(gx816->regs.a.w != 0xffff) { + gx816->regs.a.w--; + dp = (arg) & 0xff; + sp = (arg >> 8) & 0xff; + dp = (dp << 16) | gx816->regs.y; + sp = (sp << 16) | gx816->regs.x; + m = gx816->mem_read(MEMMODE_LONG, MEMSIZE_BYTE, sp); + gx816->mem_write(MEMMODE_LONG, MEMSIZE_BYTE, dp, m); + gx816->regs.x++; + gx816->regs.y++; + if(gx816->regs.e == true || (gx816->regs.e == false && (gx816->regs.p & PF_X))) { + gx816->regs.x &= 0xff; + gx816->regs.y &= 0xff; + } + } + if(gx816->regs.a.w == 0xffff) { + g65816_incpc(3); + } + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(1, sp); + snes_time->add_cpu_mcycles(1, dp); + snes_time->add_cpu_icycles(2); +} + +/*********** + *** mvp *** + **********/ + +void g65816_op_mvp(void) { +g65816_prefetch(2); +ulong sp, dp; +byte m; + if(gx816->regs.a.w != 0xffff) { + gx816->regs.a.w--; + dp = (arg) & 0xff; + sp = (arg >> 8) & 0xff; + dp = (dp << 16) | gx816->regs.y; + sp = (sp << 16) | gx816->regs.x; + m = gx816->mem_read(MEMMODE_LONG, MEMSIZE_BYTE, sp); + gx816->mem_write(MEMMODE_LONG, MEMSIZE_BYTE, dp, m); + gx816->regs.x--; + gx816->regs.y--; + if(gx816->regs.e == true || (gx816->regs.e == false && (gx816->regs.p & PF_X))) { + gx816->regs.x &= 0xff; + gx816->regs.y &= 0xff; + } + } + if(gx816->regs.a.w == 0xffff) { + g65816_incpc(3); + } + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(1, sp); + snes_time->add_cpu_mcycles(1, dp); + snes_time->add_cpu_icycles(2); +} + +/*********** + *** brk *** + **********/ + +void g65816_op_brke(void) { + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_scycles(3); + snes_time->add_cpu_mcycles(2, 0x00ffe6); + g65816_incpc(2); + g65816_stackwrite(MEMSIZE_WORD, gx816->regs.pc); + g65816_stackwrite(MEMSIZE_BYTE, gx816->regs.p); + g65816_seti(); + g65816_clrd(); + gx816->regs.pc = gx816->mem_read(MEMMODE_LONG, MEMSIZE_WORD, 0x00ffe6); +} + +void g65816_op_brkn(void) { + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_scycles(4); + snes_time->add_cpu_mcycles(2, 0x00ffe6); + g65816_incpc(2); + g65816_stackwrite(MEMSIZE_LONG, gx816->regs.pc); + g65816_stackwrite(MEMSIZE_BYTE, gx816->regs.p); + g65816_seti(); + g65816_clrd(); + gx816->regs.pc = gx816->mem_read(MEMMODE_LONG, MEMSIZE_WORD, 0x00ffe6); +} + +/*********** + *** cop *** + **********/ + +void g65816_op_cope(void) { + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_scycles(3); + snes_time->add_cpu_mcycles(2, 0x00fff4); + g65816_incpc(2); + g65816_stackwrite(MEMSIZE_WORD, gx816->regs.pc); + g65816_stackwrite(MEMSIZE_BYTE, gx816->regs.p); + g65816_seti(); + g65816_clrd(); + gx816->regs.pc = gx816->mem_read(MEMMODE_LONG, MEMSIZE_WORD, 0x00fff4); +} + +void g65816_op_copn(void) { + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_scycles(4); + snes_time->add_cpu_mcycles(2, 0x00fff4); + g65816_incpc(2); + g65816_stackwrite(MEMSIZE_LONG, gx816->regs.pc); + g65816_stackwrite(MEMSIZE_BYTE, gx816->regs.p); + g65816_seti(); + g65816_clrd(); + gx816->regs.pc = gx816->mem_read(MEMMODE_LONG, MEMSIZE_WORD, 0x00fff4); +} + +/*********** + *** stp *** + **********/ + +void g65816_op_stp(void) { + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(2); + dprintf("* stp not implemented"); +} + +/*********** + *** wai *** + **********/ + +void g65816_op_wai(void) { +byte i = 0; + if(gx816->nmi_enabled == true)i = 1; + if((ppu.vcounter_enabled == true || ppu.hcounter_enabled == true) && !(gx816->regs.p & PF_I))i = 1; + if(gx816->wai_interrupt_occurred == true || i == 0) { + gx816->wai_interrupt_occurred = false; + g65816_incpc(1); + } + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(2); +} + +/*********** + *** xce *** + **********/ + +void g65816_op_xce(void) { +bool t = gx816->regs.e; + if(gx816->regs.p & PF_C)gx816->regs.e = true; + else gx816->regs.e = false; + g65816_setm(); + g65816_setx(); + if(t == true)g65816_setc(); + else g65816_clrc(); + + if(gx816->regs.e == true)gx816->regs.s = 0x0100 | (gx816->regs.s & 0xff); + + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(1); +} + +/*********** + *** nop *** + **********/ + +void g65816_op_nop(void) { + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(1); +} + +/*********** + *** wdm *** + **********/ + +void g65816_op_wdm(void) { + g65816_incpc(2); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(1); +} + +/***************************** + *** clear/set bit opcodes *** + ****************************/ + +void g65816_op_clc(void) { + g65816_clrc(); + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_cld(void) { + g65816_clrd(); + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_cli(void) { + g65816_clri(); + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_clv(void) { + g65816_clrv(); + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_sec(void) { + g65816_setc(); + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_sed(void) { + g65816_setd(); + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_sei(void) { + g65816_seti(); + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_rep(void) { +g65816_prefetch(1); + gx816->regs.p &= ~arg; + g65816_incpc(2); + snes_time->add_cpu_pcycles(3); + + if(gx816->regs.e == true)gx816->regs.p |= 0x30; +} + +void g65816_op_sep(void) { +g65816_prefetch(1); + gx816->regs.p |= arg; + if(arg & 0x10) { + gx816->regs.x &= 0xff; + gx816->regs.y &= 0xff; + } + g65816_incpc(2); + snes_time->add_cpu_pcycles(3); +} + +/************************ + *** transfer opcodes *** + ***********************/ + +void g65816_op_taxb(void) { + gx816->regs.x = gx816->regs.a.b; + g65816_testn(gx816->regs.a.b & 0x80); + g65816_testz(gx816->regs.a.b == 0); + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_taxw(void) { + gx816->regs.x = gx816->regs.a.w; + g65816_testn(gx816->regs.a.w & 0x8000); + g65816_testz(gx816->regs.a.w == 0); + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_tayb(void) { + gx816->regs.y = gx816->regs.a.b; + g65816_testn(gx816->regs.a.b & 0x80); + g65816_testz(gx816->regs.a.b == 0); + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_tayw(void) { + gx816->regs.y = gx816->regs.a.w; + g65816_testn(gx816->regs.a.w & 0x8000); + g65816_testz(gx816->regs.a.w == 0); + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_tcd(void) { + gx816->regs.d = gx816->regs.a.w; + g65816_testn(gx816->regs.a.w & 0x8000); + g65816_testz(gx816->regs.a.w == 0); + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_tcse(void) { + gx816->regs.s = 0x0100 | gx816->regs.a.b; + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_tcsn(void) { + gx816->regs.s = gx816->regs.a.w; + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(1); +} + +void g65816_tdc(void) { + gx816->regs.a.w = gx816->regs.d; + g65816_testn(gx816->regs.d & 0x8000); + g65816_testz(gx816->regs.d == 0); + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_tsce(void) { + gx816->regs.a.w = gx816->regs.s; + g65816_testn(gx816->regs.a.b & 0x80); + g65816_testz(gx816->regs.a.b == 0); + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_tscn(void) { + gx816->regs.a.w = gx816->regs.s; + g65816_testn(gx816->regs.a.w & 0x8000); + g65816_testz(gx816->regs.a.w == 0); + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_tsxb(void) { + gx816->regs.x = gx816->regs.s & 0xff; + g65816_testn(gx816->regs.s & 0x80); + g65816_testz((gx816->regs.s & 0xff) == 0); + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_tsxw(void) { + gx816->regs.x = gx816->regs.s; + g65816_testn(gx816->regs.s & 0x8000); + g65816_testz(gx816->regs.s == 0); + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_txab(void) { + gx816->regs.a.b = gx816->regs.x; + g65816_testn(gx816->regs.a.b & 0x80); + g65816_testz(gx816->regs.a.b == 0); + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_txaw(void) { + gx816->regs.a.w = gx816->regs.x; + g65816_testn(gx816->regs.a.w & 0x8000); + g65816_testz(gx816->regs.a.w == 0); + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_txsb(void) { + gx816->regs.s = gx816->regs.x; + g65816_testn(gx816->regs.x & 0x80); + g65816_testz((gx816->regs.x & 0xff) == 0); + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_txsw(void) { + gx816->regs.s = gx816->regs.x; + g65816_testn(gx816->regs.x & 0x8000); + g65816_testz(gx816->regs.x == 0); + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_txyb(void) { + gx816->regs.y = gx816->regs.x; + g65816_testn(gx816->regs.x & 0x80); + g65816_testz((gx816->regs.x & 0xff) == 0); + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_txyw(void) { + gx816->regs.y = gx816->regs.x; + g65816_testn(gx816->regs.x & 0x8000); + g65816_testz(gx816->regs.x == 0); + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_tyab(void) { + gx816->regs.a.b = gx816->regs.y; + g65816_testn(gx816->regs.a.b & 0x80); + g65816_testz(gx816->regs.a.b == 0); + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_tyaw(void) { + gx816->regs.a.w = gx816->regs.y; + g65816_testn(gx816->regs.a.w & 0x8000); + g65816_testz(gx816->regs.a.w == 0); + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_tyxb(void) { + gx816->regs.x = gx816->regs.y; + g65816_testn(gx816->regs.y & 0x80); + g65816_testz((gx816->regs.y & 0xff) == 0); + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_tyxw(void) { + gx816->regs.x = gx816->regs.y; + g65816_testn(gx816->regs.y & 0x8000); + g65816_testz(gx816->regs.y == 0); + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(1); +} diff --git a/bsnes/cpu/g65816_ops_ora.cpp b/bsnes/cpu/g65816_ops_ora.cpp new file mode 100644 index 00000000..f6aa2d75 --- /dev/null +++ b/bsnes/cpu/g65816_ops_ora.cpp @@ -0,0 +1,335 @@ +#define g65816_flags_ora_b() g65816_testn(gx816->regs.a.b & 0x80); g65816_testz(gx816->regs.a.b == 0) +#define g65816_flags_ora_w() g65816_testn(gx816->regs.a.w & 0x8000); g65816_testz(gx816->regs.a.w == 0) + +#define g65816_inst_ora_b() gx816->regs.a.b |= gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr) +#define g65816_inst_ora_w() gx816->regs.a.w |= gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr) + +void g65816_op_ora_addrb(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDR); + g65816_inst_ora_b(); + g65816_flags_ora_b(); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(1, dest_addr); +} + +void g65816_op_ora_addrw(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDR); + g65816_inst_ora_w(); + g65816_flags_ora_w(); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(2, dest_addr); +} + +void g65816_op_ora_addrxb(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDRX); + g65816_inst_ora_b(); + g65816_flags_ora_b(); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION4); +} + +void g65816_op_ora_addrxw(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDRX); + g65816_inst_ora_w(); + g65816_flags_ora_w(); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION4); +} + +void g65816_op_ora_dpb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DP); + g65816_inst_ora_b(); + g65816_flags_ora_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_ora_dpw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DP); + g65816_inst_ora_w(); + g65816_flags_ora_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_ora_idpb(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_IDP); + g65816_inst_ora_b(); + g65816_flags_ora_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, base_addr); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_ora_idpw(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_IDP); + g65816_inst_ora_w(); + g65816_flags_ora_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, base_addr); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_ora_ildpb(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_ILDP); + g65816_inst_ora_b(); + g65816_flags_ora_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(3, base_addr); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_ora_ildpw(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_ILDP); + g65816_inst_ora_w(); + g65816_flags_ora_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(3, base_addr); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_ora_longb(void) { +g65816_prefetch(3); +g65816_getaddr(MEMMODE_LONG); + g65816_inst_ora_b(); + g65816_flags_ora_b(); + g65816_incpc(4); + snes_time->add_cpu_pcycles(4); + snes_time->add_cpu_mcycles(1, dest_addr); +} + +void g65816_op_ora_longw(void) { +g65816_prefetch(3); +g65816_getaddr(MEMMODE_LONG); + g65816_inst_ora_w(); + g65816_flags_ora_w(); + g65816_incpc(4); + snes_time->add_cpu_pcycles(4); + snes_time->add_cpu_mcycles(2, dest_addr); +} + +void g65816_op_ora_longxb(void) { +g65816_prefetch(3); +g65816_getaddr(MEMMODE_LONGX); + g65816_inst_ora_b(); + g65816_flags_ora_b(); + g65816_incpc(4); + snes_time->add_cpu_pcycles(4); + snes_time->add_cpu_mcycles(1, dest_addr); +} + +void g65816_op_ora_longxw(void) { +g65816_prefetch(3); +g65816_getaddr(MEMMODE_LONGX); + g65816_inst_ora_w(); + g65816_flags_ora_w(); + g65816_incpc(4); + snes_time->add_cpu_pcycles(4); + snes_time->add_cpu_mcycles(2, dest_addr); +} + +void g65816_op_ora_addryb(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDRY); + g65816_inst_ora_b(); + g65816_flags_ora_b(); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION4); +} + +void g65816_op_ora_addryw(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDRY); + g65816_inst_ora_w(); + g65816_flags_ora_w(); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION4); +} + +void g65816_op_ora_dpxb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DPX); + g65816_inst_ora_b(); + g65816_flags_ora_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_CONDITION2); +} + +void g65816_op_ora_dpxw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DPX); + g65816_inst_ora_w(); + g65816_flags_ora_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_CONDITION2); +} + +void g65816_op_ora_idpxb(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_IDPX); + g65816_inst_ora_b(); + g65816_flags_ora_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, base_addr); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_CONDITION2); +} + +void g65816_op_ora_idpxw(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_IDPX); + g65816_inst_ora_w(); + g65816_flags_ora_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, base_addr); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_CONDITION2); +} + +void g65816_op_ora_idpyb(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_IDPY); + g65816_inst_ora_b(); + g65816_flags_ora_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, base_addr); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2 | TIMING_CONDITION4); +} + +void g65816_op_ora_idpyw(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_IDPY); + g65816_inst_ora_w(); + g65816_flags_ora_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, base_addr); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2 | TIMING_CONDITION4); +} + +void g65816_op_ora_ildpyb(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_ILDPY); + g65816_inst_ora_b(); + g65816_flags_ora_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(3, base_addr); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_ora_ildpyw(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_ILDPY); + g65816_inst_ora_w(); + g65816_flags_ora_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(3, base_addr); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_ora_srb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_SR); + g65816_inst_ora_b(); + g65816_flags_ora_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_scycles(1); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_ora_srw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_SR); + g65816_inst_ora_w(); + g65816_flags_ora_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_scycles(2); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_ora_isryb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_ISRY); + g65816_inst_ora_b(); + g65816_flags_ora_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_scycles(2); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(2); +} + +void g65816_op_ora_isryw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_ISRY); + g65816_inst_ora_w(); + g65816_flags_ora_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_scycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(2); +} + +void g65816_op_ora_constb(void) { +g65816_prefetch(1); + gx816->regs.a.b |= arg; + g65816_flags_ora_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); +} + +void g65816_op_ora_constw(void) { +g65816_prefetch(2); + gx816->regs.a.w |= arg; + g65816_flags_ora_w(); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); +} diff --git a/bsnes/cpu/g65816_ops_pc.cpp b/bsnes/cpu/g65816_ops_pc.cpp new file mode 100644 index 00000000..efa52755 --- /dev/null +++ b/bsnes/cpu/g65816_ops_pc.cpp @@ -0,0 +1,223 @@ +/*********** + *** jmp *** + **********/ + +void g65816_op_jmp_addr(void) { +g65816_prefetch(2); + snes_time->add_cpu_pcycles(3); + gx816->regs.pc = (gx816->regs.pc & 0xff0000) | arg; +} + +void g65816_op_jmp_long(void) { +g65816_prefetch(3); + snes_time->add_cpu_pcycles(4); + gx816->regs.pc = arg; +} + +void g65816_op_jmp_iaddr(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_IADDR_PC); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(2, dest_addr); + gx816->regs.pc = (gx816->regs.pc & 0xff0000) | gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr); +} + +void g65816_op_jmp_iaddrx(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_IADDRX_PC); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(1); + gx816->regs.pc = (gx816->regs.pc & 0xff0000) | gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr); +} + +void g65816_op_jmp_iladdr(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ILADDR_PC); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(3, dest_addr); + gx816->regs.pc = gx816->mem_read(MEMMODE_NONE, MEMSIZE_LONG, dest_addr); +} + +/*********** + *** jsr *** + **********/ + +//Note: The address pushed onto the stack is one byte less than the operand size. +//Upon returning, the address counter is incremented one more to move to the next +//instruction. The third/fourth cycles store the pc address on the stack, before +//the second operand byte is read in. + +void g65816_op_jsr_addr(void) { +g65816_prefetch(2); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_scycles(2); + snes_time->add_cpu_icycles(1); + g65816_stackwrite(MEMSIZE_WORD, gx816->regs.pc + 2); + gx816->regs.pc = (gx816->regs.pc & 0xff0000) | arg; +} + +void g65816_op_jsr_long(void) { +g65816_prefetch(3); + snes_time->add_cpu_pcycles(4); + snes_time->add_cpu_scycles(3); + snes_time->add_cpu_icycles(1); + g65816_stackwrite(MEMSIZE_LONG, gx816->regs.pc + 3); + gx816->regs.pc = arg; +} + +void g65816_op_jsr_iaddrx(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_IADDRX_PC); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_scycles(2); + snes_time->add_cpu_icycles(1); + g65816_stackwrite(MEMSIZE_WORD, gx816->regs.pc + 2); + gx816->regs.pc = (gx816->regs.pc & 0xff0000) | gx816->mem_read(MEMMODE_IADDRX_PC, MEMSIZE_WORD, arg); + snes_time->add_cpu_pcycles(2); +} + +/*********** + *** ret *** + **********/ + +void g65816_op_rtie(void) { + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_scycles(3); + gx816->regs.p = g65816_stackread(MEMSIZE_BYTE); + gx816->regs.pc = (gx816->regs.pc & 0xff0000) | g65816_stackread(MEMSIZE_WORD); + if(gx816->regs.p & PF_X) { gx816->regs.x &= 0xff; gx816->regs.y &= 0xff; } + gx816->regs.p &= ~ PF_I; + snes_time->add_cpu_icycles(2); +} + +void g65816_op_rtin(void) { + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_scycles(4); + gx816->regs.p = g65816_stackread(MEMSIZE_BYTE); + gx816->regs.pc = g65816_stackread(MEMSIZE_LONG); + if(gx816->regs.p & PF_X) { gx816->regs.x &= 0xff; gx816->regs.y &= 0xff; } + snes_time->add_cpu_icycles(2); +} + +void g65816_op_rts(void) { + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_scycles(2); + snes_time->add_cpu_icycles(3); + gx816->regs.pc = ((gx816->regs.pc & 0xff0000) | g65816_stackread(MEMSIZE_WORD)) + 1; +} + +void g65816_op_rtl(void) { + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_scycles(3); + snes_time->add_cpu_icycles(2); + gx816->regs.pc = g65816_stackread(MEMSIZE_LONG) + 1; +} + +/*********** + *** bra *** + **********/ + +//Need to add condition (5) to cycle counts: if e=1, add one (i)cycle if page boundary crossed + +void g65816_op_bra(void) { +g65816_prefetch(1); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_icycles(1); + gx816->regs.pc += (signed char)(arg + 2); +} + +void g65816_op_brl(void) { +g65816_prefetch(2); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_icycles(1); + gx816->regs.pc += (signed short)(arg + 3); +} + +void g65816_op_bcc(void) { +g65816_prefetch(1); + snes_time->add_cpu_pcycles(2); + if(!(gx816->regs.p & PF_C)) { + snes_time->add_cpu_icycles(1); + gx816->regs.pc += (signed char)(arg + 2); + } else { + g65816_incpc(2); + } +} + +void g65816_op_bcs(void) { +g65816_prefetch(1); + snes_time->add_cpu_pcycles(2); + if(gx816->regs.p & PF_C) { + snes_time->add_cpu_icycles(1); + gx816->regs.pc += (signed char)(arg + 2); + } else { + g65816_incpc(2); + } +} + +void g65816_op_bne(void) { +g65816_prefetch(1); + snes_time->add_cpu_pcycles(2); + if(!(gx816->regs.p & PF_Z)) { + snes_time->add_cpu_icycles(1); + gx816->regs.pc += (signed char)(arg + 2); + } else { + g65816_incpc(2); + } +} + +void g65816_op_beq(void) { +g65816_prefetch(1); + snes_time->add_cpu_pcycles(2); + if(gx816->regs.p & PF_Z) { + snes_time->add_cpu_icycles(1); + gx816->regs.pc += (signed char)(arg + 2); + } else { + g65816_incpc(2); + } +} + +void g65816_op_bpl(void) { +g65816_prefetch(1); + snes_time->add_cpu_pcycles(2); + if(!(gx816->regs.p & PF_N)) { + snes_time->add_cpu_icycles(1); + gx816->regs.pc += (signed char)(arg + 2); + } else { + g65816_incpc(2); + } +} + +void g65816_op_bmi(void) { +g65816_prefetch(1); + snes_time->add_cpu_pcycles(2); + if(gx816->regs.p & PF_N) { + snes_time->add_cpu_icycles(1); + gx816->regs.pc += (signed char)(arg + 2); + } else { + g65816_incpc(2); + } +} + +void g65816_op_bvc(void) { +g65816_prefetch(1); + snes_time->add_cpu_pcycles(2); + if(!(gx816->regs.p & PF_V)) { + snes_time->add_cpu_icycles(1); + gx816->regs.pc += (signed char)(arg + 2); + } else { + g65816_incpc(2); + } +} + +void g65816_op_bvs(void) { +g65816_prefetch(1); + snes_time->add_cpu_pcycles(2); + if(gx816->regs.p & PF_V) { + snes_time->add_cpu_icycles(1); + gx816->regs.pc += (signed char)(arg + 2); + } else { + g65816_incpc(2); + } +} diff --git a/bsnes/cpu/g65816_ops_sbc.cpp b/bsnes/cpu/g65816_ops_sbc.cpp new file mode 100644 index 00000000..f5f3c076 --- /dev/null +++ b/bsnes/cpu/g65816_ops_sbc.cpp @@ -0,0 +1,345 @@ +#define bcd_sub_adjust_byte() \ + if(gx816->regs.p & PF_D) { \ + if(((r ) & 15) > 9)r -= 6; \ + if(((r >> 4) & 15) > 9)r -= 6 << 4; \ + } + +#define bcd_sub_adjust_word() \ + if(gx816->regs.p & PF_D) { \ + if(((r ) & 15) > 9)r -= 6; \ + if(((r >> 4) & 15) > 9)r -= 6 << 4; \ + if(((r >> 8) & 15) > 9)r -= 6 << 8; \ + if(((r >> 12) & 15) > 9)r -= 6 << 12; \ + } + +#define g65816_if_sbc_b() \ +byte c = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr); \ +int r = gx816->regs.a.b - c - !(gx816->regs.p & PF_C); \ + bcd_sub_adjust_byte() \ + g65816_testn(r & 0x80); \ + g65816_testv((gx816->regs.a.b ^ c) & (gx816->regs.a.b ^ (byte)r) & 0x80); \ + g65816_testz((byte)r == 0); \ + g65816_testc(r >= 0); \ + gx816->regs.a.b = r + +#define g65816_if_sbc_w() \ +word c = gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr); \ +int r = gx816->regs.a.w - c - !(gx816->regs.p & PF_C); \ + bcd_sub_adjust_word() \ + g65816_testn(r & 0x8000); \ + g65816_testv((gx816->regs.a.w ^ c) & (gx816->regs.a.w ^ (word)r) & 0x8000); \ + g65816_testz((word)r == 0); \ + g65816_testc(r >= 0); \ + gx816->regs.a.w = r + +void g65816_op_sbc_addrb(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDR); + g65816_if_sbc_b(); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(1, dest_addr); +} + +void g65816_op_sbc_addrw(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDR); + g65816_if_sbc_w(); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(2, dest_addr); +} + +void g65816_op_sbc_addrxb(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDRX); + g65816_if_sbc_b(); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION4); +} + +void g65816_op_sbc_addrxw(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDRX); + g65816_if_sbc_w(); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION4); +} + +void g65816_op_sbc_dpb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DP); + g65816_if_sbc_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_sbc_dpw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DP); + g65816_if_sbc_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_sbc_idpb(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_IDP); + g65816_if_sbc_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, base_addr); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_sbc_idpw(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_IDP); + g65816_if_sbc_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, base_addr); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_sbc_ildpb(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_ILDP); + g65816_if_sbc_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(3, base_addr); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_sbc_ildpw(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_ILDP); + g65816_if_sbc_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(3, base_addr); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_sbc_longb(void) { +g65816_prefetch(3); +g65816_getaddr(MEMMODE_LONG); + g65816_if_sbc_b(); + g65816_incpc(4); + snes_time->add_cpu_pcycles(4); + snes_time->add_cpu_mcycles(1, dest_addr); +} + +void g65816_op_sbc_longw(void) { +g65816_prefetch(3); +g65816_getaddr(MEMMODE_LONG); + g65816_if_sbc_w(); + g65816_incpc(4); + snes_time->add_cpu_pcycles(4); + snes_time->add_cpu_mcycles(2, dest_addr); +} + +void g65816_op_sbc_longxb(void) { +g65816_prefetch(3); +g65816_getaddr(MEMMODE_LONGX); + g65816_if_sbc_b(); + g65816_incpc(4); + snes_time->add_cpu_pcycles(4); + snes_time->add_cpu_mcycles(1, dest_addr); +} + +void g65816_op_sbc_longxw(void) { +g65816_prefetch(3); +g65816_getaddr(MEMMODE_LONGX); + g65816_if_sbc_w(); + g65816_incpc(4); + snes_time->add_cpu_pcycles(4); + snes_time->add_cpu_mcycles(2, dest_addr); +} + +void g65816_op_sbc_addryb(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDRY); + g65816_if_sbc_b(); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION4); +} + +void g65816_op_sbc_addryw(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDRY); + g65816_if_sbc_w(); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION4); +} + +void g65816_op_sbc_dpxb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DPX); + g65816_if_sbc_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_CONDITION2); +} + +void g65816_op_sbc_dpxw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DPX); + g65816_if_sbc_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_CONDITION2); +} + +void g65816_op_sbc_idpxb(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_IDPX); + g65816_if_sbc_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, base_addr); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_CONDITION2); +} + +void g65816_op_sbc_idpxw(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_IDPX); + g65816_if_sbc_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, base_addr); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_CONDITION2); +} + +void g65816_op_sbc_idpyb(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_IDPY); + g65816_if_sbc_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, base_addr); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2 | TIMING_CONDITION4); +} + +void g65816_op_sbc_idpyw(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_IDPY); + g65816_if_sbc_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, base_addr); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2 | TIMING_CONDITION4); +} + +void g65816_op_sbc_ildpyb(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_ILDPY); + g65816_if_sbc_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(3, base_addr); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_sbc_ildpyw(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_ILDPY); + g65816_if_sbc_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(3, base_addr); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_sbc_srb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_SR); + g65816_if_sbc_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_scycles(1); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_sbc_srw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_SR); + g65816_if_sbc_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_scycles(2); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_sbc_isryb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_ISRY); + g65816_if_sbc_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_scycles(2); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(2); +} + +void g65816_op_sbc_isryw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_ISRY); + g65816_if_sbc_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_scycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(2); +} + +void g65816_op_sbc_constb(void) { +g65816_prefetch(1); +int r = gx816->regs.a.b - arg - !(gx816->regs.p & PF_C); + bcd_sub_adjust_byte() + g65816_testn(r & 0x80); + g65816_testv((gx816->regs.a.b ^ arg) & (gx816->regs.a.b ^ (byte)r) & 0x80); + g65816_testz((byte)r == 0); + g65816_testc(r >= 0); + gx816->regs.a.b = r; + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); +} + +void g65816_op_sbc_constw(void) { +g65816_prefetch(2); +int r = gx816->regs.a.w - arg - !(gx816->regs.p & PF_C); + bcd_sub_adjust_word() + g65816_testn(r & 0x8000); + g65816_testv((gx816->regs.a.w ^ arg) & (gx816->regs.a.w ^ (word)r) & 0x8000); + g65816_testz((word)r == 0); + g65816_testc(r >= 0); + gx816->regs.a.w = r; + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); +} diff --git a/bsnes/cpu/g65816_ops_shift.cpp b/bsnes/cpu/g65816_ops_shift.cpp new file mode 100644 index 00000000..2e61d5f8 --- /dev/null +++ b/bsnes/cpu/g65816_ops_shift.cpp @@ -0,0 +1,595 @@ +/********** +*** asl *** +**********/ + +void g65816_op_aslb(void) { + g65816_testc(gx816->regs.a.b & 0x80); + gx816->regs.a.b <<= 1; + g65816_testn(gx816->regs.a.b & 0x80); + g65816_testz(gx816->regs.a.b == 0); + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_aslw(void) { + g65816_testc(gx816->regs.a.w & 0x8000); + gx816->regs.a.w <<= 1; + g65816_testn(gx816->regs.a.w & 0x8000); + g65816_testz(gx816->regs.a.w == 0); + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_asl_addrb(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDR); +byte m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr); + g65816_testc(m & 0x80); + m <<= 1; + g65816_testn(m & 0x80); + g65816_testz(m == 0); + gx816->mem_write(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr, m); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_asl_addrw(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDR); +word m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr); + g65816_testc(m & 0x8000); + m <<= 1; + g65816_testn(m & 0x8000); + g65816_testz(m == 0); + gx816->mem_write(MEMMODE_NONE, MEMSIZE_WORD, dest_addr, m); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(4, dest_addr); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_asl_addrxb(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDRX); +byte m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr); + g65816_testc(m & 0x80); + m <<= 1; + g65816_testn(m & 0x80); + g65816_testz(m == 0); + gx816->mem_write(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr, m); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(2); +} + +void g65816_op_asl_addrxw(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDRX); +word m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr); + g65816_testc(m & 0x8000); + m <<= 1; + g65816_testn(m & 0x8000); + g65816_testz(m == 0); + gx816->mem_write(MEMMODE_NONE, MEMSIZE_WORD, dest_addr, m); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(4, dest_addr); + snes_time->add_cpu_icycles(2); +} + +void g65816_op_asl_dpb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DP); +byte m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr); + g65816_testc(m & 0x80); + m <<= 1; + g65816_testn(m & 0x80); + g65816_testz(m == 0); + gx816->mem_write(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr, m); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_REGD); +} + +void g65816_op_asl_dpw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DP); +word m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr); + g65816_testc(m & 0x8000); + m <<= 1; + g65816_testn(m & 0x8000); + g65816_testz(m == 0); + gx816->mem_write(MEMMODE_NONE, MEMSIZE_WORD, dest_addr, m); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(4, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_REGD); +} + +void g65816_op_asl_dpxb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DPX); +byte m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr); + g65816_testc(m & 0x80); + m <<= 1; + g65816_testn(m & 0x80); + g65816_testz(m == 0); + gx816->mem_write(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr, m); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(2, TIMING_REGD); +} + +void g65816_op_asl_dpxw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DPX); +word m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr); + g65816_testc(m & 0x8000); + m <<= 1; + g65816_testn(m & 0x8000); + g65816_testz(m == 0); + gx816->mem_write(MEMMODE_NONE, MEMSIZE_WORD, dest_addr, m); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(4, dest_addr); + snes_time->add_cpu_icycles(2, TIMING_REGD); +} + +/********** +*** lsr *** +**********/ + +void g65816_op_lsrb(void) { + g65816_testc(gx816->regs.a.b & 0x01); + gx816->regs.a.b >>= 1; + g65816_clrn(); + g65816_testz(gx816->regs.a.b == 0); + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_lsrw(void) { + g65816_testc(gx816->regs.a.w & 0x0001); + gx816->regs.a.w >>= 1; + g65816_clrn(); + g65816_testz(gx816->regs.a.w == 0); + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_lsr_addrb(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDR); +byte m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr); + g65816_testc(m & 0x01); + m >>= 1; + g65816_clrn(); + g65816_testz(m == 0); + gx816->mem_write(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr, m); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_lsr_addrw(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDR); +word m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr); + g65816_testc(m & 0x0001); + m >>= 1; + g65816_clrn(); + g65816_testz(m == 0); + gx816->mem_write(MEMMODE_NONE, MEMSIZE_WORD, dest_addr, m); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(4, dest_addr); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_lsr_addrxb(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDRX); +byte m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr); + g65816_testc(m & 0x01); + m >>= 1; + g65816_clrn(); + g65816_testz(m == 0); + gx816->mem_write(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr, m); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(2); +} + +void g65816_op_lsr_addrxw(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDRX); +word m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr); + g65816_testc(m & 0x0001); + m >>= 1; + g65816_clrn(); + g65816_testz(m == 0); + gx816->mem_write(MEMMODE_NONE, MEMSIZE_WORD, dest_addr, m); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(4, dest_addr); + snes_time->add_cpu_icycles(2); +} + +void g65816_op_lsr_dpb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DP); +byte m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr); + g65816_testc(m & 0x01); + m >>= 1; + g65816_clrn(); + g65816_testz(m == 0); + gx816->mem_write(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr, m); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_REGD); +} + +void g65816_op_lsr_dpw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DP); +word m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr); + g65816_testc(m & 0x0001); + m >>= 1; + g65816_clrn(); + g65816_testz(m == 0); + gx816->mem_write(MEMMODE_NONE, MEMSIZE_WORD, dest_addr, m); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(4, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_REGD); +} + +void g65816_op_lsr_dpxb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DPX); +byte m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr); + g65816_testc(m & 0x01); + m >>= 1; + g65816_clrn(); + g65816_testz(m == 0); + gx816->mem_write(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr, m); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(2, TIMING_REGD); +} + +void g65816_op_lsr_dpxw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DPX); +word m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr); + g65816_testc(m & 0x0001); + m >>= 1; + g65816_clrn(); + g65816_testz(m == 0); + gx816->mem_write(MEMMODE_NONE, MEMSIZE_WORD, dest_addr, m); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(4, dest_addr); + snes_time->add_cpu_icycles(2, TIMING_REGD); +} + +/********** +*** rol *** +**********/ + +void g65816_op_rolb(void) { +byte c = gx816->regs.p & PF_C; + g65816_testc(gx816->regs.a.b & 0x80); + gx816->regs.a.b = (gx816->regs.a.b << 1) | c; + g65816_testn(gx816->regs.a.b & 0x80); + g65816_testz(gx816->regs.a.b == 0); + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_rolw(void) { +byte c = gx816->regs.p & PF_C; + g65816_testc(gx816->regs.a.w & 0x8000); + gx816->regs.a.w = (gx816->regs.a.w << 1) | c; + g65816_testn(gx816->regs.a.w & 0x8000); + g65816_testz(gx816->regs.a.w == 0); + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_rol_addrb(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDR); +byte c = gx816->regs.p & PF_C; +byte m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr); + g65816_testc(m & 0x80); + m = (m << 1) | c; + g65816_testn(m & 0x80); + g65816_testz(m == 0); + gx816->mem_write(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr, m); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_rol_addrw(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDR); +byte c = gx816->regs.p & PF_C; +word m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr); + g65816_testc(m & 0x8000); + m = (m << 1) | c; + g65816_testn(m & 0x8000); + g65816_testz(m == 0); + gx816->mem_write(MEMMODE_NONE, MEMSIZE_WORD, dest_addr, m); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(4, dest_addr); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_rol_addrxb(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDRX); +byte c = gx816->regs.p & PF_C; +byte m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr); + g65816_testc(m & 0x80); + m = (m << 1) | c; + g65816_testn(m & 0x80); + g65816_testz(m == 0); + gx816->mem_write(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr, m); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(2); +} + +void g65816_op_rol_addrxw(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDRX); +byte c = gx816->regs.p & PF_C; +word m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr); + g65816_testc(m & 0x8000); + m = (m << 1) | c; + g65816_testn(m & 0x8000); + g65816_testz(m == 0); + gx816->mem_write(MEMMODE_NONE, MEMSIZE_WORD, dest_addr, m); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(4, dest_addr); + snes_time->add_cpu_icycles(2); +} + +void g65816_op_rol_dpb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DP); +byte c = gx816->regs.p & PF_C; +byte m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr); + g65816_testc(m & 0x80); + m = (m << 1) | c; + g65816_testn(m & 0x80); + g65816_testz(m == 0); + gx816->mem_write(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr, m); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_REGD); +} + +void g65816_op_rol_dpw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DP); +byte c = gx816->regs.p & PF_C; +word m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr); + g65816_testc(m & 0x8000); + m = (m << 1) | c; + g65816_testn(m & 0x8000); + g65816_testz(m == 0); + gx816->mem_write(MEMMODE_NONE, MEMSIZE_WORD, dest_addr, m); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(4, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_REGD); +} + +void g65816_op_rol_dpxb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DPX); +byte c = gx816->regs.p & PF_C; +byte m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr); + g65816_testc(m & 0x80); + m = (m << 1) | c; + g65816_testn(m & 0x80); + g65816_testz(m == 0); + gx816->mem_write(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr, m); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(2, TIMING_REGD); +} + +void g65816_op_rol_dpxw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DPX); +byte c = gx816->regs.p & PF_C; +word m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, arg); + g65816_testc(m & 0x8000); + m = (m << 1) | c; + g65816_testn(m & 0x8000); + g65816_testz(m == 0); + gx816->mem_write(MEMMODE_NONE, MEMSIZE_WORD, arg, m); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(4, dest_addr); + snes_time->add_cpu_icycles(2, TIMING_REGD); +} + +/********** +*** ror *** +**********/ + +void g65816_op_rorb(void) { +byte c = (gx816->regs.p & PF_C)?0x80:0x00; + g65816_testc(gx816->regs.a.b & 0x01); + gx816->regs.a.b = (gx816->regs.a.b >> 1) | c; + g65816_testn(gx816->regs.a.b & 0x80); + g65816_testz(gx816->regs.a.b == 0); + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_rorw(void) { +word c = (gx816->regs.p & PF_C)?0x8000:0x0000; + g65816_testc(gx816->regs.a.w & 0x0001); + gx816->regs.a.w = (gx816->regs.a.w >> 1) | c; + g65816_testn(gx816->regs.a.w & 0x8000); + g65816_testz(gx816->regs.a.w == 0); + g65816_incpc(1); + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_ror_addrb(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDR); +byte c = (gx816->regs.p & PF_C)?0x80:0x00; +byte m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr); + g65816_testc(m & 0x01); + m = (m >> 1) | c; + g65816_testn(m & 0x80); + g65816_testz(m == 0); + gx816->mem_write(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr, m); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_ror_addrw(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDR); +word c = (gx816->regs.p & PF_C)?0x8000:0x0000; +word m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr); + g65816_testc(m & 0x0001); + m = (m >> 1) | c; + g65816_testn(m & 0x8000); + g65816_testz(m == 0); + gx816->mem_write(MEMMODE_NONE, MEMSIZE_WORD, dest_addr, m); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(4, dest_addr); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_ror_addrxb(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDRX); +byte c = (gx816->regs.p & PF_C)?0x80:0x00; +byte m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr); + g65816_testc(m & 0x01); + m = (m >> 1) | c; + g65816_testn(m & 0x80); + g65816_testz(m == 0); + gx816->mem_write(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr, m); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(2); +} + +void g65816_op_ror_addrxw(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDRX); +word c = (gx816->regs.p & PF_C)?0x8000:0x0000; +word m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr); + g65816_testc(m & 0x0001); + m = (m >> 1) | c; + g65816_testn(m & 0x8000); + g65816_testz(m == 0); + gx816->mem_write(MEMMODE_NONE, MEMSIZE_WORD, dest_addr, m); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(4, dest_addr); + snes_time->add_cpu_icycles(2); +} + +void g65816_op_ror_dpb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DP); +byte c = (gx816->regs.p & PF_C)?0x80:0x00; +byte m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr); + g65816_testc(m & 0x01); + m = (m >> 1) | c; + g65816_testn(m & 0x80); + g65816_testz(m == 0); + gx816->mem_write(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr, m); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_REGD); +} + +void g65816_op_ror_dpw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DP); +word c = (gx816->regs.p & PF_C)?0x8000:0x0000; +word m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr); + g65816_testc(m & 0x0001); + m = (m >> 1) | c; + g65816_testn(m & 0x8000); + g65816_testz(m == 0); + gx816->mem_write(MEMMODE_NONE, MEMSIZE_WORD, dest_addr, m); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(4, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_REGD); +} + +void g65816_op_ror_dpxb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DPX); +byte c = (gx816->regs.p & PF_C)?0x80:0x00; +byte m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr); + g65816_testc(m & 0x01); + m = (m >> 1) | c; + g65816_testn(m & 0x80); + g65816_testz(m == 0); + gx816->mem_write(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr, m); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(2, TIMING_REGD); +} + +void g65816_op_ror_dpxw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DPX); +word c = (gx816->regs.p & PF_C)?0x8000:0x0000; +word m = gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr); + g65816_testc(m & 0x0001); + m = (m >> 1) | c; + g65816_testn(m & 0x8000); + g65816_testz(m == 0); + gx816->mem_write(MEMMODE_NONE, MEMSIZE_WORD, dest_addr, m); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(4, dest_addr); + snes_time->add_cpu_icycles(2, TIMING_REGD); +} diff --git a/bsnes/cpu/g65816_ops_sta.cpp b/bsnes/cpu/g65816_ops_sta.cpp new file mode 100644 index 00000000..5f2069cc --- /dev/null +++ b/bsnes/cpu/g65816_ops_sta.cpp @@ -0,0 +1,288 @@ +#define g65816_sta_data_b() gx816->mem_write(MEMMODE_NONE, MEMSIZE_BYTE, dest_addr, gx816->regs.a.b) +#define g65816_sta_data_w() gx816->mem_write(MEMMODE_NONE, MEMSIZE_WORD, dest_addr, gx816->regs.a.w) + +void g65816_op_sta_addrb(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDR); + g65816_sta_data_b(); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(1, dest_addr); +} + +void g65816_op_sta_addrw(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDR); + g65816_sta_data_w(); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(2, dest_addr); +} + +void g65816_op_sta_addrxb(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDRX); + g65816_sta_data_b(); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION4); +} + +void g65816_op_sta_addrxw(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDRX); + g65816_sta_data_w(); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION4); +} + +void g65816_op_sta_dpb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DP); + g65816_sta_data_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_sta_dpw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DP); + g65816_sta_data_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_sta_idpb(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_IDP); + g65816_sta_data_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, base_addr); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_sta_idpw(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_IDP); + g65816_sta_data_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, base_addr); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_sta_ildpb(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_ILDP); + g65816_sta_data_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(3, base_addr); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_sta_ildpw(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_ILDP); + g65816_sta_data_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(3, base_addr); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_sta_longb(void) { +g65816_prefetch(3); +g65816_getaddr(MEMMODE_LONG); + g65816_sta_data_b(); + g65816_incpc(4); + snes_time->add_cpu_pcycles(4); + snes_time->add_cpu_mcycles(1, dest_addr); +} + +void g65816_op_sta_longw(void) { +g65816_prefetch(3); +g65816_getaddr(MEMMODE_LONG); + g65816_sta_data_w(); + g65816_incpc(4); + snes_time->add_cpu_pcycles(4); + snes_time->add_cpu_mcycles(2, dest_addr); +} + +void g65816_op_sta_longxb(void) { +g65816_prefetch(3); +g65816_getaddr(MEMMODE_LONGX); + g65816_sta_data_b(); + g65816_incpc(4); + snes_time->add_cpu_pcycles(4); + snes_time->add_cpu_mcycles(1, dest_addr); +} + +void g65816_op_sta_longxw(void) { +g65816_prefetch(3); +g65816_getaddr(MEMMODE_LONGX); + g65816_sta_data_w(); + g65816_incpc(4); + snes_time->add_cpu_pcycles(4); + snes_time->add_cpu_mcycles(2, dest_addr); +} + +void g65816_op_sta_addryb(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDRY); + g65816_sta_data_b(); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION4); +} + +void g65816_op_sta_addryw(void) { +g65816_prefetch(2); +g65816_getaddr(MEMMODE_ADDRY); + g65816_sta_data_w(); + g65816_incpc(3); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION4); +} + +void g65816_op_sta_dpxb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DPX); + g65816_sta_data_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_CONDITION2); +} + +void g65816_op_sta_dpxw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DPX); + g65816_sta_data_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_CONDITION2); +} + +void g65816_op_sta_idpxb(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_IDPX); + g65816_sta_data_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, base_addr); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_CONDITION2); +} + +void g65816_op_sta_idpxw(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_IDPX); + g65816_sta_data_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, base_addr); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(1, TIMING_CONDITION2); +} + +void g65816_op_sta_idpyb(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_IDPY); + g65816_sta_data_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, base_addr); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2 | TIMING_CONDITION4); +} + +void g65816_op_sta_idpyw(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_IDPY); + g65816_sta_data_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, base_addr); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2 | TIMING_CONDITION4); +} + +void g65816_op_sta_ildpyb(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_ILDPY); + g65816_sta_data_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(3, base_addr); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_sta_ildpyw(void) { +g65816_prefetch(1); +g65816_getiaddr(MEMMODE_ILDPY); + g65816_sta_data_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(3, base_addr); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(0, TIMING_CONDITION2); +} + +void g65816_op_sta_srb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_SR); + g65816_sta_data_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_scycles(1); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_sta_srw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_SR); + g65816_sta_data_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_scycles(2); + snes_time->add_cpu_icycles(1); +} + +void g65816_op_sta_isryb(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_ISRY); + g65816_sta_data_b(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_scycles(2); + snes_time->add_cpu_mcycles(1, dest_addr); + snes_time->add_cpu_icycles(2); +} + +void g65816_op_sta_isryw(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_ISRY); + g65816_sta_data_w(); + g65816_incpc(2); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_scycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_icycles(2); +} diff --git a/bsnes/cpu/g65816_ops_stack.cpp b/bsnes/cpu/g65816_ops_stack.cpp new file mode 100644 index 00000000..a126db5c --- /dev/null +++ b/bsnes/cpu/g65816_ops_stack.cpp @@ -0,0 +1,243 @@ +ulong g65816_stackread(byte size) { +ulong r = 0; + if(size == MEMSIZE_BYTE) { + gx816->regs.s++; + r = gx816->mem_read(MEMMODE_LONG, MEMSIZE_BYTE, gx816->regs.s); + } else if(size == MEMSIZE_WORD) { + gx816->regs.s++; + r = gx816->mem_read(MEMMODE_LONG, MEMSIZE_BYTE, gx816->regs.s); + gx816->regs.s++; + r |= gx816->mem_read(MEMMODE_LONG, MEMSIZE_BYTE, gx816->regs.s) << 8; + } else if(size == MEMSIZE_LONG) { + gx816->regs.s++; + r = gx816->mem_read(MEMMODE_LONG, MEMSIZE_BYTE, gx816->regs.s); + gx816->regs.s++; + r |= gx816->mem_read(MEMMODE_LONG, MEMSIZE_BYTE, gx816->regs.s) << 8; + gx816->regs.s++; + r |= gx816->mem_read(MEMMODE_LONG, MEMSIZE_BYTE, gx816->regs.s) << 16; + } + if(gx816->regs.e == true) { + gx816->regs.s = 0x0100 | (gx816->regs.s & 0xff); + } + return r; +} + +void g65816_stackwrite(byte size, ulong value) { + if(size == MEMSIZE_BYTE) { + gx816->mem_write(MEMMODE_LONG, MEMSIZE_BYTE, gx816->regs.s, value); + gx816->regs.s--; + } else if(size == MEMSIZE_WORD) { + gx816->mem_write(MEMMODE_LONG, MEMSIZE_BYTE, gx816->regs.s, value >> 8); + gx816->regs.s--; + gx816->mem_write(MEMMODE_LONG, MEMSIZE_BYTE, gx816->regs.s, value); + gx816->regs.s--; + } else if(size == MEMSIZE_LONG) { + gx816->mem_write(MEMMODE_LONG, MEMSIZE_BYTE, gx816->regs.s, value >> 16); + gx816->regs.s--; + gx816->mem_write(MEMMODE_LONG, MEMSIZE_BYTE, gx816->regs.s, value >> 8); + gx816->regs.s--; + gx816->mem_write(MEMMODE_LONG, MEMSIZE_BYTE, gx816->regs.s, value); + gx816->regs.s--; + } + if(gx816->regs.e == true) { + gx816->regs.s = 0x0100 | (gx816->regs.s & 0xff); + } +} + +void g65816_op_phab(void) { + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_scycles(1); + snes_time->add_cpu_icycles(1); + g65816_stackwrite(MEMSIZE_BYTE, gx816->regs.a.b); + g65816_incpc(1); +} + +void g65816_op_phaw(void) { + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_scycles(2); + snes_time->add_cpu_icycles(1); + g65816_stackwrite(MEMSIZE_WORD, gx816->regs.a.w); + g65816_incpc(1); +} + +void g65816_op_phb(void) { + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_scycles(1); + snes_time->add_cpu_icycles(1); + g65816_stackwrite(MEMSIZE_BYTE, gx816->regs.db); + g65816_incpc(1); +} + +void g65816_op_phd(void) { + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_scycles(2); + snes_time->add_cpu_icycles(1); + g65816_stackwrite(MEMSIZE_WORD, gx816->regs.d); + g65816_incpc(1); +} + +void g65816_op_phk(void) { + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_scycles(1); + snes_time->add_cpu_icycles(1); + g65816_stackwrite(MEMSIZE_BYTE, gx816->regs.pc >> 16); + g65816_incpc(1); +} + +void g65816_op_php(void) { + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_scycles(1); + snes_time->add_cpu_icycles(1); + g65816_stackwrite(MEMSIZE_BYTE, gx816->regs.p); + g65816_incpc(1); +} + +void g65816_op_phxb(void) { + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_scycles(1); + snes_time->add_cpu_icycles(1); + g65816_stackwrite(MEMSIZE_BYTE, gx816->regs.x); + g65816_incpc(1); +} + +void g65816_op_phxw(void) { + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_scycles(2); + snes_time->add_cpu_icycles(1); + g65816_stackwrite(MEMSIZE_WORD, gx816->regs.x); + g65816_incpc(1); +} + +void g65816_op_phyb(void) { + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_scycles(1); + snes_time->add_cpu_icycles(1); + g65816_stackwrite(MEMSIZE_BYTE, gx816->regs.y); + g65816_incpc(1); +} + +void g65816_op_phyw(void) { + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_scycles(2); + snes_time->add_cpu_icycles(1); + g65816_stackwrite(MEMSIZE_WORD, gx816->regs.y); + g65816_incpc(1); +} + +void g65816_op_plab(void) { + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_scycles(1); + snes_time->add_cpu_icycles(2); + gx816->regs.a.b = g65816_stackread(MEMSIZE_BYTE); + g65816_testn(gx816->regs.a.b & 0x80); + g65816_testz(gx816->regs.a.b == 0); + g65816_incpc(1); +} + +void g65816_op_plaw(void) { + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_scycles(2); + snes_time->add_cpu_icycles(2); + gx816->regs.a.w = g65816_stackread(MEMSIZE_WORD); + g65816_testn(gx816->regs.a.w & 0x8000); + g65816_testz(gx816->regs.a.w == 0); + g65816_incpc(1); +} + +void g65816_op_plb(void) { + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_scycles(1); + snes_time->add_cpu_icycles(2); + gx816->regs.db = g65816_stackread(MEMSIZE_BYTE); + g65816_testn(gx816->regs.db & 0x80); + g65816_testz(gx816->regs.db == 0); + g65816_incpc(1); +} + +void g65816_op_pld(void) { + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_scycles(2); + snes_time->add_cpu_icycles(2); + gx816->regs.d = g65816_stackread(MEMSIZE_WORD); + g65816_testn(gx816->regs.d & 0x8000); + g65816_testz(gx816->regs.d == 0); + g65816_incpc(1); +} + +void g65816_op_plp(void) { + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_scycles(1); + snes_time->add_cpu_icycles(2); + gx816->regs.p = g65816_stackread(MEMSIZE_BYTE); + g65816_incpc(1); + if(gx816->regs.e == true)gx816->regs.p |= 0x30; + if(gx816->regs.p & PF_X) { gx816->regs.x &= 0xff; gx816->regs.y &= 0xff; } +} + +void g65816_op_plxb(void) { + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_scycles(1); + snes_time->add_cpu_icycles(2); + gx816->regs.x = g65816_stackread(MEMSIZE_BYTE); + g65816_testn(gx816->regs.x & 0x80); + g65816_testz((gx816->regs.x & 0xff) == 0); + g65816_incpc(1); +} + +void g65816_op_plxw(void) { + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_scycles(2); + snes_time->add_cpu_icycles(2); + gx816->regs.x = g65816_stackread(MEMSIZE_WORD); + g65816_testn(gx816->regs.x & 0x8000); + g65816_testz(gx816->regs.x == 0); + g65816_incpc(1); +} + +void g65816_op_plyb(void) { + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_scycles(1); + snes_time->add_cpu_icycles(2); + gx816->regs.y = g65816_stackread(MEMSIZE_BYTE); + g65816_testn(gx816->regs.y & 0x80); + g65816_testz((gx816->regs.y & 0xff) == 0); + g65816_incpc(1); +} + +void g65816_op_plyw(void) { + snes_time->add_cpu_pcycles(1); + snes_time->add_cpu_scycles(2); + snes_time->add_cpu_icycles(2); + gx816->regs.y = g65816_stackread(MEMSIZE_WORD); + g65816_testn(gx816->regs.y & 0x8000); + g65816_testz(gx816->regs.y == 0); + g65816_incpc(1); +} + +void g65816_op_pea(void) { +g65816_prefetch(2); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_scycles(2); + g65816_stackwrite(MEMSIZE_WORD, arg); + g65816_incpc(3); +} + +void g65816_op_pei(void) { +g65816_prefetch(1); +g65816_getaddr(MEMMODE_DP); + snes_time->add_cpu_pcycles(2); + snes_time->add_cpu_mcycles(2, dest_addr); + snes_time->add_cpu_scycles(2); + snes_time->add_cpu_icycles(0, TIMING_REGD); + g65816_stackwrite(MEMSIZE_WORD, gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dest_addr)); + g65816_incpc(2); +} + +void g65816_op_per(void) { +g65816_prefetch(2); + snes_time->add_cpu_pcycles(3); + snes_time->add_cpu_scycles(2); + snes_time->add_cpu_icycles(1); + g65816_stackwrite(MEMSIZE_WORD, gx816->regs.pc + arg + 3); + g65816_incpc(3); +} diff --git a/bsnes/d65816.obj b/bsnes/d65816.obj new file mode 100644 index 0000000000000000000000000000000000000000..e685f195330d2ed1a8336e19d86349ce0f003c91 GIT binary patch literal 84902 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�=J{R2su~^{scB*Dz&hhO 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 literal 0 HcmV?d00001 diff --git a/bsnes/demo.smc b/bsnes/demo.smc new file mode 100644 index 0000000000000000000000000000000000000000..4abad79c38eb7fb0c53cab4c39925d45f195a69f GIT binary patch literal 131584 zcmeIwy=oLu6ae5et093PE<$AO=2}}QzCrdyg0Mvjf5>i;LU2G*-smI9LW*1X3@&Ni z!bcDbQSt(TmBg9VfWmazh~K?%=FT~XJ2T&%2!j9t0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBly@P7-8R^?TWRCe-4D#N^)O62WSn!J-rmG5=oVLBbwZ<_jj zGmqi%?NkPN)V1CDWi_w+DvPe?X1jfJQQ|0$s<*dx@2}c0T%AO|kxr^M^yN<L&+(~l zJ~S8ei0+Y9j&T{~YFxhN!MJ_BYvZ!W*T?f_aJ(^Y%iFtq?JK3!_P^VIxyX0Zm#gc? zt$2O5_ov5v7|V8kTgPS3X6Nh5RqSby`lkiD^IxpzG7fvb<j2W(|Hv}Vd+PE0@<liM zN^%4U5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5*B6@f?7CzEMCeRi-vo7A(( zbXFfcef(gswHZ-GT>c#0>i4^@5KWgg2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+K*V?X Cvyn3Z literal 0 HcmV?d00001 diff --git a/bsnes/demo.srm b/bsnes/demo.srm new file mode 100644 index 0000000000000000000000000000000000000000..6d17cf9d15fb9f4a2358a2d079f3b8c755d005fa GIT binary patch literal 8192 zcmeIu0Sy2E0K%a6Pi+o2h(KY$fB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM GyblZ@00031 literal 0 HcmV?d00001 diff --git a/bsnes/demo_mode1.smc b/bsnes/demo_mode1.smc new file mode 100644 index 0000000000000000000000000000000000000000..687d174918c33fc9b9041a9f4a10bf4eafb354c6 GIT binary patch literal 131584 zcmeIvF-sgl6ae72rxL+Hwuu}z-dI~$rxLgEN2Ce}X;knmEClxt>}j2HyA9+w2zG*n zbs!+Aw6;{Z%tjkQwAb&=vb=e3-oktvA>OmV*Qks4e1CT*K!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB=EFA@FUyvo#*=Z;kgyd%N4ChgciT&qNdv)5qwur@Oup z^>jMCJ1e{6CQ?^4bE&JF&waL<y0ZC@x@Z<sJ1QrOsV!zF!<3&~OX=t7@49GK()HDP zoZPk%`9vOzsv9;%)%|D&RsPgWm20zAyB?FdK3&-OyOzfzwXylD+$it9U&ZCerCd1w zbUA2?`sPcYN6w@E^V<J8h>N`L|GzJuJA61jT8`!CI*+xNdM}J=5g<T-009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF z5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAn-;7 F{sBA(UmO4c literal 0 HcmV?d00001 diff --git a/bsnes/demo_mode1.srm b/bsnes/demo_mode1.srm new file mode 100644 index 0000000000000000000000000000000000000000..6d17cf9d15fb9f4a2358a2d079f3b8c755d005fa GIT binary patch literal 8192 zcmeIu0Sy2E0K%a6Pi+o2h(KY$fB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM GyblZ@00031 literal 0 HcmV?d00001 diff --git a/bsnes/demo_mode4.smc b/bsnes/demo_mode4.smc new file mode 100644 index 0000000000000000000000000000000000000000..a1811a01b430b754b4e4e89581eff3a90f25b4be GIT binary patch literal 131584 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 XKoB4Z5CjMU1Ob8oL4Y9eKS1C&3TxDT literal 0 HcmV?d00001 diff --git a/bsnes/demo_mode4.srm b/bsnes/demo_mode4.srm new file mode 100644 index 0000000000000000000000000000000000000000..6d17cf9d15fb9f4a2358a2d079f3b8c755d005fa GIT binary patch literal 8192 zcmeIu0Sy2E0K%a6Pi+o2h(KY$fB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM GyblZ@00031 literal 0 HcmV?d00001 diff --git a/bsnes/g65816.obj b/bsnes/g65816.obj new file mode 100644 index 0000000000000000000000000000000000000000..f333479d5eda2636e8723eb7bbdf85a2892678cd GIT binary patch literal 289767 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 F{|E3XASD0* literal 0 HcmV?d00001 diff --git a/bsnes/gui.obj b/bsnes/gui.obj new file mode 100644 index 0000000000000000000000000000000000000000..6d84676e8f835ccecc732b7478f6bbf5a09b5b86 GIT binary patch literal 67128 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�+sXzuLl2-Mr{RF{*Q%IY>#jS+^f`?^`@_~_)Nz;layg?=>4 z&<dM^%Bm_iODM&a!pwrQ!ZP1nRm`C&&&aJP_WO!k8r<20Sx;HvY+u1D4tFpW2-uED z*S3=K(h}6RfL+^!omG^Rm*dNwH+ODDxhwRnvN^eETXS>f7U$U+!$yEw($F60Xq3ea z-JRlxx><YF%)UVNPGY^(H@g6FRbCqd<-9_So6}cGWek}T%qT4`$rY4fGQu&bg?Sa2 zNuY&?C}z$sN2Sw-(^hF2*P2vpY%dLuUs8bobm9`RX$2LzrM6;LXlOMx%W6B8GIcd= zoz8&4ZkuYgWSF*@SLiD)Ez8rLM|LLc;HM<BGv)Pbh`#E;Fv!f#M5bkDqK}jq<<0h$ zl;qjH(XfjA#dAlm$X8Zqda+^k_!H=C=W1mIURruwanfe8gE?w>aY_CG5<vf{!a_V) z`05C*YAHo3mzjOS3n_y$Q3g^7n1D0}RuNyK$0laE$K7?HN0S~dUou|Sjdaz`Eh?SO zNiqDkt5&2}6rlw0UQ8D^v!oC`B7Y~;#m<<Mk1+;+In>2YFZTOOe1lzCMrlz&sgJ)d z>H<$MESgi{<L`}TXU=U!^$xA0S^hcss*aYb-ye0$iVOUZ%?OudmK3A;p=K*Bmr`a? zaT(hbm0W_1;vB45X&twu(2r)RR9yV@{E|7ghFefxgnonyE_Ru3j^Cf_<By~&55uY* zn%1;}f}&DiShR}Di+%hhRTnlZKc}DqSY9px`}M-|GP`8iRk{A*hgVG&OUG@yq{YTr z>qeDPHfJ`{4X1)%o^%D0URqR!l&jY05@h9;&Oyq>55c9l9S+K*B&R^FfzX%1bEqK) zM!k83p|?R=CBE_^^!3^gGEj_S&69Qtuz|I3`Nd1nb6nAymX}v9DM7Glx<6N*(DqDR z5{~4UOnc|Ti5hB#uAOi=CxbPvB^XzfTaJE8{~#WQ*zl(Ei+C!z`QpGi($Y~+$)1r) zSB7?7AHcf4Y1ek^RgnvAycNFuA}qqGpI6Gx#IG%38t)&}!~1i}b8JMe-<2I%y2YUy z4w9t$^U?U{yU?kH#W}^kGY9eJ6c+jx4CbL7SGjn8Uw&y}u@9lJq$TN@+UeL0-4ub@ z8i-U7CNCURXpUs&S5y@E@M_=eOxvy$N9LRY42qP%y;4}}%d03T@yYvqF^C)FmC*U- z=jR(SUJmR^7_^H}rEKloB6p~zzT&b1G<Ef|VB~U<JM7G&QnVixDfd!kW*Hi<2;9w@ z(Sfsb4BC3aQ6p)!s%wwECG}XrEGjH1WQ#1x&a_e4nWE)x)P7%S9u|h&uSj(zy24jb z<gf6l{&z8!mF2yuF39xU989q7H>tWr=qU0_X3sHwV`#h39ek)aoROQ4rB|_p4$)<5 zMri@ujL5J-<dVSwmgQnaR=UpYOv<sog~1o{M<cbPu=7K8y_hcQ#;%w6q=V;Vt)^yW zQ_C`$g@+DUxtLMgS=VShgW1=>xU;3BnL7n$eyWZ8(a#w?N4H${<1A@VB&6f6ZpCkE L=Bizx4D0^_*Jd7x literal 0 HcmV?d00001 diff --git a/bsnes/libstr.obj b/bsnes/libstr.obj new file mode 100644 index 0000000000000000000000000000000000000000..7518ffe95403f512c4c65b98d52b0e450fbf9962 GIT binary patch literal 38996 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⩔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 literal 0 HcmV?d00001 diff --git a/bsnes/main.cpp b/bsnes/main.cpp new file mode 100644 index 00000000..b01c7881 --- /dev/null +++ b/bsnes/main.cpp @@ -0,0 +1,97 @@ +/* bsnes + project started 10/14/2004 + author byuu */ + +#include "base.h" +#include "main.h" +#include "timing/timing.h" +#include "cpu/g65816.h" +extern snes_timer *snes_time; +extern g65816 *gx816; +extern ppustate ppu; + +vfunc RunSNES; + +void RunSNES_NoDebug(void) { +byte l = 64; + while(l--) { + gx816->Run(); + gx816->Run(); + gx816->Run(); + gx816->Run(); + gx816->Run(); + gx816->Run(); + gx816->Run(); + gx816->Run(); + } +} + +void RunSNES_Debug(void) { + if(debug_get_state() == DEBUGMODE_NOROM)return; + + if(debug_get_state() == DEBUGMODE_RUN) { + gx816->Run(); + if(debugger.trace_enabled == true) { + disas_g65816_op(); + } + } else { + if(debugger.disas_op == true) { + disas_g65816_op(); + debugger.disas_op = false; + } + if(debugger.refresh_mem == true) { + debug_refresh_mem(); + debugger.refresh_mem = false; + } + if(debugger.refresh_bp == true) { + debug_refresh_bp(); + debugger.refresh_bp = false; + } + if(debug_get_state() == DEBUGMODE_WAIT)return; + + if(debug_get_state() == DEBUGMODE_STEP) { + gx816->Run(); + disas_g65816_op(); + debug_set_state(DEBUGMODE_WAIT); + } + } +} + +void InitSNES(void) { + snes_time = new snes_timer(); + gx816 = new g65816(); + gx816->PowerOn(1); + if(*emu_state.rom_name == 0) { + debug_set_state(DEBUGMODE_NOROM); + } else { + gx816->LoadROM(); + } + + if(debug_get_state() == DEBUGMODE_DISABLED) { + RunSNES = RunSNES_NoDebug; + } else { + RunSNES = RunSNES_Debug; + } +} + +#include <windows.h> +int __stdcall WinMain(HINSTANCE hinstance, HINSTANCE hprevinstance, LPSTR lpcmdline, int ncmdshow) { +char fn[MAX_PATH]; + strcpy(fn, lpcmdline); +//remove quotes from filename, if neccesary (if path contains spaces, quotes will be around command line arg) + if(*fn == '\"') { + strcpy(emu_state.rom_name, fn + 1); + emu_state.rom_name[strlen(emu_state.rom_name) - 1] = 0; + } else { + strcpy(emu_state.rom_name, fn); + } +//create save ram file name + strcpy(emu_state.sram_name, emu_state.rom_name); + if(strlen(emu_state.sram_name) > 4) { + emu_state.sram_name[strlen(emu_state.sram_name) - 4] = 0; + } + strcat(emu_state.sram_name, ".srm"); +//located in win/gui.cpp + __winmain(); + return 0; +} diff --git a/bsnes/main.h b/bsnes/main.h new file mode 100644 index 00000000..21446257 --- /dev/null +++ b/bsnes/main.h @@ -0,0 +1,34 @@ +void __winmain(void); + +/* + *1 - how many instructions to execute before saving sram data + to emu_state.sram_name +*/ +emustate emu_state = { + "", "", //rom name, sram name + 1000000 //sram save tick count *1 +}; + +debugstate debugger = { + DEBUGMODE_DISABLED, //default debug mode + false, //trace enabled + 0x7e0000, //debugger memory start pos + true, //debugger disassemble op + true, //debugger refresh mem + true, //debugger refresh breakpoints + 0 //tracelog file pointer +}; + +videostate render = { + 512, 448, //resolution + 256, 224, //snes internal resolution + false, //fullscreen + true, //show menu + 1, //frame skip + 0, //frame count + { true, true, true }, //bg1 enable + { true, true, true }, //bg2 enable + { true, true, true }, //bg3 enable + { true, true, true }, //bg4 enable + { true, true, true, true, true } //oam enable +}; diff --git a/bsnes/main.obj b/bsnes/main.obj new file mode 100644 index 0000000000000000000000000000000000000000..e04557fb60faf02d8cc6218513191e03c8ce5b33 GIT binary patch literal 11653 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 Z!ijb@(bUo%jmFe)3x0CLfk;G!zXAShNMry2 literal 0 HcmV?d00001 diff --git a/bsnes/mem/memory.cpp b/bsnes/mem/memory.cpp new file mode 100644 index 00000000..5d2072dc --- /dev/null +++ b/bsnes/mem/memory.cpp @@ -0,0 +1,516 @@ +#include "../base.h" +#include "../cpu/g65816.h" +extern g65816 *gx816; +extern emustate emu_state; +extern debugstate debugger; + +void g65816::InitializeROM(byte memory_map) { + memset(rom, 0, 0x600000); + map = memory_map; +} + +void g65816::InitializeWRAM(byte value) { + memset(wram, value, 0x020000); + memset(sram, 0x00, 0x0e0000); +} + +/*********************** + *** SNES Memory Map *** + ************************************************** + *** 00-3f 0000-1fff First 8k WRAM *** + *** 2000-5fff MMIO *** + *** 6000-7fff Expansion RAM (Unmapped) *** + *** 8000-ffff Cartridge ROM *** + *** 40-7d 0000-ffff Cartridge ROM *** + *** 7e-7f 0000-ffff 128k WRAM *** + *** 80-bf 0000-1fff First 8k WRAM *** + *** 2000-5fff MMIO *** + *** 6000-7fff Expansion RAM (Unmapped) *** + *** 8000-ffff Cartridge ROM *** + *** c0-ff 0000-ffff Cartridge ROM *** +**************************************************/ + +ulong g65816::mirror_offset(ulong addr) { +byte db; +word a; +ulong r = 0; + db = (addr >> 16) & 0xff; + a = (addr & 0xffff); + if(db >= 0x00 && db <= 0x3f) { + if(a >= 0x0000 && a <= 0x1fff) { + r = 0x7e0000 | (a & 0x1fff); + } else if(a >= 0x2000 && a <= 0x5fff) { + r = a; + } else if(a >= 0x6000 && a <= 0x7fff) { + r = (db << 16) | a; + } else if(a >= 0x8000 && a <= 0xffff) { + r = (db << 16) | a; + } + } else if(db >= 0x40 && db <= 0x7d) { + r = (db << 16) | a; + } else if(db >= 0x7e && db <= 0x7f) { + r = addr; + } else if(db >= 0x80 && db <= 0xbf) { + if(a >= 0x0000 && a <= 0x1fff) { + r = 0x7e0000 | (a & 0x1fff); + } else if(a >= 0x2000 && a <= 0x5fff) { + r = a; + } else if(a >= 0x6000 && a <= 0x7fff) { + r = (db << 16) | a; + } else if(a >= 0x8000 && a <= 0xffff) { + r = ((db & 0x7f) << 16) | a; + } + } else if(db >= 0xc0 && db <= 0xff) { + r = addr; + } + + return r; +} + +ulong g65816::convert_offset(byte read_mode, ulong addr, bool mirror) { +byte db; + switch(read_mode) { + case MEMMODE_DP: + addr = (regs.d + (addr & 0xff)) & 0xffff; + break; + case MEMMODE_DPX: + addr = (regs.d + regs.x + (addr & 0xff)) & 0xffff; + break; + case MEMMODE_DPY: + addr = (regs.d + regs.y + (addr & 0xff)) & 0xffff; + break; + case MEMMODE_IDP: + addr = (regs.d + (addr & 0xff)) & 0xffff; + addr = mem_read(MEMMODE_LONG, MEMSIZE_WORD, addr); + addr |= (regs.db << 16); + break; + case MEMMODE_IDPX: + addr = (regs.d + regs.x + (addr & 0xff)) & 0xffff; + addr = mem_read(MEMMODE_LONG, MEMSIZE_WORD, addr); + addr |= (regs.db << 16); + break; + case MEMMODE_IDPY: + addr = (regs.d + (addr & 0xff)) & 0xffff; + addr = mem_read(MEMMODE_LONG, MEMSIZE_WORD, addr); + addr += (regs.db << 16) + regs.y; + if((addr >> 16) != regs.db)index_bank_crossed = true; + else index_bank_crossed = false; + break; + case MEMMODE_ILDP: + addr = (regs.d + (addr & 0xff)); + addr = mem_read(MEMMODE_LONG, MEMSIZE_LONG, addr); + break; + case MEMMODE_ILDPY: + addr = (regs.d + (addr & 0xff)); + addr = mem_read(MEMMODE_LONG, MEMSIZE_LONG, addr); + addr += regs.y; + break; + case MEMMODE_ADDR: + addr = addr & 0xffff; + addr |= (regs.db << 16); + break; + case MEMMODE_ADDR_PC: + addr = addr & 0xffff; + addr |= (regs.pc & 0xff0000); + break; + case MEMMODE_ADDRX: + addr = (regs.db << 16) + (addr & 0xffff); + addr += regs.x; + if((addr >> 16) != regs.db)index_bank_crossed = true; + else index_bank_crossed = false; + break; + case MEMMODE_ADDRY: + addr = (regs.db << 16) + (addr & 0xffff); + addr += regs.y; + if((addr >> 16) != regs.db)index_bank_crossed = true; + else index_bank_crossed = false; + break; + case MEMMODE_IADDRX: + addr += regs.x; + addr = mem_read(MEMMODE_LONG, MEMSIZE_WORD, (addr & 0x00ffff)); + addr |= (regs.pc & 0xff0000); + break; + case MEMMODE_ILADDR: + addr = mem_read(MEMMODE_LONG, MEMSIZE_LONG, addr); + break; + case MEMMODE_IADDR_PC: + addr |= (regs.pc & 0xff0000); + break; + case MEMMODE_IADDRX_PC: + addr += regs.x; + addr |= (regs.pc & 0xff0000); + break; + case MEMMODE_ILADDR_PC: + break; + case MEMMODE_LONG: + break; + case MEMMODE_LONGX: + addr += regs.x; + break; + case MEMMODE_SR: + addr = (regs.s + (addr & 0xff)) & 0xffff; + break; + case MEMMODE_ISRY: + addr = (regs.s + (addr & 0xff)) & 0xffff; + addr = mem_read(MEMMODE_LONG, MEMSIZE_WORD, addr); + addr += (regs.db << 16) + regs.y; + break; + } + + if(mirror == true) { + return mirror_offset(addr); + } else { + return addr; + } +} + +ulong g65816::adjust_base_offset(byte read_mode, ulong addr) { +byte db; + switch(read_mode) { + case MEMMODE_DP: + addr = (regs.d + (addr & 0xff)) & 0xffff; + break; + case MEMMODE_DPX: + addr = (regs.d + regs.x + (addr & 0xff)) & 0xffff; + break; + case MEMMODE_DPY: + addr = (regs.d + regs.y + (addr & 0xff)) & 0xffff; + break; + case MEMMODE_IDP: + addr = (regs.d + (addr & 0xff)) & 0xffff; + break; + case MEMMODE_IDPX: + addr = (regs.d + regs.x + (addr & 0xff)) & 0xffff; + break; + case MEMMODE_IDPY: + addr = (regs.d + (addr & 0xff)) & 0xffff; + break; + case MEMMODE_ILDP: + addr = (regs.d + (addr & 0xff)); + break; + case MEMMODE_ILDPY: + addr = (regs.d + (addr & 0xff)); + break; + case MEMMODE_ADDR: + addr = addr & 0xffff; + addr |= (regs.db << 16); + break; + case MEMMODE_ADDR_PC: + addr = addr & 0xffff; + addr |= (regs.pc & 0xff0000); + break; + case MEMMODE_ADDRX: + addr = (regs.db << 16) | (addr & 0xffff); + addr += regs.x; + if((addr >> 16) != regs.db)index_bank_crossed = true; + else index_bank_crossed = false; + break; + case MEMMODE_ADDRY: + addr = (regs.db << 16) + (addr & 0xffff); + addr += regs.y; + if((addr >> 16) != regs.db)index_bank_crossed = true; + else index_bank_crossed = false; + break; + case MEMMODE_IADDRX: + addr += regs.x; + addr &= 0xffff; + break; + case MEMMODE_ILADDR: + addr &= 0xffff; + break; + case MEMMODE_IADDR_PC: + addr |= (regs.pc & 0xff0000); + break; + case MEMMODE_IADDRX_PC: + addr += regs.x; + addr |= (regs.pc & 0xff0000); + break; + case MEMMODE_ILADDR_PC: + break; + case MEMMODE_LONG: + break; + case MEMMODE_LONGX: + addr += regs.x; + break; + case MEMMODE_SR: + addr = (regs.s + (addr & 0xff)) & 0xffff; + break; + case MEMMODE_ISRY: + addr = (regs.s + (addr & 0xff)) & 0xffff; + break; + } + return addr; +} + +ulong g65816::read_indirect_address(byte read_mode, ulong addr) { +byte db; + switch(read_mode) { + case MEMMODE_IDP: + addr = mem_read(MEMMODE_LONG, MEMSIZE_WORD, addr); + addr |= (regs.db << 16); + break; + case MEMMODE_IDPX: + addr = mem_read(MEMMODE_LONG, MEMSIZE_WORD, addr); + addr |= (regs.db << 16); + break; + case MEMMODE_IDPY: + addr = mem_read(MEMMODE_LONG, MEMSIZE_WORD, addr); + addr += (regs.db << 16) + regs.y; + if((addr >> 16) != regs.db)index_bank_crossed = true; + else index_bank_crossed = false; + break; + case MEMMODE_ILDP: + addr = mem_read(MEMMODE_LONG, MEMSIZE_LONG, addr); + break; + case MEMMODE_ILDPY: + addr = mem_read(MEMMODE_LONG, MEMSIZE_LONG, addr); + addr += regs.y; + break; + case MEMMODE_IADDRX: + addr = mem_read(MEMMODE_LONG, MEMSIZE_WORD, (addr & 0x00ffff)); + addr |= (regs.pc & 0xff0000); + break; + case MEMMODE_ILADDR: + addr = mem_read(MEMMODE_LONG, MEMSIZE_LONG, addr); + break; + case MEMMODE_ISRY: + addr = mem_read(MEMMODE_LONG, MEMSIZE_WORD, addr); + addr += (regs.db << 16) + regs.y; + break; + default: + dprintf("* Error: Invalid read_mode for g65816::read_indirect_address [%d]", read_mode); + break; + } + return addr; +} + +ulong g65816::get_dc(byte read_mode, ulong addr) { + regs.dc = convert_offset(read_mode, addr, false); + return regs.dc; +} + +byte g65816::mem_getbyte_direct(ulong addr, byte access_mode) { +byte db; +word a; + db = (addr >> 16) & 0xff; + a = (addr & 0xffff); + if(db == 0x00 && a >= 0x2000 && a <= 0x5fff) { + return mmio_read(addr); + } + + if(db == 0x7e || db == 0x7f) { + return wram[addr & 0x01ffff]; + } else if(db != 0x7e && db != 0x7f && a >= 0x8000 && a <= 0xffff) { + return rom_read(addr, MEMSIZE_BYTE); + } else if(db >= 0x30 && db <= 0x3f) { + if(a >= 0x6000 && a <= 0x7fff) { + addr = ((db - 0x30) * 0x2000) + (a - 0x6000); + addr &= (sram_size - 1); + return sram[addr]; + } + } else if(db >= 0x70 && db <= 0x7d) { + addr -= 0x700000; + addr &= (sram_size - 1); + return sram[addr]; + } else if(db >= 0xc0 && db <= 0xff) { + return rom_read(addr, MEMSIZE_BYTE); + } + return 0; +} + +byte g65816::mem_getbyte(ulong addr, byte access_mode) { +int i; +byte r; + r = mem_getbyte_direct(addr, access_mode); + + if(debug_get_state() == DEBUGMODE_DISABLED)return r; + + if(access_mode == MEMACCESS_DEBUGGER)return r; //don't report breakpoint hits from debugger + + for(i=0;i<16;i++) { + if(bp_list[i].flags & BP_READ) { + if(bp_list[i].offset == addr) { + if(bp_list[i].flags & BP_VAL) { + if(bp_list[i].value == r) { + dprintf("* breakpoint %d hit -- read match access [%0.2x]", i, r); + bp_list[i].hit_count++; + debugger.refresh_bp = true; + debug_set_state(DEBUGMODE_WAIT); + disas_g65816_op(); + } + } else { + dprintf("* breakpoint %d hit -- read access", i); + bp_list[i].hit_count++; + debugger.refresh_bp = true; + debug_set_state(DEBUGMODE_WAIT); + disas_g65816_op(); + } + } + } + } + return r; +} + +void g65816::mem_putbyte_direct(ulong addr, byte value, byte access_mode) { +byte db; +word a; + db = (addr >> 16) & 0xff; + a = (addr & 0xffff); + if(db == 0x00 && a >= 0x2000 && a <= 0x5fff) { + mmio_write(a, value); + } else if(db == 0x7e || db == 0x7f) { + wram[addr & 0x01ffff] = value; + } else if(db >= 0x30 && db <= 0x3f) { + if(a >= 0x6000 && a <= 0x7fff) { + addr = ((db - 0x30) * 0x2000) + (a - 0x6000); + addr &= (sram_size - 1); + sram[addr] = value; + } + } else if(db >= 0x70 && db <= 0x7d) { + addr -= 0x700000; + addr &= (sram_size - 1); + sram[addr] = value; + } + + if(access_mode == MEMACCESS_DEBUGGER) { + if(gx816->map == MEMMAP_LOROM) { + if((db >= 0x00 && db <= 0x5f) || (db >= 0x80 && db <= 0xdf)) { + if(addr >= 0x8000 && addr <= 0xffff) { + rom_write(addr, value, MEMSIZE_BYTE); + } + } + } else if(gx816->map == MEMMAP_HIROM) { + if(db >= 0xc0 && db <= 0xff) { + rom_write(addr, value, MEMSIZE_BYTE); + } + } + } +} + +void g65816::mem_putbyte(ulong addr, byte value, byte access_mode) { +int i; + mem_putbyte_direct(addr, value, access_mode); + + if(debug_get_state() == DEBUGMODE_DISABLED)return; + + if(access_mode == MEMACCESS_DEBUGGER)return; //don't report breakpoint hits from debugger + + for(i=0;i<16;i++) { + if(bp_list[i].flags & BP_WRITE) { + if(bp_list[i].offset == addr) { + if(bp_list[i].flags & BP_VAL) { + if(bp_list[i].value == value) { + dprintf("* breakpoint %d hit -- write match access [%0.2x]", i, value); + bp_list[i].hit_count++; + debugger.refresh_bp = true; + debug_set_state(DEBUGMODE_WAIT); + disas_g65816_op(); + } + } else { + dprintf("* breakpoint %d hit -- write access", i); + bp_list[i].hit_count++; + debugger.refresh_bp = true; + debug_set_state(DEBUGMODE_WAIT); + disas_g65816_op(); + } + } + } + } +} + +ulong g65816::mem_read(byte read_mode, byte read_size, ulong addr, byte access_mode) { +ulong r; + addr = convert_offset(read_mode, addr); + + switch(read_size) { + case MEMSIZE_BYTE: + r = mem_getbyte(addr, access_mode); + break; + case MEMSIZE_WORD: + r = mem_getbyte(addr, access_mode) | + mem_getbyte(addr + 1, access_mode)<<8; + break; + case MEMSIZE_LONG: + r = mem_getbyte(addr, access_mode) | + mem_getbyte(addr + 1, access_mode)<<8 | + mem_getbyte(addr + 2, access_mode)<<16; + break; + } + + return r; +} + +void g65816::mem_write(byte write_mode, byte write_size, ulong addr, ulong value, byte access_mode) { + addr = convert_offset(write_mode, addr); + + switch(write_size) { + case MEMSIZE_BYTE: + mem_putbyte(addr, value, access_mode); + break; + case MEMSIZE_WORD: + mem_putbyte(addr, value, access_mode); + mem_putbyte(addr+1, value>>8, access_mode); + break; + case MEMSIZE_LONG: + mem_putbyte(addr, value, access_mode); + mem_putbyte(addr+1, value>>8, access_mode); + mem_putbyte(addr+2, value>>16, access_mode); + break; + } + + debugger.refresh_mem = true; +} + +ulong g65816::rom_read(ulong addr, byte read_size) { +ulong r; + if(map == MEMMAP_LOROM) { + if(read_size == MEMSIZE_BYTE) { + r = rom[((addr & 0x7f0000) >> 1) | (addr & 0x7fff)]; + } else if(read_size == MEMSIZE_WORD) { + r = rom[((addr & 0x7f0000) >> 1) | (addr & 0x7fff)] | + rom[(((addr + 1) & 0x7f0000) >> 1) | ((addr + 1) & 0x7fff)] << 8; + } else if(read_size == MEMSIZE_LONG) { + r = rom[((addr & 0x7f0000) >> 1) | (addr & 0x7fff)] | + rom[(((addr + 1) & 0x7f0000) >> 1) | ((addr + 1) & 0x7fff)] << 8 | + rom[(((addr + 2) & 0x7f0000) >> 1) | ((addr + 2) & 0x7fff)] << 16; + } + } else if(map == MEMMAP_HIROM) { + if(read_size == MEMSIZE_BYTE) { + r = rom[addr & 0x3fffff]; + } else if(read_size == MEMSIZE_WORD) { + r = rom[addr & 0x3fffff] | + rom[(addr + 1) & 0x3fffff] << 8; + } else if(read_size == MEMSIZE_LONG) { + r = rom[addr & 0x3fffff] | + rom[(addr + 1) & 0x3fffff] << 8 | + rom[(addr + 2) & 0x3fffff] << 16; + } + } + return r; +} + +void g65816::rom_write(ulong addr, ulong v, byte write_size) { + if(map == MEMMAP_LOROM) { + if(write_size == MEMSIZE_BYTE) { + rom[((addr & 0x7f0000) >> 1) | (addr & 0x7fff)] = v; + } else if(write_size == MEMSIZE_WORD) { + rom[((addr & 0x7f0000) >> 1) | (addr & 0x7fff)] = v; + rom[(((addr + 1) & 0x7f0000) >> 1) | ((addr + 1) & 0x7fff)] = v >> 8; + } else if(write_size == MEMSIZE_LONG) { + rom[((addr & 0x7f0000) >> 1) | (addr & 0x7fff)] = v; + rom[(((addr + 1) & 0x7f0000) >> 1) | ((addr + 1) & 0x7fff)] = v >> 8; + rom[(((addr + 2) & 0x7f0000) >> 1) | ((addr + 2) & 0x7fff)] = v >> 16; + } + } else if(map == MEMMAP_HIROM) { + if(write_size == MEMSIZE_BYTE) { + rom[addr & 0x3fffff] = v; + } else if(write_size == MEMSIZE_WORD) { + rom[addr & 0x3fffff] = v; + rom[(addr + 1) & 0x3fffff] = v >> 8; + } else if(write_size == MEMSIZE_LONG) { + rom[addr & 0x3fffff] = v; + rom[(addr + 1) & 0x3fffff] = v >> 8; + rom[(addr + 2) & 0x3fffff] = v >> 16; + } + } +} diff --git a/bsnes/memory.obj b/bsnes/memory.obj new file mode 100644 index 0000000000000000000000000000000000000000..ca0afd4a5a1f84a4d8c01fb8254184649984b321 GIT binary patch literal 11196 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 literal 0 HcmV?d00001 diff --git a/bsnes/misc/libstr.cpp b/bsnes/misc/libstr.cpp new file mode 100644 index 00000000..a9ba0b40 --- /dev/null +++ b/bsnes/misc/libstr.cpp @@ -0,0 +1,393 @@ +#include "../base.h" +#include "libvlist.cpp" + +typedef struct { +char *s; +ulong size; +}string; + +typedef struct { +vectorlist list; +ulong count; +}stringlist; + +/*********************** +string library functions +***********************/ + +void strresize(string *str, ulong size) { +char *t; +int sl; + if(str->size == size)return; + sl = strlen(str->s); + t = (char*)malloc(size + 1); + strncpy(t, str->s, size); + free(str->s); + str->s = t; + str->size = size; +} + +string *newstr(void) { +string *s; + s = (string*)malloc(sizeof(string)); + s->size = 16; + s->s = (char*)malloc(s->size + 1); + *s->s = 0; + return s; +} + +void memfree(string *s) { + if(s->s)free(s->s); + free(s); +} + +/*************************** +stringlist library functions +***************************/ + +void stradd(stringlist *sl, ulong num) { +int i; +string *s; + while(sl->count < (num + 1)) { + s = newstr(); + sl->list.add((ulong)s); + sl->count++; + } +} + +string *strget(stringlist *sl, ulong num) { +string *s; + if(sl->count < (num + 1)) { stradd(sl, num); } + s = (string*)sl->list.get(num); + return s; +} + +stringlist *newstrlist(void) { +stringlist *sl; + sl = (stringlist*)malloc(sizeof(stringlist)); + sl->count = 0; + stradd(sl, 1); + return sl; +} + +ulong count(stringlist *sl) { + return sl->count; +} + +/**************************** +string manipulation functions +****************************/ + +char *strptr(string *s) { return s->s; } +char *strptr(stringlist *sl, ulong num) { return strget(sl, num)->s; } + +void strcpy(string *dest, char *src) { +int srclen = strlen(src); + if(srclen > dest->size) { strresize(dest, srclen); } + strncpy(dest->s, src, dest->size); +} +void strcpy(string *dest, string *src) { strcpy(dest, src->s); } +void strcpy(stringlist *dest, ulong num, char *src) { strcpy(strget(dest, num), src); } +void strcpy(stringlist *dest, ulong num, string *src) { strcpy(strget(dest, num), src->s); } +void strcpy(stringlist *dest, ulong destnum, stringlist *src, ulong srcnum) { strcpy(strget(dest, destnum), strptr(src, srcnum)); } + +void strset(string *dest, ulong pos, byte c) { +char *s; + if(pos > dest->size) { strresize(dest, pos); } + dest->s[pos] = c; +} +void strset(stringlist *dest, ulong num, ulong pos, byte c) { strset(strget(dest, num), pos, c); } + +void strcat(string *dest, char *src) { +int srclen, destlen; + srclen = strlen(src); + destlen = strlen(dest->s); + if(srclen + destlen > dest->size) { strresize(dest, srclen + destlen); } + strncat(dest->s, src, srclen + destlen); +} +void strcat(string *dest, string *src) { strcat(dest, src->s); } +void strcat(stringlist *dest, ulong num, char *src) { strcat(strget(dest, num), src); } +void strcat(stringlist *dest, ulong num, string *src) { strcat(strget(dest, num), src->s); } +void strcat(stringlist *dest, ulong destnum, stringlist *src, ulong srcnum) { strcat(strget(dest, destnum), strptr(src, srcnum)); } + +ulong strlen(string *s) { return strlen(s->s); } +ulong strlen(stringlist *sl, ulong num) { return strlen(strget(sl, num)->s); } + +void strinsert(string *dest, char *src, ulong pos) { +string *s = newstr(); + strcpy(s, strptr(dest)+pos); + strset(dest, pos, 0); + strcat(dest, src); + strcat(dest, s); + memfree(s); +} +void strinsert(string *dest, string *src, ulong pos) { strinsert(dest, src->s, pos); } +void strinsert(stringlist *dest, ulong num, char *src, ulong pos) { strinsert(strget(dest, num), src, pos); } +void strinsert(stringlist *dest, ulong num, string *src, ulong pos) { strinsert(strget(dest, num), src->s, pos); } +void strinsert(stringlist *dest, ulong destnum, stringlist *src, ulong srcnum, ulong pos) { strinsert(strget(dest, destnum), strptr(src, srcnum), pos); } + +void strremove(string *dest, ulong start, ulong length = 0) { +int destlen; +char *s; +int i, sl = strlen(dest->s); + if(start > dest->size) { strresize(dest, start); } + if(!length) { + strset(dest, start, 0); + return; + } + s = strptr(dest); + for(i=start;i<sl;i++) { s[i] = s[i+length]; } + s[i] = 0; +} +void strremove(stringlist *sl, ulong num, ulong start, ulong length = 0) { strremove(strget(sl, num), start, length); } + +ulong strcmp(string *dest, char *src) { return strcmp(dest->s, src); } +ulong strcmp(string *dest, string *src) { return strcmp(dest->s, src->s); } +ulong strcmp(stringlist *dest, ulong num, char *src) { return strcmp(strget(dest, num)->s, src); } +ulong strcmp(stringlist *dest, ulong num, string *src) { return strcmp(strget(dest, num)->s, src->s); } +ulong strcmp(stringlist *dest, ulong destnum, stringlist *src, ulong srcnum) { return strcmp(strget(dest, destnum)->s, strptr(src, srcnum)); } + +ulong stricmp(char *dest, char *src) { +int i, sl = strlen(dest); + if(sl != strlen(src))return 1; + for(i=0;i<sl;i++) { + if(dest[i] >= 'A' && dest[i] <= 'Z') { + if(dest[i]!=src[i] && dest[i]+0x20!=src[i])return 1; + } else if(dest[i] >='a' && dest[i] <= 'z') { + if(dest[i]!=src[i] && dest[i]-0x20!=src[i])return 1; + } else { + if(dest[i] != src[i])return 1; + } + } + return 0; +} +ulong stricmp(string *dest, char *src) { return stricmp(dest->s, src); } +ulong stricmp(string *dest, string *src) { return stricmp(dest->s, src->s); } +ulong stricmp(stringlist *dest, ulong num, char *src) { return stricmp(strget(dest, num)->s, src); } +ulong stricmp(stringlist *dest, ulong num, string *src) { return stricmp(strget(dest, num)->s, src->s); } +ulong stricmp(stringlist *dest, ulong destnum, stringlist *src, ulong srcnum) { return stricmp(strget(dest, destnum)->s, strptr(src, srcnum)); } + +void strlower(char *str) { +int i, sl = strlen(str); + for(i=0;i<sl;i++) { if(str[i] >= 'A' && str[i] <= 'Z')str[i] += 0x20; } +} +void strlower(string *str) { strlower(str->s); } +void strlower(stringlist *sl, ulong num) { strlower(strptr(sl, num)); } + +void strupper(char *str) { +int i, sl = strlen(str); + for(i=0;i<sl;i++) { if(str[i] >='a' && str[i] <='z')str[i] -= 0x20; } +} +void strupper(string *str) { strupper(str->s); } +void strupper(stringlist *sl, ulong num) { strupper(strptr(sl, num)); } + +ulong strpos(char *str, char *key) { +int i, ssl = strlen(str), ksl = strlen(key); + if(ksl > ssl)return null; + for(i=0;i<=ssl-ksl;i++) { + if(!memcmp(str+i, key, ksl))return i; + } + return null; +} +ulong strpos(string *dest, char *src) { return strpos(dest->s, src); } +ulong strpos(string *dest, string *src) { return strpos(dest->s, src->s); } +ulong strpos(stringlist *dest, ulong num, char *src) { return strpos(strget(dest, num)->s, src); } +ulong strpos(stringlist *dest, ulong num, string *src) { return strpos(strget(dest, num)->s, src->s); } +ulong strpos(stringlist *dest, ulong destnum, stringlist *src, ulong srcnum) { return strpos(strget(dest, destnum)->s, strptr(src, srcnum)); } + +ulong strpos_q(char *str, char *key) { +int i, z, ssl = strlen(str), ksl = strlen(key); +byte x; + if(ksl > ssl)return null; + for(i=0;i<=ssl-ksl;) { + x = str[i]; + if(x == '\"' || x == '\'') { + z = i++; + while(str[i] != x && i < ssl)i++; + if(i >= ssl)i = z; + } + if(!memcmp(str+i, key, ksl)) { + return i; + } else { + i++; + } + } + return null; +} +ulong strpos_q(string *dest, char *src) { return strpos_q(dest->s, src); } +ulong strpos_q(string *dest, string *src) { return strpos_q(dest->s, src->s); } +ulong strpos_q(stringlist *dest, ulong num, char *src) { return strpos_q(strget(dest, num)->s, src); } +ulong strpos_q(stringlist *dest, ulong num, string *src) { return strpos_q(strget(dest, num)->s, src->s); } +ulong strpos_q(stringlist *dest, ulong destnum, stringlist *src, ulong srcnum) { return strpos_q(strget(dest, destnum)->s, strptr(src, srcnum)); } + +void strtr(char *dest, char *before, char *after) { +int i, l, sl = strlen(dest), bsl = strlen(before), asl = strlen(after); + if((bsl != asl) || bsl == 0)return; + for(i=0;i<sl;i++) { + for(l=0;l<bsl;l++) { + if(dest[i] == before[l])dest[i] = after[l]; + } + } +} +void strtr(string *dest, char *before, char *after) { strtr(dest->s, before, after); } +void strtr(stringlist *dest, ulong num, char *before, char *after) { strtr(strptr(dest, num), before, after); } + +ulong strbegin(char *str, char *key) { +int i, ssl = strlen(str), ksl = strlen(key); + if(ksl > ssl)return 1; + if(!memcmp(str, key, ksl))return 0; + return 1; +} +ulong strbegin(string *str, char *key) { return strbegin(str->s, key); } +ulong strbegin(stringlist *str, ulong num, char *key) { return strbegin(strptr(str, num), key); } + +ulong stribegin(char *str, char *key) { +int i, ssl = strlen(str), ksl = strlen(key); + if(ksl > ssl)return 1; + for(i=0;i<ksl;i++) { + if(str[i] >= 'A' && str[i] <= 'Z') { + if(str[i] != key[i] && str[i]+0x20 != key[i])return 1; + } else if(str[i] >= 'a' && str[i] <= 'z') { + if(str[i] != key[i] && str[i]-0x20 != key[i])return 1; + } else { + if(str[i] != key[i])return 1; + } + } + return 0; +} +ulong stribegin(string *str, char *key) { return stribegin(str->s, key); } +ulong stribegin(stringlist *str, ulong num, char *key) { return stribegin(strptr(str, num), key); } + +ulong strend(char *str, char *key) { +int i, ssl = strlen(str), ksl = strlen(key); + if(ksl > ssl)return 1; + if(!memcmp(str + ssl - ksl, key, ksl))return 0; + return 1; +} +ulong strend(string *str, char *key) { return strend(str->s, key); } +ulong strend(stringlist *str, ulong num, char *key) { return strend(strptr(str, num), key); } + +ulong striend(char *str, char *key) { +int i, z, ssl = strlen(str), ksl = strlen(key); + if(ksl > ssl)return 1; + for(i=ssl-ksl, z=0;i<ssl;i++, z++) { + if(str[i] >= 'A' && str[i] <= 'Z') { + if(str[i] != key[z] && str[i]+0x20 != key[z])return 1; + } else if(str[i] >= 'a' && str[i] <= 'z') { + if(str[i] != key[z] && str[i]-0x20 != key[z])return 1; + } else { + if(str[i] != key[z])return 1; + } + } + return 0; +} +ulong striend(string *str, char *key) { return striend(str->s, key); } +ulong striend(stringlist *str, ulong num, char *key) { return striend(strptr(str, num), key); } + +void strltrim(char *str, char *key) { +int i, ssl = strlen(str), ksl = strlen(key); + if(ksl > ssl)return; + if(!strbegin(str, key)) { + for(i=0;i<ssl-ksl;i++)str[i] = str[i + ksl]; + str[i] = 0; + } +} +void strltrim(string *str, char *key) { strltrim(str->s, key); } +void strltrim(stringlist *str, ulong num, char *key) { strltrim(strptr(str, num), key); } + +void striltrim(char *str, char *key) { +int i, ssl = strlen(str), ksl = strlen(key); + if(ksl > ssl)return; + if(!stribegin(str, key)) { + for(i=0;i<ssl-ksl;i++)str[i] = str[i + ksl]; + str[i] = 0; + } +} +void striltrim(string *str, char *key) { striltrim(str->s, key); } +void striltrim(stringlist *str, ulong num, char *key) { striltrim(strptr(str, num), key); } + +void strrtrim(char *str, char *key) { +int ssl = strlen(str), ksl = strlen(key); + if(ksl > ssl)return; + if(!strend(str, key)) { + str[ssl - ksl] = 0; + } +} +void strrtrim(string *str, char *key) { strrtrim(str->s, key); } +void strrtrim(stringlist *str, ulong num, char *key) { strrtrim(strptr(str, num), key); } + +void strirtrim(char *str, char *key) { +int ssl = strlen(str), ksl = strlen(key); + if(ksl > ssl)return; + if(!striend(str, key)) { + str[ssl - ksl] = 0; + } +} +void strirtrim(string *str, char *key) { strirtrim(str->s, key); } +void strirtrim(stringlist *str, ulong num, char *key) { strirtrim(strptr(str, num), key); } + +ulong strhex(char *str) { +ulong r = 0, m = 0; +int i, ssl = strlen(str); +byte x; + for(i=0;i<ssl;i++) { + if(str[i] >= '0' && str[i] <= '9'); + else if(str[i] >= 'A' && str[i] <= 'F'); + else if(str[i] >= 'a' && str[i] <= 'f'); + else break; + } + for(--i;i>=0;i--, m+=4) { + x = str[i]; + if(x >= '0' && x <= '9')x -= '0'; + else if(x >= 'A' && x <= 'F')x -= 'A' - 0x0a; + else if(x >= 'a' && x <= 'f')x -= 'a' - 0x0a; + else return r; + r |= x << m; + } + return r; +} +ulong strhex(string *str) { return strhex(str->s); } +ulong strhex(stringlist *str, ulong num) { return strhex(strptr(str, num)); } + +ulong strdec(char *str) { +ulong r = 0, m = 1; +int i, ssl = strlen(str); +byte x; + for(i=0;i<ssl;i++) { + if(str[i] >= '0' && str[i] <= '9'); + else break; + } + for(--i;i>=0;i--, m*=10) { + x = str[i]; + if(x >= '0' && x <= '9')x -= '0'; + else return r; + r += x * m; + } + return r; +} +ulong strdec(string *str) { return strdec(str->s); } +ulong strdec(stringlist *str, ulong num) { return strdec(strptr(str, num)); } + +ulong strbin(char *str) { +ulong r = 0, m = 0; +int i, ssl = strlen(str); +byte x; + for(i=0;i<ssl;i++) { + if(str[i] == '0' || str[i] == '1'); + else break; + } + for(--i;i>=0;i--, m++) { + x = str[i]; + if(str[i] == '0' || str[i] == '1')x -= '0'; + else return r; + r |= x << m; + } + return r; +} +ulong strbin(string *str) { return strbin(str->s); } +ulong strbin(stringlist *str, ulong num) { return strbin(strptr(str, num)); } + +#include "libstr_math.cpp" +#include "libstr_split.cpp" +#include "libstr_replace.cpp" +#include "libstr_sprintf.cpp" diff --git a/bsnes/misc/libstr_math.cpp b/bsnes/misc/libstr_math.cpp new file mode 100644 index 00000000..7bfedea5 --- /dev/null +++ b/bsnes/misc/libstr_math.cpp @@ -0,0 +1,181 @@ +#define STRMATH_ADD 1 +#define STRMATH_SUB 2 +#define STRMATH_MUL 3 +#define STRMATH_DIV 4 +#define STRMATH_MOD 5 +#define STRMATH_AND 6 +#define STRMATH_OR 7 +#define STRMATH_XOR 8 +#define STRMATH_SHL 9 +#define STRMATH_SHR 10 + +#define STRMATH_LINKED 64 + +#define STRMATHMODE_NEG 1 +#define STRMATHMODE_NOT 2 + +#define __strunktonum() \ + if (s1[0] == '0' && s1[1] == 'x')r = strhex(s1 + 2); \ + else if(s1[0] == '0' && s1[1] == 'b')r = strbin(s1 + 2); \ + else r = strdec(s1) + +#define __strmath_setmode() \ + if (str[i] == '-') { mode = STRMATHMODE_NEG; i++; } \ + else if(str[i] == '~') { mode = STRMATHMODE_NOT; i++; } \ + else if(str[i] == '+') { i++; } \ + else mode=0 + +#define __strmath_modeset() \ + if (mode == STRMATHMODE_NEG)r *= -1; \ + else if(mode == STRMATHMODE_NOT)r =~ r + +#define __strmath_set(__x) \ + s1[z] = 0; \ + z = 0; \ + __strunktonum(); \ + __strmath_modeset(); \ + array[array_size++] = r; \ + array_gate[array_size] = __x + +/*************************************** +strmath(str) + resolves all math entities from within + str, and returns numerical result + example: strmath("5+5")=10 +***************************************/ +ulong p_strmath(char *str) { +int i = 0, ssl = strlen(str); +byte x, mode = 0; +ulong r, array[128], array_size = 0, z = 0; +byte array_gate[128]; +char *s1; + if(!ssl)return 0; + s1 = (char*)malloc(ssl + 1); + __strmath_setmode(); + while(i < ssl) { + x = str[i++]; + if (x == '+') { __strmath_set(STRMATH_ADD); __strmath_setmode(); } + else if(x == '-') { __strmath_set(STRMATH_SUB); __strmath_setmode(); } + else if(x == '*') { __strmath_set(STRMATH_MUL); __strmath_setmode(); } + else if(x == '/') { __strmath_set(STRMATH_DIV); __strmath_setmode(); } + else if(x == '%') { __strmath_set(STRMATH_MOD); __strmath_setmode(); } + else if(x == '&') { __strmath_set(STRMATH_AND); __strmath_setmode(); } + else if(x == '|') { __strmath_set(STRMATH_OR ); __strmath_setmode(); } + else if(x == '^') { __strmath_set(STRMATH_XOR); __strmath_setmode(); } + else if(x == '<' && str[i] == '<') { __strmath_set(STRMATH_SHL); i++; __strmath_setmode(); } + else if(x == '>' && str[i] == '>') { __strmath_set(STRMATH_SHR); i++; __strmath_setmode(); } + else s1[z++] = x; + } + s1[z] = 0; + __strunktonum(); + __strmath_modeset(); + array[array_size++] = r; + free(s1); + + for(i=1;i<array_size;i++) { + if (array_gate[i] == STRMATH_SHL) { array[i-1] <<= array[i]; array_gate[i] = STRMATH_LINKED; } + else if(array_gate[i] == STRMATH_SHR) { array[i-1] >>= array[i]; array_gate[i] = STRMATH_LINKED; } + } + + for(i=1;i<array_size;i++) { + if (array_gate[i] == STRMATH_MUL) { array[i-1] *= array[i]; array_gate[i] = STRMATH_LINKED; } + else if(array_gate[i] == STRMATH_DIV) { array[i-1] /= array[i]; array_gate[i] = STRMATH_LINKED; } + } + + r = array[0]; + for(i=1;i<array_size;i++) { + if (array_gate[i] == STRMATH_ADD)r += array[i]; + else if(array_gate[i] == STRMATH_SUB)r -= array[i]; + else if(array_gate[i] == STRMATH_MOD)r %= array[i]; + else if(array_gate[i] == STRMATH_AND)r &= array[i]; + else if(array_gate[i] == STRMATH_OR )r |= array[i]; + else if(array_gate[i] == STRMATH_XOR)r ^= array[i]; + } + + return r; +} + +ulong strmath(char *in_str) { +ulong r = 0; +ulong pdepth = 0, cpdepth, maxpdepth = 0; +ulong pstart, pend, spos; +int i, sc, sl = strlen(in_str); +char *str = (char*)malloc(sl + 1), *str0; +char *pstr; +char num[64]; + strcpy(str, in_str); + + for(i=0;i<sl;i++) { + if(str[i]=='(') { + pdepth++; + if(pdepth > maxpdepth)maxpdepth = pdepth; + } else if(str[i]==')') { + if(pdepth == 0) { + free(str); + return null; //error! too many )'s + } + pdepth --; + } + } + + if(pdepth != 0) { + free(str); + return null; //error! unequal ('s to )'s + } + + pdepth = maxpdepth; + while(pdepth) { + cpdepth = 0; + for(i=0;i<sl;) { + if(str[i] == '(')cpdepth++; + if(str[i] == ')')cpdepth--; + i++; + if(cpdepth == pdepth) { + pstart = i; + while(str[i] != ')')i++; + pend = i; + + pstr = (char*)malloc(pend-pstart+1); + memcpy(pstr, str+pstart, pend-pstart); + pstr[pend-pstart]=0; + r = p_strmath(pstr); + free(pstr); + sprintf(num, "%d", r); + str0 = (char*)malloc(sl + strlen(num) + 1); + memcpy(str0, str, pstart - 1); + spos = pstart - 1; + memcpy(str0+spos, num, strlen(num)); + spos += strlen(num); + sc = spos; + memcpy(str0+spos, str+pend+1, sl-pend-1); + spos += sl - pend - 1; + sl = spos; + str0[sl] = 0; + free(str); + str = str0; + cpdepth--; + i = sc; + } + } + pdepth--; + } + + r = p_strmath(str); + + free(str); + return r; +} +ulong strmath(string *str) { return strmath(str->s); } +ulong strmath(stringlist *str, ulong num) { return strmath(strptr(str, num)); } + +ulong strmathentity(char *str) { +int i, ssl = strlen(str); + for(i=0;i<ssl;i++) { + if(str[i] == '+' || str[i] == '-' || str[i] == '*' || str[i] == '/' || + str[i] == '%' || str[i] == '&' || str[i] == '|' || str[i] == '^' || + (str[i] == '<' && str[i+1] == '<') || (str[i] == '>' && str[i+1] == '>'))return 1; + } + return 0; +} +ulong strmathentity(string *str) { return strmathentity(str->s); } +ulong strmathentity(stringlist *str, ulong num) { return strmathentity(strptr(str, num)); } diff --git a/bsnes/misc/libstr_replace.cpp b/bsnes/misc/libstr_replace.cpp new file mode 100644 index 00000000..415ea3d4 --- /dev/null +++ b/bsnes/misc/libstr_replace.cpp @@ -0,0 +1,82 @@ +void replace(string *str, char *key, char *token) { +int i, z, ksl = strlen(key), tsl = strlen(token), ssl = strlen(str); +ulong replace_count = 0, size; +char *data; + if(ksl > ssl)return; + if(tsl > ksl) { //the new string may be longer than the old string... + for(i=0;i<=ssl-ksl;) { //so let's find out how big of a string we'll need... + if(!memcmp(str->s + i, key, ksl)) { + replace_count++; + i += ksl; + } else i++; + } + size = ssl + ((tsl - ksl) * replace_count); + if(size > str->size)strresize(str, size); + } + data = (char*)malloc(size + 1); + for(i=z=0;i<ssl;) { + if(i <= ssl - ksl) { + if(!memcmp(str->s + i, key, ksl)) { + memcpy(data + z, token, tsl); + z += tsl; + i += ksl; + } else data[z++] = str->s[i++]; + } else data[z++] = str->s[i++]; + } + data[z] = 0; + strcpy(str, data); + free(data); +} + +void replace_q(string *str, char *key, char *token) { +int i, l, z, ksl = strlen(key), tsl = strlen(token), ssl = strlen(str); +byte x; +ulong replace_count = 0, size; +char *data; + if(ksl > ssl)return; + if(tsl > ksl) { + for(i=0;i<=ssl-ksl;) { + x = str->s[i]; + if(x == '\"' || x == '\'') { + l = i; + i++; + while(str->s[i++] != x) { + if(i == ssl) { + i = l; + break; + } + } + } + if(!memcmp(str->s + i, key, ksl)) { + replace_count++; + i += ksl; + } else i++; + } + size = ssl + ((tsl - ksl) * replace_count); + if(size > str->size)strresize(str, size); + } + data = (char*)malloc(size + 1); + for(i=z=0;i<ssl;) { + x = str->s[i]; + if(x == '\"' || x == '\'') { + l = i++; + while(str->s[i] != x && i < ssl)i++; + if(i >= ssl)i = l; + else { + memcpy(data + z, str->s + l, i - l); + z += i - l; + } + } + if(i <= ssl - ksl) { + if(!memcmp(str->s + i, key, ksl)) { + memcpy(data + z, token, tsl); + z += tsl; + i += ksl; + replace_count++; + } else data[z++] = str->s[i++]; + } else data[z++] = str->s[i++]; + } + data[z] = 0; + strcpy(str, data); + free(data); +} diff --git a/bsnes/misc/libstr_split.cpp b/bsnes/misc/libstr_split.cpp new file mode 100644 index 00000000..91f9ff1e --- /dev/null +++ b/bsnes/misc/libstr_split.cpp @@ -0,0 +1,41 @@ +void split(stringlist *dest, char *key, char *src) { +int i, ssl = strlen(src), ksl = strlen(key); +byte x; +ulong lp = 0, split_count = 0; + for(i=0;i<=ssl-ksl;) { + if(!memcmp(src + i, key, ksl)) { + x = src[i]; + src[i] = 0; + strcpy(dest, split_count++, src + lp); + src[i] = x; + i += ksl; + lp = i; + } else i++; + } + strcpy(dest, split_count++, src + lp); +} +void split(stringlist *dest, char *key, string *src) { split(dest, key, src->s); } + +void split_q(stringlist *dest, char *key, char *src) { +int i, z, ssl = strlen(src), ksl = strlen(key); +byte x; +ulong lp = 0, split_count = 0; + for(i=0;i<=ssl-ksl;) { + x = src[i]; + if(x=='\"' || x=='\'') { + z = i++; + while(src[i] != x && i < ssl)i++; + if(i >= ssl)i = z; + } + if(!memcmp(src + i, key, ksl)) { + x = src[i]; + src[i] = 0; + strcpy(dest, split_count++, src + lp); + src[i] = x; + i += ksl; + lp = i; + } else i++; + } + strcpy(dest, split_count++, src + lp); +} +void split_q(stringlist *dest, char *key, string *src) { split_q(dest, key, src->s); } diff --git a/bsnes/misc/libstr_sprintf.cpp b/bsnes/misc/libstr_sprintf.cpp new file mode 100644 index 00000000..30a67a2d --- /dev/null +++ b/bsnes/misc/libstr_sprintf.cpp @@ -0,0 +1,130 @@ +void numtobin(char *s, ulong num) { +ulong mask = 0x80000000, len = 0, z = 0; + for(;mask;mask>>=1,len++) { if(num&mask)break; } + len = 32 - len; + do { + if(num&(1<<(len-1)))s[z++] = '1'; + else s[z++] = '0'; + }while(--len); + s[z] = 0; +} + +void sprintf(string *str, char *s, ...) { +va_list args; +char t[2], n[256]; +int i, l, sl, z; +byte pad_type, pad_len; +ulong num; +char *r; + va_start(args, s); + strcpy(str, ""); + for(i=0;i<strlen(s);i++) { + if(s[i] == '%') { + i++; + if(s[i] == '0' && s[i+1] == '.' && (s[i+2] >= '0' && s[i+2] <= '9')) { + pad_type = 1; + if(s[i+3] >= '0' && s[i+3] <= '9') { pad_len = (s[i+2]-'0')*10 + (s[i+3]-'0'); i+=4; } + else { pad_len = (s[i+2]-'0'); i+=3; } + } + else if(s[i] >= '0' && s[i] <= '9') { + pad_type = 2; + if(s[i+1] >= '0' && s[i+1] <= '9') { pad_len = (s[i]-'0')*10 + (s[i+1]-'0'); i+=2; } + else { pad_len = (s[i]-'0'); i+=1; } + } + else { pad_type = 0; } + + if(s[i] == 'd') { + num = va_arg(args, ulong); + sprintf(n, "%d", num); + } else if(s[i] == 'x') { + num = va_arg(args, ulong); + sprintf(n, "%x", num); + } else if(s[i] == 'b') { + num = va_arg(args, ulong); + numtobin(n, num); + } else if(s[i] == 's') { + r = va_arg(args, char*); + } + + if(pad_type != 0) { + if(s[i] == 's')sl = strlen(r); + else sl = strlen(n); + if(sl < pad_len) { + while(sl < pad_len) { + strcat(str, (pad_type == 1)?"0":" "); + sl++; + } + } + } + + if(s[i] == 's')strcat(str, r); + else strcat(str, n); + } else { + t[0] = s[i]; + t[1] = 0; + strcat(str, t); + } + } + va_end(args); +} + + +void sprintf(stringlist *str_list, ulong str_num, char *s, ...) { +va_list args; +char t[2], n[256]; +int i, l, sl, z; +byte pad_type, pad_len; +ulong num; +char *r; +string *str = strget(str_list, str_num); + va_start(args, s); + strcpy(str, ""); + for(i=0;i<strlen(s);i++) { + if(s[i] == '%') { + i++; + if(s[i] == '0' && s[i+1] == '.' && (s[i+2] >= '0' && s[i+2] <= '9')) { + pad_type = 1; + if(s[i+3] >= '0' && s[i+3] <= '9') { pad_len = (s[i+2]-'0')*10 + (s[i+3]-'0'); i+=4; } + else { pad_len = (s[i+2]-'0'); i+=3; } + } + else if(s[i] >= '0' && s[i] <= '9') { + pad_type = 2; + if(s[i+1] >= '0' && s[i+1] <= '9') { pad_len = (s[i]-'0')*10 + (s[i+1]-'0'); i+=2; } + else { pad_len = (s[i]-'0'); i+=1; } + } + else { pad_type = 0; } + + if(s[i] == 'd') { + num = va_arg(args, ulong); + sprintf(n, "%d", num); + } else if(s[i] == 'x') { + num = va_arg(args, ulong); + sprintf(n, "%x", num); + } else if(s[i] == 'b') { + num = va_arg(args, ulong); + numtobin(n, num); + } else if(s[i] == 's') { + r = va_arg(args, char*); + } + + if(pad_type != 0) { + if(s[i] == 's')sl = strlen(r); + else sl = strlen(n); + if(sl < pad_len) { + while(sl < pad_len) { + strcat(str, (pad_type == 1)?"0":" "); + sl++; + } + } + } + + if(s[i] == 's')strcat(str, r); + else strcat(str, n); + } else { + t[0] = s[i]; + t[1] = 0; + strcat(str, t); + } + } + va_end(args); +} diff --git a/bsnes/misc/libvlist.cpp b/bsnes/misc/libvlist.cpp new file mode 100644 index 00000000..d5825364 --- /dev/null +++ b/bsnes/misc/libvlist.cpp @@ -0,0 +1,55 @@ +ulong __vector_resize(ulong val) +{ +int i; + for(i=0;i<32;i++) { + if((1<<i) >= val)break; + } +//if the value is >2 million, this will fail, in which case, +//just use value. otherwise, use the power of 2. + if((1<<i) > val)val = 1<<i; + return val; +} + +class vectorlist { +public: +ulong *list, *newlist, size, newsize, max_size; + + void alloc(ulong size) { + if(size < max_size)return; + newsize = __vector_resize(size); + newlist = (ulong*)malloc(newsize*4); + memcpy(newlist, list, max_size*4); + free(list); + list = newlist; + max_size = newsize; + } + + void add(ulong val) { + size++; + if(size > max_size)alloc(size + 1); + list[size - 1] = val; + } + + void set(ulong num, ulong val) { + if(num >= max_size)alloc(num + 1); + list[num] = val; + if(++num > size)size = num; + } + + ulong get(ulong num) { + if(num >= max_size)return 0; + return list[num]; + } + + vectorlist() { + list = 0; + size = 0; + max_size = 8; + list = (ulong*)malloc(8*4); + } + + ~vectorlist() { + if(list)free(list); + } + +}; diff --git a/bsnes/mmio.obj b/bsnes/mmio.obj new file mode 100644 index 0000000000000000000000000000000000000000..090d59ad6bd3c0ee262caf61a8b43c743a65bf79 GIT binary patch literal 81207 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	#%Js<4{YGCwq7v`!2%k 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 literal 0 HcmV?d00001 diff --git a/bsnes/ppu/mmio.cpp b/bsnes/ppu/mmio.cpp new file mode 100644 index 00000000..4a515a37 --- /dev/null +++ b/bsnes/ppu/mmio.cpp @@ -0,0 +1,198 @@ +#include "../base.h" +#include "../timing/timing.h" +#include "../cpu/g65816.h" +extern g65816 *gx816; +extern snes_timer *snes_time; +extern videostate render; + +ppustate ppu; + +#include "ppu_cache.cpp" + +#include "ppu_dma.cpp" +#include "ppu_screen.cpp" +#include "ppu_vram.cpp" +#include "ppu_palette.cpp" +#include "ppu_timing.cpp" +#include "ppu_oam.cpp" +#include "ppu_wram.cpp" +#include "ppu_mode7.cpp" +#include "ppu_scroll.cpp" +#include "ppu_muldiv.cpp" +#include "ppu_window.cpp" +#include "ppu_addsub.cpp" +#include "ppu_joypad.cpp" +#include "ppu_misc.cpp" + +#include "ppu.cpp" + +byte mmio_read(word addr) { +static word counter = 0; + switch(addr) { + case 0x2134:return mmio_r2134(); + case 0x2135:return mmio_r2135(); + case 0x2136:return mmio_r2136(); + case 0x2137:return mmio_r2137(); + case 0x2138:return mmio_r2138(); + case 0x2139:return mmio_r2139(); + case 0x213a:return mmio_r213a(); + case 0x213b:return mmio_r213b(); + case 0x213c:return mmio_r213c(); + case 0x213d:return mmio_r213d(); + case 0x213e:return mmio_r213e(); + case 0x213f:return mmio_r213f(); + + case 0x2140:case 0x2141:case 0x2142:case 0x2143: + case 0x2144:case 0x2145:case 0x2146:case 0x2147: + case 0x2148:case 0x2149:case 0x214a:case 0x214b: + case 0x214c:case 0x214d:case 0x214e:case 0x214f: + case 0x2150:case 0x2151:case 0x2152:case 0x2153: + case 0x2154:case 0x2155:case 0x2156:case 0x2157: + case 0x2158:case 0x2159:case 0x215a:case 0x215b: + case 0x215c:case 0x215d:case 0x215e:case 0x215f: + case 0x2160:case 0x2161:case 0x2162:case 0x2163: + case 0x2164:case 0x2165:case 0x2166:case 0x2167: + case 0x2168:case 0x2169:case 0x216a:case 0x216b: + case 0x216c:case 0x216d:case 0x216e:case 0x216f: + case 0x2170:case 0x2171:case 0x2172:case 0x2173: + case 0x2174:case 0x2175:case 0x2176:case 0x2177: + case 0x2178:case 0x2179:case 0x217a:case 0x217b: + case 0x217c:case 0x217d:case 0x217e:case 0x217f: + byte x; + x = rand() & 3; + if(x == 0)return gx816->regs.a.b; + if(x == 1)return gx816->regs.x; + if(x == 2)return gx816->regs.y; + if(x == 3) { counter++; return counter >> 1; } + break; + + case 0x21c2:return mmio_r21c2(); + case 0x21c3:return mmio_r21c3(); + + case 0x4016:return mmio_r4016(); + case 0x4210:return mmio_r4210(); + case 0x4211:return mmio_r4211(); + case 0x4212:return mmio_r4212(); + case 0x4214:return mmio_r4214(); + case 0x4215:return mmio_r4215(); + case 0x4216:return mmio_r4216(); + case 0x4217:return mmio_r4217(); + case 0x4218:return mmio_r4218(); + case 0x4219:return mmio_r4219(); + default: +/* + dprintf("* mmio: unknown read [%0.4x]", addr); + debug_set_state(DEBUGMODE_WAIT); + disas_g65816_op(); +*/ + break; + } + return 0x00; +} + +void mmio_write(word addr, byte value) { + switch(addr) { + case 0x2100:mmio_w2100(value);break; + case 0x2101:mmio_w2101(value);break; + case 0x2102:mmio_w2102(value);break; + case 0x2103:mmio_w2103(value);break; + case 0x2104:mmio_w2104(value);break; + case 0x2105:mmio_w2105(value);break; + case 0x2106:mmio_w2106(value);break; + case 0x2107:mmio_w2107(value);break; + case 0x2108:mmio_w2108(value);break; + case 0x2109:mmio_w2109(value);break; + case 0x210a:mmio_w210a(value);break; + case 0x210b:mmio_w210b(value);break; + case 0x210c:mmio_w210c(value);break; + case 0x210d:mmio_w210d(value);break; + case 0x210e:mmio_w210e(value);break; + case 0x210f:mmio_w210f(value);break; + case 0x2110:mmio_w2110(value);break; + case 0x2111:mmio_w2111(value);break; + case 0x2112:mmio_w2112(value);break; + case 0x2113:mmio_w2113(value);break; + case 0x2114:mmio_w2114(value);break; + case 0x2115:mmio_w2115(value);break; + case 0x2116:mmio_w2116(value);break; + case 0x2117:mmio_w2117(value);break; + case 0x2118:mmio_w2118(value);break; + case 0x2119:mmio_w2119(value);break; + case 0x211a:mmio_w211a(value);break; + case 0x211b:mmio_w211b(value);break; + case 0x211c:mmio_w211c(value);break; + case 0x211d:mmio_w211d(value);break; + case 0x211e:mmio_w211e(value);break; + case 0x211f:mmio_w211f(value);break; + case 0x2120:mmio_w2120(value);break; + case 0x2121:mmio_w2121(value);break; + case 0x2122:mmio_w2122(value);break; + case 0x2123:mmio_w2123(value);break; + case 0x2124:mmio_w2124(value);break; + case 0x2125:mmio_w2125(value);break; + case 0x2126:mmio_w2126(value);break; + case 0x2127:mmio_w2127(value);break; + case 0x2128:mmio_w2128(value);break; + case 0x2129:mmio_w2129(value);break; + case 0x212a:mmio_w212a(value);break; + case 0x212b:mmio_w212b(value);break; + case 0x212c:mmio_w212c(value);break; + case 0x212d:mmio_w212d(value);break; + case 0x212e:mmio_w212e(value);break; + case 0x212f:mmio_w212f(value);break; + case 0x2130:mmio_w2130(value);break; + case 0x2131:mmio_w2131(value);break; + case 0x2132:mmio_w2132(value);break; + case 0x2133:mmio_w2133(value);break; + case 0x2180:mmio_w2180(value);break; + case 0x2181:mmio_w2181(value);break; + case 0x2182:mmio_w2182(value);break; + case 0x2183:mmio_w2183(value);break; + case 0x4016:mmio_w4016(value);break; + case 0x4200:mmio_w4200(value);break; + case 0x4202:mmio_w4202(value);break; + case 0x4203:mmio_w4203(value);break; + case 0x4204:mmio_w4204(value);break; + case 0x4205:mmio_w4205(value);break; + case 0x4206:mmio_w4206(value);break; + case 0x4207:mmio_w4207(value);break; + case 0x4208:mmio_w4208(value);break; + case 0x4209:mmio_w4209(value);break; + case 0x420a:mmio_w420a(value);break; + case 0x420b:mmio_w420b(value);break; + case 0x420c:mmio_w420c(value);break; + case 0x420d:mmio_w420d(value);break; + + case 0x4300:case 0x4310:case 0x4320:case 0x4330: + case 0x4340:case 0x4350:case 0x4360:case 0x4370: + mmio_w43x0((addr >> 4) & 7, value);break; + + case 0x4301:case 0x4311:case 0x4321:case 0x4331: + case 0x4341:case 0x4351:case 0x4361:case 0x4371: + mmio_w43x1((addr >> 4) & 7, value);break; + + case 0x4302:case 0x4312:case 0x4322:case 0x4332: + case 0x4342:case 0x4352:case 0x4362:case 0x4372: + mmio_w43x2((addr >> 4) & 7, value);break; + + case 0x4303:case 0x4313:case 0x4323:case 0x4333: + case 0x4343:case 0x4353:case 0x4363:case 0x4373: + mmio_w43x3((addr >> 4) & 7, value);break; + + case 0x4304:case 0x4314:case 0x4324:case 0x4334: + case 0x4344:case 0x4354:case 0x4364:case 0x4374: + mmio_w43x4((addr >> 4) & 7, value);break; + + case 0x4305:case 0x4315:case 0x4325:case 0x4335: + case 0x4345:case 0x4355:case 0x4365:case 0x4375: + mmio_w43x5((addr >> 4) & 7, value);break; + + case 0x4306:case 0x4316:case 0x4326:case 0x4336: + case 0x4346:case 0x4356:case 0x4366:case 0x4376: + mmio_w43x6((addr >> 4) & 7, value);break; + + case 0x4307:case 0x4317:case 0x4327:case 0x4337: + case 0x4347:case 0x4357:case 0x4367:case 0x4377: + mmio_w43x7((addr >> 4) & 7, value);break; + } +} diff --git a/bsnes/ppu/ppu.cpp b/bsnes/ppu/ppu.cpp new file mode 100644 index 00000000..a2745ecf --- /dev/null +++ b/bsnes/ppu/ppu.cpp @@ -0,0 +1,476 @@ +#define RENDER_MAIN 0 +#define RENDER_SUB 1 + +#include "ppu_render.cpp" + +/********************** + *** priority table *** + ********************************* + *** p = $2105 bit 3 *** + *** o = tile priority bit *** + *** s = sprite priority (0-3) *** + ********************************* + *** p = 0 : p = 1 *** + *** bg4(o = 0) : bg4(o = 0) *** + *** bg3(o = 0) : bg3(o = 0) *** + *** oam(s = 0) : oam(s = 0) *** + *** bg4(o = 1) : bg4(o = 1) *** + *** bg3(o = 1) : oam(s = 1) *** + *** oam(s = 1) : bg2(o = 0) *** + *** bg2(o = 0) : bg1(o = 0) *** + *** bg1(o = 0) : oam(s = 2) *** + *** oam(s = 2) : bg2(o = 1) *** + *** bg2(o = 1) : bg1(o = 1) *** + *** bg1(o = 1) : oam(s = 3) *** + *** oam(s = 3) : bg3(o = 1) *** + ********************************/ + +#define debug_ppu_render_line_bg(bgnum, depth, bg, pri) \ + if(render.##bgnum##_enabled[DEBUG_BGENABLED_ALL] == true) { \ + if(render.##bgnum##_enabled[DEBUG_BGENABLED_PRI##pri##] == true) { \ + ppu_render_line_bg(depth, bg, pri); \ + } \ + } +#define debug_ppu_render_line_oam(bgnum, pri) \ + if(render.##bgnum##_enabled[DEBUG_BGENABLED_ALL] == true) { \ + if(render.##bgnum##_enabled[DEBUG_BGENABLED_PRI##pri##] == true) { \ + ppu_render_line_oam(pri); \ + } \ + } + +void ppu_render_line_mode0(void) { + if(ppu.bg_priority_mode == 0) { + debug_ppu_render_line_bg (bg4, COLORDEPTH_4, BG4, 0); + debug_ppu_render_line_bg (bg3, COLORDEPTH_4, BG3, 0); + debug_ppu_render_line_oam(oam, 0); + debug_ppu_render_line_bg (bg4, COLORDEPTH_4, BG4, 1); + debug_ppu_render_line_bg (bg3, COLORDEPTH_4, BG3, 1); + debug_ppu_render_line_oam(oam, 1); + debug_ppu_render_line_bg (bg2, COLORDEPTH_4, BG2, 0); + debug_ppu_render_line_bg (bg1, COLORDEPTH_4, BG1, 0); + debug_ppu_render_line_oam(oam, 2); + debug_ppu_render_line_bg (bg2, COLORDEPTH_4, BG2, 1); + debug_ppu_render_line_bg (bg1, COLORDEPTH_4, BG1, 1); + debug_ppu_render_line_oam(oam, 3); + } else { + debug_ppu_render_line_bg (bg4, COLORDEPTH_4, BG4, 0); + debug_ppu_render_line_bg (bg3, COLORDEPTH_4, BG3, 0); + debug_ppu_render_line_oam(oam, 0); + debug_ppu_render_line_bg (bg4, COLORDEPTH_4, BG4, 1); + debug_ppu_render_line_oam(oam, 1); + debug_ppu_render_line_bg (bg2, COLORDEPTH_4, BG2, 0); + debug_ppu_render_line_bg (bg1, COLORDEPTH_4, BG1, 0); + debug_ppu_render_line_oam(oam, 2); + debug_ppu_render_line_bg (bg2, COLORDEPTH_4, BG2, 1); + debug_ppu_render_line_bg (bg1, COLORDEPTH_4, BG1, 1); + debug_ppu_render_line_oam(oam, 3); + debug_ppu_render_line_bg (bg3, COLORDEPTH_4, BG3, 1); + } +} + +void ppu_render_line_mode1(void) { + if(ppu.bg_priority_mode == 0) { + debug_ppu_render_line_bg (bg3, COLORDEPTH_4, BG3, 0); + debug_ppu_render_line_oam(oam, 0); + debug_ppu_render_line_bg (bg3, COLORDEPTH_4, BG3, 1); + debug_ppu_render_line_oam(oam, 1); + debug_ppu_render_line_bg (bg2, COLORDEPTH_16, BG2, 0); + debug_ppu_render_line_bg (bg1, COLORDEPTH_16, BG1, 0); + debug_ppu_render_line_oam(oam, 2); + debug_ppu_render_line_bg (bg2, COLORDEPTH_16, BG2, 1); + debug_ppu_render_line_bg (bg1, COLORDEPTH_16, BG1, 1); + debug_ppu_render_line_oam(oam, 3); + } else { + debug_ppu_render_line_bg (bg3, COLORDEPTH_4, BG3, 0); + debug_ppu_render_line_oam(oam, 0); + debug_ppu_render_line_oam(oam, 1); + debug_ppu_render_line_bg (bg2, COLORDEPTH_16, BG2, 0); + debug_ppu_render_line_bg (bg1, COLORDEPTH_16, BG1, 0) + debug_ppu_render_line_oam(oam, 2); + debug_ppu_render_line_bg (bg2, COLORDEPTH_16, BG2, 1); + debug_ppu_render_line_bg (bg1, COLORDEPTH_16, BG1, 1) + debug_ppu_render_line_oam(oam, 3); + debug_ppu_render_line_bg (bg3, COLORDEPTH_4, BG3, 1); + } +} + +void ppu_render_line_mode2(void) { + if(ppu.bg_priority_mode == 0) { + debug_ppu_render_line_oam(oam, 0); + debug_ppu_render_line_oam(oam, 1); + debug_ppu_render_line_bg (bg2, COLORDEPTH_16, BG2, 0); + debug_ppu_render_line_bg (bg1, COLORDEPTH_16, BG1, 0); + debug_ppu_render_line_oam(oam, 2); + debug_ppu_render_line_bg (bg2, COLORDEPTH_16, BG2, 1); + debug_ppu_render_line_bg (bg1, COLORDEPTH_16, BG1, 1); + debug_ppu_render_line_oam(oam, 3); + } else { + debug_ppu_render_line_oam(oam, 0); + debug_ppu_render_line_oam(oam, 1); + debug_ppu_render_line_bg (bg2, COLORDEPTH_16, BG2, 0); + debug_ppu_render_line_bg (bg1, COLORDEPTH_16, BG1, 0) + debug_ppu_render_line_oam(oam, 2); + debug_ppu_render_line_bg (bg2, COLORDEPTH_16, BG2, 1); + debug_ppu_render_line_bg (bg1, COLORDEPTH_16, BG1, 1) + debug_ppu_render_line_oam(oam, 3); + } +} + +void ppu_render_line_mode3(void) { + if(ppu.bg_priority_mode == 0) { + debug_ppu_render_line_oam(oam, 0); + debug_ppu_render_line_oam(oam, 1); + debug_ppu_render_line_bg(bg2, COLORDEPTH_16, BG2, 0); + debug_ppu_render_line_bg(bg1, COLORDEPTH_256, BG1, 0); + debug_ppu_render_line_oam(oam, 2); + debug_ppu_render_line_bg(bg2, COLORDEPTH_16, BG2, 1); + debug_ppu_render_line_bg(bg1, COLORDEPTH_256, BG1, 1); + debug_ppu_render_line_oam(oam, 3); + } else { + debug_ppu_render_line_oam(oam, 0); + debug_ppu_render_line_oam(oam, 1); + debug_ppu_render_line_bg(bg2, COLORDEPTH_16, BG2, 0); + debug_ppu_render_line_bg(bg1, COLORDEPTH_256, BG1, 0); + debug_ppu_render_line_oam(oam, 2); + debug_ppu_render_line_bg(bg2, COLORDEPTH_16, BG2, 1); + debug_ppu_render_line_bg(bg1, COLORDEPTH_256, BG1, 1); + debug_ppu_render_line_oam(oam, 3); + } +} + +void ppu_render_line_mode4(void) { + if(ppu.bg_priority_mode == 0) { + debug_ppu_render_line_oam(oam, 0); + debug_ppu_render_line_oam(oam, 1); + debug_ppu_render_line_bg(bg2, COLORDEPTH_4, BG2, 0); + debug_ppu_render_line_bg(bg1, COLORDEPTH_256, BG1, 0); + debug_ppu_render_line_oam(oam, 2); + debug_ppu_render_line_bg(bg2, COLORDEPTH_4, BG2, 1); + debug_ppu_render_line_bg(bg1, COLORDEPTH_256, BG1, 1); + debug_ppu_render_line_oam(oam, 3); + } else { + debug_ppu_render_line_oam(oam, 0); + debug_ppu_render_line_oam(oam, 1); + debug_ppu_render_line_bg(bg2, COLORDEPTH_4, BG2, 0); + debug_ppu_render_line_bg(bg1, COLORDEPTH_256, BG1, 0); + debug_ppu_render_line_oam(oam, 2); + debug_ppu_render_line_bg(bg2, COLORDEPTH_4, BG2, 1); + debug_ppu_render_line_bg(bg1, COLORDEPTH_256, BG1, 1); + debug_ppu_render_line_oam(oam, 3); + } +} + +void ppu_render_line_mode5(void) { + debug_ppu_render_line_bg(bg2, COLORDEPTH_4, BG2, 0); + debug_ppu_render_line_bg(bg1, COLORDEPTH_16, BG1, 0); + debug_ppu_render_line_bg(bg2, COLORDEPTH_4, BG2, 0); + debug_ppu_render_line_bg(bg1, COLORDEPTH_16, BG1, 1); +} + +void ppu_render_line_mode6(void) { + debug_ppu_render_line_bg(bg1, COLORDEPTH_16, BG1, 0); + debug_ppu_render_line_bg(bg1, COLORDEPTH_16, BG1, 1); +} + +void ppu_render_line_mode7(void) { + if(ppu.bg_priority_mode == 0) { + debug_ppu_render_line_oam(oam, 0); + debug_ppu_render_line_oam(oam, 1); + ppu_render_line_m7(); + debug_ppu_render_line_oam(oam, 2); + debug_ppu_render_line_oam(oam, 3); + } else { + debug_ppu_render_line_oam(oam, 0); + debug_ppu_render_line_oam(oam, 1); + ppu_render_line_m7(); + debug_ppu_render_line_oam(oam, 2); + debug_ppu_render_line_oam(oam, 3); + } +} + +void ppu_render_scanline(void) { +int x, y; + ppu.hirq_triggered = false; + ppu.vline_pos++; + if(ppu.vline_pos > 261)ppu.vline_pos = 0; + +//new screen initialize + if(ppu.vline_pos == 0) { + ppu.virq_triggered = false; + ppu.active_hdma_channels = ppu.toggle_active_hdma_channels; + if(ppu.visible_scanlines != ppu.toggle_visible_scanlines) { + ppu.visible_scanlines = ppu.toggle_visible_scanlines; + video_setsnesmode(); + video_setmode(false, 512, ppu.visible_scanlines * 2); + } + gx816->nmi_pin = 1; + } + +//screen refresh + if(ppu.vline_pos == ppu.visible_scanlines) { + if(render.frame_count == 0) { + UpdateDisplay(); + } + render.frame_count++; + if(render.frame_count >= render.frame_skip) { + render.frame_count = 0; + } + ppu.interlace_frame ^= 1; + } + +//automatic joypad read + if(ppu.vline_pos == (ppu.visible_scanlines + 1) && ppu.auto_joypad_read == true) { + UpdateJoypad(); + } + +//nmi + if(ppu.vline_pos == (ppu.visible_scanlines + 8) && gx816->nmi_pin == 1) { + gx816->nmi_pin = 0; + if(gx816->nmi_enabled == true) { + gx816->InvokeIRQ(0xffea); + } + } + + UpdateHDMA(); //found in ppu_dma.cpp + y = ppu.vline_pos; + if(y < ppu.visible_scanlines && (render.frame_skip == 0 || render.frame_count == 0)) { + if(ppu.display_disable == true) { + memset(ppu.screen + y * 512, 0, 1024); + } else { + ppu_clear_pixel_cache(); + switch(ppu.bg_mode) { + case 0:ppu_render_line_mode0();break; + case 1:ppu_render_line_mode1();break; + case 2:ppu_render_line_mode2();break; + case 3:ppu_render_line_mode3();break; + case 4:ppu_render_line_mode4();break; + case 5:ppu_render_line_mode5();break; + case 6:ppu_render_line_mode6();break; + case 7:ppu_render_line_mode7();break; + } + ppu_render_line_to_screen(); + } + } +} + +void ppu_update_scanline(void) { +//starting a new screen? + if(ppu.vline_pos > snes_time->vscan_pos) { + while(ppu.vline_pos != 0) { + ppu_render_scanline(); + } + } + + while(snes_time->vscan_pos > ppu.vline_pos) { + ppu_render_scanline(); + } + + if(!(gx816->regs.p & PF_I)) { + if(ppu.vcounter_enabled == true && ppu.hcounter_enabled == true) { + if(snes_time->vscan_pos == ppu.virq_pos && ppu.virq_triggered == false) { + if(snes_time->hscan_pos >= ppu.hirq_pos && ppu.hirq_triggered == false) { + ppu.irq_triggered = true; + ppu.virq_triggered = true; + ppu.hirq_triggered = true; + gx816->InvokeIRQ(0xffee); + } + } + } else if(ppu.vcounter_enabled == true) { + if(snes_time->vscan_pos == ppu.virq_pos && ppu.virq_triggered == false) { + ppu.irq_triggered = true; + ppu.virq_triggered = true; + gx816->InvokeIRQ(0xffee); + } + } else if(ppu.hcounter_enabled == true) { + if(snes_time->hscan_pos >= ppu.hirq_pos && ppu.hirq_triggered == false) { + ppu.irq_triggered = true; + ppu.hirq_triggered = true; + gx816->InvokeIRQ(0xffee); + } + } + } +} + +byte oam_read(word addr) { + addr &= 1023; + if(addr >= 512) { + addr &= 31; + return ppu.oam[addr + 512]; + } else { + return ppu.oam[addr]; + } +} + +void oam_write(word addr, byte value) { + addr &= 1023; + if(addr >= 512) { + addr &= 31; + ppu.oam[addr + 512] = value; + } else { + ppu.oam[addr] = value; + } +} + +void PPUInit(byte first_time) { +int i, l; +byte r, g, b; +double m; +word *ptr; + if(first_time == 1) { + ppu.screen = (word*)malloc(512 * 480 * 2); + ppu.vram = (byte*)malloc(0x10000); + ppu.cgram = (byte*)malloc(512); + ppu.oam = (byte*)malloc(544); + ppu.light_table = (word*)malloc(16 * 65536 * 2); + ppu_init_tiledata_cache(); + + for(l=0;l<16;l++) { + ppu.mosaic_table[l] = (word*)malloc(4096 * 2); + for(i=0;i<4096;i++) { + ppu.mosaic_table[l][i] = (i / (l + 1)) * (l + 1); + } + } + + ptr = (word*)ppu.light_table; + for(l=0;l<16;l++) { + m = (double)l / 15.0; + for(i=0;i<65536;i++) { + r = (i ) & 31; + g = (i >> 5) & 31; + b = (i >> 10) & 31; + if(l == 0) { r = g = b = 0; } + else if(l == 15); + else { + r = (double)r * m; + g = (double)g * m; + b = (double)b * m; + } + *ptr++ = (r) | (g << 5) | (b << 10); + } + } + } + ppu_clear_pixel_cache(); + memset(ppu.screen, 0, 512 * 480 * 2); + memset(ppu.vram, 0, 0x10000); + memset(ppu.cgram, 0, 512); + memset(ppu.oam, 0, 544); + ppu.ppu_cycles = 0; + ppu.ppu_prev_cycles = 0; + ppu.display_disable = true; + ppu.display_brightness = 15; + ppu.visible_scanlines = 224; + ppu.toggle_visible_scanlines = 224; + + ppu.interlace = false; + ppu.interlace_frame = 0; + ppu.hline_pos = 0; + ppu.vline_pos = 261; + ppu.irq_triggered = false; + ppu.virq_triggered = false; + ppu.hirq_triggered = false; + ppu.vram_write_pos = 0; + ppu.cgram_write_pos = 0; + ppu.wram_write_pos = 0; + ppu.vram_inc_size = 2; + ppu.vram_inc_reg = 0; + ppu.oam_write_pos = 0; + ppu.oam_tiledata_loc = 0; + + ppu.bg_enabled[OAM] = false; + ppu.ss_bg_enabled[OAM] = false; + ppu.mosaic_size = 0; + ppu.mosaic_enabled[BG4] = false; + ppu.mosaic_enabled[BG3] = false; + ppu.mosaic_enabled[BG2] = false; + ppu.mosaic_enabled[BG1] = false; + ppu.bg_windowing_enabled[OAM] = false; + ppu.ss_bg_windowing_enabled[OAM] = false; + ppu.bg_window1_enabled[OAM] = false; + ppu.bg_window2_enabled[OAM] = false; + ppu.bg_window1_clipmode[OAM] = false; + ppu.bg_window2_clipmode[OAM] = false; + ppu.bg_window_mask[OAM] = false; + + for(i=0;i<4;i++) { + ppu.bg_enabled[i] = false; + ppu.ss_bg_enabled[i] = false; + ppu.bg_window1_enabled[i] = false; + ppu.bg_window2_enabled[i] = false; + ppu.bg_windowing_enabled[i] = false; + ppu.ss_bg_windowing_enabled[i] = false; + ppu.bg_window1_clipmode[i] = 0; + ppu.bg_window2_clipmode[i] = 0; + ppu.bg_window_mask[i] = 0; + ppu.bg_tilemap_loc[i] = 0; + ppu.bg_tiledata_loc[i] = 0; + ppu.bg_hscroll_pos[i] = 0; + ppu.bg_vscroll_pos[i] = 0; + } + ppu.bg_priority_mode = 0; + ppu.oam_base_size = 0; + ppu.oam_name_sel = 0; + ppu.bg_mode = 0; + + ppu.mul_a = 0; + ppu.mul_b = 0; + ppu.div_a = 0; + ppu.div_b = 0; + ppu.r_4214 = 0; + ppu.r_4216 = 0; + + ppu.smul_a = 0; + ppu.smul_b = 0; + ppu.smul_r = 0; + + ppu.window1_left = 0; + ppu.window1_right = 0; + ppu.window2_left = 0; + ppu.window2_right = 0; + + ppu.color_window1_enabled = 0; + ppu.color_window2_enabled = 0; + ppu.color_window1_clipmode = 0; + ppu.color_window2_clipmode = 0; + ppu.color_window_mask = 0; + ppu.color_mask = 0; + ppu.ss_color_mask = 0; + ppu.addsub_mode = 0; + ppu.color_mode = 0; + ppu.color_halve = 0; + for(i=0;i<6;i++) { + ppu.bg_color_enabled[i] = false; + } + ppu.color_r = 0; + ppu.color_g = 0; + ppu.color_b = 0; + + ppu.toggle_active_hdma_channels = 0; + ppu.active_hdma_channels = 0; + for(i=0;i<8;i++) { + memset(&dma_channel[i], 0, sizeof(dmachannel)); + ppu.hdma_completed[i] = false; + ppu.hdma_scanlines_remaining[i] = 0; + ppu.hdma_index_pointer[i] = 0; + } + + ppu.vcounter_enabled = false; + ppu.hcounter_enabled = false; + ppu.hirq_pos = 0; + ppu.virq_pos = 0; + + ppu.auto_joypad_read = false; + ppu.joypad_strobe_value = 0; + + ppu.latch_toggle = 0; + ppu.latch_vpos = 0; + ppu.latch_hpos = 0; + + ppu.m7a = ppu.m7b = + ppu.m7c = ppu.m7d = + ppu.m7x = ppu.m7y = 0; + + ppu.m7hofs = ppu.m7vofs = 0x0000; + + ppu.mode7_hflip = false; + ppu.mode7_vflip = false; +} diff --git a/bsnes/ppu/ppu_addsub.cpp b/bsnes/ppu/ppu_addsub.cpp new file mode 100644 index 00000000..bd711235 --- /dev/null +++ b/bsnes/ppu/ppu_addsub.cpp @@ -0,0 +1,60 @@ +/* + $2130 : fixed color / screen add sub + abcd--ef + + ab: main + cd: subscreen + 00: all the time + 01: inside window only + 10: outside window only + 11: all the time + e: 0 = enable addsub for fixed color + 1 = enable addsub for sub screen + f: color / chardata = direct color + (mode3, 4, 7 only) +*/ +void mmio_w2130(byte value) { + ppu.color_mask = (value >> 6) & 3; + ppu.ss_color_mask = (value >> 4) & 3; + ppu.addsub_mode = (value & 0x02)?1:0; +} + +/* + $2131 : addsub designation + mrgsdcba + + m: 0 = enable add color data mode + 1 = enable sub color data mode + r: 1/2 color mode + g: Backarea enable + s: OAM enable + d: BG4 enable + c: BG3 enable + b: BG2 enable + a: BG1 enable +*/ +void mmio_w2131(byte value) { + ppu.color_mode = (value & 0x80)?COLORMODE_SUB:COLORMODE_ADD; + ppu.color_halve = (value & 0x40)?1:0; + ppu.bg_color_enabled[BACK] = (value & 0x20)?true:false; + ppu.bg_color_enabled[OAM] = (value & 0x10)?true:false; + ppu.bg_color_enabled[BG4] = (value & 0x08)?true:false; + ppu.bg_color_enabled[BG3] = (value & 0x04)?true:false; + ppu.bg_color_enabled[BG2] = (value & 0x02)?true:false; + ppu.bg_color_enabled[BG1] = (value & 0x01)?true:false; +} + +/* + $2132 : addsub settings + bgrddddd + + b: affect blue + g: affect green + r: affect red + d: color constant +*/ +void mmio_w2132(byte value) { + if(value & 0x80)ppu.color_b = (value & 0x1f); + if(value & 0x40)ppu.color_g = (value & 0x1f); + if(value & 0x20)ppu.color_r = (value & 0x1f); +} diff --git a/bsnes/ppu/ppu_cache.cpp b/bsnes/ppu/ppu_cache.cpp new file mode 100644 index 00000000..591db059 --- /dev/null +++ b/bsnes/ppu/ppu_cache.cpp @@ -0,0 +1,49 @@ +#define BLENDTYPE_BACK 0 +#define BLENDTYPE_MAIN 1 +#define BLENDTYPE_SUB 2 +#define BLENDTYPE_COMBINE 3 + +#define COLORDEPTH_4 0 +#define COLORDEPTH_16 1 +#define COLORDEPTH_256 2 + +struct { + byte color_main, color_sub; + byte src_main, src_sub; + byte blend_type; +}ppu_pixel_cache[512]; + +#define TILE_2BIT 0 +#define TILE_4BIT 1 +#define TILE_8BIT 2 + +byte *ppu_bg_tiledata[3]; +byte *ppu_bg_tiledata_state[3]; + +//this should be reset once every scanline +void ppu_clear_pixel_cache(void) { +int i; + for(i=0;i<512;i++) { + ppu_pixel_cache[i].color_main = + ppu_pixel_cache[i].color_sub = 0; + ppu_pixel_cache[i].src_main = + ppu_pixel_cache[i].src_sub = BACK; + ppu_pixel_cache[i].blend_type = BLENDTYPE_BACK; + } +} + +void ppu_init_tiledata_cache(void) { +int i; + ppu_bg_tiledata[TILE_2BIT] = (byte*)malloc(262144); + ppu_bg_tiledata[TILE_4BIT] = (byte*)malloc(131072); + ppu_bg_tiledata[TILE_8BIT] = (byte*)malloc( 65536); + ppu_bg_tiledata_state[TILE_2BIT] = (byte*)malloc( 4096); + ppu_bg_tiledata_state[TILE_4BIT] = (byte*)malloc( 2048); + ppu_bg_tiledata_state[TILE_8BIT] = (byte*)malloc( 1024); + memset(ppu_bg_tiledata[TILE_2BIT], 0, 262144); + memset(ppu_bg_tiledata[TILE_4BIT], 0, 131072); + memset(ppu_bg_tiledata[TILE_4BIT], 0, 65536); + memset(ppu_bg_tiledata_state[TILE_2BIT], 0, 4096); + memset(ppu_bg_tiledata_state[TILE_4BIT], 0, 2048); + memset(ppu_bg_tiledata_state[TILE_8BIT], 0, 1024); +} diff --git a/bsnes/ppu/ppu_dma.cpp b/bsnes/ppu/ppu_dma.cpp new file mode 100644 index 00000000..66f2812f --- /dev/null +++ b/bsnes/ppu/ppu_dma.cpp @@ -0,0 +1,485 @@ +#define DMATRANSFER_CPUTOMMIO 0 +#define DMATRANSFER_MMIOTOCPU 1 + +#define DMAINDEX_ABSOLUTE 0 +#define DMAINDEX_INDIRECT 1 + +#define DMAWRITE_INC 0 +#define DMAWRITE_DEC 1 + +#define HDMAMODE_NORMAL 0 +#define HDMAMODE_CONTINUOUS 1 + +typedef struct { + byte transfer_mode; + byte index_mode; + bool fixed_address; + byte write_dir; + byte transfer_type; + word dest_addr; + ulong src_addr; + word transfer_size; + byte indirect_bank_address; + byte hdma_mode; + word hdma_indirect_pointer; +}dmachannel; + +dmachannel dma_channel[8]; + +/* + $43x0 : DMA control + da-ifttt + + d: (dma only) + 0=read from cpu mem, write to $21?? + 1=read from $21??, write to cpu mem + a: (hdma only) + 0=absolute addressing + 1=indirect addressing + f: 1=fixed address, 0=inc/dec address + i: 0=increment address, 1=decrement address + t: transfer type + 000: 1 reg 1 write (01->2118 23->2118 45->2118 67->2118 89->2118) + 001: 2 regs 1 write (01->2118 23->2119 45->2118 67->2119 89->2118) + 010: 1 reg 2 writes (01->2118 23->2118 45->2118 67->2118 89->2118) + 011: 2 regs 2 writes (01->2118 23->2118 45->2119 67->2119 89->2118) + 100: 4 regs 1 write (01->2118 23->2119 45->211a 67->211b 89->2118) + 101-111: unknown +*/ +void mmio_w43x0(byte c, byte value) { + dma_channel[c].transfer_mode = (value & 0x80)?DMATRANSFER_MMIOTOCPU:DMATRANSFER_CPUTOMMIO; + dma_channel[c].index_mode = (value & 0x40)?DMAINDEX_INDIRECT:DMAINDEX_ABSOLUTE; + dma_channel[c].write_dir = (value & 0x10)?DMAWRITE_DEC:DMAWRITE_INC; + dma_channel[c].fixed_address = (value & 0x08)?true:false; + dma_channel[c].transfer_type = (value & 0x07); +} + +/* + $43x1 : DMA destination address + bbbbbbbb + + b: $2100 | b = destination register - limited to $21xx regs only +*/ +void mmio_w43x1(byte c, byte value) { + dma_channel[c].dest_addr = 0x2100 | value; +} + +/* + $43x2-$43x4 : 24-bit DMA source address + + after a dma transfer, this address must be incremented +*/ +void mmio_w43x2(byte c, byte value) { + dma_channel[c].src_addr = (dma_channel[c].src_addr & 0xffff00) | value; +} + +void mmio_w43x3(byte c, byte value) { + dma_channel[c].src_addr = (dma_channel[c].src_addr & 0xff00ff) | (value << 8); +} + +void mmio_w43x4(byte c, byte value) { + dma_channel[c].src_addr = (dma_channel[c].src_addr & 0x00ffff) | (value << 16); +} + +/* + $43x5/$43x6 : DMA transfer size +*/ +void mmio_w43x5(byte c, byte value) { + dma_channel[c].transfer_size = (dma_channel[c].transfer_size & 0xff00) | value; +} + +void mmio_w43x6(byte c, byte value) { + dma_channel[c].transfer_size = (dma_channel[c].transfer_size & 0x00ff) | (value << 8); +} + +/* + $43x7 : HDMA indirect bank address +*/ +void mmio_w43x7(byte c, byte value) { + dma_channel[c].indirect_bank_address = value; +} + +void dma_cputommio(byte c, byte a) { +byte x; + x = gx816->mem_read(MEMMODE_LONG, MEMSIZE_BYTE, dma_channel[c].src_addr); + mmio_write(dma_channel[c].dest_addr + a, x); + if(dma_channel[c].fixed_address == false) { + if(dma_channel[c].write_dir == DMAWRITE_INC)dma_channel[c].src_addr++; + if(dma_channel[c].write_dir == DMAWRITE_DEC)dma_channel[c].src_addr--; + } + dma_channel[c].transfer_size--; +} + +void dma_mmiotocpu(byte c, byte a) { +byte x; + x = mmio_read(dma_channel[c].dest_addr + a); + gx816->mem_write(MEMMODE_LONG, MEMSIZE_BYTE, dma_channel[c].src_addr, x); + if(dma_channel[c].fixed_address == false) { + if(dma_channel[c].write_dir == DMAWRITE_INC)dma_channel[c].src_addr++; + if(dma_channel[c].write_dir == DMAWRITE_DEC)dma_channel[c].src_addr--; + } + dma_channel[c].transfer_size--; +} + +void dma_transfer_type0(byte c) { + if(dma_channel[c].transfer_mode == DMATRANSFER_CPUTOMMIO) { + while(1) { + dma_cputommio(c, 0); if(dma_channel[c].transfer_size == 0)break; + } + } else if(dma_channel[c].transfer_mode == DMATRANSFER_MMIOTOCPU) { + while(1) { + dma_mmiotocpu(c, 0); if(dma_channel[c].transfer_size == 0)break; + } + } +} + +void dma_transfer_type1(byte c) { + if(dma_channel[c].transfer_mode == DMATRANSFER_CPUTOMMIO) { + while(1) { + dma_cputommio(c, 0); if(dma_channel[c].transfer_size == 0)break; + dma_cputommio(c, 1); if(dma_channel[c].transfer_size == 0)break; + } + } else if(dma_channel[c].transfer_mode == DMATRANSFER_MMIOTOCPU) { + while(1) { + dma_mmiotocpu(c, 0); if(dma_channel[c].transfer_size == 0)break; + dma_mmiotocpu(c, 1); if(dma_channel[c].transfer_size == 0)break; + } + } +} + +void dma_transfer_type2(byte c) { + if(dma_channel[c].transfer_mode == DMATRANSFER_CPUTOMMIO) { + while(1) { + dma_cputommio(c, 0); if(dma_channel[c].transfer_size == 0)break; + } + } else if(dma_channel[c].transfer_mode == DMATRANSFER_MMIOTOCPU) { + while(1) { + dma_mmiotocpu(c, 0); if(dma_channel[c].transfer_size == 0)break; + } + } +} + +void dma_transfer_type3(byte c) { + if(dma_channel[c].transfer_mode == DMATRANSFER_CPUTOMMIO) { + while(1) { + dma_cputommio(c, 0); if(dma_channel[c].transfer_size == 0)break; + dma_cputommio(c, 0); if(dma_channel[c].transfer_size == 0)break; + dma_cputommio(c, 1); if(dma_channel[c].transfer_size == 0)break; + dma_cputommio(c, 1); if(dma_channel[c].transfer_size == 0)break; + } + } else if(dma_channel[c].transfer_mode == DMATRANSFER_MMIOTOCPU) { + while(1) { + dma_mmiotocpu(c, 0); if(dma_channel[c].transfer_size == 0)break; + dma_mmiotocpu(c, 0); if(dma_channel[c].transfer_size == 0)break; + dma_mmiotocpu(c, 1); if(dma_channel[c].transfer_size == 0)break; + dma_mmiotocpu(c, 1); if(dma_channel[c].transfer_size == 0)break; + } + } +} + +void dma_transfer_type4(byte c) { + if(dma_channel[c].transfer_mode == DMATRANSFER_CPUTOMMIO) { + while(1) { + dma_cputommio(c, 0); if(dma_channel[c].transfer_size == 0)break; + dma_cputommio(c, 1); if(dma_channel[c].transfer_size == 0)break; + dma_cputommio(c, 2); if(dma_channel[c].transfer_size == 0)break; + dma_cputommio(c, 3); if(dma_channel[c].transfer_size == 0)break; + } + } else if(dma_channel[c].transfer_mode == DMATRANSFER_MMIOTOCPU) { + while(1) { + dma_mmiotocpu(c, 0); if(dma_channel[c].transfer_size == 0)break; + dma_mmiotocpu(c, 1); if(dma_channel[c].transfer_size == 0)break; + dma_mmiotocpu(c, 2); if(dma_channel[c].transfer_size == 0)break; + dma_mmiotocpu(c, 3); if(dma_channel[c].transfer_size == 0)break; + } + } +} + +/* + $420b : DMA enable + $420c : HDMA enable + + each bit corresponds to the respecting DMA channel (7,6,5,4,3,2,1,0) + setting a bit in this register will perform the DMA transfer. multiple + transfers can be done at once. requires one cycle per byte transferred + hdma bits are sticky (they are not cleared automatically after write) +*/ +void mmio_w420b(byte value) { +int i; + for(i=0;i<8;i++) { + if(value & (1 << i)) { + snes_time->add_cpu_cycles(dma_channel[i].transfer_size * 6); + if (dma_channel[i].transfer_type == 0)dma_transfer_type0(i); + else if(dma_channel[i].transfer_type == 1)dma_transfer_type1(i); + else if(dma_channel[i].transfer_type == 2)dma_transfer_type2(i); + else if(dma_channel[i].transfer_type == 3)dma_transfer_type3(i); + else if(dma_channel[i].transfer_type == 4)dma_transfer_type4(i); + else dprintf("* mmio_w420b(): Unknown DMA transfer type: %d", dma_channel[i].transfer_type); + } + } +} + +void mmio_w420c(byte value) { +//don't actually enable hdma channels until the start of the next frame + ppu.toggle_active_hdma_channels = value; +} + +byte hdma_read_absolute(byte c) { +ulong x; + x = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dma_channel[c].src_addr + ppu.hdma_index_pointer[c]); + ppu.hdma_index_pointer[c]++; + return x; +} + +byte hdma_read_indirect(byte c) { +ulong x; + x = gx816->mem_read(MEMMODE_NONE, MEMSIZE_WORD, dma_channel[c].src_addr + ppu.hdma_index_pointer[c]); + x += dma_channel[c].hdma_indirect_pointer; + x = (dma_channel[c].indirect_bank_address << 16) | (x & 0xffff); + x = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, x); + dma_channel[c].hdma_indirect_pointer++; + return x; +} + +void hdma_transfer_type0_absolute(byte c) { + mmio_write(dma_channel[c].dest_addr, hdma_read_absolute(c)); +} + +void hdma_transfer_type0_indirect(byte c) { + mmio_write(dma_channel[c].dest_addr, hdma_read_indirect(c)); + if(dma_channel[c].hdma_mode == HDMAMODE_NORMAL)dma_channel[c].hdma_indirect_pointer -= 1; +} + +void hdma_transfer_type1_absolute(byte c) { + mmio_write(dma_channel[c].dest_addr, hdma_read_absolute(c)); + mmio_write(dma_channel[c].dest_addr + 1, hdma_read_absolute(c)); +} + +void hdma_transfer_type1_indirect(byte c) { + mmio_write(dma_channel[c].dest_addr, hdma_read_indirect(c)); + mmio_write(dma_channel[c].dest_addr + 1, hdma_read_indirect(c)); + if(dma_channel[c].hdma_mode == HDMAMODE_NORMAL)dma_channel[c].hdma_indirect_pointer -= 2; +} + +void hdma_transfer_type2_absolute(byte c) { + mmio_write(dma_channel[c].dest_addr, hdma_read_absolute(c)); + mmio_write(dma_channel[c].dest_addr, hdma_read_absolute(c)); +} + +void hdma_transfer_type2_indirect(byte c) { + mmio_write(dma_channel[c].dest_addr, hdma_read_indirect(c)); + mmio_write(dma_channel[c].dest_addr, hdma_read_indirect(c)); + if(dma_channel[c].hdma_mode == HDMAMODE_NORMAL)dma_channel[c].hdma_indirect_pointer -= 2; +} + +void hdma_transfer_type3_absolute(byte c) { + mmio_write(dma_channel[c].dest_addr, hdma_read_absolute(c)); + mmio_write(dma_channel[c].dest_addr, hdma_read_absolute(c)); + mmio_write(dma_channel[c].dest_addr + 1, hdma_read_absolute(c)); + mmio_write(dma_channel[c].dest_addr + 1, hdma_read_absolute(c)); +} + +void hdma_transfer_type3_indirect(byte c) { + mmio_write(dma_channel[c].dest_addr, hdma_read_indirect(c)); + mmio_write(dma_channel[c].dest_addr, hdma_read_indirect(c)); + mmio_write(dma_channel[c].dest_addr + 1, hdma_read_indirect(c)); + mmio_write(dma_channel[c].dest_addr + 1, hdma_read_indirect(c)); + if(dma_channel[c].hdma_mode == HDMAMODE_NORMAL)dma_channel[c].hdma_indirect_pointer -= 4; +} + +void hdma_transfer_type4_absolute(byte c) { + mmio_write(dma_channel[c].dest_addr, hdma_read_absolute(c)); + mmio_write(dma_channel[c].dest_addr + 1, hdma_read_absolute(c)); + mmio_write(dma_channel[c].dest_addr + 2, hdma_read_absolute(c)); + mmio_write(dma_channel[c].dest_addr + 3, hdma_read_absolute(c)); +} + +void hdma_transfer_type4_indirect(byte c) { + mmio_write(dma_channel[c].dest_addr, hdma_read_indirect(c)); + mmio_write(dma_channel[c].dest_addr + 1, hdma_read_indirect(c)); + mmio_write(dma_channel[c].dest_addr + 2, hdma_read_indirect(c)); + mmio_write(dma_channel[c].dest_addr + 3, hdma_read_indirect(c)); + if(dma_channel[c].hdma_mode == HDMAMODE_NORMAL)dma_channel[c].hdma_indirect_pointer -= 4; +} + +byte hdma_transfer_type_size_table[8] = { 1, 2, 2, 4, 4, 0, 0, 0 }; + +void HDMAFirstWrite(byte c) { + if(dma_channel[c].hdma_mode == HDMAMODE_NORMAL && ppu.hdma_scanlines_remaining[c] == 0x00) { + return; + } + if(dma_channel[c].hdma_mode == HDMAMODE_CONTINUOUS && ppu.hdma_scanlines_remaining[c] == 0x80) { + return; + } + + if(dma_channel[c].index_mode == DMAINDEX_ABSOLUTE) { + if (dma_channel[c].transfer_type == 0)hdma_transfer_type0_absolute(c); + else if(dma_channel[c].transfer_type == 1)hdma_transfer_type1_absolute(c); + else if(dma_channel[c].transfer_type == 2)hdma_transfer_type2_absolute(c); + else if(dma_channel[c].transfer_type == 3)hdma_transfer_type3_absolute(c); + else if(dma_channel[c].transfer_type == 4)hdma_transfer_type4_absolute(c); + else dprintf("* mmio_w420c(): Unknown HDMA transfer type: %d", dma_channel[c].transfer_type); + } else { //indirect + if (dma_channel[c].transfer_type == 0)hdma_transfer_type0_indirect(c); + else if(dma_channel[c].transfer_type == 1)hdma_transfer_type1_indirect(c); + else if(dma_channel[c].transfer_type == 2)hdma_transfer_type2_indirect(c); + else if(dma_channel[c].transfer_type == 3)hdma_transfer_type3_indirect(c); + else if(dma_channel[c].transfer_type == 4)hdma_transfer_type4_indirect(c); + else dprintf("* mmio_w420c(): Unknown HDMA transfer type: %d", dma_channel[c].transfer_type); + } +} + +void UpdateHDMAAbsoluteNormal(byte c) { + if(ppu.hdma_scanlines_remaining[c] == 0x00) { + ppu.hdma_scanlines_remaining[c] = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dma_channel[c].src_addr + ppu.hdma_index_pointer[c]); + + if(ppu.hdma_scanlines_remaining[c] == 0) { + ppu.hdma_completed[c] = true; + return; + } + + dma_channel[c].hdma_indirect_pointer = 0; + if(ppu.hdma_scanlines_remaining[c] > 0x80) { + dma_channel[c].hdma_mode = HDMAMODE_CONTINUOUS; + } else { + dma_channel[c].hdma_mode = HDMAMODE_NORMAL; + } + + ppu.hdma_index_pointer[c]++; + HDMAFirstWrite(c); + } + + ppu.hdma_scanlines_remaining[c]--; +} + +void UpdateHDMAAbsoluteContinuous(byte c) { + if(ppu.hdma_scanlines_remaining[c] == 0x80) { + ppu.hdma_scanlines_remaining[c] = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dma_channel[c].src_addr + ppu.hdma_index_pointer[c]); + + if(ppu.hdma_scanlines_remaining[c] == 0) { + ppu.hdma_completed[c] = true; + return; + } + + dma_channel[c].hdma_indirect_pointer = 0; + if(ppu.hdma_scanlines_remaining[c] > 0x80) { + dma_channel[c].hdma_mode = HDMAMODE_CONTINUOUS; + } else { + dma_channel[c].hdma_mode = HDMAMODE_NORMAL; + } + + ppu.hdma_index_pointer[c]++; + HDMAFirstWrite(c); + ppu.hdma_scanlines_remaining[c]--; + } else { + if (dma_channel[c].transfer_type == 0)hdma_transfer_type0_absolute(c); + else if(dma_channel[c].transfer_type == 1)hdma_transfer_type1_absolute(c); + else if(dma_channel[c].transfer_type == 2)hdma_transfer_type2_absolute(c); + else if(dma_channel[c].transfer_type == 3)hdma_transfer_type3_absolute(c); + else if(dma_channel[c].transfer_type == 4)hdma_transfer_type4_absolute(c); + else dprintf("* mmio_w420c(): Unknown HDMA transfer type: %d", dma_channel[c].transfer_type); + + ppu.hdma_scanlines_remaining[c]--; + } +} + +void UpdateHDMAIndirectNormal(byte c) { + if(ppu.hdma_scanlines_remaining[c] == 0x00) { + ppu.hdma_index_pointer[c] += 2; + ppu.hdma_scanlines_remaining[c] = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dma_channel[c].src_addr + ppu.hdma_index_pointer[c]); + + if(ppu.hdma_scanlines_remaining[c] == 0) { + ppu.hdma_completed[c] = true; + return; + } + + dma_channel[c].hdma_indirect_pointer = 0; + if(ppu.hdma_scanlines_remaining[c] > 0x80) { + dma_channel[c].hdma_mode = HDMAMODE_CONTINUOUS; + } else { + dma_channel[c].hdma_mode = HDMAMODE_NORMAL; + } + + ppu.hdma_index_pointer[c]++; + HDMAFirstWrite(c); + } + + ppu.hdma_scanlines_remaining[c]--; +} + +void UpdateHDMAIndirectContinuous(byte c) { + if(ppu.hdma_scanlines_remaining[c] == 0x80) { + ppu.hdma_index_pointer[c] += 2; + ppu.hdma_scanlines_remaining[c] = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dma_channel[c].src_addr + ppu.hdma_index_pointer[c]); + + if(ppu.hdma_scanlines_remaining[c] == 0) { + ppu.hdma_completed[c] = true; + return; + } + + dma_channel[c].hdma_indirect_pointer = 0; + if(ppu.hdma_scanlines_remaining[c] > 0x80) { + dma_channel[c].hdma_mode = HDMAMODE_CONTINUOUS; + } else { + dma_channel[c].hdma_mode = HDMAMODE_NORMAL; + } + ppu.hdma_index_pointer[c]++; + HDMAFirstWrite(c); + ppu.hdma_scanlines_remaining[c]--; + } else { + if (dma_channel[c].transfer_type == 0)hdma_transfer_type0_indirect(c); + else if(dma_channel[c].transfer_type == 1)hdma_transfer_type1_indirect(c); + else if(dma_channel[c].transfer_type == 2)hdma_transfer_type2_indirect(c); + else if(dma_channel[c].transfer_type == 3)hdma_transfer_type3_indirect(c); + else if(dma_channel[c].transfer_type == 4)hdma_transfer_type4_indirect(c); + else dprintf("* mmio_w420c(): Unknown HDMA transfer type: %d", dma_channel[c].transfer_type); + + ppu.hdma_scanlines_remaining[c]--; + } +} + +void UpdateHDMA(void) { +int c; + for(c=0;c<8;c++) { + if(ppu.active_hdma_channels & (1 << c)) { + if(ppu.vline_pos == 0)continue; + if(ppu.vline_pos == 1) { + ppu.hdma_completed[c] = false; + ppu.hdma_index_pointer[c] = 0; + ppu.hdma_scanlines_remaining[c] = gx816->mem_read(MEMMODE_NONE, MEMSIZE_BYTE, dma_channel[c].src_addr); + ppu.hdma_index_pointer[c]++; + + if(ppu.hdma_scanlines_remaining[c] == 0) { + ppu.hdma_completed[c] = true; + continue; + } + + dma_channel[c].hdma_indirect_pointer = 0; + if(ppu.hdma_scanlines_remaining[c] > 0x80) { + dma_channel[c].hdma_mode = HDMAMODE_CONTINUOUS; + } else { + dma_channel[c].hdma_mode = HDMAMODE_NORMAL; + } + + HDMAFirstWrite(c); + ppu.hdma_scanlines_remaining[c]--; + } else { + if(ppu.hdma_completed[c] == true)continue; + + if(dma_channel[c].index_mode == DMAINDEX_ABSOLUTE) { + if(dma_channel[c].hdma_mode == HDMAMODE_NORMAL) { + UpdateHDMAAbsoluteNormal(c); + } else { + UpdateHDMAAbsoluteContinuous(c); + } + } else { //indirect + if(dma_channel[c].hdma_mode == HDMAMODE_NORMAL) { + UpdateHDMAIndirectNormal(c); + } else { + UpdateHDMAIndirectContinuous(c); + } + } + } + } + } +} diff --git a/bsnes/ppu/ppu_joypad.cpp b/bsnes/ppu/ppu_joypad.cpp new file mode 100644 index 00000000..082325d0 --- /dev/null +++ b/bsnes/ppu/ppu_joypad.cpp @@ -0,0 +1,67 @@ +joypad_state joypad1; + +/* + *1 - The joypad contains a small bit shifter that has 16 bits. + Reading from 4016 reads one bit from this buffer, then moves + the buffer left one, and adds a '1' to the rightmost bit. + Writing a one to $4016 will fill the buffer with the current + joypad button states, and lock the bit shifter at position + zero. All reads will be the first buffer state, or 'B'. + A zero must be written back to $4016 to unlock the buffer, + so that reads will increment the bit shifting position. +*/ +byte mmio_r4016(void) { +byte r = 0; + if(ppu.joypad_strobe_value == 1) { //*1 + r |= joypad1.b; + } else { + if (joypad1.read_pos == 0)r |= joypad1.b; + else if(joypad1.read_pos == 1)r |= joypad1.y; + else if(joypad1.read_pos == 2)r |= joypad1.select; + else if(joypad1.read_pos == 3)r |= joypad1.start; + else if(joypad1.read_pos == 4)r |= joypad1.up; + else if(joypad1.read_pos == 5)r |= joypad1.down; + else if(joypad1.read_pos == 6)r |= joypad1.left; + else if(joypad1.read_pos == 7)r |= joypad1.right; + else if(joypad1.read_pos == 8)r |= joypad1.a; + else if(joypad1.read_pos == 9)r |= joypad1.x; + else if(joypad1.read_pos == 10)r |= joypad1.l; + else if(joypad1.read_pos == 11)r |= joypad1.r; + else if(joypad1.read_pos == 16)r |= 1; //joypad connected bit (1=yes, 0=no) + else r |= 1; //after 16th read, all subsequent reads return 1 + if(++joypad1.read_pos > 17)joypad1.read_pos = 17; + } + return r; +} + +byte mmio_r4218(void) { +byte r; + if(ppu.auto_joypad_read == false) return 0x00; //cannot read joypad if auto joypad read not enabled + if(ppu.vline_pos >= 225 && ppu.vline_pos <= 227)return 0x00; //cannot read joypad while SNES is polling the joypad data + r = joypad1.a << 7 | + joypad1.x << 6 | + joypad1.l << 5 | + joypad1.r << 4; + return r; +} + +byte mmio_r4219(void) { +byte r; + if(ppu.auto_joypad_read == false) return 0x00; //cannot read joypad if auto joypad read not enabled + if(ppu.vline_pos >= 225 && ppu.vline_pos <= 227)return 0x00; //cannot read joypad while SNES is polling the joypad data + r = joypad1.b << 7 | + joypad1.y << 6 | + joypad1.select << 5 | + joypad1.start << 4 | + joypad1.up << 3 | + joypad1.down << 2 | + joypad1.left << 1 | + joypad1.right; + return r; +} + +void mmio_w4016(byte value) { + ppu.joypad_strobe_value = value; + if(value == 1)UpdateJoypad(); + if(value == 0)joypad1.read_pos = 0; +} diff --git a/bsnes/ppu/ppu_misc.cpp b/bsnes/ppu/ppu_misc.cpp new file mode 100644 index 00000000..80d1cf91 --- /dev/null +++ b/bsnes/ppu/ppu_misc.cpp @@ -0,0 +1,15 @@ +/* + $21c2/$21c3 + + These seem to be version information registers... I don't know + what their purpose is, but I do know that the SNES demo rom + expects these values to be returned in order to proceed through + the character test. +*/ +byte mmio_r21c2(void) { + return 0x20; +} + +byte mmio_r21c3(void) { + return 0x00; +} diff --git a/bsnes/ppu/ppu_mode7.cpp b/bsnes/ppu/ppu_mode7.cpp new file mode 100644 index 00000000..302eed08 --- /dev/null +++ b/bsnes/ppu/ppu_mode7.cpp @@ -0,0 +1,74 @@ +/* + $211a : mode7 settings register + ab0000yx + + ab: + 00 = use screen repetition if outside screen area + 01 = ??? + 10 = use character 0x00 repetition if outside screen area + 11 = use back color if outside screen area + y: vertical screen flip + x: horizontal screen flip +*/ +void mmio_w211a(byte value) { + ppu.mode7_vflip = (value & 0x02)?true:false; + ppu.mode7_hflip = (value & 0x01)?true:false; +} + +byte m7_latch = 0x00; + +/* + $211b : m7a / 16-bit source operand for signed multiplication +*/ +void mmio_w211b(byte value) { +static byte latch = 0; + ppu.m7a = (value << 8) | m7_latch; + m7_latch = value; + + if(!latch) { ppu.smul_a = (ppu.smul_a & 0xff00) | value; } + else { ppu.smul_a = (ppu.smul_a & 0x00ff) | (value << 8); } + + latch ^= 1; +} + +/* + $211c : m7b / 8-bit source operand for signed multiplication +*/ +void mmio_w211c(byte value) { + ppu.m7b = (value << 8) | m7_latch; + m7_latch = value; + + ppu.smul_b = value; +} + +/* + $211d : m7c +*/ +void mmio_w211d(byte value) { + ppu.m7c = (value << 8) | m7_latch; + m7_latch = value; +} + +/* + $211e : m7d +*/ +void mmio_w211e(byte value) { + ppu.m7d = (value << 8) | m7_latch; + m7_latch = value; +} + +/* + $211f : m7x +*/ +void mmio_w211f(byte value) { + ppu.m7x = (value << 8) | m7_latch; + m7_latch = value; +} + +/* + $2120 : m7y +*/ +void mmio_w2120(byte value) { + ppu.m7y = (value << 8) | m7_latch; + m7_latch = value; +} diff --git a/bsnes/ppu/ppu_muldiv.cpp b/bsnes/ppu/ppu_muldiv.cpp new file mode 100644 index 00000000..731be27b --- /dev/null +++ b/bsnes/ppu/ppu_muldiv.cpp @@ -0,0 +1,57 @@ +byte mmio_r2134(void) { +ulong r; + r = (signed short)ppu.smul_a * (signed char)ppu.smul_b; + return r & 0xff; +} + +byte mmio_r2135(void) { +ulong r; + r = (signed short)ppu.smul_a * (signed char)ppu.smul_b; + return (r >> 8) & 0xff; +} + +byte mmio_r2136(void) { +ulong r; + r = (signed short)ppu.smul_a * (signed char)ppu.smul_b; + return (r >> 16) & 0xff; +} + +void mmio_w4202(byte value) { + ppu.mul_a = value; +} + +void mmio_w4203(byte value) { + ppu.mul_b = value; + ppu.r_4216 = ppu.mul_a * ppu.mul_b; +} + +void mmio_w4204(byte value) { + ppu.div_a = (ppu.div_a & 0xff00) | value; +} + +void mmio_w4205(byte value) { + ppu.div_a = (ppu.div_a & 0x00ff) | (value << 8); +} + +void mmio_w4206(byte value) { + ppu.div_b = value; + + ppu.r_4214 = (ppu.div_b)?ppu.div_a / ppu.div_b : 0; + ppu.r_4216 = (ppu.div_b)?ppu.div_a % ppu.div_b : 0; +} + +byte mmio_r4214(void) { + return ppu.r_4214; +} + +byte mmio_r4215(void) { + return ppu.r_4214 >> 8; +} + +byte mmio_r4216(void) { + return ppu.r_4216; +} + +byte mmio_r4217(void) { + return ppu.r_4216 >> 8; +} diff --git a/bsnes/ppu/ppu_oam.cpp b/bsnes/ppu/ppu_oam.cpp new file mode 100644 index 00000000..162c6d83 --- /dev/null +++ b/bsnes/ppu/ppu_oam.cpp @@ -0,0 +1,86 @@ +/* + $2101 : OAM settings + sssnnbbb + + s: base sprite size + small : large + 000: 8x8 : 16x16 + 001: 8x8 : 32x32 + 010: 8x8 : 64x64 + 011: 16x16 : 32x32 + 100: 16x16 : 64x64 + 101: 32x32 : 64x64 + 110: 16x32 : 32x64 + 111: 16x32 : 32x32 + small/large is determined by oam size bit + n: name selection (0-3) + b: oam tiledata location (>>14) -- highest bit ignored +*/ +void mmio_w2101(byte value) { + ppu.oam_base_size = (value >> 5); + ppu.oam_name_sel = (value >> 3) & 3; + ppu.oam_tiledata_loc = (value & 3) << 14; +} + +/* + $2102/$2103 : OAM access address + $2102: llllllll + $2103: ???????h + + 9-bit address, h = bit 8, l = bits 7-0 +*/ +byte ppu_oam_write_posl = 0x00, ppu_oam_write_posh = 0x00; + +void mmio_w2102(byte value) { + ppu_oam_write_posl = value; + ppu.oam_write_pos = ((ppu_oam_write_posh << 8) | (ppu_oam_write_posl)) * 2; +} + +void mmio_w2103(byte value) { + ppu_oam_write_posh = value & 0x01; + ppu.oam_write_pos = ((ppu_oam_write_posh << 8) | (ppu_oam_write_posl)) * 2; +} + +byte ppu_oam_latch_data = 0; + +/* + $2104 : OAM write + + write one byte to OAM data. even writes (bit 0 = 0) are cached + to the OAM latch, and no data is transferred to OAM ram. odd writes + (bit 0 = 1) write the latch value, and then the requested value (2 bytes) + to oam data. writes to OAM address 0x0200 and above (priority / x bit 8 table) + always write, but even writes still update the latch data. +*/ +void mmio_w2104(byte value) { + if(ppu.oam_write_pos >= 0x0200) { + if((ppu.oam_write_pos & 1) == 0) { + ppu_oam_latch_data = value; + } + oam_write(ppu.oam_write_pos, value); + } else if((ppu.oam_write_pos & 1) == 0) { + ppu_oam_latch_data = value; + } else { + oam_write((ppu.oam_write_pos & 0x03fe), ppu_oam_latch_data); + oam_write((ppu.oam_write_pos & 0x03fe) + 1, value); + } + ppu.oam_write_pos++; + ppu.oam_write_pos &= 0x03ff; +} + +/* + $2138 : OAM read + + read one byte from OAM data. if address is even (bit 0 = 0), + latch data is updated. +*/ +byte mmio_r2138(void) { +byte r; + r = oam_read(ppu.oam_write_pos); + if((ppu.oam_write_pos & 1) == 0) { + ppu_oam_latch_data = r; + } + ppu.oam_write_pos++; + ppu.oam_write_pos &= 0x03ff; + return r; +} diff --git a/bsnes/ppu/ppu_palette.cpp b/bsnes/ppu/ppu_palette.cpp new file mode 100644 index 00000000..f8f3b142 --- /dev/null +++ b/bsnes/ppu/ppu_palette.cpp @@ -0,0 +1,36 @@ +/* + $2121 : cgram write position + + takes an 8-bit value that indexes into color palette cgram data. + multiply value by 2 to get actual offset into ppu.cgram +*/ +void mmio_w2121(byte value) { +//if(ppu.vline_pos < 224 && ppu.display_disable == false)return; + ppu.cgram_write_pos = value << 1; +} + +/* + $2122 : cgram write + + writes to cgram using cgram_write_pos * 2 as an index +*/ +void mmio_w2122(byte val) { +//if(ppu.vline_pos < 224 && ppu.display_disable == false)return; + ppu.cgram[ppu.cgram_write_pos] = val; + ppu.cgram_write_pos++; + ppu.cgram_write_pos &= 0x01ff; +} + +/* + $213b : cgram read + + read from cgram using cgram_write_pos * 2 as an index +*/ +byte mmio_r213b(void) { +byte r; +//if(ppu.vline_pos < 224 && ppu.display_disable == false)return; + r = ppu.cgram[ppu.cgram_write_pos]; + ppu.cgram_write_pos++; + ppu.cgram_write_pos &= 0x01ff; + return r; +} diff --git a/bsnes/ppu/ppu_render.cpp b/bsnes/ppu/ppu_render.cpp new file mode 100644 index 00000000..648760ee --- /dev/null +++ b/bsnes/ppu/ppu_render.cpp @@ -0,0 +1,753 @@ +byte ppu_addsub_adjust_buffer[96] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31 +}; + +#define ppu_pal_pixel(__i) \ + (*((word*)ppu.cgram + __i)) + +word ppu_addsub_pixels(byte x, byte cdest_index, byte cdest_bg, byte csrc_index, byte csrc_bg) { +int r, g, b; +byte hd = 0, hs = 0; +word cdest = ppu_pal_pixel(cdest_index); +word csrc = ppu_pal_pixel(csrc_index); +//oam palettes 0-3 are not affected by color add/sub + if(cdest_bg == OAM) { + if(cdest_index < 192) { + return cdest; + } + } + if(ppu.bg_color_enabled[cdest_bg] == true) { + hd = hs = ppu.color_halve; + } + switch(ppu.color_mode) { + case COLORMODE_ADD: + r = *(ppu_addsub_adjust_buffer + 32 + (( ((cdest ) & 31) >> hd) + ( ((csrc ) & 31) >> hs) )); + g = *(ppu_addsub_adjust_buffer + 32 + (( ((cdest >> 5) & 31) >> hd) + ( ((csrc >> 5) & 31) >> hs) )); + b = *(ppu_addsub_adjust_buffer + 32 + (( ((cdest >> 10) & 31) >> hd) + ( ((csrc >> 10) & 31) >> hs) )); + break; + case COLORMODE_SUB: + r = *(ppu_addsub_adjust_buffer + 32 + (( ((cdest ) & 31) >> hd) - ( ((csrc ) & 31) >> hs) )); + g = *(ppu_addsub_adjust_buffer + 32 + (( ((cdest >> 5) & 31) >> hd) - ( ((csrc >> 5) & 31) >> hs) )); + b = *(ppu_addsub_adjust_buffer + 32 + (( ((cdest >> 10) & 31) >> hd) - ( ((csrc >> 10) & 31) >> hs) )); + break; + } + return ((r) | (g << 5) | (b << 10)); +} + +word ppu_addsub_pixel(byte x, byte cdest_index, byte cdest_bg) { +int r, g, b; +byte hd = 0; +word cdest = ppu_pal_pixel(cdest_index); +//only oam palettes 4-7 are affected by color add/sub + if(cdest_bg == OAM) { + if(cdest_index < 192) { + return cdest; + } + } + if(ppu.bg_color_enabled[cdest_bg] == true) { + hd = ppu.color_halve; + } + switch(ppu.color_mode) { + case COLORMODE_ADD: + r = *(ppu_addsub_adjust_buffer + 32 + (( ((cdest ) & 31) >> hd) + (ppu.color_r >> hd) )); + g = *(ppu_addsub_adjust_buffer + 32 + (( ((cdest >> 5) & 31) >> hd) + (ppu.color_g >> hd) )); + b = *(ppu_addsub_adjust_buffer + 32 + (( ((cdest >> 10) & 31) >> hd) + (ppu.color_b >> hd) )); + break; + case COLORMODE_SUB: + r = *(ppu_addsub_adjust_buffer + 32 + (( ((cdest ) & 31) >> hd) - (ppu.color_r >> hd) )); + g = *(ppu_addsub_adjust_buffer + 32 + (( ((cdest >> 5) & 31) >> hd) - (ppu.color_g >> hd) )); + b = *(ppu_addsub_adjust_buffer + 32 + (( ((cdest >> 10) & 31) >> hd) - (ppu.color_b >> hd) )); + break; + } + return ((r) | (g << 5) | (b << 10)); +} + +#define ppu_render_bg_tile_line_4(__m) \ + col = 0; \ + if(d0 & __m)col += 1; \ + if(d1 & __m)col += 2; \ + *dest++ = col +#define ppu_render_bg_tile_line_16(__m) \ + col = 0; \ + if(d0 & __m)col += 1; \ + if(d1 & __m)col += 2; \ + if(d2 & __m)col += 4; \ + if(d3 & __m)col += 8; \ + *dest++ = col +#define ppu_render_bg_tile_line_256(__m) \ + col = 0; \ + if(d0 & __m)col += 1; \ + if(d1 & __m)col += 2; \ + if(d2 & __m)col += 4; \ + if(d3 & __m)col += 8; \ + if(d4 & __m)col += 16; \ + if(d5 & __m)col += 32; \ + if(d6 & __m)col += 64; \ + if(d7 & __m)col += 128; \ + *dest++ = col + +void ppu_render_bg_tile(byte color_depth, byte bg, word tile_num) { +byte mask, d0, d1, d2, d3, d4, d5, d6, d7, col; +int x, y; +ulong pos; +byte *dest; + switch(color_depth) { + case COLORDEPTH_4: + dest = (byte*)ppu_bg_tiledata[TILE_2BIT] + tile_num * 64; + pos = tile_num * 16; + y = 8; + while(y--) { + d0 = ppu.vram[pos ]; + d1 = ppu.vram[pos + 1]; + ppu_render_bg_tile_line_4(0x80); + ppu_render_bg_tile_line_4(0x40); + ppu_render_bg_tile_line_4(0x20); + ppu_render_bg_tile_line_4(0x10); + ppu_render_bg_tile_line_4(0x08); + ppu_render_bg_tile_line_4(0x04); + ppu_render_bg_tile_line_4(0x02); + ppu_render_bg_tile_line_4(0x01); + pos += 2; + } + ppu_bg_tiledata_state[TILE_2BIT][tile_num] = 0; + break; + case COLORDEPTH_16: + dest = (byte*)ppu_bg_tiledata[TILE_4BIT] + tile_num * 64; + pos = tile_num * 32; + y = 8; + while(y--) { + d0 = ppu.vram[pos ]; + d1 = ppu.vram[pos + 1]; + d2 = ppu.vram[pos + 16]; + d3 = ppu.vram[pos + 17]; + ppu_render_bg_tile_line_16(0x80); + ppu_render_bg_tile_line_16(0x40); + ppu_render_bg_tile_line_16(0x20); + ppu_render_bg_tile_line_16(0x10); + ppu_render_bg_tile_line_16(0x08); + ppu_render_bg_tile_line_16(0x04); + ppu_render_bg_tile_line_16(0x02); + ppu_render_bg_tile_line_16(0x01); + pos += 2; + } + ppu_bg_tiledata_state[TILE_4BIT][tile_num] = 0; + break; + case COLORDEPTH_256: + dest = (byte*)ppu_bg_tiledata[TILE_8BIT] + tile_num * 64; + pos = tile_num * 64; + y = 8; + while(y--) { + d0 = ppu.vram[pos ]; + d1 = ppu.vram[pos + 1]; + d2 = ppu.vram[pos + 16]; + d3 = ppu.vram[pos + 17]; + d4 = ppu.vram[pos + 32]; + d5 = ppu.vram[pos + 33]; + d6 = ppu.vram[pos + 48]; + d7 = ppu.vram[pos + 49]; + ppu_render_bg_tile_line_256(0x80); + ppu_render_bg_tile_line_256(0x40); + ppu_render_bg_tile_line_256(0x20); + ppu_render_bg_tile_line_256(0x10); + ppu_render_bg_tile_line_256(0x08); + ppu_render_bg_tile_line_256(0x04); + ppu_render_bg_tile_line_256(0x02); + ppu_render_bg_tile_line_256(0x01); + pos += 2; + } + ppu_bg_tiledata_state[TILE_8BIT][tile_num] = 0; + break; + } +} + +#define PPU_MAIN 0 +#define PPU_SUB 1 + +bool windows_not_obstructing(byte layer, byte bg, byte x); +bool color_windows_not_obstructing(byte x, byte color_mask_type); + +//light table is mirrored twice so that the high bit (bit 15) in the color +//is ignored, and does not cause color to reach into next light table. +#define ppu_write_pixel() \ + *(ptr + x) = *(light_table + cx) + +void ppu_render_line_to_screen(void) { +int x; +word *ptr, *light_table; +word c, cx, cy; +word screen_width = render.snes_width; + ptr = (word*)ppu.screen; + light_table = (word*)ppu.light_table + (ppu.display_brightness * 65536); + if(ppu.interlace == true) { + ptr += ((ppu.vline_pos * 2) + ppu.interlace_frame) * 512; + } else { + ptr += ppu.vline_pos * 512; + } + for(x=0;x<screen_width;x++) { + switch(ppu_pixel_cache[x].blend_type) { + case BLENDTYPE_BACK: + if(ppu.bg_color_enabled[BACK] == true && color_windows_not_obstructing(x, PPU_SUB) == false) { + cx = ppu_addsub_pixel(x, 0, BACK); + } else { + cx = ppu_pal_pixel(0); + } + ppu_write_pixel(); + break; + case BLENDTYPE_MAIN: + if(ppu.bg_color_enabled[ppu_pixel_cache[x].src_main] == true && color_windows_not_obstructing(x, PPU_SUB) == false) { + cx = ppu_addsub_pixel(x, ppu_pixel_cache[x].color_main, ppu_pixel_cache[x].src_main); + } else { + cx = ppu_pal_pixel(ppu_pixel_cache[x].color_main); + } + ppu_write_pixel(); + break; + case BLENDTYPE_SUB: + if(ppu.bg_color_enabled[BACK] && color_windows_not_obstructing(x, PPU_SUB) == false) { + cx = ppu_addsub_pixels(x, 0, BACK, ppu_pixel_cache[x].color_sub, ppu_pixel_cache[x].src_sub); + } else { + cx = ppu_pal_pixel(ppu_pixel_cache[x].color_sub); + } + ppu_write_pixel(); + break; + case BLENDTYPE_COMBINE: + if(color_windows_not_obstructing(x, PPU_SUB) == false) { + if(ppu_pixel_cache[x].src_sub == BACK) { + cx = ppu_addsub_pixels(x, ppu_pixel_cache[x].color_main, ppu_pixel_cache[x].src_main, 0, BACK); + } else { + cx = ppu_addsub_pixels(x, ppu_pixel_cache[x].color_main, ppu_pixel_cache[x].src_main, + ppu_pixel_cache[x].color_sub, ppu_pixel_cache[x].src_sub); + } + } else { + cx = ppu_pal_pixel(ppu_pixel_cache[x].color_main); + } + ppu_write_pixel(); + break; + } + } +} + +void ppu_set_pixel(byte bg, word x, byte pal_index) { + if(ppu.bg_enabled[bg] == true && ppu.ss_bg_enabled[bg] == true) { + if(windows_not_obstructing(PPU_MAIN, bg, x) == false)return; + ppu_pixel_cache[x].blend_type = BLENDTYPE_MAIN; + ppu_pixel_cache[x].src_main = bg; + ppu_pixel_cache[x].color_main = pal_index; + if(color_windows_not_obstructing(x, PPU_SUB) == false) { + ppu_pixel_cache[x].src_sub = bg; + ppu_pixel_cache[x].color_sub = pal_index; + } + } else if(ppu.bg_enabled[bg] == true && bg == OAM && pal_index < 192) { + if(windows_not_obstructing(PPU_MAIN, bg, x) == false)return; + ppu_pixel_cache[x].blend_type = BLENDTYPE_MAIN; + ppu_pixel_cache[x].src_main = bg; + ppu_pixel_cache[x].color_main = pal_index; + } else if(ppu.bg_enabled[bg] == true) { + if(windows_not_obstructing(PPU_MAIN, bg, x) == false)return; + if(ppu.bg_color_enabled[bg] == true && ppu_pixel_cache[x].src_sub != BACK) { + ppu_pixel_cache[x].blend_type = BLENDTYPE_COMBINE; + } else { + ppu_pixel_cache[x].blend_type = BLENDTYPE_MAIN; + } + ppu_pixel_cache[x].src_main = bg; + ppu_pixel_cache[x].color_main = pal_index; + } else if(ppu.ss_bg_enabled[bg] == true) { + if(windows_not_obstructing(PPU_SUB, bg, x) == false)return; + ppu_pixel_cache[x].src_sub = bg; + ppu_pixel_cache[x].color_sub = pal_index; + if(ppu_pixel_cache[x].blend_type == BLENDTYPE_BACK) { + ppu_pixel_cache[x].blend_type = BLENDTYPE_SUB; + } else if(ppu_pixel_cache[x].blend_type == BLENDTYPE_MAIN) { + if(ppu_pixel_cache[x].src_main != OAM || (ppu_pixel_cache[x].src_main == OAM && ppu_pixel_cache[x].color_main >= 192)) { + if(ppu.bg_color_enabled[ppu_pixel_cache[x].src_main] == true) { + ppu_pixel_cache[x].blend_type = BLENDTYPE_COMBINE; + } + } + } + } +} + +struct { + byte num; + byte width, height; + word x, y; + word character; + byte v_flip, h_flip; + byte palette; + byte priority; +}current_sprite; + +void ppu_set_sprite_attributes(byte sprite_num) { +ulong t; +byte size, b; +word x; + t = ppu.oam[(sprite_num << 2) ] | + ppu.oam[(sprite_num << 2) + 1] << 8 | + ppu.oam[(sprite_num << 2) + 2] << 16 | + ppu.oam[(sprite_num << 2) + 3] << 24; + b = ppu.oam[512 + (sprite_num >> 2)]; + + if ((sprite_num & 3) == 0) { size = (b & 0x02)?1:0; x = (b & 0x01)?0x100:0; } + else if((sprite_num & 3) == 1) { size = (b & 0x08)?1:0; x = (b & 0x04)?0x100:0; } + else if((sprite_num & 3) == 2) { size = (b & 0x20)?1:0; x = (b & 0x10)?0x100:0; } + else if((sprite_num & 3) == 3) { size = (b & 0x80)?1:0; x = (b & 0x40)?0x100:0; } + + current_sprite.num = sprite_num; + current_sprite.priority = (t >> 28) & 3; + current_sprite.x = x | (t & 0xff); + current_sprite.y = (t >> 8) & 0xff; + current_sprite.v_flip = (t & 0x80000000)?1:0; + current_sprite.h_flip = (t & 0x40000000)?1:0; + current_sprite.palette = (t >> 25) & 7; + current_sprite.character = (t >> 16) & 0x01ff; + +//size: 0 = small, 1 = large + switch(ppu.oam_base_size) { + case 0: + if(!size) { current_sprite.width = 8; current_sprite.height = 8; } + else { current_sprite.width = 16; current_sprite.height = 16; } + break; + case 1: + if(!size) { current_sprite.width = 8; current_sprite.height = 8; } + else { current_sprite.width = 32; current_sprite.height = 32; } + break; + case 2: + if(!size) { current_sprite.width = 8; current_sprite.height = 8; } + else { current_sprite.width = 64; current_sprite.height = 64; } + break; + case 3: + if(!size) { current_sprite.width = 16; current_sprite.height = 16; } + else { current_sprite.width = 32; current_sprite.height = 32; } + break; + case 4: + if(!size) { current_sprite.width = 16; current_sprite.height = 16; } + else { current_sprite.width = 64; current_sprite.height = 64; } + break; + case 5: + if(!size) { current_sprite.width = 32; current_sprite.height = 32; } + else { current_sprite.width = 64; current_sprite.height = 64; } + break; + case 6: + if(!size) { current_sprite.width = 16; current_sprite.height = 32; } + else { current_sprite.width = 32; current_sprite.height = 64; } + break; + case 7: + if(!size) { current_sprite.width = 16; current_sprite.height = 32; } + else { current_sprite.width = 32; current_sprite.height = 32; } + break; + } +} + +bool windows_not_obstructing(byte layer, byte bg, byte x) { +byte w1_mask, w2_mask; //1 = masked, 0 = not masked + if(layer == PPU_MAIN) { + if(ppu.bg_windowing_enabled[bg] == false)return true; + } else if(layer == PPU_SUB) { + if(ppu.ss_bg_windowing_enabled[bg] == false)return true; + } + + if(ppu.bg_window1_enabled[bg] == true && ppu.bg_window2_enabled[bg] == false) { + if(ppu.bg_window1_clipmode[bg] == CLIPMODE_IN) { + if(x >= ppu.window1_left && x <= ppu.window1_right)return false; + return true; + } else { + if(x <= ppu.window1_left || x >= ppu.window1_right)return false; + return true; + } + } else if(ppu.bg_window2_enabled[bg] == true && ppu.bg_window1_enabled[bg] == false) { + if(ppu.bg_window2_clipmode[bg] == CLIPMODE_IN) { + if(x >= ppu.window2_left && x <= ppu.window2_right)return false; + return true; + } else { + if(x <= ppu.window2_left || x >= ppu.window2_right)return false; + return true; + } + } else if(ppu.bg_window1_enabled[bg] == true && ppu.bg_window2_enabled[bg] == true) { + if(ppu.bg_window1_clipmode[bg] == CLIPMODE_IN) { + if(x >= ppu.window1_left && x <= ppu.window1_right)w1_mask = 1; + else w1_mask = 0; + } else { + if(x <= ppu.window1_left || x >= ppu.window1_right)w1_mask = 1; + else w1_mask = 0; + } + + if(ppu.bg_window2_clipmode[bg] == CLIPMODE_IN) { + if(x >= ppu.window2_left && x <= ppu.window2_right)w2_mask = 1; + else w2_mask = 0; + } else { + if(x <= ppu.window2_left || x >= ppu.window2_right)w2_mask = 1; + else w2_mask = 0; + } + + switch(ppu.bg_window_mask[bg]) { + case WINDOWMASK_OR: + if((w1_mask | w2_mask) == 1)return false; + return true; + case WINDOWMASK_AND: + if((w1_mask & w2_mask) == 1)return false; + return true; + case WINDOWMASK_XOR: + if((w1_mask ^ w2_mask) == 1)return false; + return true; + case WINDOWMASK_XNOR: + if((w1_mask ^ w2_mask) == 0)return false; + return true; + } + } + return true; +} + +bool color_windows_not_obstructing(byte x, byte color_mask_type) { +byte w1_mask, w2_mask; //1 = masked, 0 = not masked +byte color_mask; +bool r; + if(color_mask_type == PPU_MAIN)color_mask = ppu.color_mask; + else color_mask = ppu.ss_color_mask; + + if(color_mask == 0)return false; + if(color_mask == 3)return true; + + if(ppu.color_window1_enabled == false && ppu.color_window2_enabled == false) { + r = true; + } else if(ppu.color_window1_enabled == true && ppu.color_window2_enabled == false) { + if(ppu.color_window1_clipmode == CLIPMODE_IN) { + if(x >= ppu.window1_left && x <= ppu.window1_right)r = false; + else r = true; + } else { + if(x <= ppu.window1_left || x >= ppu.window1_right)r = false; + else r = true; + } + } else if(ppu.color_window1_enabled == false && ppu.color_window2_enabled == true) { + if(ppu.color_window2_clipmode == CLIPMODE_IN) { + if(x >= ppu.window2_left && x <= ppu.window2_right)r = false; + else r = true; + } else { + if(x <= ppu.window2_left || x >= ppu.window2_right)r = false; + else r = true; + } + } else if(ppu.color_window1_enabled == true && ppu.color_window2_enabled == true) { + if(ppu.color_window1_clipmode == CLIPMODE_IN) { + if(x >= ppu.window1_left && x <= ppu.window1_right)w1_mask = 1; + else w1_mask = 0; + } else { + if(x <= ppu.window1_left || x >= ppu.window1_right)w1_mask = 1; + else w1_mask = 0; + } + + if(ppu.color_window2_clipmode == CLIPMODE_IN) { + if(x >= ppu.window2_left && x <= ppu.window2_right)w2_mask = 1; + else w2_mask = 0; + } else { + if(x <= ppu.window2_left || x >= ppu.window2_right)w2_mask = 1; + else w2_mask = 0; + } + + switch(ppu.color_window_mask) { + case WINDOWMASK_OR: + if((w1_mask | w2_mask) == 1)r = false; + else r = true; + break; + case WINDOWMASK_AND: + if((w1_mask & w2_mask) == 1)r = false; + else r = true; + break; + case WINDOWMASK_XOR: + if((w1_mask ^ w2_mask) == 1)r = false; + else r = true; + break; + case WINDOWMASK_XNOR: + if((w1_mask ^ w2_mask) == 0)r = false; + else r = true; + break; + } + } + + if(color_mask == 2) { + r = (r == true)?false:true; + } + + return r; +} + +/* + *1 - When bit 8 of a sprite's character number is set, such that character data + is read from the upper half (upper 8k) of sprite vram, bits 4-3 of $2101 + are added to bits 14-13 of the tiledata location. The address wraps around + the 64k bank. Why this happens, or what it's for, I have no idea. + *2 - The sprite tiledata is stored with 16 tiles making up the first row, followed + by 16 tiles making up the second row, and so on. Therefore, to get the + correct y tile, y / 8 * 1 row (16 tiles) must be used. +*/ + +#define ppu_render_oam_tile_line(__m) \ + x &= 511; \ + if(x < 256) { \ + col = 0; \ + if(d0 & __m)col += 1; \ + if(d1 & __m)col += 2; \ + if(d2 & __m)col += 4; \ + if(d3 & __m)col += 8; \ + if(col) { \ + col += pal_index; \ + col += 128; \ + ppu_set_pixel(OAM, x, col); \ + } \ + } \ + x++ + +void ppu_render_oam_sprite(void) { +word pos, col, chr, tiledata_inc; +byte d0, d1, d2, d3, pal_index; +int x, y, z, x1, mx, mask, p; +int tile_width; + if(ppu.bg_enabled[OAM] == false && ppu.ss_bg_enabled[OAM] == false)return; + + tile_width = current_sprite.width >> SH_8; //e.x. 16x16 sprite = 2x2 tiles + + y = ppu.vline_pos; + x = current_sprite.x; + if(current_sprite.v_flip) { + y = (current_sprite.height - 1) - (ppu.vline_pos - current_sprite.y); + } else { + y = ppu.vline_pos - current_sprite.y; + } + y &= 255; + + chr = current_sprite.character; + tiledata_inc = (chr & 0x100)?(ppu.oam_name_sel << 13):0; //*1 + chr += (y >> SH_8) << SH_16; //*2 + pal_index = (current_sprite.palette << SH_16); + for(x1=0;x1<tile_width;x1++) { + if(current_sprite.h_flip)mx = (tile_width - 1) - x1; + else mx = x1; + pos = ppu.oam_tiledata_loc + ((chr + mx) << SH_32) + ((y & 7) << SH_2) + tiledata_inc; + d0 = ppu.vram[pos ]; + d1 = ppu.vram[pos + 1]; + d2 = ppu.vram[pos + 16]; + d3 = ppu.vram[pos + 17]; + if(current_sprite.h_flip) { + ppu_render_oam_tile_line(0x01); + ppu_render_oam_tile_line(0x02); + ppu_render_oam_tile_line(0x04); + ppu_render_oam_tile_line(0x08); + ppu_render_oam_tile_line(0x10); + ppu_render_oam_tile_line(0x20); + ppu_render_oam_tile_line(0x40); + ppu_render_oam_tile_line(0x80); + } else { + ppu_render_oam_tile_line(0x80); + ppu_render_oam_tile_line(0x40); + ppu_render_oam_tile_line(0x20); + ppu_render_oam_tile_line(0x10); + ppu_render_oam_tile_line(0x08); + ppu_render_oam_tile_line(0x04); + ppu_render_oam_tile_line(0x02); + ppu_render_oam_tile_line(0x01); + } + } +} + +void ppu_render_line_oam(byte priority) { +int s; + if(ppu.bg_enabled[OAM] != true && ppu.ss_bg_enabled[OAM] != true)return; + + s = 128; + while(s--) { + ppu_set_sprite_attributes(s); + if(current_sprite.priority == priority) { +//if the sprite is within screen boundaries... render the current line from the sprite + if(ppu.vline_pos >= current_sprite.y && ppu.vline_pos < (current_sprite.y + current_sprite.height)) { + ppu_render_oam_sprite(); +//or if the sprite is so close to the bottom of the screen that the bottom of it actually wraps back around to the top... + } else if((current_sprite.y + current_sprite.height) >= 256 && ppu.vline_pos < ((current_sprite.y + current_sprite.height) & 255)) { + ppu_render_oam_sprite(); + } + } + } +} + +/* + *1 - map_index + The tilemap can be 32x32, 64x32, 32x64, or 64x64. Rather than expanding the width + and height of the tilemap, the game instead stores duplicate tilemaps immediately + following the previous ones. For example, if you were in 64x64 mode, there would + be four tilemaps. Each tilemap would be 2048 bytes in size + (32 tiles * 32 tiles * 2 bytes/tile), the first tilemap would make the top left + corner, the second the top right, the third the bottom left, and the fourth, the + bottom right. Because x / y are divided by the tile size, the tile size setting + (8x8 or 16x16) does not affect the result. + *2 - pos = ppu.bg_tilemap_loc[bg] + map_index + ((y1 / tile_size) & 31) * 64 + ( ((x / tile_size) & 31) * 2); + Format: tilemap start location + + map index (either map 0 or map 1; see *1) + + ((y tile #) mapped to tilemap boundary) * # of bytes per tilemap line + + (((x tile #) mapped to tilemap boundary) * 2 (# of bytes per tilemap entry)); +*/ + +void ppu_render_line_bg(byte color_depth, byte bg, byte priority) { +int x, y, z, x1, y1; +int mirror_x, mirror_y, p; +int screen_x, screen_y; +int bg_x, bg_y; +int xpos, ypos, mosaic_x, mosaic_y; +word t, base_xpos, base_pos, pos, ppos = 0; +word col; +byte *src, *bg_tiledata, *bg_tiledata_state; +byte tiledata_size; +byte tile_size, tile_width, tile_height; +byte mask, pal_index, pal_size; +word tile_num, screen_width, screen_height, screen_width_mask, screen_height_mask, map_index; +word *mosaic_table; + if(ppu.bg_enabled[bg] == false && ppu.ss_bg_enabled[bg] == false)return; + + switch(color_depth) { + case COLORDEPTH_4: + pal_size = 4; + tiledata_size = SH_16; + break; + case COLORDEPTH_16: + pal_size = 16; + tiledata_size = SH_32; + break; + case COLORDEPTH_256: + pal_size = 256; + tiledata_size = SH_64; + break; + } + bg_tiledata = (byte*)ppu_bg_tiledata[color_depth]; + bg_tiledata_state = (byte*)ppu_bg_tiledata_state[color_depth]; + + screen_width = render.snes_width; + screen_height = render.snes_width; //this is correct -- ppu tilemap is based around 256x256, etc. + tile_size = (ppu.bg_tile_size[bg])?SH_16:SH_8; + tile_width = tile_size; + tile_height = tile_size; + + if(ppu.interlace == true && (ppu.bg_mode == 5 || ppu.bg_mode == 6)) { + screen_y = (ppu.vline_pos << SH_2) + ppu.interlace_frame; + } else { + screen_y = ppu.vline_pos; + } + +//Not sure why, but modes 5 and 6 seem to force 16-width tiles. +//The tile size attribute in $2105 has no effect on tile width. + if(ppu.bg_mode == 5 || ppu.bg_mode == 6) { + tile_width = SH_16; + } + + if(tile_size == SH_16) { + screen_width <<= SH_2; + screen_height <<= SH_2; + } + + if(ppu.bg_tilemap_size[bg] & 0x01)screen_width <<= SH_2; + if(ppu.bg_tilemap_size[bg] & 0x02)screen_height <<= SH_2; + + screen_width_mask = screen_width - 1; + screen_height_mask = screen_height - 1; + + if(render.snes_width == 512) { + bg_x = (ppu.bg_hscroll_pos[bg] << SH_2) & screen_width_mask; + } else { + bg_x = ppu.bg_hscroll_pos[bg] & screen_width_mask; + } + if(render.snes_height == 448) { + bg_y = (screen_y + ((ppu.bg_vscroll_pos[bg] << SH_2) & screen_height_mask)) & screen_height_mask; + } else { + bg_y = (screen_y + (ppu.bg_vscroll_pos[bg] & screen_height_mask)) & screen_height_mask; + } + + mosaic_table = (word*)ppu.mosaic_table[ppu.mosaic_size]; + mosaic_y = mosaic_table[bg_y]; + + for(screen_x=0;screen_x<render.snes_width;screen_x++) { + switch(ppu.bg_tilemap_size[bg]) { + case 0: + map_index = 0; + break; + case 1: + map_index = ((bg_x >> tile_size) > 31)?32*32*2:0; + break; + case 2: + map_index = ((bg_y >> tile_size) > 31)?32*32*2:0; + break; + case 3: + map_index = ((bg_x >> tile_size) > 31)?32*32*2:0; + map_index += ((bg_y >> tile_size) > 31)?32*32*2*2:0; + break; + } + + mosaic_x = mosaic_table[bg_x]; + + base_xpos = ((mosaic_x >> SH_8) & 31); + base_pos = (((mosaic_y >> tile_height) & 31) << SH_32) + ((mosaic_x >> tile_width) & 31); + pos = ppu.bg_tilemap_loc[bg] + map_index + (base_pos << SH_2); + t = *((word*)ppu.vram + (pos >> SH_2)); + mirror_y = (t & 0x8000)?1:0; + mirror_x = (t & 0x4000)?1:0; + if(((t >> 13) & 1) == priority) { + tile_num = t & 0x03ff; + if(tile_width == SH_16) { + if((mosaic_x & 15) >= 8)tile_num++; + if(mirror_x) { + if((mosaic_x & 15) >= 8)tile_num--; + else tile_num++; + } + tile_num &= 0x03ff; + } + if(tile_height == SH_16) { + if((mosaic_y & 15) >= 8)tile_num += 16; + if(mirror_y) { + if((mosaic_y & 15) >= 8)tile_num -= 16; + else tile_num += 16; + } + tile_num &= 0x03ff; + } + tile_num += (ppu.bg_tiledata_loc[bg] >> tiledata_size); + + if(bg_tiledata_state[tile_num] == 1) { + ppu_render_bg_tile(color_depth, bg, tile_num); + } + + pal_index = ((t >> 10) & 7) * pal_size; + + if(mirror_y) { ypos = (7 - (mosaic_y & 7)); } + else { ypos = ( (mosaic_y & 7)); } + +//loop while we are rendering from the same tile, as there's no need to do all of the above work +//unless we have rendered all of the visible tile, taking mosaic into account. + while(1) { + if(mirror_x) { xpos = (7 - (mosaic_x & 7)); } + else { xpos = ( (mosaic_x & 7)); } + col = *(bg_tiledata + (tile_num << SH_64) + (ypos << SH_8) + (xpos)); + if(col) { + ppu_set_pixel(bg, screen_x, col + pal_index); + } + + bg_x++; + bg_x &= screen_width_mask; + mosaic_x = mosaic_table[bg_x]; + + if(base_xpos != ((mosaic_x >> SH_8) & 31))break; + screen_x++; + if(screen_x >= render.snes_width)break; + } + } else { + while(1) { + bg_x++; + bg_x &= screen_width_mask; + mosaic_x = mosaic_table[bg_x]; + + if(base_xpos != ((mosaic_x >> SH_8) & 31))break; + screen_x++; + if(screen_x >= render.snes_width)break; + } + } + } +} + +#ifdef PUBLIC_DOMAIN + #include "ppu_render_mode7f.cpp" +#else + #include "ppu_render_mode7i.cpp" +#endif diff --git a/bsnes/ppu/ppu_render_mode7f.cpp b/bsnes/ppu/ppu_render_mode7f.cpp new file mode 100644 index 00000000..3fd66357 --- /dev/null +++ b/bsnes/ppu/ppu_render_mode7f.cpp @@ -0,0 +1,74 @@ +/* + This mode7 code was derived from my own research, assisted by + information in the public domain. It is therefore redistributed + to the public domain. This code is infinitesimally less accurate + than ppu_render_mode7i.cpp, but nearly twice as slow. + However, this file contains no licensing restrictions. + Please define the precompiler variable PUBLIC_DOMAIN to use this + code instead of mode7i. +*/ + +/* + *1 - converts 16-bit word value to float within the range -128/127. decimal + has range of 256 with steps of 0.00390625 + *2 - (int)float will turn anything from -0.999 to 0.999 to 0. this code will + check to see if the number is negative, and if the decimal place (.xxx) + is zero or not. If value is positive or the decimal place ends on .000, + the number is converted directly to an int. otherwise, one is subtracted + to make up for the rounding error of 1 mentioned above. + result is masked by 1023 (width / height of map is 1024) to prevent + negative values from causing an error with the division in the tile + calculation below. + *3 - mode7 vram consists of 32k of data. there are two 16k regions interleaved + in bytes. the low byte is the tile number, and the high byte is tile data. + the tile data directly indexes into the palette. the map is 128*128, and + there are 256 possible characters. each character is 64 bytes in length. +*/ +void ppu_render_line_m7(void) { +int x, y, sx, sy; +byte tile, palette; +float x_stepx, x_stepy, y_stepx, y_stepy; +float centerx, centery, scrollx, scrolly, startx, starty; + y = (ppu.mode7_vflip == true)?223 - ppu.vline_pos:ppu.vline_pos; + +//*1 + x_stepx = (float)((signed short)ppu.m7a) / 256.0; + x_stepy = (float)((signed short)ppu.m7b) / 256.0; + y_stepx = (float)((signed short)ppu.m7c) / 256.0; + y_stepy = (float)((signed short)ppu.m7d) / 256.0; + + centerx = (float)(1.0 - x_stepx) * (signed short)ppu.m7x - x_stepy * (signed short)ppu.m7y; + centery = (float)(1.0 - y_stepy) * (signed short)ppu.m7y - y_stepx * (signed short)ppu.m7x; + + scrollx = (float)((signed short)ppu.m7hofs); + scrolly = (float)((signed short)ppu.m7vofs); + + startx = centerx + ((scrollx) * x_stepx) + ((scrolly + y) * x_stepy); + starty = centery + ((scrollx) * y_stepx) + ((scrolly + y) * y_stepy); + + for(x=0;x<256;x++) { +//*2 + if(startx < 0.0 && (int)(startx * 32768) & 32767) { + sx = (int)(startx - 1) & 1023; + } else { + sx = (int)startx & 1023; + } + if(starty < 0.0 && (int)(starty * 32768) & 32767) { + sy = (int)(starty - 1) & 1023; + } else { + sy = (int)starty & 1023; + } +//*3 + tile = ppu.vram[(((sy / 8) & 127) * 128 + ((sx / 8) & 127)) << 1]; + palette = ppu.vram[((tile * 64 + (sy & 7) * 8 + (sx & 7)) << 1) + 1]; + if(palette) { + if(ppu.mode7_hflip == true) { + ppu_set_pixel(BG1, 255 - x, palette); + } else { + ppu_set_pixel(BG1, x, palette); + } + } + startx += x_stepx; + starty += y_stepx; + } +} diff --git a/bsnes/ppu/ppu_render_mode7i.cpp b/bsnes/ppu/ppu_render_mode7i.cpp new file mode 100644 index 00000000..6fc9dffe --- /dev/null +++ b/bsnes/ppu/ppu_render_mode7i.cpp @@ -0,0 +1,66 @@ +/* + The algorithm in this file was derived from the snes9x source code. + The snes9x source code is not public domain. If you wish to use this + code, you must abide by the terms of the snes9x license. If you do not + wish to abide by the snes9x licensing terms, please define the precompiler + variable PUBLIC_DOMAIN so that ppu_render_mode7f.cpp is used instead of + this file. You must also remove this file from any work that you release + that does not follow the snes9x license. + See license.txt for more info on the license of this software and snes9x. +*/ + +#define CLIP_10BIT_SIGNED(x) \ + ((x) & ((1 << 10) - 1)) + (((((x) & (1 << 13)) ^ (1 << 13)) - (1 << 13)) >> 3) + +#define CAST_WORDTOINT(x) \ + (int)(((x & 0x8000)?(x | 0xffff0000):(x & 0x00007fff))) + +void ppu_render_line_m7(void) { +int x; +int step_m7a, step_m7c, m7a, m7b, m7c, m7d; +int hoffset, voffset; +int centerx, centery; +int xx, yy; +int px, py; +int tile, palette; + hoffset = (CAST_WORDTOINT(ppu.m7hofs) << 7) >> 7; + voffset = (CAST_WORDTOINT(ppu.m7vofs) << 7) >> 7; + + centerx = (CAST_WORDTOINT(ppu.m7x) << 7) >> 7; + centery = (CAST_WORDTOINT(ppu.m7y) << 7) >> 7; + + if(ppu.mode7_vflip == true) { + yy = 223 - ppu.vline_pos; + } else { + yy = ppu.vline_pos; + } + yy += CLIP_10BIT_SIGNED(voffset - centery); + + m7b = CAST_WORDTOINT(ppu.m7b) * yy + (centerx << 8); + m7d = CAST_WORDTOINT(ppu.m7d) * yy + (centery << 8); + + step_m7a = CAST_WORDTOINT(ppu.m7a); + step_m7c = CAST_WORDTOINT(ppu.m7c); + + xx = CLIP_10BIT_SIGNED(hoffset - centerx); + + m7a = CAST_WORDTOINT(ppu.m7a) * xx; + m7c = CAST_WORDTOINT(ppu.m7c) * xx; + + for(x=0;x<256;x++) { + px = ((m7a + m7b) >> 8) & 1023; + py = ((m7c + m7d) >> 8) & 1023; + + tile = ppu.vram[(((py / 8) & 127) * 128 + ((px / 8) & 127)) << 1]; + palette = ppu.vram[((tile * 64 + (py & 7) * 8 + (px & 7)) << 1) + 1]; + + if(ppu.mode7_hflip == true) { + ppu_set_pixel(BG1, 255 - x, palette); + } else { + ppu_set_pixel(BG1, x, palette); + } + + m7a += step_m7a; + m7c += step_m7c; + } +} diff --git a/bsnes/ppu/ppu_screen.cpp b/bsnes/ppu/ppu_screen.cpp new file mode 100644 index 00000000..9b6c7990 --- /dev/null +++ b/bsnes/ppu/ppu_screen.cpp @@ -0,0 +1,92 @@ +/* + $2100 : screen brightness / enable + d---bbbb + + d: display (0=on, 1=off) + b: brightness (0-15) +*/ +extern emustate emu_state; + +void mmio_w2100(byte value) { + ppu.display_disable = (value & 0x80)?true:false; + ppu.display_brightness = (value & 0x0f); +} + +/* + $2105 : screen mode register + dcbapmmm + + d: bg4 tile size (0=8x8, 1=16x16) + c: bg3 tile size (0=8x8, 1=16x16) + b: bg2 tile size (0=8x8, 1=16x16) + a: bg1 tile size (0=8x8, 1=16x16) + p: bg priority mode + m: screen mode +*/ +void mmio_w2105(byte value) { + ppu.bg_tile_size[BG4] = (value & 0x80)?1:0; + ppu.bg_tile_size[BG3] = (value & 0x40)?1:0; + ppu.bg_tile_size[BG2] = (value & 0x20)?1:0; + ppu.bg_tile_size[BG1] = (value & 0x10)?1:0; + ppu.bg_priority_mode = (value & 0x08)?1:0; + ppu.bg_mode = (value & 0x07); + video_setsnesmode(); +} + +/* + $2106 : mosaic + ssssdcba + + s: size (0=smallest, 15=largest) + d: affect bg4 + c: affect bg3 + b: affect bg2 + a: affect bg1 +*/ +void mmio_w2106(byte value) { + ppu.mosaic_size = (value >> 4) & 15; + ppu.mosaic_enabled[BG4] = (value & 0x08)?true:false; + ppu.mosaic_enabled[BG3] = (value & 0x04)?true:false; + ppu.mosaic_enabled[BG2] = (value & 0x02)?true:false; + ppu.mosaic_enabled[BG1] = (value & 0x01)?true:false; +} + +/* + $212c : main screen desgination + $212d : sub screen designation + ---sdcba + + s: oam enable + d: bg4 enable + c: bg3 enable + b: bg2 enable + a: bg1 enable +*/ +void mmio_w212c(byte value) { + ppu.bg_enabled[OAM] = (value & 0x10)?true:false; + ppu.bg_enabled[BG4] = (value & 0x08)?true:false; + ppu.bg_enabled[BG3] = (value & 0x04)?true:false; + ppu.bg_enabled[BG2] = (value & 0x02)?true:false; + ppu.bg_enabled[BG1] = (value & 0x01)?true:false; +} + +void mmio_w212d(byte value) { + ppu.ss_bg_enabled[OAM] = (value & 0x10)?true:false; + ppu.ss_bg_enabled[BG4] = (value & 0x08)?true:false; + ppu.ss_bg_enabled[BG3] = (value & 0x04)?true:false; + ppu.ss_bg_enabled[BG2] = (value & 0x02)?true:false; + ppu.ss_bg_enabled[BG1] = (value & 0x01)?true:false; +} + +/* + $2133 : screen mode settings + ?????h?i + + h: snes height (0 = 224, 1 = 240) + i: interlace (0 = off, 1 = on) +*/ +void mmio_w2133(byte value) { + ppu.toggle_visible_scanlines = (value & 0x04)?240:224; + ppu.interlace = (value & 0x01)?true:false; + video_setsnesmode(); +} diff --git a/bsnes/ppu/ppu_scroll.cpp b/bsnes/ppu/ppu_scroll.cpp new file mode 100644 index 00000000..c5345950 --- /dev/null +++ b/bsnes/ppu/ppu_scroll.cpp @@ -0,0 +1,46 @@ +/* + $210d-$2114 : Scroll registers + + 210d/210e: bg1 hscroll/bg1 vscroll + 210f/2110: bg2 hscroll/bg2 vscroll + 2111/2112: bg3 hscroll/bg3 vscroll + 2113/2114: bg4 hscroll/bg4 vscroll + + you must write to this register twice to write the full address. + starting positions are 0, 0. only 11 bits of the address are used. +*/ +void mmio_w210d(byte value) { + ppu.bg_hscroll_pos[BG1] = (value << 8) | (ppu.bg_hscroll_pos[BG1] >> 8); + ppu.m7hofs = (value << 8) | m7_latch; + m7_latch = value; +} + +void mmio_w210e(byte value) { + ppu.bg_vscroll_pos[BG1] = (value << 8) | (ppu.bg_vscroll_pos[BG1] >> 8); + ppu.m7vofs = (value << 8) | m7_latch; + m7_latch = value; +} + +void mmio_w210f(byte value) { + ppu.bg_hscroll_pos[BG2] = (value << 8) | (ppu.bg_hscroll_pos[BG2] >> 8); +} + +void mmio_w2110(byte value) { + ppu.bg_vscroll_pos[BG2] = (value << 8) | (ppu.bg_vscroll_pos[BG2] >> 8); +} + +void mmio_w2111(byte value) { + ppu.bg_hscroll_pos[BG3] = (value << 8) | (ppu.bg_hscroll_pos[BG3] >> 8); +} + +void mmio_w2112(byte value) { + ppu.bg_vscroll_pos[BG3] = (value << 8) | (ppu.bg_vscroll_pos[BG3] >> 8); +} + +void mmio_w2113(byte value) { + ppu.bg_hscroll_pos[BG4] = (value << 8) | (ppu.bg_hscroll_pos[BG4] >> 8); +} + +void mmio_w2114(byte value) { + ppu.bg_vscroll_pos[BG4] = (value << 8) | (ppu.bg_vscroll_pos[BG4] >> 8); +} diff --git a/bsnes/ppu/ppu_timing.cpp b/bsnes/ppu/ppu_timing.cpp new file mode 100644 index 00000000..31169975 --- /dev/null +++ b/bsnes/ppu/ppu_timing.cpp @@ -0,0 +1,160 @@ +/* + $213d : vertical latch position + + returns the current scanline. must read from + $2137 before reading from this register. +*/ +byte mmio_r2137(void) { + ppu.latch_toggle = 0; + ppu.latch_vpos = snes_time->vscan_pos; + ppu.latch_hpos = snes_time->hscan_pos; + return 0x00; +} + +byte mmio_r213c(void) { +word r; + r = ppu.latch_hpos; + if(ppu.latch_toggle)r >>= 8; + ppu.latch_toggle ^= 1; + return r; +} + +byte mmio_r213d(void) { +word r; + r = ppu.latch_vpos; + if(ppu.latch_toggle)r >>= 8; + ppu.latch_toggle ^= 1; + return r; +} + +/* + $213e : ppu1 status register + + trm0vvvv + t: time over (?) + r: range over (?) + m: master/slave mode select (?) + v: version # (returns 1) +*/ +byte mmio_r213e(void) { + return 0x01; +} + +/* + $213f : ppu2 status register + fl0mvvvv + f: field # (?) + l: external signal applied (should be 0) + m: ntsc/pal mode (0=ntsc, 1=pal) + v: version # (returns 0) +*/ +byte mmio_r213f(void) { + return 0x00; +} + +/* + $4200 : counter enable + n-vh---j + + n: nmi enable + v: vertical counter enable + h: horizontal counter enable + j: automatic joypad enable + + the v/h counters must be enabled to invoke IRQs. the vertical + counter will override the horizontal counter. in other words, + if both v+h are set, only vertical IRQs will be performed. +*/ +void mmio_w4200(byte value) { + gx816->nmi_enabled = (value & 0x80)?true:false; + ppu.vcounter_enabled = (value & 0x20)?true:false; + ppu.hcounter_enabled = (value & 0x10)?true:false; + ppu.auto_joypad_read = (value & 0x01)?true:false; +} + +/* + $4207/$4208 : horizontal counter position + + 9-bit value, used to invoke horizontal IRQs + horizontal range: 0-339 +*/ +void mmio_w4207(byte value) { + ppu.hirq_pos = (ppu.hirq_pos & 0x0100) | value; +} +void mmio_w4208(byte value) { + ppu.hirq_pos = (ppu.hirq_pos & 0x00ff) | (value & 1) << 8; +} + +/* + $4209/$420a : vertical counter position + + 9-bit value, used to invoke vertical IRQs + vertical range: 0-261 +*/ +void mmio_w4209(byte value) { + ppu.virq_pos = (ppu.virq_pos & 0x0100) | value; +} +void mmio_w420a(byte value) { + ppu.virq_pos = (ppu.virq_pos & 0x00ff) | (value & 1) << 8; +} + +/* + $420d : memory speed + 0000000x + + x: 0 = SlowROM + 1 = FastROM +*/ +void mmio_w420d(byte value) { + gx816->toggle_memory_speed = (value) & 0x01; +} + +/* + $4210 : nmi status + n------- + + n: outside nmi (0=no, 1=yes) +*/ +byte mmio_r4210(void) { +byte r; + r = (gx816->nmi_pin ^ 1)?0x80:0x00; + gx816->nmi_pin = 1; + return r; +} + +/* + $4211 : irq toggle + i?------ + + i: irq state (1=in irq, 0=not in irq)? + ?: unknown, always return 1? +*/ +byte mmio_r4211(void) { +byte r; + r = 0x40; + if(ppu.irq_triggered == true)r |= 0x80; + ppu.irq_triggered = false; + return r; +} + +/* + $4212 : video status + vh-----j + + v: vblank (0=no, 1=yes) + h: hblank (0=no, 1=yes) + j: joypad ready (for auto joypad mode) +*/ +byte mmio_r4212(void) { +byte r; + r = 0x00; + +//set when the SNES is updating the joypad data automatically + if(snes_time->vscan_pos >= (ppu.visible_scanlines + 1) && snes_time->vscan_pos <= (ppu.visible_scanlines + 3))r |= 0x01; + +//set when the SNES is in hblank/vblank + if(snes_time->hscan_pos >= 256)r |= 0x40; + if(snes_time->vscan_pos >= ppu.visible_scanlines)r |= 0x80; + + return r; +} diff --git a/bsnes/ppu/ppu_vram.cpp b/bsnes/ppu/ppu_vram.cpp new file mode 100644 index 00000000..5ce65a35 --- /dev/null +++ b/bsnes/ppu/ppu_vram.cpp @@ -0,0 +1,131 @@ +/* + $2107-$210a : bg1-4 tilemap location + bbbbbbss + + b: location of bg tilemap - highest bit is ignored + s: tilemap size (00 = 32x32, 01 = 64x32, 10 = 32x64, 11 = 64x64) +*/ +void mmio_w2107(byte value) { + ppu.bg_tilemap_loc[BG1] = (value & 0x7c) << 9; + ppu.bg_tilemap_size[BG1] = value & 3; +} +void mmio_w2108(byte value) { + ppu.bg_tilemap_loc[BG2] = (value & 0x7c) << 9; + ppu.bg_tilemap_size[BG2] = value & 3; +} +void mmio_w2109(byte value) { + ppu.bg_tilemap_loc[BG3] = (value & 0x7c) << 9; + ppu.bg_tilemap_size[BG3] = value & 3; +} +void mmio_w210a(byte value) { + ppu.bg_tilemap_loc[BG4] = (value & 0x7c) << 9; + ppu.bg_tilemap_size[BG4] = value & 3; +} + +/* + $210b/$210c: bg1-4 tiledata location + bbbbaaaa + + a: bg1/3 tiledata location (210b/210c) + b: bg2/4 tiledata location (210b/210c) +*/ +void mmio_w210b(byte value) { + ppu.bg_tiledata_loc[BG1] = (value & 0x07) << 13; + ppu.bg_tiledata_loc[BG2] = (value & 0x70) << 9; +} +void mmio_w210c(byte value) { + ppu.bg_tiledata_loc[BG3] = (value & 0x07) << 13; + ppu.bg_tiledata_loc[BG4] = (value & 0x70) << 9; +} + +/* + $2115 : vram write counter + i---ggrr + + i: 0 = increment on $2118/$2139 + 1 = increment on $2119/$213a + g: graphic increment + r: increment rate + 00 = increment by 2 + 01 = increment by 64 + 10 = increment by 128 + 11 = increment by 256 +*/ +void mmio_w2115(byte value) { +if(value & 0x0c)dprintf("$2115 = %0.2x", value); + ppu.vram_inc_reg = (value & 0x80)?1:0; + switch(value & 3) { + case 0x00:ppu.vram_inc_size = 1;break; + case 0x01:ppu.vram_inc_size = 32;break; + case 0x02:ppu.vram_inc_size = 64;break; + case 0x03:ppu.vram_inc_size = 128;break; + } +} + +/* + $2116/$2117 : vram write position + + 15-bit value ($2116/$2117) determining position in vram to write to using $2118 + this value is doubled to get true write position (0000-ffff) +*/ +void mmio_w2116(byte value) { + ppu.vram_write_pos = ((ppu.vram_write_pos & 0xff00) | value) & 0x7fff; +} +void mmio_w2117(byte value) { + ppu.vram_write_pos = ((value << 8) | (ppu.vram_write_pos & 0xff)) & 0x7fff; +} + +/* + $2118/$2119 : vram write + + $2118/$2119 write to vram using vram_write_pos, this is then incremented based on + the settings of $2115 (vram_inc_size / vram_inc_reg) +*/ +void mmio_w2118(byte value) { +word w = ppu.vram_write_pos * 2; + ppu.vram[w] = value; + if(ppu.vram_inc_reg == 0) { + ppu.vram_write_pos += ppu.vram_inc_size; + ppu.vram_write_pos &= 0x7fff; + } + ppu_bg_tiledata_state[TILE_2BIT][(w >> 4)] = 1; + ppu_bg_tiledata_state[TILE_4BIT][(w >> 5)] = 1; + ppu_bg_tiledata_state[TILE_8BIT][(w >> 6)] = 1; +} + +void mmio_w2119(byte value) { +word w = ppu.vram_write_pos * 2 + 1; + ppu.vram[w] = value; + if(ppu.vram_inc_reg == 1) { + ppu.vram_write_pos += ppu.vram_inc_size; + ppu.vram_write_pos &= 0x7fff; + } + ppu_bg_tiledata_state[TILE_2BIT][(w >> 4)] = 1; + ppu_bg_tiledata_state[TILE_4BIT][(w >> 5)] = 1; + ppu_bg_tiledata_state[TILE_8BIT][(w >> 6)] = 1; +} + +/* + $2139/$213a : vram read +*/ +byte mmio_r2139(void) { +word w = ppu.vram_write_pos * 2; +byte r; + r = ppu.vram[w]; + if(ppu.vram_inc_reg == 0) { + ppu.vram_write_pos += ppu.vram_inc_size; + ppu.vram_write_pos &= 0x7fff; + } + return r; +} + +byte mmio_r213a(void) { +word w = ppu.vram_write_pos * 2 + 1; +byte r; + r = ppu.vram[w]; + if(ppu.vram_inc_reg == 1) { + ppu.vram_write_pos += ppu.vram_inc_size; + ppu.vram_write_pos &= 0x7fff; + } + return r; +} diff --git a/bsnes/ppu/ppu_window.cpp b/bsnes/ppu/ppu_window.cpp new file mode 100644 index 00000000..a7a012cf --- /dev/null +++ b/bsnes/ppu/ppu_window.cpp @@ -0,0 +1,121 @@ +/* + $2123/$2124/$2125 : window mask settings + + $2123: + hgfedcba + (bg2) + h: enable window 2 + g: clip window 2 (0=in, 1=out) + f: enable window 1 + e: clip window 1 (0=in, 1=out) + (bg1) + h: enable window 2 + g: clip window 2 (0=in, 1=out) + f: enable window 1 + e: clip window 1 (0=in, 1=out) + $2124: same as $2123, but with bg4/3 + $2125: + hgfedcba + h: enable color window 2 + g: clip window 2 (0=in, 1=out) + f: enable color window 1 + e: clip window 1 (0=in, 1=out) + d: enable OAM window 2 + c: clip window 2 (0=in, 1=out) + b: enable OAM window 1 + a: clip window 1 (0=in, 1=out) +*/ +void mmio_w2123(byte value) { + ppu.bg_window2_enabled [BG2] = (value & 0x80)?true:false; + ppu.bg_window2_clipmode[BG2] = (value & 0x40)?CLIPMODE_OUT:CLIPMODE_IN; + ppu.bg_window1_enabled [BG2] = (value & 0x20)?true:false; + ppu.bg_window1_clipmode[BG2] = (value & 0x10)?CLIPMODE_OUT:CLIPMODE_IN; + ppu.bg_window2_enabled [BG1] = (value & 0x08)?true:false; + ppu.bg_window2_clipmode[BG1] = (value & 0x04)?CLIPMODE_OUT:CLIPMODE_IN; + ppu.bg_window1_enabled [BG1] = (value & 0x02)?true:false; + ppu.bg_window1_clipmode[BG1] = (value & 0x01)?CLIPMODE_OUT:CLIPMODE_IN; +} + +void mmio_w2124(byte value) { + ppu.bg_window2_enabled [BG4] = (value & 0x80)?true:false; + ppu.bg_window2_clipmode[BG4] = (value & 0x40)?CLIPMODE_OUT:CLIPMODE_IN; + ppu.bg_window1_enabled [BG4] = (value & 0x20)?true:false; + ppu.bg_window1_clipmode[BG4] = (value & 0x10)?CLIPMODE_OUT:CLIPMODE_IN; + ppu.bg_window2_enabled [BG3] = (value & 0x08)?true:false; + ppu.bg_window2_clipmode[BG3] = (value & 0x04)?CLIPMODE_OUT:CLIPMODE_IN; + ppu.bg_window1_enabled [BG3] = (value & 0x02)?true:false; + ppu.bg_window1_clipmode[BG3] = (value & 0x01)?CLIPMODE_OUT:CLIPMODE_IN; +} + +void mmio_w2125(byte value) { + ppu.color_window2_enabled = (value & 0x80)?true:false; + ppu.color_window2_clipmode = (value & 0x40)?CLIPMODE_OUT:CLIPMODE_IN; + ppu.color_window1_enabled = (value & 0x20)?true:false; + ppu.color_window1_clipmode = (value & 0x10)?CLIPMODE_OUT:CLIPMODE_IN; + ppu.bg_window2_enabled [OAM] = (value & 0x08)?true:false; + ppu.bg_window2_clipmode[OAM] = (value & 0x04)?CLIPMODE_OUT:CLIPMODE_IN; + ppu.bg_window1_enabled [OAM] = (value & 0x02)?true:false; + ppu.bg_window1_clipmode[OAM] = (value & 0x01)?CLIPMODE_OUT:CLIPMODE_IN; +} + +/* + $2126-$2129 : window position settings + + $2126: window 1 left + $2127: window 1 right + $2128: window 2 left + $2129: window 2 right +*/ +void mmio_w2126(byte value) { ppu.window1_left = value; } +void mmio_w2127(byte value) { ppu.window1_right = value; } +void mmio_w2128(byte value) { ppu.window2_left = value; } +void mmio_w2129(byte value) { ppu.window2_right = value; } + +/* + $212a/$212b : window mask settings + $212a: ddccbbaa (d=bg4, c=bg3, b=bg2, a=bg1) + $212b: ----ccss (c=color add/sub, s=oam) + + 00=or + 01=and + 10=xor + 11=xnor +*/ +void mmio_w212a(byte value) { + ppu.bg_window_mask[BG4] = (value >> 6) & 3; + ppu.bg_window_mask[BG3] = (value >> 4) & 3; + ppu.bg_window_mask[BG2] = (value >> 2) & 3; + ppu.bg_window_mask[BG1] = (value ) & 3; +} + +void mmio_w212b(byte value) { + ppu.color_window_mask = (value >> 2) & 3; + ppu.bg_window_mask[OAM] = (value ) & 3; +} + +/* + $212e/$212f : main window designation + + ---odcba + + o: OAM enable + d: BG4 enable + c: BG3 enable + b: BG2 enable + a: BG1 enable +*/ +void mmio_w212e(byte value) { + ppu.bg_windowing_enabled[OAM] = (value & 0x10)?true:false; + ppu.bg_windowing_enabled[BG4] = (value & 0x08)?true:false; + ppu.bg_windowing_enabled[BG3] = (value & 0x04)?true:false; + ppu.bg_windowing_enabled[BG2] = (value & 0x02)?true:false; + ppu.bg_windowing_enabled[BG1] = (value & 0x01)?true:false; +} + +void mmio_w212f(byte value) { + ppu.ss_bg_windowing_enabled[OAM] = (value & 0x10)?true:false; + ppu.ss_bg_windowing_enabled[BG4] = (value & 0x08)?true:false; + ppu.ss_bg_windowing_enabled[BG3] = (value & 0x04)?true:false; + ppu.ss_bg_windowing_enabled[BG2] = (value & 0x02)?true:false; + ppu.ss_bg_windowing_enabled[BG1] = (value & 0x01)?true:false; +} diff --git a/bsnes/ppu/ppu_wram.cpp b/bsnes/ppu/ppu_wram.cpp new file mode 100644 index 00000000..87f59f23 --- /dev/null +++ b/bsnes/ppu/ppu_wram.cpp @@ -0,0 +1,24 @@ +/* + $2180 : wram write + + write byte to wram write pointer ($2181-$2183), then increment pointer. + always stays within 7e0000-7fffff, high 7 bits of 24-bit offset ignored. +*/ +void mmio_w2180(byte value) { + gx816->mem_write(MEMMODE_LONG, MEMSIZE_BYTE, 0x7e0000 | ppu.wram_write_pos, value); + ppu.wram_write_pos++; + ppu.wram_write_pos &= 0x01ffff; +} + +/* + $2181-$2183: wram write pointer set +*/ +void mmio_w2181(byte value) { + ppu.wram_write_pos = ((ppu.wram_write_pos & 0xffff00) | value) & 0x01ffff; +} +void mmio_w2182(byte value) { + ppu.wram_write_pos = ((ppu.wram_write_pos & 0xff00ff) | (value << 8)) & 0x01ffff; +} +void mmio_w2183(byte value) { + ppu.wram_write_pos = ((ppu.wram_write_pos & 0x00ffff) | (value << 16)) & 0x01ffff; +} diff --git a/bsnes/qc.bat b/bsnes/qc.bat new file mode 100644 index 00000000..76e3786d --- /dev/null +++ b/bsnes/qc.bat @@ -0,0 +1,3 @@ +del c:\root\bsnes_testrom\bsnes.exe +copy bsnes.exe c:\root\bsnes_testrom\bsnes.exe +@pause \ No newline at end of file diff --git a/bsnes/timing.obj b/bsnes/timing.obj new file mode 100644 index 0000000000000000000000000000000000000000..450d6b3d0804783094608f43206c4e62ad2cfdd1 GIT binary patch literal 3162 zcma)-?`vCC7{|{|(j@(rAGQ;z%A$sfFO1#dY86Iq>L#13+ES=<jwWq>lt_Ps<fff{ zW6;vkU6kNIpu)htQU@Cp1(&ocmBDbu7m6?xLAQ!8^hHWU7r)QB_x9eK+jM>4p8K40 zzUT8i=iGD79TK|P^^;el-7d!ZnPQ=2E|rkxQ)-p<!g<=R)zo*JkA)`ld1I@?SW*2V z>nn$()H%t}=YxNyffw)%$3w}~WML}!f+i*18_+GPS^@PFKHp#?FMy6EXfCP=*r031 zTZLvr)ivYwVf-dEwiY<By?FOS)2r(C;`Koj-v>{EX6YWh0CIW{nokV6rt^hn!=T%B z{lME)HE_8#<NXbt&8cXb%{Pwj4rrzgx@Nq2XqFAS-SVS-P*OEY<mTV^L+C!c2Tug# z^L_IEfvyc_gG`(6lgU(iqA)E@XC|K;Iyv}!z}WE0(6N`JFC8BZ$d&N7EAI5l2?rkM zdWi^LDh$>J-^Ju(L5I9ix#KI=A`9c`qxFLe?o~Is)*LK-lm&8aXp4B4@1O~FG3P-& z3Qfn3W{_)Q-sP_qP2BtT7u>7Yz2`5((|E<2nCmRo-qIr!YsFl?Si80`yGvvzMV`+$ zjY|Eo`BC)8c~%B4+KRbn(O+K(e}oL!1@VFE4-b}uci$<yF|%T=oa-sp>I(<tPTn0g zb+6I~f~-_3{R)rvfRowq2^5b;JWL-1roQ;<x)_f&CjTY7N8}r~so(qQv7lS_*f&-@ z;|dYb9i!0-(?eQ3-FO&#gqNJo0~|oz#R$yP4VTk$)F<B<-a7;xD}~+#Pp9`b(0e!? zJHd?zyD;6f5T#ZV;~|xS`-x|GP-8e`|GN(H=Fj%;R{WL;)1dp*k$f2ZARg>Pnj#y$ za5mV1BrXd}KS~_~FZL8%NUKBq!$Gg=x=nHw7d=c8#Ok_(3nXWCu*+8vMA@T+rG<Wd zO-XrYol7OUNA<5aLcw5Yr4IH49`!8v^~9-%uQ%RWwjr(@lMk&{%H{H_+rP<c;mykJ zwrfr(wzuVPz8uD~$I>e!j_~Gob-c1=gO4L<G9n+QLNT607`}^z(wXU$+I+Xp*pwh; zW-_x=PbsfZBAaDmVs0iI$|RkSwX-@jc4qu*Ql@r6`^N6W-}6nvOn*!YXcyF9ks3{V zX|1HOG2{P^euZ{{R*|gp_ZUKH>+-15h+G0R%J4TOo-&HGbC=FW?TmJ&kxX^F5P*Q9 z{&b~(Nhc4Wg{*~>$xBk9nmHDoTRj#@S}d{XLbVHhC@I$C?3ryn)*@QST$WkTm1rir zEMz(%I{BBY6IXQd2ZV*STF7({o5`&fay!=qP-2PLmh1h0b@EqeA#1mg>54X!+bv|; z-&lE+WS81k|I^7^N+Zrd<2R+-%1rLGkm<g$3vrZ`^YxD}-qOilYJzz!WZGS3ve!cP z?`Vi|{??zePClZN{T4FaJ7%)qLZ*AtE{G_}sy8b*iYk!3raD~~GTqr`au+8rVnifA zo65&<cvGSn9*&OfRCA&jWsyjqv0)^7Ts(b-MW#|xY-S=arE;;vn~CXEEHPJ@mBa?= zsZ%0p&kY>vSJ+eiN4O8}L^2smWDBt@AD?d=dYTW_MEU)QW}1mis7x~zrK-hiDl(z= SA{Da92`R;wHD{R{R`7qD{x!+~ literal 0 HcmV?d00001 diff --git a/bsnes/timing/timing.cpp b/bsnes/timing/timing.cpp new file mode 100644 index 00000000..60449829 --- /dev/null +++ b/bsnes/timing/timing.cpp @@ -0,0 +1,103 @@ +#include "../base.h" +#include "../cpu/g65816.h" +extern g65816 *gx816; +#include "timing.h" + +snes_timer::snes_timer() { + master_cycles = 0; + vscan_pos = 0; + hscan_pos = 0; +} + +#define MEMSPEED_FAST 6 +#define MEMSPEED_SLOW 8 +#define MEMSPEED_XSLOW 12 +ulong snes_timer::get_master_cycle_count(ulong offset) { +byte db; +word addr; + db = (offset >> 16) & 0xff; + addr = (offset) & 0xffff; + if(db >= 0x00 && db <= 0x3f) { + if (addr >= 0x0000 && addr <= 0x1fff)return MEMSPEED_SLOW; + else if(addr >= 0x2000 && addr <= 0x3fff)return MEMSPEED_FAST; + else if(addr >= 0x4000 && addr <= 0x41ff)return MEMSPEED_XSLOW; + else if(addr >= 0x4200 && addr <= 0x5fff)return MEMSPEED_FAST; + else if(addr >= 0x6000 && addr <= 0xffff)return MEMSPEED_SLOW; + } else if(db >= 0x40 && db <= 0x7f) { + return MEMSPEED_SLOW; + } else if(db >= 0x80 && db <= 0xbf) { + if (addr >= 0x0000 && addr <= 0x1fff)return MEMSPEED_SLOW; + else if(addr >= 0x2000 && addr <= 0x3fff)return MEMSPEED_FAST; + else if(addr >= 0x4000 && addr <= 0x41ff)return MEMSPEED_XSLOW; + else if(addr >= 0x4200 && addr <= 0x5fff)return MEMSPEED_FAST; + else if(addr >= 0x6000 && addr <= 0x7fff)return MEMSPEED_SLOW; + else if(addr >= 0x8000 && addr <= 0xffff) { + if(gx816->memory_speed == MEMSPEED_SLOWROM) { + return MEMSPEED_SLOW; + } else { //gx816->memory_speed == MEMSPEED_FASTROM + return MEMSPEED_FAST; + } + } + } else if(db >= 0xc0 && db <= 0xff) { + if(gx816->memory_speed == MEMSPEED_SLOWROM) { + return MEMSPEED_SLOW; + } else { //gx816->memory_speed == MEMSPEED_FASTROM + return MEMSPEED_FAST; + } + } + return MEMSPEED_FAST; //this should never be hit +} + +/* + vpa = 1, vda = 1 -> add_cpu_pcycles (opcode fetch) + vpa = 1, vda = 0 -> add_cpu_pcycles (operand fetch) + vpa = 0, vda = 1 -> add_cpu_dcycles (memory fetch) + vpa = 0, vda = 0 -> add_cpu_icycles (internal operation) +*/ +void snes_timer::add_cpu_pcycles(ulong n) { +ulong speed; + speed = get_master_cycle_count(gx816->regs.pc); + master_cycles += n * speed; +} + +void snes_timer::add_cpu_scycles(ulong n) { +ulong speed; + speed = get_master_cycle_count(gx816->regs.s); + master_cycles += n * speed; +} + +void snes_timer::add_cpu_mcycles(ulong n, ulong addr) { +ulong speed; + speed = get_master_cycle_count(addr); + master_cycles += n * speed; +} + +void snes_timer::add_cpu_icycles(ulong n) { + master_cycles += n * MEMSPEED_FAST; +} + +void snes_timer::add_cpu_icycles(ulong n, ulong flags) { + if(flags & TIMING_BANKCROSS) { + if(gx816->index_bank_crossed == true)n++; + } +//regs.dl != 0x00 + if(flags & TIMING_CONDITION2) { + if((gx816->regs.d & 0xff) != 0x00)n++; + } +//add 1 cycle for indexing across page boundaries, or write, or x=0 + if(flags & TIMING_CONDITION4) { + if(gx816->index_bank_crossed == true || !(gx816->regs.p & PF_X))n++; + } + master_cycles += n * MEMSPEED_FAST; +} + +void snes_timer::add_cpu_cycles(ulong n) { + master_cycles += n; +} + +void snes_timer::update_timer(void) { + vscan_pos = (master_cycles / CYCLES_PER_SCANLINE) % 262; + hscan_pos = (master_cycles % CYCLES_PER_SCANLINE) / 4; +} + +snes_timer *snes_time; diff --git a/bsnes/timing/timing.h b/bsnes/timing/timing.h new file mode 100644 index 00000000..b3dfb6d7 --- /dev/null +++ b/bsnes/timing/timing.h @@ -0,0 +1,23 @@ +#define CYCLES_PER_SCANLINE 1360 +#define WRAM_REFRESH_DOT_POS 132 + +#define TIMING_NONE 0x00 +#define TIMING_BANKCROSS 0x01 +#define TIMING_REGD 0x02 +#define TIMING_CONDITION2 0x02 +#define TIMING_CONDITION4 0x04 + +class snes_timer { +public: +ulong master_cycles; +word vscan_pos, hscan_pos; + ulong get_master_cycle_count(ulong offset); + void add_cpu_pcycles(ulong n); + void add_cpu_scycles(ulong n); + void add_cpu_mcycles(ulong n, ulong addr); + void add_cpu_icycles(ulong n); + void add_cpu_icycles(ulong n, ulong flags); + void add_cpu_cycles(ulong n); + void update_timer(void); + snes_timer(); +}; diff --git a/bsnes/win/gui.cpp b/bsnes/win/gui.cpp new file mode 100644 index 00000000..7d0f8f7e --- /dev/null +++ b/bsnes/win/gui.cpp @@ -0,0 +1,434 @@ +#include "../base.h" +#include "../timing/timing.h" +extern snes_timer *snes_time; +#include "../cpu/g65816.h" +extern g65816 *gx816; +extern emustate emu_state; +extern debugstate debugger; +extern videostate render; +extern ppustate ppu; +#include <windows.h> + +#define BSNES_TITLE "bsnes v0.0.002 ir9" + +enum { + MENU_FILE_LOAD = 100, + MENU_FILE_RESET, + MENU_FILE_EXIT, + MENU_SETTINGS_VIDEOMODE_512x448w, + MENU_SETTINGS_VIDEOMODE_640x480f, + MENU_SETTINGS_FRAMESKIP_OFF, + MENU_SETTINGS_FRAMESKIP_1, + MENU_SETTINGS_FRAMESKIP_2, + MENU_SETTINGS_FRAMESKIP_3, + MENU_SETTINGS_FRAMESKIP_4, + MENU_SETTINGS_FRAMESKIP_5, + MENU_SETTINGS_FRAMESKIP_6, + MENU_SETTINGS_FRAMESKIP_7, + MENU_SETTINGS_FRAMESKIP_8, + MENU_SETTINGS_FRAMESKIP_9, + MENU_SETTINGS_DEBUGGER, + MENU_HELP_ABOUT +}; + +HWND hwndMain = 0; +HMENU hmenuMain; +HFONT hFontFixed, hFont; + +extern joypad_state joypad1; + +#define KEY_UP VK_UP +#define KEY_DOWN VK_DOWN +#define KEY_LEFT VK_LEFT +#define KEY_RIGHT VK_RIGHT +#define KEY_SHIFT VK_RSHIFT +#define KEY_ENTER VK_RETURN +#define KEY_A 'A' +#define KEY_S 'S' +#define KEY_D 'D' +#define KEY_Z 'Z' +#define KEY_X 'X' +#define KEY_C 'C' + +#define KeyState(key) ((GetAsyncKeyState(key) & 0x8000)?1:0) + +void UpdateJoypad(void) { + joypad1.up = KeyState(KEY_UP ); + joypad1.down = KeyState(KEY_DOWN ); + joypad1.left = KeyState(KEY_LEFT ); + joypad1.right = KeyState(KEY_RIGHT); + joypad1.select = KeyState(KEY_SHIFT); + joypad1.start = KeyState(KEY_ENTER); + joypad1.y = KeyState(KEY_A ); + joypad1.b = KeyState(KEY_Z ); + joypad1.x = KeyState(KEY_S ); + joypad1.a = KeyState(KEY_X ); + joypad1.l = KeyState(KEY_D ); + joypad1.r = KeyState(KEY_C ); +} + +void alert(char *s, ...) { +char str[4096]; +va_list args; + va_start(args, s); + vsprintf(str, s, args); + va_end(args); + MessageBox(0, str, "bsnes", MB_OK); +} + +void FixWindowSize(HWND hwnd, ulong width, ulong height, ulong px = null, ulong py = null) { +RECT rc; +ulong x, y, wx, wy; + ShowWindow(hwnd, SW_HIDE); + SetWindowPos(hwnd, 0, 0, 0, width, height, SWP_NOZORDER); + GetClientRect(hwnd, &rc); + x = width + width - (rc.right - rc.left); + y = height + height - (rc.bottom - rc.top); + wx = (GetSystemMetrics(SM_CXSCREEN) - x) / 2; + wy = (GetSystemMetrics(SM_CYSCREEN) - y) / 2; + if(px == null || py == null) { + SetWindowPos(hwnd, 0, wx, wy, x, y, SWP_NOZORDER); + } else { + SetWindowPos(hwnd, 0, px, py, x, y, SWP_NOZORDER); + } +} + +HBRUSH black_brush; + +long __stdcall wndprocMain(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); + +void RegisterMainWindow() { +WNDCLASS wc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hbrBackground = black_brush; + wc.hCursor = LoadCursor(0, IDC_ARROW); + wc.hIcon = LoadIcon(0, IDI_APPLICATION); + wc.hInstance = GetModuleHandle(0); + wc.lpfnWndProc = wndprocMain; + wc.lpszClassName = "bsnes"; + wc.lpszMenuName = 0; + wc.style = CS_HREDRAW | CS_VREDRAW; + RegisterClass(&wc); +} + +void CreateMainMenu(void) { +HMENU hsubmenu, hbranchmenu; + hmenuMain = CreateMenu(); + + hsubmenu = CreatePopupMenu(); + AppendMenu(hsubmenu, MF_STRING, MENU_FILE_LOAD, "&Load ROM"); + AppendMenu(hsubmenu, MF_STRING, MENU_FILE_RESET, "&Reset"); + AppendMenu(hsubmenu, MF_SEPARATOR, 0, ""); + AppendMenu(hsubmenu, MF_STRING, MENU_FILE_EXIT, "E&xit"); + AppendMenu(hmenuMain, MF_STRING | MF_POPUP, (unsigned int)hsubmenu, "&File"); + + hsubmenu = CreatePopupMenu(); + + hbranchmenu = CreatePopupMenu(); + AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_VIDEOMODE_512x448w, "512x448 Windowed"); + AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_VIDEOMODE_640x480f, "640x480 Fullscreen"); + AppendMenu(hsubmenu, MF_STRING | MF_POPUP, (unsigned int)hbranchmenu, "&Video Mode"); + + hbranchmenu = CreatePopupMenu(); + AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_FRAMESKIP_OFF, "Off"); + AppendMenu(hbranchmenu, MF_SEPARATOR, 0, ""); + AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_FRAMESKIP_1, "1"); + AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_FRAMESKIP_2, "2"); + AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_FRAMESKIP_3, "3"); + AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_FRAMESKIP_4, "4"); + AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_FRAMESKIP_5, "5"); + AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_FRAMESKIP_6, "6"); + AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_FRAMESKIP_7, "7"); + AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_FRAMESKIP_8, "8"); + AppendMenu(hbranchmenu, MF_STRING, MENU_SETTINGS_FRAMESKIP_9, "9"); + AppendMenu(hsubmenu, MF_STRING | MF_POPUP, (unsigned int)hbranchmenu, "&Frameskip"); + + AppendMenu(hsubmenu, MF_SEPARATOR, 0, ""); + AppendMenu(hsubmenu, MF_STRING | (debug_get_state() == DEBUGMODE_WAIT)?MF_CHECKED:MF_UNCHECKED, MENU_SETTINGS_DEBUGGER, "&Debug Mode"); + AppendMenu(hmenuMain, MF_STRING | MF_POPUP, (unsigned int)hsubmenu, "&Settings"); + + hsubmenu = CreatePopupMenu(); + AppendMenu(hsubmenu, MF_STRING, MENU_HELP_ABOUT, "&About"); + AppendMenu(hmenuMain, MF_STRING | MF_POPUP, (unsigned int)hsubmenu, "&Help"); + + CheckMenuItem(hmenuMain, MENU_SETTINGS_VIDEOMODE_512x448w, MF_CHECKED); + CheckMenuItem(hmenuMain, MENU_SETTINGS_FRAMESKIP_OFF + render.frame_skip, MF_CHECKED); +} + +void CreateMainWindow(void) { + hwndMain = CreateWindow("bsnes", BSNES_TITLE, WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, + 0, 0, 640, 480, 0, 0, GetModuleHandle(0), 0); + CreateMainMenu(); +} + +void SetMainWindowPos(bool update_style) { + if(render.fullscreen == true) { + if(update_style == true) { + SetWindowLong(hwndMain, GWL_STYLE, WS_POPUP); + SetWindowLong(hwndMain, GWL_EXSTYLE, WS_EX_TOPMOST); + SetWindowPos(hwndMain, HWND_TOPMOST, 0, 0, 640, 480, 0); + } + if(render.show_menu == true) { + ShowCursor(TRUE); + } else { + ShowCursor(FALSE); + } + } else { + if(update_style == true) { + SetWindowLong(hwndMain, GWL_STYLE, WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX); + SetWindowLong(hwndMain, GWL_EXSTYLE, 0); + } + FixWindowSize(hwndMain, render.width, render.height); + } +} + +void UpdateMainWindowStyle(bool update_style) { + if(render.fullscreen == false) { + ShowWindow(hwndMain, SW_HIDE); + } + + if(render.show_menu == true) { + SetMenu(hwndMain, hmenuMain); + SetMainWindowPos(update_style); + } else { + SetMenu(hwndMain, 0); + SetMainWindowPos(update_style); + } + + if(render.fullscreen == false) { + ShowWindow(hwndMain, SW_NORMAL); + } +} + +HWND NewWindow(WNDPROC wndproc, char *classname, char *title, ulong color, ulong width, ulong height) { +WNDCLASS wc; +HWND hwnd; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hbrBackground = (color == null)?(HBRUSH)(COLOR_WINDOW):black_brush; + wc.hCursor = LoadCursor(0, IDC_ARROW); + wc.hIcon = LoadIcon(0, IDI_APPLICATION); + wc.hInstance = GetModuleHandle(0); + wc.lpfnWndProc = wndproc; + wc.lpszClassName = classname; + wc.lpszMenuName = 0; + wc.style = CS_HREDRAW | CS_VREDRAW; + RegisterClass(&wc); + + hwnd = CreateWindow(classname, title, WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, + 0, 0, width, height, 0, 0, wc.hInstance, 0); + return hwnd; +} + +#include "render.cpp" + +bool GUIOpenFile(char *fn) { +OPENFILENAME ofn; + memset(&ofn, 0, sizeof(ofn)); + + ofn.lStructSize = sizeof(ofn); + ofn.hwndOwner = hwndMain; + ofn.lpstrFilter = "SNES ROM Images (*.smc;*.swc;*.fig;*.ufo;*.gd3;*.078)\0*.smc;*.swc;*.fig;*.ufo;*.gd3;*.078\0All Files (*.*)\0*.*\0"; + ofn.lpstrFile = fn; + ofn.nMaxFile = MAX_PATH; + ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST; + ofn.lpstrDefExt = "smc"; + + if(GetOpenFileName(&ofn)) { + return true; + } else { + return false; + } +} + +void EnableDebugger(byte first_time); +void DisableDebugger(void); + +long __stdcall wndprocMain(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { +char fn[MAX_PATH]; +bool result; + switch(msg) { + case WM_DESTROY: + PostQuitMessage(0); + break; + case WM_PAINT: + UpdateDisplay_NoRender(); + break; + case WM_KEYDOWN: + switch(wparam) { + case VK_ESCAPE: + if(render.show_menu == true) { + render.show_menu = false; + } else { + render.show_menu = true; + } + UpdateMainWindowStyle(false); + break; + } + break; + case WM_COMMAND: + switch(LOWORD(wparam)) { + case MENU_FILE_LOAD: + strcpy(fn, ""); + result = GUIOpenFile(fn); + if(result == true) { + strcpy(emu_state.rom_name, fn); + fn[strlen(fn) - 4] = 0; + strcat(fn, ".srm"); + strcpy(emu_state.sram_name, fn); + gx816->PowerOn(0); + gx816->LoadROM(); + if(debug_get_state() == DEBUGMODE_NOROM) { + debug_set_state(DEBUGMODE_DISABLED); + } + } + break; + case MENU_FILE_RESET: + gx816->Reset(); + break; + case MENU_FILE_EXIT: + PostQuitMessage(0); + break; + case MENU_SETTINGS_VIDEOMODE_512x448w: + video_setmode(false, 512, 448); + CheckMenuItem(hmenuMain, MENU_SETTINGS_VIDEOMODE_512x448w, MF_CHECKED); + CheckMenuItem(hmenuMain, MENU_SETTINGS_VIDEOMODE_640x480f, MF_UNCHECKED); + break; + case MENU_SETTINGS_VIDEOMODE_640x480f: + video_setmode(true, 512, 448); + CheckMenuItem(hmenuMain, MENU_SETTINGS_VIDEOMODE_512x448w, MF_UNCHECKED); + CheckMenuItem(hmenuMain, MENU_SETTINGS_VIDEOMODE_640x480f, MF_CHECKED); + break; + case MENU_SETTINGS_FRAMESKIP_OFF: + case MENU_SETTINGS_FRAMESKIP_1: + case MENU_SETTINGS_FRAMESKIP_2: + case MENU_SETTINGS_FRAMESKIP_3: + case MENU_SETTINGS_FRAMESKIP_4: + case MENU_SETTINGS_FRAMESKIP_5: + case MENU_SETTINGS_FRAMESKIP_6: + case MENU_SETTINGS_FRAMESKIP_7: + case MENU_SETTINGS_FRAMESKIP_8: + case MENU_SETTINGS_FRAMESKIP_9: + CheckMenuItem(hmenuMain, MENU_SETTINGS_FRAMESKIP_OFF + render.frame_skip, MF_UNCHECKED); + render.frame_skip = LOWORD(wparam) - MENU_SETTINGS_FRAMESKIP_OFF; + render.frame_count = 0; + CheckMenuItem(hmenuMain, MENU_SETTINGS_FRAMESKIP_OFF + render.frame_skip, MF_CHECKED); + break; + case MENU_SETTINGS_DEBUGGER: + if(debug_get_state() == DEBUGMODE_NOROM)break; + + if(debug_get_state() == DEBUGMODE_DISABLED) { + CheckMenuItem(hmenuMain, MENU_SETTINGS_DEBUGGER, MF_CHECKED); + EnableDebugger(0); + } else { + CheckMenuItem(hmenuMain, MENU_SETTINGS_DEBUGGER, MF_UNCHECKED); + DisableDebugger(); + } + break; + case MENU_HELP_ABOUT: + MessageBox(hwndMain, "bsnes -- written by byuu", "About", MB_OK); + break; + } + break; + } + + return DefWindowProc(hwnd, msg, wparam, lparam); +} + +void CreateFonts(void) { +HDC hdc; +long height; + hdc = GetDC(0); + height = -MulDiv(8, GetDeviceCaps(hdc, LOGPIXELSY), 72); + ReleaseDC(0, hdc); + hFontFixed = CreateFont(height, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Courier New"); + + hdc = GetDC(0); + height = -MulDiv(8, GetDeviceCaps(hdc, LOGPIXELSY), 72); + ReleaseDC(0, hdc); + hFont = CreateFont(height, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Tahoma"); +} + +#include "gui_cpu.cpp" +#include "gui_mem.cpp" +#include "gui_bp.cpp" +#include "gui_bgtoggle.cpp" + +void EnableDebugger(byte first_time) { + debug_set_state(DEBUGMODE_WAIT); + + hwndDCPU = NewWindow(wndprocDCPU, "bsnes_cpu", "g65816 cpu debugger", null, DCPU_WIDTH, DCPU_HEIGHT); + CreateDCPU(); + FixWindowSize(hwndDCPU, DCPU_WIDTH, DCPU_HEIGHT, 0, 1024 - 320); + + hwndDMEM = NewWindow(wndprocDMEM, "bsnes_mem", "g65816 memory editor -- snes memory mode", null, 495, 238); + CreateDMEM(); + FixWindowSize(hwndDMEM, 495, 238, 316, 441); + + hwndDBP = NewWindow(wndprocDBP, "bsnes_bp", "g65816 breakpoint editor", null, 310, 238); + CreateDBP(); + FixWindowSize(hwndDBP, 310, 238, 0, 441); + + hwndDBGToggle = NewWindow(wndprocDBGToggle, "bsnes_bgtoggle", "ppu bg toggle", null, 275, 90); + CreateDBGToggle(); + FixWindowSize(hwndDBGToggle, 275, 90, 0, 326); + + FixWindowSize(hwndMain, 256, 224, 681, 1024 - 320); + + ShowWindow(hwndDCPU, SW_NORMAL); + ShowWindow(hwndDMEM, SW_NORMAL); + ShowWindow(hwndDBP, SW_NORMAL); + ShowWindow(hwndDBGToggle, SW_NORMAL); + ShowWindow(hwndMain, SW_NORMAL); + + if(first_time == 0) { + debug_refresh_mem(); + debug_refresh_bp(); + debug_update_cycles(); + dprintf("* Debugger Enabled"); + UpdateDisplay(); + } +} + +void DisableDebugger(void) { + debug_set_state(DEBUGMODE_DISABLED); + + DestroyWindow(hwndDCPU); + DestroyWindow(hwndDBP); + DestroyWindow(hwndDMEM); + DestroyWindow(hwndDBGToggle); +} + +void __winmain(void) { +MSG msg; + CreateFonts(); + black_brush = CreateSolidBrush(RGB(0, 0, 0)); + RegisterMainWindow(); + CreateMainWindow(); + UpdateMainWindowStyle(false); + + if(debug_get_state() == DEBUGMODE_WAIT) { + EnableDebugger(1); + } + video_setmode(render.fullscreen, render.width, render.height); + + InitDisplay(); + CreateColorTable(); + SelectRenderer(); + + InitSNES(); + + UpdateDisplay(); + UpdateDisplay(); + + while(1) { + if(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) { + if(msg.message == WM_QUIT)break; + TranslateMessage(&msg); + DispatchMessage(&msg); + } else { + RunSNES(); + } + } +} diff --git a/bsnes/win/gui_bgtoggle.cpp b/bsnes/win/gui_bgtoggle.cpp new file mode 100644 index 00000000..40a05344 --- /dev/null +++ b/bsnes/win/gui_bgtoggle.cpp @@ -0,0 +1,149 @@ +#define DBGTOGGLE_BG1ENABLE 100 +#define DBGTOGGLE_BG1ENABLE0 101 +#define DBGTOGGLE_BG1ENABLE1 102 +#define DBGTOGGLE_BG2ENABLE 103 +#define DBGTOGGLE_BG2ENABLE0 104 +#define DBGTOGGLE_BG2ENABLE1 105 +#define DBGTOGGLE_BG3ENABLE 106 +#define DBGTOGGLE_BG3ENABLE0 107 +#define DBGTOGGLE_BG3ENABLE1 108 +#define DBGTOGGLE_BG4ENABLE 109 +#define DBGTOGGLE_BG4ENABLE0 110 +#define DBGTOGGLE_BG4ENABLE1 111 +#define DBGTOGGLE_OAMENABLE 112 +#define DBGTOGGLE_OAMENABLE0 113 +#define DBGTOGGLE_OAMENABLE1 114 +#define DBGTOGGLE_OAMENABLE2 115 +#define DBGTOGGLE_OAMENABLE3 116 + +#define BGTOGGLE_CLICK(src, val) \ + case src: \ + state = SendDlgItemMessage(hwndDBGToggle, src, BM_GETCHECK, 0, 0); \ + if(state == 0) { \ + val = true; \ + SendDlgItemMessage(hwndDBGToggle, src, BM_SETCHECK, 1, 0); \ + } else { \ + val = false; \ + SendDlgItemMessage(hwndDBGToggle, src, BM_SETCHECK, 0, 0); \ + } \ + break + +HWND hwndDBGToggle; + +long __stdcall wndprocDBGToggle(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { +int state; + if(msg == WM_DESTROY || msg == WM_CLOSE)return 0; + if(msg == WM_COMMAND) { + if(HIWORD(wparam) == BN_CLICKED) { + switch(LOWORD(wparam)) { + BGTOGGLE_CLICK(DBGTOGGLE_BG1ENABLE, render.bg1_enabled[DEBUG_BGENABLED_ALL ]); + BGTOGGLE_CLICK(DBGTOGGLE_BG1ENABLE0, render.bg1_enabled[DEBUG_BGENABLED_PRI0]); + BGTOGGLE_CLICK(DBGTOGGLE_BG1ENABLE1, render.bg1_enabled[DEBUG_BGENABLED_PRI1]); + BGTOGGLE_CLICK(DBGTOGGLE_BG2ENABLE, render.bg2_enabled[DEBUG_BGENABLED_ALL ]); + BGTOGGLE_CLICK(DBGTOGGLE_BG2ENABLE0, render.bg2_enabled[DEBUG_BGENABLED_PRI0]); + BGTOGGLE_CLICK(DBGTOGGLE_BG2ENABLE1, render.bg2_enabled[DEBUG_BGENABLED_PRI1]); + BGTOGGLE_CLICK(DBGTOGGLE_BG3ENABLE, render.bg3_enabled[DEBUG_BGENABLED_ALL ]); + BGTOGGLE_CLICK(DBGTOGGLE_BG3ENABLE0, render.bg3_enabled[DEBUG_BGENABLED_PRI0]); + BGTOGGLE_CLICK(DBGTOGGLE_BG3ENABLE1, render.bg3_enabled[DEBUG_BGENABLED_PRI1]); + BGTOGGLE_CLICK(DBGTOGGLE_BG4ENABLE, render.bg4_enabled[DEBUG_BGENABLED_ALL ]); + BGTOGGLE_CLICK(DBGTOGGLE_BG4ENABLE0, render.bg4_enabled[DEBUG_BGENABLED_PRI0]); + BGTOGGLE_CLICK(DBGTOGGLE_BG4ENABLE1, render.bg4_enabled[DEBUG_BGENABLED_PRI1]); + BGTOGGLE_CLICK(DBGTOGGLE_OAMENABLE, render.oam_enabled[DEBUG_BGENABLED_ALL ]); + BGTOGGLE_CLICK(DBGTOGGLE_OAMENABLE0, render.oam_enabled[DEBUG_BGENABLED_PRI0]); + BGTOGGLE_CLICK(DBGTOGGLE_OAMENABLE1, render.oam_enabled[DEBUG_BGENABLED_PRI1]); + BGTOGGLE_CLICK(DBGTOGGLE_OAMENABLE2, render.oam_enabled[DEBUG_BGENABLED_PRI2]); + BGTOGGLE_CLICK(DBGTOGGLE_OAMENABLE3, render.oam_enabled[DEBUG_BGENABLED_PRI3]); + } + } + } + return DefWindowProc(hwnd, msg, wparam, lparam); +} + +void CreateDBGToggle(void) { +int x, y, wl, wr, h; + x = 5; + y = 5; + wl = 90; + wr = 45; + h = 16; + CreateWindow("BUTTON", "Enable BG1", WS_CHILD|WS_VISIBLE|BS_CHECKBOX, x, y, wl, h, + hwndDBGToggle, (HMENU)DBGTOGGLE_BG1ENABLE, GetModuleHandle(0), 0); + x += wl; + CreateWindow("BUTTON", "Pri 0", WS_CHILD|WS_VISIBLE|BS_CHECKBOX, x, y, wr, h, + hwndDBGToggle, (HMENU)DBGTOGGLE_BG1ENABLE0, GetModuleHandle(0), 0); + x += wr; + CreateWindow("BUTTON", "Pri 1", WS_CHILD|WS_VISIBLE|BS_CHECKBOX, x, y, wr, h, + hwndDBGToggle, (HMENU)DBGTOGGLE_BG1ENABLE1, GetModuleHandle(0), 0); + + x = 5; + y += h; + CreateWindow("BUTTON", "Enable BG2", WS_CHILD|WS_VISIBLE|BS_CHECKBOX, x, y, wl, h, + hwndDBGToggle, (HMENU)DBGTOGGLE_BG2ENABLE, GetModuleHandle(0), 0); + x += wl; + CreateWindow("BUTTON", "Pri 0", WS_CHILD|WS_VISIBLE|BS_CHECKBOX, x, y, wr, h, + hwndDBGToggle, (HMENU)DBGTOGGLE_BG2ENABLE0, GetModuleHandle(0), 0); + x += wr; + CreateWindow("BUTTON", "Pri 1", WS_CHILD|WS_VISIBLE|BS_CHECKBOX, x, y, wr, h, + hwndDBGToggle, (HMENU)DBGTOGGLE_BG2ENABLE1, GetModuleHandle(0), 0); + + x = 5; + y += h; + CreateWindow("BUTTON", "Enable BG3", WS_CHILD|WS_VISIBLE|BS_CHECKBOX, x, y, wl, h, + hwndDBGToggle, (HMENU)DBGTOGGLE_BG3ENABLE, GetModuleHandle(0), 0); + x += wl; + CreateWindow("BUTTON", "Pri 0", WS_CHILD|WS_VISIBLE|BS_CHECKBOX, x, y, wr, h, + hwndDBGToggle, (HMENU)DBGTOGGLE_BG3ENABLE0, GetModuleHandle(0), 0); + x += wr; + CreateWindow("BUTTON", "Pri 1", WS_CHILD|WS_VISIBLE|BS_CHECKBOX, x, y, wr, h, + hwndDBGToggle, (HMENU)DBGTOGGLE_BG3ENABLE1, GetModuleHandle(0), 0); + + x = 5; + y += h; + CreateWindow("BUTTON", "Enable BG4", WS_CHILD|WS_VISIBLE|BS_CHECKBOX, x, y, wl, h, + hwndDBGToggle, (HMENU)DBGTOGGLE_BG4ENABLE, GetModuleHandle(0), 0); + x += wl; + CreateWindow("BUTTON", "Pri 0", WS_CHILD|WS_VISIBLE|BS_CHECKBOX, x, y, wr, h, + hwndDBGToggle, (HMENU)DBGTOGGLE_BG4ENABLE0, GetModuleHandle(0), 0); + x += wr; + CreateWindow("BUTTON", "Pri 1", WS_CHILD|WS_VISIBLE|BS_CHECKBOX, x, y, wr, h, + hwndDBGToggle, (HMENU)DBGTOGGLE_BG4ENABLE1, GetModuleHandle(0), 0); + + x = 5; + y += h; + CreateWindow("BUTTON", "Enable OAM", WS_CHILD|WS_VISIBLE|BS_CHECKBOX, x, y, wl, h, + hwndDBGToggle, (HMENU)DBGTOGGLE_OAMENABLE, GetModuleHandle(0), 0); + x += wl; + CreateWindow("BUTTON", "Pri 0", WS_CHILD|WS_VISIBLE|BS_CHECKBOX, x, y, wr, h, + hwndDBGToggle, (HMENU)DBGTOGGLE_OAMENABLE0, GetModuleHandle(0), 0); + x += wr; + CreateWindow("BUTTON", "Pri 1", WS_CHILD|WS_VISIBLE|BS_CHECKBOX, x, y, wr, h, + hwndDBGToggle, (HMENU)DBGTOGGLE_OAMENABLE1, GetModuleHandle(0), 0); + x += wr; + CreateWindow("BUTTON", "Pri 2", WS_CHILD|WS_VISIBLE|BS_CHECKBOX, x, y, wr, h, + hwndDBGToggle, (HMENU)DBGTOGGLE_OAMENABLE2, GetModuleHandle(0), 0); + x += wr; + CreateWindow("BUTTON", "Pri 3", WS_CHILD|WS_VISIBLE|BS_CHECKBOX, x, y, wr, h, + hwndDBGToggle, (HMENU)DBGTOGGLE_OAMENABLE3, GetModuleHandle(0), 0); + + SendDlgItemMessage(hwndDBGToggle, DBGTOGGLE_BG1ENABLE, WM_SETFONT, (WPARAM)hFont, TRUE); + SendDlgItemMessage(hwndDBGToggle, DBGTOGGLE_BG1ENABLE0, WM_SETFONT, (WPARAM)hFont, TRUE); + SendDlgItemMessage(hwndDBGToggle, DBGTOGGLE_BG1ENABLE1, WM_SETFONT, (WPARAM)hFont, TRUE); + SendDlgItemMessage(hwndDBGToggle, DBGTOGGLE_BG2ENABLE, WM_SETFONT, (WPARAM)hFont, TRUE); + SendDlgItemMessage(hwndDBGToggle, DBGTOGGLE_BG2ENABLE0, WM_SETFONT, (WPARAM)hFont, TRUE); + SendDlgItemMessage(hwndDBGToggle, DBGTOGGLE_BG2ENABLE1, WM_SETFONT, (WPARAM)hFont, TRUE); + SendDlgItemMessage(hwndDBGToggle, DBGTOGGLE_BG3ENABLE, WM_SETFONT, (WPARAM)hFont, TRUE); + SendDlgItemMessage(hwndDBGToggle, DBGTOGGLE_BG3ENABLE0, WM_SETFONT, (WPARAM)hFont, TRUE); + SendDlgItemMessage(hwndDBGToggle, DBGTOGGLE_BG3ENABLE1, WM_SETFONT, (WPARAM)hFont, TRUE); + SendDlgItemMessage(hwndDBGToggle, DBGTOGGLE_BG4ENABLE, WM_SETFONT, (WPARAM)hFont, TRUE); + SendDlgItemMessage(hwndDBGToggle, DBGTOGGLE_BG4ENABLE0, WM_SETFONT, (WPARAM)hFont, TRUE); + SendDlgItemMessage(hwndDBGToggle, DBGTOGGLE_BG4ENABLE1, WM_SETFONT, (WPARAM)hFont, TRUE); + SendDlgItemMessage(hwndDBGToggle, DBGTOGGLE_OAMENABLE, WM_SETFONT, (WPARAM)hFont, TRUE); + SendDlgItemMessage(hwndDBGToggle, DBGTOGGLE_OAMENABLE0, WM_SETFONT, (WPARAM)hFont, TRUE); + SendDlgItemMessage(hwndDBGToggle, DBGTOGGLE_OAMENABLE1, WM_SETFONT, (WPARAM)hFont, TRUE); + SendDlgItemMessage(hwndDBGToggle, DBGTOGGLE_OAMENABLE2, WM_SETFONT, (WPARAM)hFont, TRUE); + SendDlgItemMessage(hwndDBGToggle, DBGTOGGLE_OAMENABLE3, WM_SETFONT, (WPARAM)hFont, TRUE); + + for(int i=DBGTOGGLE_BG1ENABLE;i<=DBGTOGGLE_OAMENABLE3;i++) { + SendDlgItemMessage(hwndDBGToggle, i, BM_SETCHECK, 1, 0); + } +} diff --git a/bsnes/win/gui_bp.cpp b/bsnes/win/gui_bp.cpp new file mode 100644 index 00000000..d488ea9c --- /dev/null +++ b/bsnes/win/gui_bp.cpp @@ -0,0 +1,131 @@ +#define DBP_LIST 100 +#define DBP_ADDBP 101 +#define DBP_REMBP 102 +#define DBP_CLRBP 103 +#define DBP_STATIC1 104 +#define DBP_BPNUM 105 +#define DBP_BPOFFSET 106 +#define DBP_BPR 107 +#define DBP_BPW 108 +#define DBP_BPX 109 +#define DBP_BPV 110 +#define DBP_BPVAL 111 + +HWND hwndDBP; + +void debug_refresh_bp(void) { +char str[64*16], t[64]; + if(debug_get_state() == DEBUGMODE_DISABLED)return; + + strcpy(str, ""); + for(int i=0;i<16;i++) { + sprintf(t, "%0.2d: ", i); + strcat(str, t); + if(gx816->bp_list[i].flags == BP_OFF) { + strcat(str, "------ ---- -- (Disabled)"); + } else { + sprintf(t, "%0.6x %c%c%c%c ", gx816->bp_list[i].offset, + (gx816->bp_list[i].flags & BP_READ )?'R':'r', + (gx816->bp_list[i].flags & BP_WRITE)?'W':'w', + (gx816->bp_list[i].flags & BP_EXEC )?'X':'x', + (gx816->bp_list[i].flags & BP_VAL )?'V':'v'); + strcat(str, t); + if(gx816->bp_list[i].flags & BP_VAL) { + sprintf(t, "%0.2x ", gx816->bp_list[i].value); + strcat(str, t); + } else strcat(str, "-- "); + sprintf(t, "%10d", gx816->bp_list[i].hit_count); + strcat(str, t); + } + if(i != 15)strcat(str, "\n"); + } + + SetDlgItemText(hwndDBP, DBP_LIST, str); +} + +long __stdcall wndprocDBP(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { +char str[256]; +ulong num, offset, val, flags; +int i; + if(msg == WM_DESTROY || msg == WM_CLOSE)return 0; //don't allow debugger to be closed (yet) + + if(msg == WM_COMMAND) { + switch(LOWORD(wparam)) { + case DBP_ADDBP: + GetDlgItemText(hwndDBP, DBP_BPNUM, str, 255); + num = strdec(str); + GetDlgItemText(hwndDBP, DBP_BPOFFSET, str, 255); + offset = strhex(str) & 0xffffff; + GetDlgItemText(hwndDBP, DBP_BPVAL, str, 255); + val = strhex(str); + + flags = 0; + if(SendMessage(GetDlgItem(hwndDBP, DBP_BPR), BM_GETCHECK, 0, 0))flags |= BP_READ; + if(SendMessage(GetDlgItem(hwndDBP, DBP_BPW), BM_GETCHECK, 0, 0))flags |= BP_WRITE; + if(SendMessage(GetDlgItem(hwndDBP, DBP_BPX), BM_GETCHECK, 0, 0))flags |= BP_EXEC; + if(SendMessage(GetDlgItem(hwndDBP, DBP_BPV), BM_GETCHECK, 0, 0))flags |= BP_VAL; + + if(num > 15)dprintf("Invalid breakpoint #: %d -- 0-15 valid", num); + else { + gx816->bp_list[num].offset = offset; + gx816->bp_list[num].flags = flags; + if(gx816->bp_list[num].flags & BP_VAL)gx816->bp_list[num].value = val; + else gx816->bp_list[num].value = 0; + gx816->bp_list[num].hit_count = 0; + } + debugger.refresh_bp = true; + break; + case DBP_REMBP: + GetDlgItemText(hwndDBP, DBP_BPNUM, str, 255); + num = strdec(str); + if(num > 15)dprintf("Invalid breakpoint #: %d -- 0-15 valid", num); + else { + gx816->bp_list[num].offset = 0; + gx816->bp_list[num].flags = BP_OFF; + gx816->bp_list[num].value = 0; + gx816->bp_list[num].hit_count = 0; + } + debugger.refresh_bp = true; + break; + case DBP_CLRBP: + for(i=0;i<16;i++) { + gx816->bp_list[i].offset = 0; + gx816->bp_list[i].flags = BP_OFF; + gx816->bp_list[i].value = 0; + gx816->bp_list[i].hit_count = 0; + } + debugger.refresh_bp = true; + break; + } + } + + return DefWindowProc(hwnd, msg, wparam, lparam); +} + +void CreateDBP(void) { + CreateWindowEx(WS_EX_STATICEDGE, "STATIC", "", WS_CHILD|WS_VISIBLE, 5, 5, 205, 228, hwndDBP, (HMENU)DBP_LIST, GetModuleHandle(0), 0); + CreateWindow("STATIC", "BP #: Offset:", WS_CHILD|WS_VISIBLE, 215, 5, 90, 15, hwndDBP, (HMENU)DBP_STATIC1, GetModuleHandle(0), 0); + CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", "00", WS_CHILD|WS_VISIBLE, 215, 20, 30, 23, hwndDBP, (HMENU)DBP_BPNUM, GetModuleHandle(0), 0); + CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", "000000", WS_CHILD|WS_VISIBLE, 245, 20, 60, 23, hwndDBP, (HMENU)DBP_BPOFFSET, GetModuleHandle(0), 0); + CreateWindow("BUTTON", "R", WS_CHILD|WS_VISIBLE|BS_AUTOCHECKBOX, 215, 44, 30, 18, hwndDBP, (HMENU)DBP_BPR, GetModuleHandle(0), 0); + CreateWindow("BUTTON", "W", WS_CHILD|WS_VISIBLE|BS_AUTOCHECKBOX, 245, 44, 30, 18, hwndDBP, (HMENU)DBP_BPW, GetModuleHandle(0), 0); + CreateWindow("BUTTON", "X", WS_CHILD|WS_VISIBLE|BS_AUTOCHECKBOX, 275, 44, 30, 18, hwndDBP, (HMENU)DBP_BPX, GetModuleHandle(0), 0); + CreateWindow("BUTTON", "V", WS_CHILD|WS_VISIBLE|BS_AUTOCHECKBOX, 215, 65, 30, 18, hwndDBP, (HMENU)DBP_BPV, GetModuleHandle(0), 0); + CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", "00", WS_CHILD|WS_VISIBLE, 245, 62, 60, 23, hwndDBP, (HMENU)DBP_BPVAL, GetModuleHandle(0), 0); + CreateWindow("BUTTON", "Set BP", WS_CHILD|WS_VISIBLE, 215, 88, 90, 20, hwndDBP, (HMENU)DBP_ADDBP, GetModuleHandle(0), 0); + CreateWindow("BUTTON", "Clear BP", WS_CHILD|WS_VISIBLE, 215, 108, 90, 20, hwndDBP, (HMENU)DBP_REMBP, GetModuleHandle(0), 0); + CreateWindow("BUTTON", "Clear All BPs", WS_CHILD|WS_VISIBLE, 215, 128, 90, 20, hwndDBP, (HMENU)DBP_CLRBP, GetModuleHandle(0), 0); + + SendDlgItemMessage(hwndDBP, DBP_LIST, WM_SETFONT, (WPARAM)hFontFixed, TRUE); + SendDlgItemMessage(hwndDBP, DBP_STATIC1, WM_SETFONT, (WPARAM)hFont, TRUE); + SendDlgItemMessage(hwndDBP, DBP_BPNUM, WM_SETFONT, (WPARAM)hFontFixed, TRUE); + SendDlgItemMessage(hwndDBP, DBP_BPOFFSET, WM_SETFONT, (WPARAM)hFontFixed, TRUE); + SendDlgItemMessage(hwndDBP, DBP_BPR, WM_SETFONT, (WPARAM)hFontFixed, TRUE); + SendDlgItemMessage(hwndDBP, DBP_BPW, WM_SETFONT, (WPARAM)hFontFixed, TRUE); + SendDlgItemMessage(hwndDBP, DBP_BPX, WM_SETFONT, (WPARAM)hFontFixed, TRUE); + SendDlgItemMessage(hwndDBP, DBP_BPV, WM_SETFONT, (WPARAM)hFontFixed, TRUE); + SendDlgItemMessage(hwndDBP, DBP_BPVAL, WM_SETFONT, (WPARAM)hFontFixed, TRUE); + SendDlgItemMessage(hwndDBP, DBP_ADDBP, WM_SETFONT, (WPARAM)hFont, TRUE); + SendDlgItemMessage(hwndDBP, DBP_REMBP, WM_SETFONT, (WPARAM)hFont, TRUE); + SendDlgItemMessage(hwndDBP, DBP_CLRBP, WM_SETFONT, (WPARAM)hFont, TRUE); +} diff --git a/bsnes/win/gui_cpu.cpp b/bsnes/win/gui_cpu.cpp new file mode 100644 index 00000000..d1d30917 --- /dev/null +++ b/bsnes/win/gui_cpu.cpp @@ -0,0 +1,208 @@ +#define DCPU_WIDTH 675 +#define DCPU_HEIGHT 295 + +#define DCPU_DISAS 100 +#define DCPU_RUN 101 +#define DCPU_STEP 102 +#define DCPU_PROCEED 103 +#define DCPU_INFO 104 +#define DCPU_TRACE 105 +#define DCPU_CYCLES 106 + +HWND hwndDCPU; + +char dcpu_disas_mem[20][256]; + +void dprintf(char *s, ...) { +char str[256*20]; +va_list args; +int i; + if(debug_get_state() == DEBUGMODE_DISABLED)return; + + va_start(args, s); + vsprintf(str, s, args); + va_end(args); + + if(debugger.trace_enabled == true) { + fprintf(debugger.trace_fp, "%s\r\n", str); + } + +//if(debug_get_state() != DEBUGMODE_RUN) { + for(i=0;i<19;i++)strcpy(dcpu_disas_mem[i], dcpu_disas_mem[i+1]); + strcpy(dcpu_disas_mem[19], str); + + strcpy(str, ""); + for(i=0;i<20;i++) { + strcat(str, dcpu_disas_mem[i]); + if(i != 20)strcat(str, "\n"); + } + + SetDlgItemText(hwndDCPU, DCPU_DISAS, str); +//} +} + +void debug_update_cycles(void) { +char str[256]; + if(debug_get_state() == DEBUGMODE_DISABLED || + debug_get_state() == DEBUGMODE_RUN)return; + + sprintf(str, "Scanline: %d\nCC: %d", ppu.vline_pos, snes_time->master_cycles); + SetDlgItemText(hwndDCPU, DCPU_CYCLES, str); +} + +void debug_set_state(byte state) { + debugger.mode = state; + if(hwndDCPU && state != DEBUGMODE_DISABLED) { + if(state == DEBUGMODE_WAIT || state == DEBUGMODE_STEP) { + SetDlgItemText(hwndDCPU, DCPU_RUN, "Run"); + } else { + SetDlgItemText(hwndDCPU, DCPU_RUN, "Stop"); + } + } + + if(state == DEBUGMODE_DISABLED) { + RunSNES = RunSNES_NoDebug; + } else { + RunSNES = RunSNES_Debug; + } +} + +long __stdcall wndprocDCPU(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { +FILE *fp; + if(msg == WM_DESTROY || msg == WM_CLOSE)return 0; //don't allow debugger to be closed (yet) + + if(msg == WM_COMMAND) { + switch(LOWORD(wparam)) { + case DCPU_RUN: + if(debug_get_state() != DEBUGMODE_RUN) { + debug_set_state(DEBUGMODE_RUN); + SetDlgItemText(hwndDCPU, DCPU_RUN, "Stop"); + } else { + debug_set_state(DEBUGMODE_WAIT); + disas_g65816_op(); + SetDlgItemText(hwndDCPU, DCPU_RUN, "Run"); + } + break; + case DCPU_STEP: + if(debug_get_state() == DEBUGMODE_WAIT) { + debug_set_state(DEBUGMODE_STEP); + } + break; + case DCPU_PROCEED: + fp = fopen("data.bin", "wb"); + fwrite(ppu.vram, 0x10000, 1, fp); + fwrite(gx816->wram, 0x20000, 1, fp); + fwrite(ppu.cgram, 512, 1, fp); + fwrite(ppu.oam, 544, 1, fp); + fclose(fp); + break; + case DCPU_INFO: + fp = fopen("data.txt", "w"); + fprintf(fp, + "Memory speed: %s\n" + "Mode: %d\n" + "BGs On: %d%d%d%d%d\n" + "Sub BGs On: %d%d%d%d%d\n" + "Tilemap Loc: %0.4x,%0.4x,%0.4x,%0.4x\n" + "Tiledata Loc: %0.4x,%0.4x,%0.4x,%0.4x,%0.4x\n" + "Tilemap Size: %d,%d,%d,%d\n" + "Tile Size: %d,%d,%d,%d\n" + "BG Priority Mode: %d\n" + "OAM Base Size: %d\nOAM Name Sel: %d\nBG Enabled: %d\nBG Brightness: %d\n" + "Window 1 Left: %d\nWindow 1 Right: %d\nWindow 2 Left: %d\nWindow 2 Right: %d\n" + "Window 1 Enabled: %d%d%d%d%d\nWindow 2 Enabled: %d%d%d%d%d\n" + "Window 1 Clipmode: %s,%s,%s,%s,%s\nWindow 2 Clipmode: %s,%s,%s,%s,%s\n" + "BG Windowing Enabled: %d%d%d%d%d\nSub BG Windowing Enabled: %d%d%d%d%d\n" + "BG Window Mask: %d,%d,%d,%d,%d\n" + "Color Windows Enabled: %d%d\nColor Window Clipmodes: %d,%d\n" + "Color Window Mask: %d\nColor Mask: %d\nSub Color Mask: %d\nAddsub Mode: %d\nColor Mode: %d\nColor Halve: %d\n" + "BG Color Enabled: %d%d%d%d%d%d\n" + "RGB Color Settings: %d,%d,%d\nVIRQ Counter Enabled: %s\nHIRQ Counter Enabled: %s\n" + "VIRQ Pos: %d\nHIRQ Pos: %d\n" + "Mode 7 Screen Flip: x: %s, y: %s\n" + "m7a: %0.4x (%f) m7b: %0.4x (%f) m7c: %0.4x (%f) m7d: %0.4x (%f)\n" + "m7x: %0.4x m7y: %0.4x\n", + (gx816->memory_speed == 1)?"FastROM":"SlowROM", ppu.bg_mode, + (ppu.bg_enabled[0] == true)?1:0, (ppu.bg_enabled[1] == true)?1:0, + (ppu.bg_enabled[2] == true)?1:0, (ppu.bg_enabled[3] == true)?1:0, (ppu.bg_enabled[4] == true)?1:0, + (ppu.ss_bg_enabled[0] == true)?1:0, (ppu.ss_bg_enabled[1] == true)?1:0, + (ppu.ss_bg_enabled[2] == true)?1:0, (ppu.ss_bg_enabled[3] == true)?1:0, (ppu.ss_bg_enabled[4] == true)?1:0, + ppu.bg_tilemap_loc[0], ppu.bg_tilemap_loc[1], ppu.bg_tilemap_loc[2], ppu.bg_tilemap_loc[3], + ppu.bg_tiledata_loc[0], ppu.bg_tiledata_loc[1], ppu.bg_tiledata_loc[2], ppu.bg_tiledata_loc[3], ppu.oam_tiledata_loc, + ppu.bg_tilemap_size[0], ppu.bg_tilemap_size[1], ppu.bg_tilemap_size[2], ppu.bg_tilemap_size[3], + ppu.bg_tile_size[0], ppu.bg_tile_size[1], ppu.bg_tile_size[2], ppu.bg_tile_size[3], ppu.bg_priority_mode, + ppu.oam_base_size, ppu.oam_name_sel, (ppu.display_disable == true)?0:1, ppu.display_brightness, + ppu.window1_left, ppu.window1_right, ppu.window2_left, ppu.window2_right, + (ppu.bg_window1_enabled[BG1] == true)?1:0, (ppu.bg_window1_enabled[BG2] == true)?1:0, + (ppu.bg_window1_enabled[BG3] == true)?1:0, (ppu.bg_window1_enabled[BG4] == true)?1:0, + (ppu.bg_window1_enabled[OAM] == true)?1:0, + (ppu.bg_window2_enabled[BG1] == true)?1:0, (ppu.bg_window2_enabled[BG2] == true)?1:0, + (ppu.bg_window2_enabled[BG3] == true)?1:0, (ppu.bg_window2_enabled[BG4] == true)?1:0, + (ppu.bg_window2_enabled[OAM] == true)?1:0, + (ppu.bg_window1_clipmode[BG1] == 1)?"in":"out", (ppu.bg_window1_clipmode[BG2] == 1)?"in":"out", + (ppu.bg_window1_clipmode[BG3] == 1)?"in":"out", (ppu.bg_window1_clipmode[BG4] == 1)?"in":"out", + (ppu.bg_window1_clipmode[OAM] == 1)?"in":"out", + (ppu.bg_window2_clipmode[BG1] == 1)?"in":"out", (ppu.bg_window2_clipmode[BG2] == 1)?"in":"out", + (ppu.bg_window2_clipmode[BG3] == 1)?"in":"out", (ppu.bg_window2_clipmode[BG4] == 1)?"in":"out", + (ppu.bg_window2_clipmode[BG4] == 1)?"in":"out", + (ppu.bg_windowing_enabled[BG1] == true)?1:0, (ppu.bg_windowing_enabled[BG2] == true)?1:0, + (ppu.bg_windowing_enabled[BG3] == true)?1:0, (ppu.bg_windowing_enabled[BG4] == true)?1:0, + (ppu.bg_windowing_enabled[OAM] == true)?1:0, + (ppu.ss_bg_windowing_enabled[BG1] == true)?1:0, (ppu.ss_bg_windowing_enabled[BG2] == true)?1:0, + (ppu.ss_bg_windowing_enabled[BG3] == true)?1:0, (ppu.ss_bg_windowing_enabled[BG4] == true)?1:0, + (ppu.ss_bg_windowing_enabled[OAM] == true)?1:0, + ppu.bg_window_mask[BG1], ppu.bg_window_mask[BG2], + ppu.bg_window_mask[BG3], ppu.bg_window_mask[BG4], ppu.bg_window_mask[OAM], + (ppu.color_window1_enabled == true)?1:0, (ppu.color_window2_enabled == true)?1:0, + ppu.color_window1_clipmode, ppu.color_window2_clipmode, ppu.color_window_mask, + ppu.color_mask, ppu.ss_color_mask, ppu.addsub_mode, ppu.color_mode, ppu.color_halve, + (ppu.bg_color_enabled[BG1] == true)?1:0, (ppu.bg_color_enabled[BG2] == true)?1:0, + (ppu.bg_color_enabled[BG3] == true)?1:0, (ppu.bg_color_enabled[BG4] == true)?1:0, + (ppu.bg_color_enabled[OAM] == true)?1:0, (ppu.bg_color_enabled[BACK] == true)?1:0, + ppu.color_r, ppu.color_g, ppu.color_b, + (ppu.vcounter_enabled == true)?"Yes":"No", (ppu.hcounter_enabled == true)?"Yes":"No", + ppu.virq_pos, ppu.hirq_pos, + (ppu.mode7_vflip == true)?"Yes":"No", (ppu.mode7_hflip == true)?"Yes":"No", + ppu.m7a, (float)ppu.m7a / 256, ppu.m7b, (float)ppu.m7b / 256, + ppu.m7c, (float)ppu.m7c / 256, ppu.m7d, (float)ppu.m7d / 256, + ppu.m7x, ppu.m7y + ); + fclose(fp); + break; + case DCPU_TRACE: + if(debugger.trace_enabled == true) { + fclose(debugger.trace_fp); + debugger.trace_enabled = false; + } else { + debugger.trace_fp = fopen("trace.log", "wb"); + debugger.trace_enabled = true; + } + break; + } + } + + return DefWindowProc(hwnd, msg, wparam, lparam); +} + +void CreateDCPU(void) { +int i; + for(i=0;i<20;i++)strcpy(dcpu_disas_mem[i], ""); + CreateWindowEx(WS_EX_STATICEDGE, "STATIC", "", WS_CHILD|WS_VISIBLE, 5, 5, 580, 285, hwndDCPU, (HMENU)DCPU_DISAS, GetModuleHandle(0), 0); + + CreateWindow("BUTTON", "Run", WS_CHILD|WS_VISIBLE, 590, 5, 80, 20, hwndDCPU, (HMENU)DCPU_RUN, GetModuleHandle(0), 0); + CreateWindow("BUTTON", "Step", WS_CHILD|WS_VISIBLE, 590, 25, 80, 20, hwndDCPU, (HMENU)DCPU_STEP, GetModuleHandle(0), 0); + CreateWindow("BUTTON", "Export Mem", WS_CHILD|WS_VISIBLE, 590, 45, 80, 20, hwndDCPU, (HMENU)DCPU_PROCEED, GetModuleHandle(0), 0); + CreateWindow("BUTTON", "Info", WS_CHILD|WS_VISIBLE, 590, 65, 80, 20, hwndDCPU, (HMENU)DCPU_INFO, GetModuleHandle(0), 0); + CreateWindow("BUTTON", "Trace", WS_CHILD|WS_VISIBLE|BS_AUTOCHECKBOX, 590, 85, 80, 20, hwndDCPU, (HMENU)DCPU_TRACE, GetModuleHandle(0), 0); + + CreateWindow("STATIC", "Scanline: 0\nCycles: 0", WS_CHILD|WS_VISIBLE, 590, 260, 80, 30, hwndDCPU, (HMENU)DCPU_CYCLES, GetModuleHandle(0), 0); + + SendDlgItemMessage(hwndDCPU, DCPU_DISAS, WM_SETFONT, (WPARAM)hFontFixed, TRUE); + SendDlgItemMessage(hwndDCPU, DCPU_RUN, WM_SETFONT, (WPARAM)hFont, TRUE); + SendDlgItemMessage(hwndDCPU, DCPU_STEP, WM_SETFONT, (WPARAM)hFont, TRUE); + SendDlgItemMessage(hwndDCPU, DCPU_PROCEED, WM_SETFONT, (WPARAM)hFont, TRUE); + SendDlgItemMessage(hwndDCPU, DCPU_INFO, WM_SETFONT, (WPARAM)hFont, TRUE); + SendDlgItemMessage(hwndDCPU, DCPU_TRACE, WM_SETFONT, (WPARAM)hFont, TRUE); + SendDlgItemMessage(hwndDCPU, DCPU_CYCLES, WM_SETFONT, (WPARAM)hFont, TRUE); +} + diff --git a/bsnes/win/gui_mem.cpp b/bsnes/win/gui_mem.cpp new file mode 100644 index 00000000..ba68a08a --- /dev/null +++ b/bsnes/win/gui_mem.cpp @@ -0,0 +1,252 @@ +#define DMEM_VIEW 100 +#define DMEM_EDITWRAM 101 +#define DMEM_UP40 102 +#define DMEM_DOWN40 103 +#define DMEM_UP400 104 +#define DMEM_DOWN400 105 +#define DMEM_UP4000 106 +#define DMEM_DOWN4000 107 +#define DMEM_UP40000 108 +#define DMEM_DOWN40000 109 +#define DMEM_TOWRAM 110 +#define DMEM_TOROM 111 +#define DMEM_TOVRAM 112 +#define DMEM_TOCGRAM 113 +#define DMEM_TOOAM 114 +#define DMEM_TOOFFSET 115 +#define DMEM_EDITLOC 116 +#define DMEM_EDITVAL 117 +#define DMEM_STATIC1 118 +#define DMEM_STATIC2 119 + +#define DMEMMODE_WRAM 0 +#define DMEMMODE_VRAM 1 +#define DMEMMODE_CGRAM 2 +#define DMEMMODE_OAM 3 +ulong dmem_mode = DMEMMODE_WRAM; + +HWND hwndDMEM; + +void debug_refresh_mem(void) { +char str[64*16], t[16]; +ulong ptr; +int x, y; + if(debug_get_state() == DEBUGMODE_DISABLED)return; + + ptr = debugger.mem_ptr; + strcpy(str, ""); + + if(dmem_mode == DMEMMODE_WRAM) { + ptr &= 0xffffff; + for(y=0;y<16;y++) { + sprintf(t, "%0.6x: ", ptr); + strcat(str, t); + for(x=0;x<16;x++) { + sprintf(t, "%0.2x", gx816->mem_read(MEMMODE_LONG, MEMSIZE_BYTE, ptr++, MEMACCESS_DEBUGGER)); + ptr &= 0xffffff; + strcat(str, t); + if(x != 15)strcat(str, " "); + } + if(y != 15)strcat(str, "\n"); + } + } else if(dmem_mode == DMEMMODE_VRAM) { + ptr &= 0xffff; + for(y=0;y<16;y++) { + sprintf(t, "--%0.4x: ", ptr); + strcat(str, t); + for(x=0;x<16;x++) { + sprintf(t, "%0.2x", ppu.vram[ptr++]); + ptr &= 0xffff; + strcat(str, t); + if(x != 15)strcat(str, " "); + } + if(y != 15)strcat(str, "\n"); + } + } else if(dmem_mode == DMEMMODE_CGRAM) { + ptr &= 0x01ff; + for(y=0;y<16;y++) { + sprintf(t, "---%0.3x: ", ptr); + strcat(str, t); + for(x=0;x<16;x++) { + sprintf(t, "%0.2x", ppu.cgram[ptr++]); + ptr &= 0x01ff; + strcat(str, t); + if(x != 15)strcat(str, " "); + } + if(y != 15)strcat(str, "\n"); + } + } else if(dmem_mode == DMEMMODE_OAM) { + ptr &= 0x03ff; + for(y=0;y<16;y++) { + sprintf(t, "---%0.3x: ", ptr); + strcat(str, t); + for(x=0;x<16;x++) { + sprintf(t, "%0.2x", oam_read(ptr++)); + ptr &= 0x03ff; + strcat(str, t); + if(x != 15)strcat(str, " "); + } + if(y != 15)strcat(str, "\n"); + } + } + + SetDlgItemText(hwndDMEM, DMEM_VIEW, str); +} + +void __mask_mem_ptr(void) { + if(dmem_mode == DMEMMODE_WRAM )debugger.mem_ptr &= 0xffffff; + if(dmem_mode == DMEMMODE_VRAM )debugger.mem_ptr &= 0x00ffff; + if(dmem_mode == DMEMMODE_CGRAM)debugger.mem_ptr &= 0x0001ff; + if(dmem_mode == DMEMMODE_OAM )debugger.mem_ptr &= 0x0003ff; +} + +long __stdcall wndprocDMEM(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { +char str[256]; +ulong pos, val; + if(msg == WM_DESTROY || msg == WM_CLOSE)return 0; //don't allow debugger to be closed (yet) + + if(msg == WM_COMMAND) { + switch(LOWORD(wparam)) { + case DMEM_EDITWRAM: + GetDlgItemText(hwndDMEM, DMEM_EDITLOC, str, 255); + pos = strhex(str); + GetDlgItemText(hwndDMEM, DMEM_EDITVAL, str, 255); + val = strhex(str); + if(dmem_mode == DMEMMODE_WRAM) { + gx816->mem_write(MEMMODE_LONG, MEMSIZE_BYTE, pos & 0xffffff, val, MEMACCESS_DEBUGGER); + } else if(dmem_mode == DMEMMODE_VRAM) { + ppu.vram[pos & 0xffff] = val; + } else if(dmem_mode == DMEMMODE_CGRAM) { + ppu.cgram[pos & 0x01ff] = val; + } else if(dmem_mode == DMEMMODE_OAM) { + oam_write(pos, val); + } + debug_refresh_mem(); + break; + case DMEM_UP40: + debugger.mem_ptr -= 0x40; + debug_refresh_mem(); + __mask_mem_ptr(); + break; + case DMEM_DOWN40: + debugger.mem_ptr += 0x40; + debug_refresh_mem(); + __mask_mem_ptr(); + break; + case DMEM_UP400: + debugger.mem_ptr -= 0x400; + debug_refresh_mem(); + __mask_mem_ptr(); + break; + case DMEM_DOWN400: + debugger.mem_ptr += 0x400; + debug_refresh_mem(); + __mask_mem_ptr(); + break; + case DMEM_UP4000: + debugger.mem_ptr -= 0x4000; + debug_refresh_mem(); + __mask_mem_ptr(); + break; + case DMEM_DOWN4000: + debugger.mem_ptr += 0x4000; + debug_refresh_mem(); + __mask_mem_ptr(); + break; + case DMEM_UP40000: + debugger.mem_ptr -= 0x40000; + debug_refresh_mem(); + __mask_mem_ptr(); + break; + case DMEM_DOWN40000: + debugger.mem_ptr += 0x40000; + debug_refresh_mem(); + __mask_mem_ptr(); + break; + case DMEM_TOWRAM: + dmem_mode = DMEMMODE_WRAM; + debugger.mem_ptr = 0x7e0000; + SetWindowText(hwndDMEM, "g65816 memory editor -- snes memory mode"); + debug_refresh_mem(); + break; + case DMEM_TOROM: + dmem_mode = DMEMMODE_WRAM; + if(gx816->map == MEMMAP_LOROM)debugger.mem_ptr = 0x008000; + else if(gx816->map == MEMMAP_HIROM)debugger.mem_ptr = 0xc00000; + SetWindowText(hwndDMEM, "g65816 memory editor -- snes memory mode"); + debug_refresh_mem(); + break; + case DMEM_TOVRAM: + dmem_mode = DMEMMODE_VRAM; + debugger.mem_ptr = 0x0000; + SetWindowText(hwndDMEM, "g65816 memory editor -- ppu vram mode"); + debug_refresh_mem(); + break; + case DMEM_TOCGRAM: + dmem_mode = DMEMMODE_CGRAM; + debugger.mem_ptr = 0x0000; + SetWindowText(hwndDMEM, "g65816 memory editor -- ppu cgram mode"); + debug_refresh_mem(); + break; + case DMEM_TOOAM: + dmem_mode = DMEMMODE_OAM; + debugger.mem_ptr = 0x0000; + SetWindowText(hwndDMEM, "g65816 memory editor -- ppu oam mode"); + debug_refresh_mem(); + break; + case DMEM_TOOFFSET: + GetDlgItemText(hwndDMEM, DMEM_EDITLOC, str, 255); + debugger.mem_ptr = strhex(str); + __mask_mem_ptr(); + debug_refresh_mem(); + break; + } + } + + return DefWindowProc(hwnd, msg, wparam, lparam); +} + +void CreateDMEM(void) { + CreateWindowEx(WS_EX_STATICEDGE, "STATIC", "", WS_CHILD|WS_VISIBLE, 5, 5, 390, 228, hwndDMEM, (HMENU)DMEM_VIEW, GetModuleHandle(0), 0); + CreateWindow("STATIC", "Offset: Val:", WS_CHILD|WS_VISIBLE, 400, 5, 100, 15, hwndDMEM, (HMENU)DMEM_STATIC1, GetModuleHandle(0), 0); + CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", "000000", WS_CHILD|WS_VISIBLE, 400, 20, 60, 23, hwndDMEM, (HMENU)DMEM_EDITLOC, GetModuleHandle(0), 0); + CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", "00", WS_CHILD|WS_VISIBLE, 460, 20, 30, 23, hwndDMEM, (HMENU)DMEM_EDITVAL, GetModuleHandle(0), 0); + CreateWindow("BUTTON", "Edit Memory", WS_CHILD|WS_VISIBLE, 400, 45, 90, 20, hwndDMEM, (HMENU)DMEM_EDITWRAM, GetModuleHandle(0), 0); + CreateWindow("BUTTON", "-40", WS_CHILD|WS_VISIBLE, 400, 70, 45, 20, hwndDMEM, (HMENU)DMEM_UP40, GetModuleHandle(0), 0); + CreateWindow("BUTTON", "+40", WS_CHILD|WS_VISIBLE, 445, 70, 45, 20, hwndDMEM, (HMENU)DMEM_DOWN40, GetModuleHandle(0), 0); + CreateWindow("BUTTON", "-400", WS_CHILD|WS_VISIBLE, 400, 90, 45, 20, hwndDMEM, (HMENU)DMEM_UP400, GetModuleHandle(0), 0); + CreateWindow("BUTTON", "+400", WS_CHILD|WS_VISIBLE, 445, 90, 45, 20, hwndDMEM, (HMENU)DMEM_DOWN400, GetModuleHandle(0), 0); + CreateWindow("BUTTON", "-4000", WS_CHILD|WS_VISIBLE, 400, 110, 45, 20, hwndDMEM, (HMENU)DMEM_UP4000, GetModuleHandle(0), 0); + CreateWindow("BUTTON", "+4000", WS_CHILD|WS_VISIBLE, 445, 110, 45, 20, hwndDMEM, (HMENU)DMEM_DOWN4000, GetModuleHandle(0), 0); + CreateWindow("BUTTON", "-40000", WS_CHILD|WS_VISIBLE, 400, 130, 45, 20, hwndDMEM, (HMENU)DMEM_UP40000, GetModuleHandle(0), 0); + CreateWindow("BUTTON", "+40000", WS_CHILD|WS_VISIBLE, 445, 130, 45, 20, hwndDMEM, (HMENU)DMEM_DOWN40000, GetModuleHandle(0), 0); + CreateWindow("STATIC", "Seek To:", WS_CHILD|WS_VISIBLE, 400, 155, 100, 15, hwndDMEM, (HMENU)DMEM_STATIC2, GetModuleHandle(0), 0); + CreateWindow("BUTTON", "WRAM", WS_CHILD|WS_VISIBLE, 400, 173, 45, 20, hwndDMEM, (HMENU)DMEM_TOWRAM, GetModuleHandle(0), 0); + CreateWindow("BUTTON", "ROM", WS_CHILD|WS_VISIBLE, 445, 173, 45, 20, hwndDMEM, (HMENU)DMEM_TOROM, GetModuleHandle(0), 0); + CreateWindow("BUTTON", "VRAM", WS_CHILD|WS_VISIBLE, 400, 193, 45, 20, hwndDMEM, (HMENU)DMEM_TOVRAM, GetModuleHandle(0), 0); + CreateWindow("BUTTON", "CGRAM", WS_CHILD|WS_VISIBLE, 445, 193, 45, 20, hwndDMEM, (HMENU)DMEM_TOCGRAM, GetModuleHandle(0), 0); + CreateWindow("BUTTON", "OAM", WS_CHILD|WS_VISIBLE, 400, 213, 45, 20, hwndDMEM, (HMENU)DMEM_TOOAM, GetModuleHandle(0), 0); + CreateWindow("BUTTON", "Offset", WS_CHILD|WS_VISIBLE, 445, 213, 45, 20, hwndDMEM, (HMENU)DMEM_TOOFFSET, GetModuleHandle(0), 0); + + SendDlgItemMessage(hwndDMEM, DMEM_VIEW, WM_SETFONT, (WPARAM)hFontFixed, TRUE); + SendDlgItemMessage(hwndDMEM, DMEM_EDITLOC, WM_SETFONT, (WPARAM)hFontFixed, TRUE); + SendDlgItemMessage(hwndDMEM, DMEM_EDITVAL, WM_SETFONT, (WPARAM)hFontFixed, TRUE); + SendDlgItemMessage(hwndDMEM, DMEM_STATIC1, WM_SETFONT, (WPARAM)hFont, TRUE); + SendDlgItemMessage(hwndDMEM, DMEM_EDITWRAM, WM_SETFONT, (WPARAM)hFont, TRUE); + SendDlgItemMessage(hwndDMEM, DMEM_UP40, WM_SETFONT, (WPARAM)hFont, TRUE); + SendDlgItemMessage(hwndDMEM, DMEM_DOWN40, WM_SETFONT, (WPARAM)hFont, TRUE); + SendDlgItemMessage(hwndDMEM, DMEM_UP400, WM_SETFONT, (WPARAM)hFont, TRUE); + SendDlgItemMessage(hwndDMEM, DMEM_DOWN400, WM_SETFONT, (WPARAM)hFont, TRUE); + SendDlgItemMessage(hwndDMEM, DMEM_UP4000, WM_SETFONT, (WPARAM)hFont, TRUE); + SendDlgItemMessage(hwndDMEM, DMEM_DOWN4000, WM_SETFONT, (WPARAM)hFont, TRUE); + SendDlgItemMessage(hwndDMEM, DMEM_UP40000, WM_SETFONT, (WPARAM)hFont, TRUE); + SendDlgItemMessage(hwndDMEM, DMEM_DOWN40000, WM_SETFONT, (WPARAM)hFont, TRUE); + SendDlgItemMessage(hwndDMEM, DMEM_STATIC2, WM_SETFONT, (WPARAM)hFont, TRUE); + SendDlgItemMessage(hwndDMEM, DMEM_TOWRAM, WM_SETFONT, (WPARAM)hFont, TRUE); + SendDlgItemMessage(hwndDMEM, DMEM_TOROM, WM_SETFONT, (WPARAM)hFont, TRUE); + SendDlgItemMessage(hwndDMEM, DMEM_TOVRAM, WM_SETFONT, (WPARAM)hFont, TRUE); + SendDlgItemMessage(hwndDMEM, DMEM_TOCGRAM, WM_SETFONT, (WPARAM)hFont, TRUE); + SendDlgItemMessage(hwndDMEM, DMEM_TOOAM, WM_SETFONT, (WPARAM)hFont, TRUE); + SendDlgItemMessage(hwndDMEM, DMEM_TOOFFSET, WM_SETFONT, (WPARAM)hFont, TRUE); +} + diff --git a/bsnes/win/render.cpp b/bsnes/win/render.cpp new file mode 100644 index 00000000..d3bff380 --- /dev/null +++ b/bsnes/win/render.cpp @@ -0,0 +1,269 @@ +#include <ddraw.h> + +LPDIRECTDRAW lpdd = 0; +LPDIRECTDRAWSURFACE lpdds = 0, lpddsb = 0; +LPDIRECTDRAWCLIPPER lpddc = 0; +DDSURFACEDESC ddsd; +DDSCAPS ddscaps; + +void DestroyDDraw(void) { + if(lpddc) { + lpddc->Release(); + lpddc = 0; + } + if(lpddsb) { + lpddsb->Release(); + lpddsb = 0; + } + if(lpdds) { + lpdds->Release(); + lpdds = 0; + } + if(lpdd) { + lpdd->Release(); + lpdd = 0; + } +} + +void CreateDDraw_Win(void) { + DestroyDDraw(); + + DirectDrawCreate(0, &lpdd, 0); + lpdd->SetCooperativeLevel(hwndMain, DDSCL_NORMAL); + + memset(&ddsd, 0, sizeof(DDSURFACEDESC)); + ddsd.dwSize = sizeof(DDSURFACEDESC); + ddsd.dwFlags = DDSD_CAPS; + ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; + lpdd->CreateSurface(&ddsd, &lpdds, 0); + + lpdd->CreateClipper(0, &lpddc, 0); + lpddc->SetHWnd(0, hwndMain); + lpdds->SetClipper(lpddc); + + memset(&ddsd, 0, sizeof(DDSURFACEDESC)); + ddsd.dwSize = sizeof(DDSURFACEDESC); + ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; + ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY; + ddsd.dwWidth = 512; + ddsd.dwHeight = 480; + lpdd->CreateSurface(&ddsd, &lpddsb, 0); +} + +void CreateDDraw_Full(void) { + DestroyDDraw(); + + DirectDrawCreate(0, &lpdd, 0); + lpdd->SetCooperativeLevel(hwndMain, DDSCL_FULLSCREEN | DDSCL_EXCLUSIVE); + lpdd->SetDisplayMode(640, 480, 16); + + memset(&ddsd, 0, sizeof(DDSURFACEDESC)); + ddsd.dwSize = sizeof(DDSURFACEDESC); + ddsd.dwFlags = DDSD_CAPS; + ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; + lpdd->CreateSurface(&ddsd, &lpdds, 0); + + memset(&ddsd, 0, sizeof(DDSURFACEDESC)); + ddsd.dwSize = sizeof(DDSURFACEDESC); + ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT; + ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY; + ddsd.dwWidth = 512; + ddsd.dwHeight = 480; + lpdd->CreateSurface(&ddsd, &lpddsb, 0); +} + +void InitDisplay(void) { + CreateDDraw_Win(); +} + +//size is doubled (mirrored) to prevent having to mask the top unused bit of snes color data +ulong render_color_lookup[65536]; + +void InitColorTable16(void) { +int i, r, g, b; + for(i=0;i<65536;i++) { + r = (i ) & 31; + g = (i >> 5) & 31; + b = (i >> 10) & 31; + render_color_lookup[i] = (r << 11) | (g << 6) | (b); + } +} + +void InitColorTable24(void) { +int i, r, g, b; + for(i=0;i<65536;i++) { + r = (i ) & 31; + g = (i >> 5) & 31; + b = (i >> 10) & 31; + render_color_lookup[i] = (r << 19) | (g << 11) | (b << 3); + } +} + +vfunc RenderScene; + +//sets up color table and sets render proc +void CreateColorTable(void) { + lpdds->GetSurfaceDesc(&ddsd); + switch(ddsd.ddpfPixelFormat.dwRGBBitCount) { + case 16: + InitColorTable16(); + break; + case 32: + InitColorTable24(); + break; + default: + alert("Error: Bit depth [%d] unsupported (supported depths: 16, 32)", ddsd.ddpfPixelFormat.dwRGBBitCount); + exit(0); + break; + } +} + +#include "render_modes.cpp" + +void SelectRenderer(void) { + lpdds->GetSurfaceDesc(&ddsd); + switch(ddsd.ddpfPixelFormat.dwRGBBitCount) { + case 16: + if (render.snes_width == 512 && render.snes_height == 480) { + RenderScene = RenderScene16_512x480; + } else if(render.snes_width == 512 && render.snes_height == 448) { + RenderScene = RenderScene16_512x448; + } else if(render.snes_width == 512 && render.snes_height == 240) { + RenderScene = RenderScene16_512x240; + } else if(render.snes_width == 512 && render.snes_height == 224) { + RenderScene = RenderScene16_512x224; + } else if(render.snes_width == 256 && render.snes_height == 480) { + RenderScene = RenderScene16_256x480; + } else if(render.snes_width == 256 && render.snes_height == 448) { + RenderScene = RenderScene16_256x448; + } else if(render.snes_width == 256 && render.snes_height == 240) { + RenderScene = RenderScene16_256x240; + } else if(render.snes_width == 256 && render.snes_height == 224) { + RenderScene = RenderScene16_256x224; + } else { + alert("Error: Unsupported SNES Resolution: %dx%d", render.snes_width, render.snes_height); + } + break; + case 32: + if (render.snes_width == 512 && render.snes_height == 480) { + RenderScene = RenderScene32_512x480; + } else if(render.snes_width == 512 && render.snes_height == 448) { + RenderScene = RenderScene32_512x448; + } else if(render.snes_width == 512 && render.snes_height == 240) { + RenderScene = RenderScene32_512x240; + } else if(render.snes_width == 512 && render.snes_height == 224) { + RenderScene = RenderScene32_512x224; + } else if(render.snes_width == 256 && render.snes_height == 480) { + RenderScene = RenderScene32_256x480; + } else if(render.snes_width == 256 && render.snes_height == 448) { + RenderScene = RenderScene32_256x448; + } else if(render.snes_width == 256 && render.snes_height == 240) { + RenderScene = RenderScene32_256x240; + } else if(render.snes_width == 256 && render.snes_height == 224) { + RenderScene = RenderScene32_256x224; + } else { + alert("Error: Unsupported SNES Resolution: %dx%d", render.snes_width, render.snes_height); + } + break; + default: + alert("Error: Bit depth [%d] unsupported (supported depths: 16, 32)", ddsd.ddpfPixelFormat.dwRGBBitCount); + exit(0); + break; + } +} + +void UpdateDisplay(void) { +RECT rsrc, rdest; +POINT p; +HRESULT hr; + RenderScene(); + + p.x = p.y = 0; + if(render.fullscreen == true) { + SetRect(&rdest, 0, 0, 512, 448); + OffsetRect(&rdest, (640 - 512) / 2, (480 - 448) / 2); + } else { + ClientToScreen(hwndMain, &p); + GetClientRect(hwndMain, &rdest); + OffsetRect(&rdest, p.x, p.y); + } + + SetRect(&rsrc, 0, 0, render.snes_width, render.snes_height); + + hr = lpdds->Blt(&rdest, lpddsb, &rsrc, DDBLT_WAIT, 0); + if(hr == DDERR_SURFACELOST) { + lpdds->Restore(); + lpddsb->Restore(); + } +} + +void UpdateDisplay_NoRender(void) { +RECT rsrc, rdest; +POINT p; +HRESULT hr; + p.x = p.y = 0; + if(render.fullscreen == true) { + SetRect(&rdest, 0, 0, 512, 448); + OffsetRect(&rdest, (640 - 512) / 2, (480 - 448) / 2); + } else { + ClientToScreen(hwndMain, &p); + GetClientRect(hwndMain, &rdest); + OffsetRect(&rdest, p.x, p.y); + } + + SetRect(&rsrc, 0, 0, render.snes_width, render.snes_height); + + hr = lpdds->Blt(&rdest, lpddsb, &rsrc, DDBLT_WAIT, 0); + if(hr == DDERR_SURFACELOST) { + lpdds->Restore(); + lpddsb->Restore(); + } +} + +void video_setmode(bool fullscreen, word width, word height) { +bool prev_mode = render.fullscreen; + render.fullscreen = fullscreen; + render.width = width; + render.height = height; + + FixWindowSize(hwndMain, width, height); + ShowWindow(hwndMain, SW_NORMAL); + + if(prev_mode != fullscreen) { + if(fullscreen == true) { + render.show_menu = false; + UpdateMainWindowStyle(true); + CreateDDraw_Full(); + ShowWindow(hwndMain, SW_NORMAL); + } else { + render.show_menu = true; + UpdateMainWindowStyle(true); + CreateDDraw_Win(); + FixWindowSize(hwndMain, width, height); + ShowWindow(hwndMain, SW_NORMAL); + } + } +} + +void video_setsnesmode(void) { + if(ppu.bg_mode == 5 || ppu.bg_mode == 6) { + render.snes_width = 512; + } else { + render.snes_width = 256; + } + if(ppu.interlace == true) { + if(ppu.visible_scanlines == 240) { + render.snes_height = 480; + } else { + render.snes_height = 448; + } + } else { + if(ppu.visible_scanlines == 240) { + render.snes_height = 240; + } else { + render.snes_height = 224; + } + } + + SelectRenderer(); +} diff --git a/bsnes/win/render_modes.cpp b/bsnes/win/render_modes.cpp new file mode 100644 index 00000000..40980dcb --- /dev/null +++ b/bsnes/win/render_modes.cpp @@ -0,0 +1,303 @@ +void RenderScene16_256x224(void) { +word *screen; +word *src; +word pitch; +HRESULT hr; +int x, y; + hr = lpddsb->Lock(0, &ddsd, DDLOCK_WAIT, 0); + if(hr != DD_OK)return; + pitch = ddsd.lPitch >> 1; + for(y=0;y<224;y++) { + screen = (word*)ddsd.lpSurface + (pitch * y); + src = (word*)ppu.screen + y * 512; + for(x=0;x<256;x++) { + *screen++ = render_color_lookup[*src++]; + } + } + lpddsb->Unlock(0); +} + +void RenderScene32_256x224(void) { +ulong *screen; +word *src; +word pitch; +HRESULT hr; +int x, y; + hr = lpddsb->Lock(0, &ddsd, DDLOCK_WAIT, 0); + if(hr != DD_OK)return; + pitch = ddsd.lPitch >> 2; + for(y=0;y<224;y++) { + screen = (ulong*)ddsd.lpSurface + (pitch * y); + src = (word*)ppu.screen + y * 512; + for(x=0;x<256;x++) { + *screen++ = render_color_lookup[*src++]; + } + } + lpddsb->Unlock(0); +} + +void RenderScene16_256x240(void) { +word *screen; +word *src; +word pitch; +HRESULT hr; +int x, y; + hr = lpddsb->Lock(0, &ddsd, DDLOCK_WAIT, 0); + if(hr != DD_OK)return; + pitch = ddsd.lPitch >> 1; + for(y=0;y<240;y++) { + screen = (word*)ddsd.lpSurface + (pitch * y); + src = (word*)ppu.screen + y * 512; + for(x=0;x<256;x++) { + *screen++ = render_color_lookup[*src++]; + } + } + lpddsb->Unlock(0); +} + +void RenderScene32_256x240(void) { +ulong *screen; +word *src; +word pitch; +HRESULT hr; +int x, y; + hr = lpddsb->Lock(0, &ddsd, DDLOCK_WAIT, 0); + if(hr != DD_OK)return; + pitch = ddsd.lPitch >> 2; + for(y=0;y<240;y++) { + screen = (ulong*)ddsd.lpSurface + (pitch * y); + src = (word*)ppu.screen + y * 512; + for(x=0;x<256;x++) { + *screen++ = render_color_lookup[*src++]; + } + } + lpddsb->Unlock(0); +} + +void RenderScene16_256x448(void) { +word *screen; +word *src; +word pitch; +HRESULT hr; +int x, y; + hr = lpddsb->Lock(0, &ddsd, DDLOCK_WAIT, 0); + if(hr != DD_OK)return; + pitch = ddsd.lPitch >> 1; + for(y=0;y<448;y++) { + screen = (word*)ddsd.lpSurface + (pitch * y); + src = (word*)ppu.screen + y * 512; + for(x=0;x<256;x++) { + *screen++ = render_color_lookup[*src++]; + } + } + lpddsb->Unlock(0); +} + +void RenderScene32_256x448(void) { +ulong *screen; +word *src; +word pitch; +HRESULT hr; +int x, y; + hr = lpddsb->Lock(0, &ddsd, DDLOCK_WAIT, 0); + if(hr != DD_OK)return; + pitch = ddsd.lPitch >> 2; + for(y=0;y<448;y++) { + screen = (ulong*)ddsd.lpSurface + (pitch * y); + src = (word*)ppu.screen + y * 512; + for(x=0;x<256;x++) { + *screen++ = render_color_lookup[*src++]; + } + } + lpddsb->Unlock(0); +} + +void RenderScene16_256x480(void) { +word *screen; +word *src; +word pitch; +HRESULT hr; +int x, y; + hr = lpddsb->Lock(0, &ddsd, DDLOCK_WAIT, 0); + if(hr != DD_OK)return; + pitch = ddsd.lPitch >> 1; + for(y=0;y<480;y++) { + screen = (word*)ddsd.lpSurface + (pitch * y); + src = (word*)ppu.screen + y * 512; + for(x=0;x<256;x++) { + *screen++ = render_color_lookup[*src++]; + } + } + lpddsb->Unlock(0); +} + +void RenderScene32_256x480(void) { +ulong *screen; +word *src; +word pitch; +HRESULT hr; +int x, y; + hr = lpddsb->Lock(0, &ddsd, DDLOCK_WAIT, 0); + if(hr != DD_OK)return; + pitch = ddsd.lPitch >> 2; + for(y=0;y<480;y++) { + screen = (ulong*)ddsd.lpSurface + (pitch * y); + src = (word*)ppu.screen + y * 512; + for(x=0;x<256;x++) { + *screen++ = render_color_lookup[*src++]; + } + } + lpddsb->Unlock(0); +} + +void RenderScene16_512x224(void) { +word *screen; +word *src; +word pitch; +HRESULT hr; +int x, y; + hr = lpddsb->Lock(0, &ddsd, DDLOCK_WAIT, 0); + if(hr != DD_OK)return; + pitch = ddsd.lPitch >> 1; + for(y=0;y<224;y++) { + screen = (word*)ddsd.lpSurface + (pitch * y); + src = (word*)ppu.screen + y * 512; + for(x=0;x<512;x++) { + *screen++ = render_color_lookup[*src++]; + } + } + lpddsb->Unlock(0); +} + +void RenderScene32_512x224(void) { +ulong *screen; +word *src; +word pitch; +HRESULT hr; +int x, y; + hr = lpddsb->Lock(0, &ddsd, DDLOCK_WAIT, 0); + if(hr != DD_OK)return; + pitch = ddsd.lPitch >> 2; + for(y=0;y<224;y++) { + screen = (ulong*)ddsd.lpSurface + (pitch * y); + src = (word*)ppu.screen + y * 512; + for(x=0;x<512;x++) { + *screen++ = render_color_lookup[*src++]; + } + } + lpddsb->Unlock(0); +} + +void RenderScene16_512x240(void) { +word *screen; +word *src; +word pitch; +HRESULT hr; +int x, y; + hr = lpddsb->Lock(0, &ddsd, DDLOCK_WAIT, 0); + if(hr != DD_OK)return; + pitch = ddsd.lPitch >> 1; + for(y=0;y<240;y++) { + screen = (word*)ddsd.lpSurface + (pitch * y); + src = (word*)ppu.screen + y * 512; + for(x=0;x<512;x++) { + *screen++ = render_color_lookup[*src++]; + } + } + lpddsb->Unlock(0); +} + +void RenderScene32_512x240(void) { +ulong *screen; +word *src; +word pitch; +HRESULT hr; +int x, y; + hr = lpddsb->Lock(0, &ddsd, DDLOCK_WAIT, 0); + if(hr != DD_OK)return; + pitch = ddsd.lPitch >> 2; + for(y=0;y<240;y++) { + screen = (ulong*)ddsd.lpSurface + (pitch * y); + src = (word*)ppu.screen + y * 512; + for(x=0;x<512;x++) { + *screen++ = render_color_lookup[*src++]; + } + } + lpddsb->Unlock(0); +} + +void RenderScene16_512x448(void) { +word *screen; +word *src; +word pitch; +HRESULT hr; +int x, y; + hr = lpddsb->Lock(0, &ddsd, DDLOCK_WAIT, 0); + if(hr != DD_OK)return; + pitch = ddsd.lPitch >> 1; + for(y=0;y<448;y++) { + screen = (word*)ddsd.lpSurface + (pitch * y); + src = (word*)ppu.screen + y * 512; + for(x=0;x<512;x++) { + *screen++ = render_color_lookup[*src++]; + } + } + lpddsb->Unlock(0); +} + +void RenderScene32_512x448(void) { +ulong *screen; +word *src; +word pitch; +HRESULT hr; +int x, y; + hr = lpddsb->Lock(0, &ddsd, DDLOCK_WAIT, 0); + if(hr != DD_OK)return; + pitch = ddsd.lPitch >> 2; + for(y=0;y<448;y++) { + screen = (ulong*)ddsd.lpSurface + (pitch * y); + src = (word*)ppu.screen + y * 512; + for(x=0;x<512;x++) { + *screen++ = render_color_lookup[*src++]; + } + } + lpddsb->Unlock(0); +} + +void RenderScene16_512x480(void) { +word *screen; +word *src; +word pitch; +HRESULT hr; +int x, y; + hr = lpddsb->Lock(0, &ddsd, DDLOCK_WAIT, 0); + if(hr != DD_OK)return; + pitch = ddsd.lPitch >> 1; + for(y=0;y<480;y++) { + screen = (word*)ddsd.lpSurface + (pitch * y); + src = (word*)ppu.screen + y * 512; + for(x=0;x<512;x++) { + *screen++ = render_color_lookup[*src++]; + } + } + lpddsb->Unlock(0); +} + +void RenderScene32_512x480(void) { +ulong *screen; +word *src; +word pitch; +HRESULT hr; +int x, y; + hr = lpddsb->Lock(0, &ddsd, DDLOCK_WAIT, 0); + if(hr != DD_OK)return; + pitch = ddsd.lPitch >> 2; + for(y=0;y<480;y++) { + screen = (ulong*)ddsd.lpSurface + (pitch * y); + src = (word*)ppu.screen + y * 512; + for(x=0;x<512;x++) { + *screen++ = render_color_lookup[*src++]; + } + } + lpddsb->Unlock(0); +}