www.pudn.com > sn068s.zip > PPU.ASM


%define Set_Based_Tile_Cache 
;%define Profile_VRAM_Writes 
;%define Check_Within_Tile_Set 
;%define NO_DMA_WRITE 
;%define TRAP_BGSC 
;%define TRAP_BGHOFS 
;%define TRAP_BGVOFS 
 
; PPU.asm - Contains the hardware mapping functions 
 
%define SNEeSe_PPU_asm 
 
%include "misc.ni" 
%include "PPU.ni" 
%include "scankeys.ni" 
%include "key.ni" 
%include "DMA.ni" 
%include "sprites.ni" 
%include "screen.ni" 
%include "cpumem.ni" 
 
extern SNES_R2134,SNES_R2135,SNES_R2136,SNES_R2137 
extern SNES_R213C,SNES_R213D 
 
extern SNES_R4200,SNES_R4210,SNES_R4211,SNES_R4212 
 
extern SNES_W211A,SNES_W211B,SNES_W211C,SNES_W211D 
extern SNES_W211E,SNES_W211F,SNES_W2120 
extern SNES_W4200,SNES_W4207,SNES_W4208,SNES_W4209 
extern SNES_W420A,SNES_W420D,SNES_W4210,SNES_W4211 
extern HVBJOY 
 
extern SCREEN_MODE_7 
 
extern _Reset_Mode_7 
extern _LastRenderLine 
extern _BrightnessLevel 
extern _NMITIMEN 
extern _JOYSTICK_ENABLED,_JOYSTICK_ENABLED2 
extern _MickeyMouse,_MickeyRead,_MouseButts 
extern _key 
extern _KEY_A_1,_KEY_X_1,_KEY_L_1,_KEY_R_1 
extern _KEY_B_1,_KEY_Y_1,_KEY_SELECT_1,_KEY_START_1 
extern _KEY_UP_1,_KEY_DOWN_1,_KEY_LEFT_1,_KEY_RIGHT_1 
extern _KEY_A_2,_KEY_X_2,_KEY_L_2,_KEY_R_2 
extern _KEY_B_2,_KEY_Y_2,_KEY_SELECT_2,_KEY_START_2 
extern _KEY_UP_2,_KEY_DOWN_2,_KEY_LEFT_2,_KEY_RIGHT_2 
extern _Real_SNES_Palette 
extern OPHCT,OPVCT,_SNES_COUNTRY 
extern Ready_Line_Render,Sync_Render 
extern _PaletteChanged 
extern _Offset_Change_Disable 
extern _M7H_13,_M7V_13,Redo_M7 
extern _M7SEL,M7_Handler_Table,M7_Handler 
extern _fixedpalettecheck 
 
extern Recache_Tile_Set 
 
extern _SPC_MASK 
extern _OutputScreen 
extern _MosaicLine 
 
extern _Map_Address 
extern _Map_Byte 
extern _InvalidHWWrite 
 
section .text 
EXPORT_C PPU_text_start 
section .data 
EXPORT_C PPU_data_start 
section .bss 
EXPORT_C PPU_bss_start 
 
section .data 
ALIGND 
EXPORT_C Read_Map_20_5F 
    ; 2000-20FF: Unmapped 
    DUPLICATE dd,0x100,UNSUPPORTED_READ 
EXPORT_C Read_Map_21 
    DUPLICATE dd,0x16,UNSUPPORTED_READ 
    dd   SNES_R2116  ; VMADDL 
    dd   SNES_R2117  ; VMADDH 
    DUPLICATE dd,0x1C,UNSUPPORTED_READ 
    dd   SNES_R2134  ; MPYL 
    dd   SNES_R2135  ; MPYM 
    dd   SNES_R2136  ; MPYH 
    dd   SNES_R2137  ; SLHV 
    dd   SNES_R2138  ; OAMDATAREAD 
    dd   SNES_R2139_NORM ; VMDATALREAD 
    dd   SNES_R213A_NORM ; VMDATAHREAD 
    dd   SNES_R213B  ; CGDATAREAD     ; v0.14 
    dd   SNES_R213C  ; OPHCT          ; v0.14 
    dd   SNES_R213D  ; OPVCT 
    dd   SNES_R213E  ; STAT77   ; Not supported yet (properly..) 
    dd   SNES_R213F  ; STAT78 
    DUPLICATE dd,0x40,UNSUPPORTED_READ    ; APUI00-APUI03 
    dd   SNES_R2180  ; WMDATA   ; 2180 WMDATA - read/write to Work RAM 
    dd   SNES_R2181  ; WMADDL   ; v0.14 
    dd   SNES_R2182  ; WMADDM   ; v0.14 
    dd   SNES_R2183  ; WMADDH   ; v0.14 
    DUPLICATE dd,0x3E,UNSUPPORTED_READ 
    dd   SNES_R21C2  ; Unknown? 
    dd   SNES_R21C3  ; Unknown? 
    DUPLICATE dd,0x3C,UNSUPPORTED_READ 
    ; 2200-3FFF: Unmapped 
    DUPLICATE dd,0x1E00,UNSUPPORTED_READ 
EXPORT_C Read_Map_40 
    DUPLICATE dd,0x16,UNSUPPORTED_READ 
    dd   SNES_R4016  ; JOYC1 
    dd   SNES_R4017  ; JOYC2 
    DUPLICATE dd,0xE8,UNSUPPORTED_READ 
    ; 4100-41FF: Unmapped 
    DUPLICATE dd,0x100,UNSUPPORTED_READ 
EXPORT_C Read_Map_42 
    dd   SNES_R4200  ; NMITIMEN 
    DUPLICATE dd,0x01,UNSUPPORTED_READ 
    dd   SNES_R4202  ; WRMPYA 
    dd   SNES_R4203  ; WRMPYB 
    DUPLICATE dd,0x07,UNSUPPORTED_READ 
    dd   SNES_R420B  ; MDMAEN 
    dd   SNES_R420C  ; HDMAEN 
    DUPLICATE dd,3,UNSUPPORTED_READ 
    dd   SNES_R4210  ; RDNMI 
    dd   SNES_R4211  ; TIMEUP 
    dd   SNES_R4212  ; HVBJOY 
    dd   SNES_R4213  ; RDIO     ; Not yet supported... probably never 
    dd   SNES_R4214  ; RDDIVL 
    dd   SNES_R4215  ; RDDIVH 
    dd   SNES_R4216  ; RDMPYL 
    dd   SNES_R4217  ; RDMPYH 
    dd   SNES_R4218  ; JOY1L 
    dd   SNES_R4219  ; JOY1H 
    dd   SNES_R421A  ; JOY2L 
    dd   SNES_R421B  ; JOY2H 
    dd   SNES_R421C  ; JOY3L    ; Not yet supported 
    dd   SNES_R421D  ; JOY3H 
    dd   SNES_R421E  ; JOY4L    ; Not yet supported 
    dd   SNES_R421F  ; JOY4H 
    DUPLICATE dd,0xE0,UNSUPPORTED_READ 
 
EXPORT_C Read_Map_43 
    MAP_READ_DMA_LIST 0 
    MAP_READ_DMA_LIST 1 
    MAP_READ_DMA_LIST 2 
    MAP_READ_DMA_LIST 3 
    MAP_READ_DMA_LIST 4 
    MAP_READ_DMA_LIST 5 
    MAP_READ_DMA_LIST 6 
    MAP_READ_DMA_LIST 7 
    DUPLICATE dd,0x80,UNSUPPORTED_READ 
    ; 4300-5FFF: Unmapped 
    DUPLICATE dd,0x1D00,UNSUPPORTED_READ 
 
ALIGND 
EXPORT_C Write_Map_20_5F 
    ; 2000-20FF: Unmapped 
    DUPLICATE dd,0x100,UNSUPPORTED_WRITE 
EXPORT_C Write_Map_21 
    dd   SNES_W2100  ; INIDISP 
    dd   SNES_W2101  ; OBSEL 
    dd   SNES_W2102  ; OAMADDL 
    dd   SNES_W2103  ; OAMADDH 
    dd   SNES_W2104  ; OAMDATA 
    dd   SNES_W2105  ; BGMODE 
    dd   SNES_W2106  ; MOSAIC 
    dd   SNES_W2107  ; BG1SC 
    dd   SNES_W2108  ; BG2SC 
    dd   SNES_W2109  ; BG3SC 
    dd   SNES_W210A  ; BG4SC 
    dd   SNES_W210B  ; BG12NBA 
    dd   SNES_W210C  ; BG34NBA 
    dd   SNES_W210D  ; BG1HOFS 
    dd   SNES_W210E  ; BG1VOFS 
    dd   SNES_W210F  ; BG2HOFS 
    dd   SNES_W2110  ; BG2VOFS 
    dd   SNES_W2111  ; BG3HOFS 
    dd   SNES_W2112  ; BG3VOFS 
    dd   SNES_W2113  ; BG4HOFS 
    dd   SNES_W2114  ; BG4VOFS 
    dd   SNES_W2115  ; VMAIN 
    dd   SNES_W2116  ; VMADDL 
    dd   SNES_W2117  ; VMADDH 
    dd   SNES_W2118_NORM ; VMDATAL 
    dd   SNES_W2119_NORM ; VMDATAH 
    dd   SNES_W211A  ; M7SEL 
    dd   SNES_W211B  ; M7A 
    dd   SNES_W211C  ; M7B 
    dd   SNES_W211D  ; M7C 
    dd   SNES_W211E  ; M7D 
    dd   SNES_W211F  ; M7X 
    dd   SNES_W2120  ; M7Y 
    dd   SNES_W2121  ; CGADD 
    dd   SNES_W2122  ; CGDATA 
    dd   SNES_W2123  ; W12SEL 
    dd   SNES_W2124  ; W34SEL 
    dd   SNES_W2125  ; WOBJSEL 
    dd   SNES_W2126  ; WH0 
    dd   SNES_W2127  ; WH1 
    dd   SNES_W2128  ; WH2 
    dd   SNES_W2129  ; WH3 
    dd   SNES_W212A  ; WBGLOG 
    dd   SNES_W212B  ; WOBJLOG 
    dd   SNES_W212C  ; TM 
    dd   SNES_W212D  ; TS 
    dd   SNES_W212E  ; TMW 
    dd   SNES_W212F  ; TSW 
    dd   SNES_W2130  ; CGWSEL 
    dd   SNES_W2131  ; CGADSUB 
    dd   SNES_W2132  ; COLDATA 
    dd   SNES_W2133  ; SETINI 
    DUPLICATE dd,0x0C,UNSUPPORTED_WRITE 
    DUPLICATE dd,0x40,UNSUPPORTED_WRITE   ; APUI00-APUI03 
    dd   SNES_W2180  ; WMDATA   ; 2180 WMDATA - read/write to Work RAM 
    dd   SNES_W2181  ; WMADDL   ; 2181-3 WMAddress 
    dd   SNES_W2182  ; WMADDM 
    dd   SNES_W2183  ; WMADDH 
    DUPLICATE dd,0x7C,UNSUPPORTED_WRITE 
    ; 2200-3FFF: Unmapped 
    DUPLICATE dd,0x1E00,UNSUPPORTED_WRITE 
EXPORT_C Write_Map_40 
    DUPLICATE dd,0x16,UNSUPPORTED_WRITE 
    dd   SNES_W4016  ; JOYC1 
    dd   SNES_W4017  ; JOYC2 
    DUPLICATE dd,0xE8,UNSUPPORTED_WRITE 
    ; 4100-41FF: Unmapped 
    DUPLICATE dd,0x100,UNSUPPORTED_WRITE 
EXPORT_C Write_Map_42 
    dd   SNES_W4200  ; NMITIMEN 
    dd   SNES_W4201  ; WRIO 
    dd   SNES_W4202  ; WRMPYA 
    dd   SNES_W4203  ; WRMPYB 
    dd   SNES_W4204  ; WRDIVL 
    dd   SNES_W4205  ; WRDIVH 
    dd   SNES_W4206  ; WRDIVB 
    dd   SNES_W4207  ; HTIMEL 
    dd   SNES_W4208  ; HTIMEH 
    dd   SNES_W4209  ; VTIMEL 
    dd   SNES_W420A  ; VTIMEH 
%ifdef NO_DMA_WRITE 
    DUPLICATE dd,2,UNSUPPORTED_WRITE 
%else 
    dd   SNES_W420B  ; MDMAEN 
    dd   SNES_W420C  ; HDMAEN 
%endif 
    dd   SNES_W420D  ; MEMSEL 
    DUPLICATE dd,2,UNSUPPORTED_WRITE 
    dd   SNES_W4210  ; RDNMI 
    dd   SNES_W4211  ; TIMEUP 
    dd   IGNORE_WRITE    ; HVBJOY 
    dd   IGNORE_WRITE    ; RDIO 
    dd   IGNORE_WRITE    ; RDDIVL 
    dd   IGNORE_WRITE    ; RDDIVH 
    dd   IGNORE_WRITE    ; RDMPYL 
    dd   IGNORE_WRITE    ; RDMPYH 
    dd   IGNORE_WRITE    ; JOY1L 
    dd   IGNORE_WRITE    ; JOY1H 
    dd   IGNORE_WRITE    ; JOY2L 
    dd   IGNORE_WRITE    ; JOY2H 
    dd   IGNORE_WRITE    ; JOY3L 
    dd   IGNORE_WRITE    ; JOY3H 
    dd   IGNORE_WRITE    ; JOY4L 
    dd   IGNORE_WRITE    ; JOY4H 
    DUPLICATE dd,0xE0,UNSUPPORTED_WRITE 
 
EXPORT_C Write_Map_43 
%ifdef NO_DMA_WRITE 
    DUPLICATE dd,0x80,UNSUPPORTED_WRITE 
%else 
    MAP_WRITE_DMA_LIST 0 
    MAP_WRITE_DMA_LIST 1 
    MAP_WRITE_DMA_LIST 2 
    MAP_WRITE_DMA_LIST 3 
    MAP_WRITE_DMA_LIST 4 
    MAP_WRITE_DMA_LIST 5 
    MAP_WRITE_DMA_LIST 6 
    MAP_WRITE_DMA_LIST 7 
%endif 
    DUPLICATE dd,0x80,UNSUPPORTED_WRITE 
    ; 4300-5FFF: Unmapped 
    DUPLICATE dd,0x1D00,UNSUPPORTED_WRITE 
 
ALIGND 
; BG12NBA/BG34NBA to tileset-in-cache address tables 
BGNBA_Table_2: 
dd 0<<12,1<<12,2<<12,3<<12,4<<12,5<<12,6<<12,7<<12 
BGNBA_Table_4: 
dd 0<<11,1<<11,2<<11,3<<11,4<<11,5<<11,6<<11,7<<11 
BGNBA_Table_8: 
dd 0<<10,1<<10,2<<10,3<<10,4<<10,5<<10,6<<10,7<<10 
; BGMODE layer depth tables 
; Standard 
;  1 = 2-bit   2 = 4-bit   3=8-bit 
; Offset Change 
;  5 = 2-bit   6 = 4-bit   7=8-bit 
; Special 
;  4 = mode-7  9 = 2-bit mode-0        0 = no more layers 
; 4 layers, 8 bytes per layer ([4][8] array) 
BGMODE_Depth_Table: 
db 9,2,6,3,7,2,6,4 
db 9,2,6,2,5,1,0,0 
db 9,1,0,0,0,0,0,0 
db 9,0,0,0,0,0,0,0 
 
; These layers are allowed *** 
BGMODE_Allowed_Layer_Mask_Table: 
db 0x1F,0x17,0x13,0x13,0x13,0x13,0x11,0x13 
 
; These layers require tileset recaching before rendering 
BGMODE_Tile_Layer_Mask_Table: 
db 0x1F,0x17,0x13,0x13,0x13,0x13,0x11,0x10 
 
; These layers allow per-tile offset change 
BGMODE_Allowed_Offset_Change_Table: 
db 0,0,0xFF,0,0xFF,0,0xFF,0 
 
ALIGND 
LineRenderSmall: 
dd 0,_Render_8x8_C2,_Render_8x8_C4,_Render_8x8_C8 
dd SCREEN_MODE_7 
dd _Render_Offset_8x8_C2,_Render_Offset_8x8_C4,_Render_Offset_8x8_C8 
dd 0,_Render_8x8_C2 
 
LineRenderLarge: 
dd 0,_Render_16x16_C2,_Render_16x16_C4,_Render_16x16_C8 
dd SCREEN_MODE_7 
dd _Render_Offset_16x16_C2,_Render_Offset_16x16_C4,_Render_Offset_16x16_C8 
dd 0,_Render_16x16_C2 
 
LineRenderEvenSmall: 
dd 0,_Render_16x8_Even_C2,_Render_16x8_Even_C4,0 
dd SCREEN_MODE_7,_Render_16x8_Even_C2,_Render_16x8_Even_C4,0 
 
LineRenderEvenLarge: 
dd 0,_Render_16x16_Even_C2,_Render_16x16_Even_C4,0 
dd SCREEN_MODE_7,_Render_16x16_Even_C2,_Render_16x16_Even_C4,0 
 
Depth_NBA_Table: 
%ifdef USE_8BPL_CACHE_FOR_4BPL 
dd 0,BGNBA_Table_2,BGNBA_Table_8,BGNBA_Table_8  ;* 
%else 
dd 0,BGNBA_Table_2,BGNBA_Table_4,BGNBA_Table_8 
%endif 
 
section .bss 
ALIGNB 
EXPORT_C WRAM   ,skipk 128  ; Buffer for Work RAM 
EXPORT_C VRAM   ,skipk 64   ; Buffer for Video RAM 
EXPORT_C SRAM   ,skipk 64   ; Buffer for Save RAM 
EXPORT_C SPCRAM ,skipk 64   ; Buffer for SPC RAM/ROM 
EXPORT_C Blank  ,skipk 64   ; Blank ROM buffer 
_PortRAM:skipk 24           ; Ports 0x2000-0x5FFF 
 
VRAMAddress:    skipl   ; VRAM address in PPU 
SCINC:          skipl   ; Used in updating VRAM address 
 
EXPORT Tile_Recache_Set_Begin,skipl 
EXPORT Tile_Recache_Set_End  ,skipl 
 
EXPORT Mosaic_Size,skipl    ; 000xxxxx  xxxxx=2-16 pixel size 
EXPORT Mosaic_Size_Select,skipl ;Table selector 
EXPORT MOSAIC     ,skipb    ; xxxxabcd  xxxx=0-F pixel size,a-d = affect BG4-1 
EXPORT_C INIDISP  ,skipb    ; x000bbbb x=screen on/off,bbbb=Brightness 
EXPORT_C BGMODE   ,skipb    ; abcdefff a-d=tile size bg4-1 (8/16),e=priority bg3,fff=mode 
EXPORT_C Base_BGMODE,skipb  ; 00000fff fff=mode 
 
EXPORT_C BG12NBA,skipb  ; aaaabbbb  aaaa=base address 2, bbbb=base address 1 
EXPORT_C BG34NBA,skipb  ; aaaabbbb  aaaa=base address 4, bbbb=base address 3 
 
EXPORT_C VMAIN,skipb    ; i000abcd  i=inc type,ab=full graphic,cd=SC increment 
 
EXPORT_C W12SEL ,skipb  ; Holds plane 1/2 window mask settings 
EXPORT_C W34SEL ,skipb  ; Holds plane 3/4 window mask settings 
EXPORT_C WOBJSEL,skipb  ; Holds colour/object window mask settings 
                 skipb 
EXPORT_C Win1_Count_In,skipb 
EXPORT_C WH0,skipb      ; Holds window 1 left position 
EXPORT_C TM ,skipb      ; 000odcba  o=OBJ enable,a-d=BG1-4 enable 
EXPORT_C WBGLOG,skipb   ; BG Window mask logic 
EXPORT_C Win1_Count_Out,skipb 
EXPORT_C WH1,skipb      ; Holds window 1 right position 
EXPORT_C TMW,skipb 
EXPORT_C WOBJLOG,skipb  ; OBJ/Colour Window mask logic 
EXPORT_C Win2_Count_In,skipb 
EXPORT_C WH2,skipb      ; Holds window 2 left position 
EXPORT_C TS ,skipb      ; 000odcba  o=OBJ enable,a-d=BG1-4 enable 
EXPORT_C CGWSEL,skipb 
EXPORT_C Win2_Count_Out,skipb 
EXPORT_C WH3,skipb      ; Holds window 2 right position 
EXPORT_C TSW,skipb 
EXPORT_C CGADSUB,skipb 
EXPORT_C Win1_Bands_In ,skipb 2 
EXPORT_C Win2_Bands_In ,skipb 2 
EXPORT_C Win1_Bands_Out,skipb 4 
EXPORT_C Win2_Bands_Out,skipb 4 
 
EXPORT_C COLDATA,skipl  ; Actual data from COLDATA 
CGAddress:  skipl   ; Palette position for writes to CGRAM 
 
WMADDL:     skipb   ; Work RAM Address Lo Byte 
WMADDM:     skipb   ; Work RAM Address Mid Byte 
WMADDH:     skipb   ; Work RAM Address Hi Byte - Just bit 0 used! 
            skipb 
JOYC1:      skipb   ; This holds the controller read control byte 
Controller1_Pos:    skipb   ; Shift count for controller 1 read 
Controller23_Pos:   skipb   ; Shift count for controller 2/3 read 
Controller45_Pos:   skipb   ; Shift count for controller 4/5 read 
 
RDDIVL:     skipb   ; Quotient of divide 
RDDIVH:     skipb 
RDMPYL:     skipb   ; Multiplication or remainder 
RDMPYH:     skipb 
WRMPYA:     skipb   ; Multiplicand A 
WRMPYB:     skipb   ; Multiplicand B 
WRDIVL:     skipb   ; Dividend C 
WRDIVH:     skipb 
 
CGHigh:     skipb   ; Holds whether writing to first or second byte 
CGReadHigh: skipb   ; Whether reading lo or high byte 
 
BGOFS_Last_Write:skipb 
 
ALIGNB 
JOY1L:      skipb   ; These are updated per VBL rather than per read! 
JOY1H:      skipb 
            skipw 
JOY2L:      skipb 
JOY2H:      skipb 
            skipw 
JOY3L:      skipb   ; These are not updated yet 
JOY3H:      skipb 
            skipw 
JOY4L:      skipb 
JOY4H:      skipb 
            skipw 
 
EXPORT_C Current_Line_Timing,skipl 
 
EXPORT_C SETINI,skipb 
EXPORT STAT78,skipb     ; Enable support for field register 
 
EXPORT Redo_BGs,skipb 
EXPORT Redo_Offset_Change,skipb 
 
EXPORT SCR_TM,skipb     ; TM taken from here 
EXPORT SCR_TS,skipb     ; TS taken from here 
EXPORT_C Layer_Disable_Mask,skipb   ; This is used to force planes to disable! 
EXPORT_C Layering_Mode,skipb 
 
EXPORT BGMODE_Allowed_Layer_Mask,skipb 
EXPORT BGMODE_Tile_Layer_Mask,skipb 
EXPORT BGMODE_Allowed_Offset_Change,skipb 
 
; MapAddress - base address of tilemap 
; LMapAddress - address of tilemap for left 32 tiles 
; RMapAddress - address of tilemap for right 32 tiles 
; L/R will be same if tilemap only 32 tiles wide! 
; VL/VR are vertically adjusted for current scanline 
; SetAddress - address of tileset in cache 
; FirstOffset - first pixel on visible screen in leftmost tile 
; TileCount1 - count of visible tiles in left 32 tiles of tilemap 
; TileCount2 - count of visible tiles in right 32 tiles of tilemap 
; Redo - procedure used to recalculate first offset and tile counts 
 
%macro BG_DATA 1 
ALIGNB 
EXPORT TableBG%1 
EXPORT TileCount1BG%1,skipb 
EXPORT TileCount2BG%1,skipb 
EXPORT_C BGSC%1,skipb   ; xxxxxxab  xxxxxx=base address, ab=SC Size 
EXPORT DepthBG%1,skipb 
EXPORT Redo_BG%1,skipl 
EXPORT FirstPixelBG%1,skipl 
EXPORT FirstTileBG%1,skipl 
 
EXPORT VScroll_%1,skipl 
EXPORT HScroll_%1,skipl 
EXPORT VLMapAddressBG%1,skipl 
EXPORT VRMapAddressBG%1,skipl 
 
LineRenderBG%1: skipl 
EXPORT SetAddressBG%1,skipl ; Address of BG tileset 
EXPORT LMapAddressBG%1,skipl 
EXPORT RMapAddressBG%1,skipl 
 
EXPORT MapAddressBG%1       ; Screen address of BG 
EXPORT TLMapAddressBG%1,skipl 
EXPORT TRMapAddressBG%1,skipl 
EXPORT BLMapAddressBG%1,skipl 
EXPORT BRMapAddressBG%1,skipl 
 
TileHeightBG%1: skipb 
TileWidthBG%1:  skipb 
EXPORT MosaicBG%1,skipb 
EXPORT NBABG%1,skipb        ; Unused in BG3/4 
NBATableBG%1:   skipl       ; Unused in BG3/4 
EXPORT M0_Color_BG%1,skipl 
EXPORT Priority_Used_BG%1,skipb 
EXPORT Priority_Unused_BG%1,skipb 
EXPORT OC_Flag_BG%1,skipb   ; Unused in BG3/4 
%endmacro 
 
BG_DATA 1 
BG_DATA 2 
BG_DATA 3 
BG_DATA 4 
 
EXPORT_EQU_C BG1SC,_BGSC1 
EXPORT_EQU_C BG2SC,_BGSC2 
EXPORT_EQU_C BG3SC,_BGSC3 
EXPORT_EQU_C BG4SC,_BGSC4 
 
EXPORT_EQU_C BG1HOFS,HScroll_1 
EXPORT_EQU_C BG1VOFS,VScroll_1 
EXPORT_EQU_C BG2HOFS,HScroll_2 
EXPORT_EQU_C BG2VOFS,VScroll_2 
EXPORT_EQU_C BG3HOFS,HScroll_3 
EXPORT_EQU_C BG3VOFS,VScroll_3 
EXPORT_EQU_C BG4HOFS,HScroll_4 
EXPORT_EQU_C BG4VOFS,VScroll_4 
 
PaletteData:    skipw 
 
section .text 
ALIGNC 
EXPORT Reset_Ports 
 pusha 
 
 call Reset_Sprites 
 call Reset_DMA 
 call _Reset_Mode_7 
 
; cld 
; mov edi,_PortRAM 
; mov ecx,0x6000 / 4 
; mov eax,-1     ; Reset to 0xFF 
; push es 
; push ds 
; pop es 
; rep stosd 
; pop es 
 
 ; Reset renderer 
 mov byte [_Layer_Disable_Mask],0xFF 
 
 mov al,[BGMODE_Allowed_Layer_Mask_Table] 
 mov [BGMODE_Allowed_Layer_Mask],al 
 mov al,[BGMODE_Tile_Layer_Mask_Table] 
 mov [BGMODE_Tile_Layer_Mask],al 
 mov al,[BGMODE_Allowed_Offset_Change_Table] 
 mov [BGMODE_Allowed_Offset_Change],al 
 
 ; Set eax to 0, as we're setting most everything to 0... 
 xor eax,eax 
 
 mov dword [Render_Select],_Render_Layering_Option_0 
 mov byte [_Layering_Mode],0 
 
 mov dword [_LastRenderLine],223 
 
 mov [SCR_TM],al 
 mov [SCR_TS],al 
 
 mov [WMADDL],eax 
 
 mov [VRAMAddress],eax 
 mov dword [SCINC],1 
 mov [_VMAIN],al 
 mov [MOSAIC],al 
 mov [MosaicBG1],al 
 mov [MosaicBG2],al 
 mov [MosaicBG3],al 
 mov [MosaicBG4],al 
 mov dword [Mosaic_Size],1 
 mov dword [Mosaic_Size_Select],0 
 
 mov byte [STAT78],3 
 
 mov [CGAddress],eax 
 mov [CGHigh],al 
 mov [CGReadHigh],al 
 
 mov [BGOFS_Last_Write],al 
 
 mov [_BGSC1],al 
 mov [_BGSC2],al 
 mov [_BGSC3],al 
 mov [_BGSC4],al 
 
 mov byte [Redo_BGs],0x0F 
 mov byte [Redo_Offset_Change],0 
 
 mov [_BGMODE],al 
 mov [_Base_BGMODE],al 
 mov [_BG1SC],al 
 mov [_BG2SC],al 
 mov [_BG3SC],al 
 mov [_BG4SC],al 
 mov [_BG12NBA],al 
 mov [_BG34NBA],al 
 mov [NBABG1],al 
 mov [NBABG2],al 
 mov [NBABG3],al 
 mov [NBABG4],al 
 
 mov [WRMPYA],al 
 mov [WRDIVL],al 
 mov [WRDIVH],al 
 mov [RDDIVL],al 
 mov [RDDIVH],al 
 mov [RDMPYL],al 
 mov [RDMPYH],al 
 
 mov [_WH0],al 
 mov [_WH2],al 
 inc eax 
 mov [_WH1],al 
 mov [_WH3],al 
 dec eax 
 mov [_WBGLOG],al 
 mov [_WOBJLOG],al 
 mov [_W12SEL],al 
 mov [_W34SEL],al 
 mov [_WOBJSEL],al 
 mov [_TM],al 
 mov [_TS],al 
 mov [_SETINI],al 
 mov [_COLDATA],eax 
 mov [_CGWSEL],al 
 mov [_CGADSUB],al 
 
 mov [JOYC1],al 
 mov byte [Controller1_Pos],16 
 mov byte [Controller23_Pos],16 
 mov byte [Controller45_Pos],16 
 mov dword [JOY1L],(1<<31) 
 mov dword [JOY2L],(1<<31) 
 mov dword [JOY3L],(1<<31) 
 mov dword [JOY4L],(1<<31) 
 
 mov [_BrightnessLevel],al 
 mov byte [_INIDISP],0x80 
 mov dword [Redo_BG1],Redo_8 
 mov dword [Redo_BG2],Redo_8 
 mov dword [Redo_BG3],Redo_8 
 mov dword [Redo_BG4],Redo_8 
 mov [_BG1HOFS],eax 
 mov [_BG1VOFS],eax 
 mov [_BG2HOFS],eax 
 mov [_BG2VOFS],eax 
 mov [_BG3HOFS],eax 
 mov [_BG3VOFS],eax 
 mov [_BG4HOFS],eax 
 mov [_BG4VOFS],eax 
 
 mov [SetAddressBG1],eax 
 mov [SetAddressBG2],eax 
 mov [SetAddressBG3],eax 
 mov [SetAddressBG4],eax 
 
 mov dword [M0_Color_BG1],0x03030303 
 mov dword [M0_Color_BG2],0x23232323 
 mov dword [M0_Color_BG3],0x43434343 
 mov dword [M0_Color_BG4],0x63636363 
 
 mov byte [OC_Flag_BG1],0x20 
 mov byte [OC_Flag_BG2],0x40 
 
 mov dword [NBATableBG1],BGNBA_Table_2 
 mov dword [NBATableBG2],BGNBA_Table_2 
 mov dword [NBATableBG3],BGNBA_Table_2 
 mov dword [NBATableBG4],BGNBA_Table_2 
 
 mov dword [LineRenderBG1],_Render_8x8_C2 
 mov dword [LineRenderBG2],_Render_8x8_C2 
 mov dword [LineRenderBG3],_Render_8x8_C2 
 mov dword [LineRenderBG4],_Render_8x8_C2 
 
 mov byte [TileHeightBG1],1 
 mov byte [TileHeightBG2],1 
 mov byte [TileHeightBG3],1 
 mov byte [TileHeightBG4],1 
 mov byte [TileWidthBG1],1 
 mov byte [TileWidthBG2],1 
 mov byte [TileWidthBG3],1 
 mov byte [TileWidthBG4],1 
 mov byte [DepthBG1],1 
 mov byte [DepthBG2],1 
 mov byte [DepthBG3],1 
 mov byte [DepthBG4],1 
 
 mov eax,_VRAM 
 mov [TLMapAddressBG1],eax  ;MapAddressBG1 
 mov [TLMapAddressBG2],eax  ;MapAddressBG2 
 mov [TLMapAddressBG3],eax  ;MapAddressBG3 
 mov [TLMapAddressBG4],eax  ;MapAddressBG4 
 mov [TRMapAddressBG1],eax 
 mov [TRMapAddressBG2],eax 
 mov [TRMapAddressBG3],eax 
 mov [TRMapAddressBG4],eax 
 mov [BLMapAddressBG1],eax 
 mov [BLMapAddressBG2],eax 
 mov [BLMapAddressBG3],eax 
 mov [BLMapAddressBG4],eax 
 mov [BRMapAddressBG1],eax 
 mov [BRMapAddressBG2],eax 
 mov [BRMapAddressBG3],eax 
 mov [BRMapAddressBG4],eax 
%if 0 
 mov [LMapAddressBG1],eax 
 mov [LMapAddressBG2],eax 
 mov [LMapAddressBG3],eax 
 mov [LMapAddressBG4],eax 
 mov [RMapAddressBG1],eax 
 mov [RMapAddressBG2],eax 
 mov [RMapAddressBG3],eax 
 mov [RMapAddressBG4],eax 
%endif 
 mov dword [OffsetChangeMap_VOffset],0 
 mov dword [OffsetChangeVMap_VOffset],0 
 
 mov eax,[Screen_Mode] 
 mov [Render_Mode],eax 
 
 Set_21_Read 0x39,SNES_R2139_NORM 
 Set_21_Read 0x3A,SNES_R213A_NORM 
 Set_21_Write 0x15,SNES_W2115 
 Set_21_Write 0x18,SNES_W2118_NORM_First 
 Set_21_Write 0x19,SNES_W2119_NORM_First 
 
 popa 
 ret 
 
; JOYPAD UPDATE FUNCTION.. now called during VBL to accomodate JOYC1 & JOYC2 
ALIGNC 
EXPORT UPDATE_CONTROLLERS 
 pusha 
 
 call UPDATE_JOYSTICKS 
 
 test byte [_NMITIMEN],1 
 jz  .reset_controllers_read 
 mov byte [Controller1_Pos],0 
 mov byte [Controller23_Pos],0 
 mov byte [Controller45_Pos],0 
.reset_controllers_read: 
; UPDATE CONTROLLER 1 
 
%if 0 
 cmp byte [_JOYSTICK_ENABLED],1 
 jne .controller_1_no_joystick 
 
 call _poll_joystick 
 
 ; Below needs to be rewritten to support Allegro 3.1 
 mov al,0 
 
 cmp dword [_joy_b4],0  ; Is A pressed? 
 je .not_pressed_joy_1_a 
 or al,0x80 
.not_pressed_joy_1_a: 
 
 cmp dword [_joy_b2],0  ; Is X pressed? 
 je .not_pressed_joy_1_x 
 or al,0x40 
.not_pressed_joy_1_x: 
 
 cmp dword [_joy_b5],0  ; Is L pressed? 
 je .not_pressed_joy_1_l 
 or al,0x20 
.not_pressed_joy_1_l: 
 
 cmp dword [_joy_b6],0  ; Is R pressed? 
 je .not_pressed_joy_1_r 
 or al,0x10 
.not_pressed_joy_1_r: 
 
 mov [JOY1L],al 
 
 mov al,0 
 
 cmp dword [_joy_b3],0  ; Is B pressed? 
 je .not_pressed_joy_1_b 
 or al,0x80 
.not_pressed_joy_1_b: 
 
 cmp dword [_joy_b1],0  ; Is Y pressed? 
 je .not_pressed_joy_1_y 
 or al,0x40 
.not_pressed_joy_1_y: 
 
 JUMP_VAR_KEY_UP _KEY_SELECT_1,.not_pressed_joy_1_select,edx 
 ; Is  pressed? 
 or al,0x20 
.not_pressed_key_1_select: 
 
 JUMP_VAR_KEY_UP _KEY_START_1,.not_pressed_key_1_start,edx 
 ; Is  pressed? 
 or al,0x10 
.not_pressed_key_1_start: 
 
 JUMP_VAR_KEY_UP _KEY_UP_1,.not_pressed_key_1_up,edx 
 ; Is  pressed? 
 or al,8 
 jmp short .not_pressed_key_1_down 
.not_pressed_key_1_up: 
 JUMP_VAR_KEY_UP _KEY_DOWN_1,.not_pressed_key_1_down,edx 
 ; Is  pressed? 
 or al,4 
.not_pressed_key_1_down: 
 
 JUMP_VAR_KEY_UP _KEY_LEFT_1,.not_pressed_key_1_left,edx 
 ; Is  pressed? 
 or al,2 
 jmp short .not_pressed_key_1_right 
.not_pressed_key_1_left: 
 JUMP_VAR_KEY_UP _KEY_RIGHT_1,.not_pressed_key_1_right,edx 
; Is  pressed? 
 or al,1 
.not_pressed_key_1_right: 
 
 mov [JOY1H],al 
 
.do_controller_2: 
 
; UPDATE CONTROLLER 2 
 
%if 0 
 cmp byte [_JOYSTICK_ENABLED2],1 
 jne .controller_2_no_joystick 
 
 call _poll_joystick 
 
 ; Below needs to be rewritten to support Allegro 3.1 
 mov al,0 
 
 cmp dword [_joy_b4],0  ; Is A pressed? 
 je .not_pressed_joy_2_a 
 or al,0x80 
.not_pressed_joy_2_a: 
 
 cmp dword [_joy_b2],0  ; Is X pressed? 
 je .not_pressed_joy_2_x 
 or al,0x40 
.not_pressed_joy_2_x: 
 
 cmp dword [_joy_b5],0  ; Is L pressed? 
 je .not_pressed_joy_2_l 
 or al,0x20 
.not_pressed_joy_2_l: 
 
 cmp dword [_joy_b6],0  ; Is R pressed? 
 je .not_pressed_joy_2_r 
 or al,0x10 
.not_pressed_joy_2_r: 
 
 mov [JOY2L],al 
 
 mov al,0 
 
 cmp dword [_joy_b3],0  ; Is B pressed? 
 je .not_pressed_joy_2_b 
 or al,0x80 
.not_pressed_joy_2_b: 
 
 cmp dword [_joy_b1],0  ; Is Y pressed? 
 je .not_pressed_joy_2_y 
 or al,0x40 
.not_pressed_joy_2_y: 
 
 JUMP_VAR_KEY_UP _KEY_SELECT_2,.not_pressed_joy_2_select,edx 
 ; Is  pressed? 
 or al,0x20 
.not_pressed_key_2_select: 
 
 JUMP_VAR_KEY_UP _KEY_START_2,.not_pressed_key_2_start,edx 
 ; Is  pressed? 
 or al,0x10 
.not_pressed_key_2_start: 
 
 JUMP_VAR_KEY_UP _KEY_UP_2,.not_pressed_key_2_up,edx 
 ; Is  pressed? 
 or al,8 
 jmp short .not_pressed_key_2_down 
.not_pressed_key_2_up: 
 JUMP_VAR_KEY_UP _KEY_DOWN_2,.not_pressed_key_2_down,edx 
 ; Is  pressed? 
 or al,4 
.not_pressed_key_2_down: 
 
 JUMP_VAR_KEY_UP _KEY_LEFT_2,.not_pressed_key_2_left,edx 
 ; Is  pressed? 
 or al,2 
 jmp short .not_pressed_key_2_right 
.not_pressed_key_2_left: 
 JUMP_VAR_KEY_UP _KEY_RIGHT_2,.not_pressed_key_2_right,edx 
; Is  pressed? 
 or al,1 
.not_pressed_key_2_right: 
 
 mov [JOY2H],al 
 
.end: 
 popa 
 ret 
 
ALIGNC 
UPDATE_JOYSTICKS: 
; Update joystick if enabled 
%if 0 
 cmp byte [_JOYSTICK_ENABLED],1 
 je .poll 
 cmp byte [_JOYSTICK_ENABLED2],1 
 jne .end 
 
.poll: 
 pusha 
 call _poll_joystick 
 popa 
%endif 
.end: 
 ret 
 
; Read from 21xx handlers 
 
ALIGNC 
SNES_R2116: ; VMADDL 
 mov al,[VRAMAddress] 
 ret 
 
ALIGNC 
SNES_R2117: ; VMADDH 
 mov al,[VRAMAddress+1] 
 ret 
 
; SNES_R2134: ; MPYL in mode7.asm 
; SNES_R2135: ; MPYM in mode7.asm 
; SNES_R2136: ; MPYH in mode7.asm 
; SNES_R2137: ; SLHV in timing.ni 
; SNES_R2138: ; OAMDATAREAD in sprites.asm 
 
%macro VRAM_Read_Reset 0-1 0 
%ifnidni %1,check_full 
 mov ah,[_VMAIN] 
 Set_21_Write 0x15,SNES_W2115_Before_VRAM_read 
 test ah,0x0C 
 jz %%no_full 
 
 Set_21_Write 0x18,SNES_W2118_FULL 
 Set_21_Write 0x19,SNES_W2119_FULL 
 jmp short %%done 
 
%%no_full: 
 Set_21_Write 0x18,SNES_W2118_NORM 
 Set_21_Write 0x19,SNES_W2119_NORM 
%%done: 
%endif 
 Set_21_Read 0x39,SNES_R2139_First 
 Set_21_Read 0x3A,SNES_R213A_First 
%ifndef NO_DMA_WRITE 
 call Update_DMA_PPU_Handlers 
%endif 
%endmacro 
 
%macro VRAM_Read_Fixup 0    ; VMAIN register value must be in bl 
 Set_21_Write 0x15,SNES_W2115 
 test bl,0x0C 
 jz %%no_full 
 Set_21_Write 0x18,SNES_W2118_FULL_First 
 Set_21_Write 0x19,SNES_W2119_FULL_First 
 Set_21_Read 0x39,SNES_R2139_FULL 
 Set_21_Read 0x3A,SNES_R213A_FULL 
 jmp short %%done 
 
%%no_full: 
 Set_21_Write 0x18,SNES_W2118_NORM_First 
 Set_21_Write 0x19,SNES_W2119_NORM_First 
 Set_21_Read 0x39,SNES_R2139_NORM 
 Set_21_Read 0x3A,SNES_R213A_NORM 
%%done: 
%ifndef NO_DMA_WRITE 
 call Update_DMA_PPU_Handlers 
%endif 
%endmacro 
 
ALIGNC 
SNES_R2139_First:   ; VMDATALREAD, first read 
 push ebx 
 mov edi,[VRAMAddress] 
 mov bl,[_VMAIN] 
 test bl,bl 
 mov al,[_VRAM+edi*2] 
 js .no_increment 
 add edi,[SCINC] 
 and edi,0x7FFF 
 mov [VRAMAddress],edi 
 VRAM_Read_Fixup 
.no_increment: 
 pop ebx 
 ret 
 
ALIGNC 
SNES_R2139_NORM:    ; VMDATALREAD, normal increment 
 mov edi,[VRAMAddress] 
 dec edi 
 mov al,[_VMAIN] 
 and edi,0x7FFF 
 test al,al 
 mov al,[_VRAM+edi*2] 
 js .no_increment 
 inc edi 
 add edi,[SCINC] 
 and edi,0x7FFF 
 mov [VRAMAddress],edi 
.no_increment: 
 ret 
 
ALIGNC 
SNES_R2139_FULL:    ; VMDATALREAD, full graphic increment 
 mov edi,[VRAMAddress] 
 push edx 
 push eax 
 mov edx,edi 
 mov eax,edi 
 shr edx,7 
_2139_Bitshift equ $-1 
 and eax,byte 0x7F 
_2139_Bitmask equ $-1 
 and edx,byte 7 
 shl eax,3 
 and edi,0x7C00 
_2139_Topmask equ $-4 
 or edi,eax 
 pop eax 
 or edi,edx 
 pop edx 
 dec edi 
 and edi,0x7FFF 
 mov al,[_VMAIN] 
 test al,al 
 mov al,[_VRAM+edi*2] 
 js .no_increment 
 mov edi,[SCINC] 
 add edi,[VRAMAddress]  ; Always words (since <<1)! 
 and edi,0x7FFF 
 mov [VRAMAddress],edi 
.no_increment: 
 ret 
 
ALIGNC 
SNES_R213A_First:   ; VMDATAHREAD, first read 
 push ebx 
 mov edi,[VRAMAddress] 
 mov bl,[_VMAIN] 
 test bl,bl 
 mov al,[_VRAM+1+edi*2] 
 jns .no_increment 
 add edi,[SCINC] 
 and edi,0x7FFF 
 mov [VRAMAddress],edi 
 VRAM_Read_Fixup 
.no_increment: 
 pop ebx 
 ret 
 
ALIGNC 
SNES_R213A_NORM:    ; VMDATAHREAD, normal increment 
 mov edi,[VRAMAddress] 
 dec edi 
 mov al,[_VMAIN] 
 and edi,0x7FFF 
 test al,al 
 mov al,[_VRAM+1+edi*2] 
 jns .no_increment 
 inc edi 
 add edi,[SCINC] 
 and edi,0x7FFF 
 mov [VRAMAddress],edi 
.no_increment: 
 ret 
 
ALIGNC 
SNES_R213A_FULL:    ; VMDATAHREAD, full graphic increment 
 mov edi,[VRAMAddress] 
 push edx 
 push eax 
 mov edx,edi 
 mov eax,edi 
 shr edx,7 
_213A_Bitshift equ $-1 
 and eax,byte 0x7F 
_213A_Bitmask equ $-1 
 and edx,byte 7 
 shl eax,3 
 and edi,0x7C00 
_213A_Topmask equ $-4 
 or edi,eax 
 pop eax 
 or edi,edx 
 pop edx 
 dec edi 
 and edi,0x7FFF 
 mov al,[_VMAIN] 
 test al,al 
 mov al,[_VRAM+1+edi*2] 
 jns .no_increment 
 mov edi,[SCINC] 
 add edi,[VRAMAddress]  ; Always words (since <<1)! 
 and edi,0x7FFF 
 mov [VRAMAddress],edi 
.no_increment: 
 ret 
 
ALIGNC 
SNES_R213B: ; CGDATAREAD 
 push ebx 
 xor ebx,ebx 
 push edx 
 mov bl,[CGReadHigh] 
 mov edx,[CGAddress] 
 mov al,[_Real_SNES_Palette+ebx+edx*2] 
 xor bl,1 
 jnz .no_increment 
 inc edx 
 mov [CGAddress],dl ; Chop address for wrap 
.no_increment: 
 mov [CGReadHigh],bl 
 pop edx 
 pop ebx 
 ret 
 
; SNES_R213C: ; OPHCT in timing.ni 
; SNES_R213D: ; OPVCT in timing.ni 
 
ALIGNC 
SNES_R213E: ; STAT77 
 mov al,1   ; This is not supported yet! 
 ret 
 
ALIGNC 
SNES_R213F: ; STAT78 
 mov al,0 
 mov [OPHCT],al 
 mov [OPVCT],al 
 mov al,[STAT78] 
 or al,[_SNES_COUNTRY]  ; 0x10 means PAL, not NTSC 
 ret 
 
; SNES_R2140_SKIP: ; APUI00 in APUskip.asm 
; SNES_R2141_SKIP: ; APUI01 in APUskip.asm 
; SNES_R2142_SKIP: ; APUI02 in APUskip.asm 
; SNES_R2143_SKIP: ; APUI03 in APUskip.asm 
 
; SNES_R2140_SPC:  ; APUI00 in spc700.asm 
; SNES_R2141_SPC:  ; APUI01 in spc700.asm 
; SNES_R2142_SPC:  ; APUI02 in spc700.asm 
; SNES_R2143_SPC:  ; APUI03 in spc700.asm 
 
ALIGNC 
SNES_R2180: ; WMDATA 
 mov edi,[WMADDL] 
 mov al,[_WRAM+edi] 
 inc edi 
 and edi,0x01FFFF 
 mov [WMADDL],edi 
 ret 
 
ALIGNC 
SNES_R2181: ; WMADDL 
 mov al,[WMADDL] 
 ret 
 
ALIGNC 
SNES_R2182: ; WMADDM 
 mov al,[WMADDM] 
 ret 
 
ALIGNC 
SNES_R2183: ; WMADDH 
 mov al,[WMADDH] 
 ret 
 
ALIGNC 
SNES_R21C2: ; Unknown? SNES test cart reads it, returning same value as 9X 
 mov al,0x20 
 ret 
 
ALIGNC 
SNES_R21C3: ; Unknown? SNES test cart reads it, returning same value as 9X 
 mov al,0 
 ret 
 
; Read from 40xx handlers 
%if 0 
 SNEeSe 0.13, random speculation time, this register or at least bit 0 
  handles input for controller 1 
 
 v0.15 17th bit is an indication of joypad connected status 
 
 $4016 is very similar in function (almost identical) to the register at 
  the same address on the NES/Famicom 
 
%endif 
 
ALIGNC 
SNES_R4016: ; JOYC1 
 test byte [JOYC1],1 
 jz .read_enabled 
 mov al,0 
 ret 
 
.read_enabled: 
 push ecx 
 push ebx 
 cmp byte [_JOYSTICK_ENABLED],2     ; Is mouse plugged in? 
 je .do_mouse 
 
 mov cl,[Controller1_Pos] 
 dec cl 
 mov ebx,[JOY1L] 
 mov [Controller1_Pos],cl 
 ror ebx,cl 
 mov al,bl 
 and al,1 
 pop ebx 
 pop ecx 
 ret 
 
.unused: 
 mov byte [Controller1_Pos],16 
 mov al,1           ; Joypad connected 
 pop ebx 
 pop ecx 
 ret 
 
ALIGNC 
.do_mouse: 
 mov cl,[Controller1_Pos] 
 dec cl 
 and cl,0x0F 
 mov [Controller1_Pos],cl 
 mov bx,[_MickeyRead] 
 shr bx,cl 
 mov al,bl 
 and al,1 
 pop ebx 
 pop ecx 
 ret 
 
ALIGNC 
SNES_R4017: ; JOYC2 
 test byte [JOYC1],1 
 jz .read_enabled 
 mov al,0 
 ret 
 
.read_enabled: 
 push ecx 
 push ebx 
 cmp byte [_JOYSTICK_ENABLED2],2    ; Is mouse plugged in? 
 je .do_mouse 
 
 mov cl,[Controller23_Pos] 
 dec cl 
 mov ebx,[JOY2L] 
 mov [Controller23_Pos],cl 
 ror ebx,cl 
 mov al,bl 
 and al,1 
 pop ebx 
 pop ecx 
 ret 
 
.unused: 
 mov byte [Controller23_Pos],16 
 mov al,1           ; (???) Return 0 this bit is for mtap I think 
 pop ebx 
 pop ecx 
 ret 
 
ALIGNC 
.do_mouse: 
 mov cl,[Controller23_Pos] 
 dec cl 
 and cl,0x0F 
 mov [Controller23_Pos],cl 
 mov bx,[_MickeyRead] 
 shr bx,cl 
 mov al,bl 
 and al,1 
 pop ebx 
 pop ecx 
 ret 
 
; Read from 42xx handlers 
ALIGNC 
SNES_R4202: ; WRMPYA 
 mov al,[WRMPYA] 
 ret 
 
ALIGNC 
SNES_R4203: ; WRMPYB 
 mov al,[WRMPYB] 
 ret 
 
; SNES_R4200: ; NMITIMEN in timing.ni 
; SNES_R4210: ; RDNMI in timing.ni 
; SNES_R4211: ; TIMEUP in timing.ni 
; SNES_R4212: ; HVBJOY in timing.ni 
 
ALIGNC 
SNES_R4213: ; RDIO 
 mov al,0xFF    ; v0.14 - not supported but returns a value 
 ret 
 
ALIGNC 
SNES_R4214: ; RDDIVL 
 mov al,[RDDIVL] 
 ret 
 
ALIGNC 
SNES_R4215: ; RDDIVH 
 mov al,[RDDIVH] 
 ret 
 
ALIGNC 
SNES_R4216: ; RDMPYL 
 mov al,[RDMPYL] 
 ret 
 
ALIGNC 
SNES_R4217: ; RDMPYH 
 mov al,[RDMPYH] 
 ret 
 
ALIGNC 
SNES_R4218: ; JOY1L 
 cmp byte [_JOYSTICK_ENABLED],2     ; Is mouse plugged in? 
 je .mouse 
 mov al,[JOY1L] 
 ret 
 
.mouse: 
 mov al,[_MouseButts] 
 or al,0x01 
 ret 
 
ALIGNC 
SNES_R4219: ; JOY1H 
 mov al,[JOY1H] 
 ret 
 
ALIGNC 
SNES_R421A: ; JOY2L 
 cmp byte [_JOYSTICK_ENABLED2],2    ; Is mouse plugged in? 
 je .mouse 
 mov al,[JOY2L] 
 ret 
 
.mouse: 
 mov al,[_MouseButts] 
 or al,0x01 
 ret 
 
ALIGNC 
SNES_R421B: ; JOY2H 
 mov al,[JOY2H] 
 ret 
 
ALIGNC 
SNES_R421C: ; JOY3L 
 mov al,[JOY3L] 
 ret 
 
ALIGNC 
SNES_R421D: ; JOY3H 
 mov al,[JOY3H] 
 ret 
 
ALIGNC 
SNES_R421E: ; JOY4L 
 mov al,[JOY4L] 
 ret 
 
ALIGNC 
SNES_R421F: ; JOY4H 
 mov al,[JOY4H] 
 ret 
 
; Read from 43xx handlers 
; SNES_R43xx: ; in DMA.asm 
 
;  -------- 
 
; Write to 21xx handlers 
ALIGNC 
SNES_W2100: ; INIDISP 
 cmp [_INIDISP],al 
 je .no_change 
 SyncRender 
 mov [_INIDISP],al 
 and al,0x0F 
 cmp [_BrightnessLevel],al 
 ja .no_brightness_change 
 mov [_BrightnessLevel],al  ; Sets the brightness level for SetPalette 
 mov byte [_PaletteChanged],1 
.no_brightness_change: 
 mov al,[_INIDISP] 
.no_change: 
 ret 
 
; SNES_W2101: ; OBSEL in sprites.asm 
; SNES_W2102: ; OAMADDL in sprites.asm 
; SNES_W2103: ; OAMADDH in sprites.asm 
; SNES_W2104: ; OAMDATA in sprites.asm 
 
ALIGNC 
EXPORT_C Toggle_Offset_Change 
 xor byte [_Offset_Change_Disable],0xFF 
 ret 
 
ALIGNC 
SNES_W2105: ; BGMODE 
 ; Note: Render_Mode is declared in screen.asm 
 cmp [_BGMODE],al 
 je near .no_change 
 SyncRender  ;* 
; int mode = BGMODE & 7; 
 push ebx 
 mov edi,eax 
 push ecx 
 
 mov ebx,eax 
 push edx 
 and ebx,byte 7 
 push esi 
 mov [_Base_BGMODE],bl 
 and edi,byte 7 
 mov ebx,0x03030303 
 mov ecx,0x23232323 
 mov edx,0x43434343 
 mov esi,0x63636363 
 jz .mode_0_palettes 
 
 mov ecx,ebx 
 mov edx,ebx 
 mov esi,ebx 
.mode_0_palettes: 
 mov [M0_Color_BG1],ebx 
 mov [M0_Color_BG2],ecx 
 mov [M0_Color_BG3],edx 
 mov [M0_Color_BG4],esi 
 
 pop esi 
 pop edx 
 
 mov [_BGMODE],al 
 
; BGMODE_Allowed_Layer_Mask = BGMODE_Allowed_Layer_Mask_Table[mode]; 
; BGMODE_Tile_Layer_Mask = BGMODE_Tile_Layer_Mask_Table[mode]; 
; Render_Mode = Screen_Mode[mode]; 
 mov cl,0x0F 
 xor ebx,ebx 
 mov byte [Redo_BGs],cl 
 mov bl,[BGMODE_Allowed_Layer_Mask_Table+edi] 
 mov cl,[BGMODE_Tile_Layer_Mask_Table+edi] 
 mov [BGMODE_Allowed_Layer_Mask],bl 
 mov [BGMODE_Tile_Layer_Mask],cl 
 mov bl,[BGMODE_Allowed_Offset_Change_Table+edi] 
 mov ecx,[Screen_Mode+edi*4] 
 lea edi,[BGMODE_Depth_Table+edi] 
 mov [BGMODE_Allowed_Offset_Change],bl 
 mov [Redo_Offset_Change],bl 
 mov [Render_Mode],ecx 
 
; DepthBG1 = BGMODE_Depth_Table[0][mode]; 
; if (DepthBG1 & 3) 
; { 
;  int tilesize; 
;  NBATableBG1 = Depth_NBA_Table[DepthBG1 & 3]; 
;  SetAddressBG1 = NBATableBG1[NBABG1]; 
;  tilesize = BGMODE & 0x10 ? 2 : 1; 
;  TileHeightBG1 = TileWidthBG1 = tilesize; 
; 
;  DepthBG2 = BGMODE_Depth_Table[1][mode]; 
;  if (DepthBG2 & 3) 
;  { 
;   NBATableBG2 = Depth_NBA_Table[DepthBG2 & 3]; 
;   SetAddressBG2 = NBATableBG2[NBABG2]; 
;   tilesize = BGMODE & 0x20 ? 2 : 1; 
;   TileHeightBG2 = TileWidthBG2 = tilesize; 
; 
;   DepthBG3 = BGMODE_Depth_Table[2][mode]; 
;   DepthBG4 = BGMODE_Depth_Table[3][mode]; 
; 
;   tilesize = BGMODE & 0x40 ? 2 : 1; 
;   TileHeightBG3 = TileWidthBG3 = tilesize; 
; 
;   tilesize = BGMODE & 0x80 ? 2 : 1; 
;   TileHeightBG4 = TileWidthBG4 = tilesize; 
;  } 
 
 mov cl,[edi] 
 mov bl,[NBABG1] 
 mov [DepthBG1],cl 
 and ecx,byte 3 
 jz near .no_more_tile_layers 
 
 mov ecx,[Depth_NBA_Table+ecx*4] 
 mov [NBATableBG1],ecx 
 mov ecx,[ecx+ebx*4] 
 mov [SetAddressBG1],ecx 
 
 test al,0x10   ; get (tile height / 8) for BG1 
 mov bl,2 
 jnz .large_tiles_bg1_1 
 mov bl,1 
.large_tiles_bg1_1: 
 mov [TileHeightBG1],bl 
 mov [TileWidthBG1],bl 
 
 mov cl,[edi+8] 
 mov bl,[NBABG2] 
 mov [DepthBG2],cl 
 and ecx,byte 3 
 jz .no_more_tile_layers 
 mov ecx,[Depth_NBA_Table+ecx*4] 
 mov [NBATableBG2],ecx 
 mov ecx,[ecx+ebx*4] 
 mov [SetAddressBG2],ecx 
 
 test al,0x20   ; get (tile height / 8) for BG2 
 mov bl,2 
 jnz .large_tiles_bg2_1 
 mov bl,1 
.large_tiles_bg2_1: 
 mov [TileHeightBG2],bl 
 mov [TileWidthBG2],bl 
 
 mov bl,[edi+16] 
 mov cl,[edi+24] 
 mov [DepthBG3],bl 
 mov [DepthBG4],cl 
 
 test al,0x40   ; get (tile height / 8) for BG3 
 mov bl,2 
 jnz .large_tiles_bg3_1 
 mov bl,1 
.large_tiles_bg3_1: 
 mov [TileHeightBG3],bl 
 mov [TileWidthBG3],bl 
 
 test al,al     ; get (tile height / 8) for BG4 
 mov bl,2 
 js .large_tiles_bg4_1 
 mov bl,1 
.large_tiles_bg4_1: 
 mov [TileHeightBG4],bl 
 mov [TileWidthBG4],bl 
 
 
.no_more_tile_layers: 
 call _Update_Layering 
 mov al,[_BGMODE] 
 
 mov edi,eax 
 and edi,byte 7 
 xor ebx,ebx 
 cmp edi,byte 5 
 jae near .wide_mode 
 
 mov bl,[DepthBG1] 
 test al,0x10 
 mov ecx,Redo_16 
 mov edi,[LineRenderLarge+ebx*4] 
 jnz .large_tiles_bg1_2 
 mov ecx,Redo_8 
 mov edi,[LineRenderSmall+ebx*4] 
.large_tiles_bg1_2: 
 mov [Redo_BG1],ecx 
 mov [LineRenderBG1],edi 
 
 mov bl,[DepthBG2] 
 test al,0x20 
 mov ecx,Redo_16 
 mov edi,[LineRenderLarge+ebx*4] 
 jnz .large_tiles_bg2_2 
 mov ecx,Redo_8 
 mov edi,[LineRenderSmall+ebx*4] 
.large_tiles_bg2_2: 
 mov [Redo_BG2],ecx 
 mov [LineRenderBG2],edi 
 
 mov bl,[DepthBG3] 
 test al,0x40 
 mov ecx,Redo_16 
 mov edi,[LineRenderLarge+ebx*4] 
 jnz .large_tiles_bg3_2 
 mov ecx,Redo_8 
 mov edi,[LineRenderSmall+ebx*4] 
.large_tiles_bg3_2: 
 mov [Redo_BG3],ecx 
 mov [LineRenderBG3],edi 
 
 mov bl,[DepthBG4] 
 test al,al 
 mov ecx,Redo_16 
 mov edi,[LineRenderLarge+ebx*4] 
 js .large_tiles_bg4_2 
 mov ecx,Redo_8 
 mov edi,[LineRenderSmall+ebx*4] 
.large_tiles_bg4_2: 
 mov [Redo_BG4],ecx 
 mov [LineRenderBG4],edi 
 
 pop ecx 
 pop ebx 
.no_change: 
 ret 
 
ALIGNC 
; Mode 5/6: uses half-wide tile counts for half-512 
.wide_mode: 
 mov bl,2 
 ; 16-wide tiles on background layers always 
 mov [TileWidthBG1],bl 
 mov [TileWidthBG2],bl 
 mov [TileWidthBG3],bl 
 mov [TileWidthBG4],bl 
 
 mov ecx,Redo_16_Mode_5_6 
 
 mov bl,[DepthBG1] 
 test al,0x10 
 mov edi,[LineRenderEvenLarge+ebx*4] 
 jnz .large_tiles_wide_bg1 
 mov edi,[LineRenderEvenSmall+ebx*4] 
.large_tiles_wide_bg1: 
 mov [Redo_BG1],ecx 
 mov [LineRenderBG1],edi 
 
 mov bl,[DepthBG2] 
 test al,0x20 
 mov edi,[LineRenderEvenLarge+ebx*4] 
 jnz .large_tiles_wide_bg2 
 mov edi,[LineRenderEvenSmall+ebx*4] 
.large_tiles_wide_bg2: 
 mov [Redo_BG2],ecx 
 mov [LineRenderBG2],edi 
 
 mov bl,[DepthBG3] 
 test al,0x40 
 mov edi,[LineRenderEvenLarge+ebx*4] 
 jnz .large_tiles_wide_bg3 
 mov edi,[LineRenderEvenSmall+ebx*4] 
.large_tiles_wide_bg3: 
 mov [Redo_BG3],ecx 
 mov [LineRenderBG3],edi 
 
 mov bl,[DepthBG4] 
 test al,al 
 mov edi,[LineRenderEvenLarge+ebx*4] 
 js .large_tiles_wide_bg4 
 mov edi,[LineRenderEvenSmall+ebx*4] 
.large_tiles_wide_bg4: 
 mov [Redo_BG4],ecx 
 mov [LineRenderBG4],edi 
 
 pop ecx 
 pop ebx 
 ret 
 
ALIGNC 
Redo_0: 
 ret 
 
ALIGNC 
;If BGMODE == (2,4,6) set up tilemap run list involving BG3HOFS 
EXPORT Redo_8 
Redo_16_Mode_5_6: 
 mov al,[HScroll+edx] 
 test al,al     ; If 0 simply plot 32 tiles 
 jz .plot_32 
 
 ;FirstPixel = (HScroll & 7) 
 ;TileCount1 = (256 - (HScroll & ~7)) / 8; 
 ;FirstTile = (32 - TileCount1) * 2; 
 ;TileCount2 = (32 - TileCount1) + ((Hscroll & 7) ? 1 : 0); 
 mov cl,al 
 mov ebx,0x20 
 shr ecx,3 
 and eax,byte 0x07 
 and ecx,byte 0x1F 
 mov [FirstPixel+edx],eax 
 sub ebx,ecx 
 test al,al 
 mov [TileCount1+edx],bl 
 mov ah,cl 
 jz .no_odd_tile 
 inc ah 
.no_odd_tile: 
 add ecx,ecx 
 mov [TileCount2+edx],ah 
 mov [FirstTile+edx],ecx 
 ret 
ALIGNC 
.plot_32: 
 xor eax,eax 
 mov byte [TileCount1+edx],32 
 mov [FirstPixel+edx],eax 
 mov [FirstTile+edx],eax 
 mov [TileCount2+edx],al 
 ret 
 
ALIGNC 
Redo_16: 
 mov eax,[HScroll+edx] 
 test eax,0x1FF ; If 0 simply plot 16 tiles 
 jz .plot_16 
 
 ;FirstPixel = (HScroll & 15); 
 ;FirstTile = ((HScroll / 16) & 31) * 2; 
 ;needed_tiles = (HScroll & 7) ? 33 : 32; 
 ;if ((64 - ((HScroll / 8) & 63)) < needed_Tiles) 
 ;// if max tiles < max needed tiles, some tiles in a second run 
 ;{ 
 ; TileCount1 = 64 - ((HScroll / 8) & 63); 
 ; TileCount2 = needed_tiles - TileCount1; 
 ;} 
 ;else 
 ;// max tiles >= max needed tiles, all tiles in first run 
 ;{ 
 ; TileCount1 = needed_tiles; 
 ; TileCount2 = 0; 
 ;} 
 
 ;needed_tiles = FirstPixel ? 17 : 16; 
 ;if ((32 - ((HScroll / 16) & 31)) < needed_Tiles) 
 ; TileCount1 = 32 - ((HScroll / 16) & 31); 
 mov cl,al 
 mov ebx,0x40 
 shr eax,3 
 and ecx,byte 0x0F 
 and eax,byte 0x3F 
 sub ebx,eax 
 and eax,byte ~1 
 mov [FirstPixel+edx],ecx 
 mov [FirstTile+edx],eax 
 mov al,32 
 and ecx,byte 7 
 jz .no_odd_tile 
 mov al,33 
.no_odd_tile: 
 cmp bl,al 
 jae .no_second_run 
 sub al,bl 
 mov [TileCount1+edx],bl 
 mov [TileCount2+edx],al 
 ret 
 
.no_second_run: 
 mov byte [TileCount2+edx],0 
 mov [TileCount1+edx],al 
 ret 
 
.plot_16: 
 xor eax,eax 
 mov byte [TileCount1+edx],32 
 mov [FirstPixel+edx],eax 
 mov [FirstTile+edx],eax 
 mov [TileCount2+edx],al 
 ret 
 
ALIGNC 
SNES_W2106: ; MOSAIC 
 cmp [MOSAIC],al 
 je .no_change 
 SyncRender  ;* 
 test al,0xF0   ;Is mosaic enabled? 
 mov [MOSAIC],al 
 mov edi,eax 
 jnz .mosaic_enabled 
 mov al,0 
 mov [MosaicBG1],al 
 mov [MosaicBG2],al 
 mov [MosaicBG3],al 
 mov [MosaicBG4],al 
 mov eax,edi 
.no_change: 
 ret 
ALIGNC 
.mosaic_enabled: 
 and al,0x01 
 mov [MosaicBG1],al 
 mov eax,edi 
 and al,0x02 
 mov [MosaicBG2],al 
 mov eax,edi 
 and al,0x04 
 mov [MosaicBG3],al 
 mov eax,edi 
 and al,0x08 
 mov [MosaicBG4],al 
 mov eax,edi 
 shr edi,4 
 and edi,byte 15 
 inc edi 
 mov [Mosaic_Size],edi 
 dec edi 
 shl edi,8 
 mov [Mosaic_Size_Select],edi 
 ret 
 
ALIGNC 
SNES_W2107: ; BG1SC 
%ifdef TRAP_BGSC 
 pusha  ;* 
 xor ebx,ebx 
 mov bl,[_Current_Line_Timing] 
 shl ebx,16 
 or ebx,0x2107 
 mov [_Map_Address],ebx ; Set up Map Address so message works! 
 mov [_Map_Byte],al     ; Set up Map Byte so message works 
 call _InvalidHWWrite   ; Unmapped hardware address! 
 popa   ;* 
%endif 
 cmp [_BG1SC],al 
 je .no_change 
 SyncRender  ;* 
 push ebx 
 mov [_BG1SC],al 
;mov bl,al 
;and bl,3 
;mov [_BGSC1],bl 
 mov [_BGSC1],al 
 mov edi,eax 
 and edi,byte 0x7C 
 shl edi,9 
 add edi,_VRAM 
 mov [MapAddressBG1],edi 
 
 test al,1 
 jz .no_wide_screen_map 
 add edi,32*32*2 
.no_wide_screen_map: 
 mov [TRMapAddressBG1],edi 
 
 add edi,32*32*2 
 test al,2 
 jnz .tall_screen_map 
 mov edi,[TLMapAddressBG1] 
.tall_screen_map: 
 mov [BLMapAddressBG1],edi 
 
 test al,1 
 jz .no_wide_screen_map_2 
 add edi,32*32*2 
.no_wide_screen_map_2: 
 mov [BRMapAddressBG1],edi 
 
 pop ebx 
.no_change: 
 ret 
 
ALIGNC 
SNES_W2108: ; BG2SC 
%ifdef TRAP_BGSC 
 pusha  ;* 
 xor ebx,ebx 
 mov bl,[_Current_Line_Timing] 
 shl ebx,16 
 or ebx,0x2108 
 mov [_Map_Address],ebx ; Set up Map Address so message works! 
 mov [_Map_Byte],al     ; Set up Map Byte so message works 
 call _InvalidHWWrite   ; Unmapped hardware address! 
 popa   ;* 
%endif 
 cmp [_BG2SC],al 
 je .no_change 
 SyncRender  ;* 
 push ebx 
 mov [_BG2SC],al 
;mov bl,al 
;and bl,3 
;mov [_BGSC2],bl 
 mov [_BGSC2],al 
 mov edi,eax 
 and edi,byte 0x7C 
 shl edi,9 
 add edi,_VRAM 
 mov [MapAddressBG2],edi 
 
 test al,1 
 jz .no_wide_screen_map 
 add edi,32*32*2 
.no_wide_screen_map: 
 mov [TRMapAddressBG2],edi 
 
 add edi,32*32*2 
 test al,2 
 jnz .tall_screen_map 
 mov edi,[TLMapAddressBG2] 
.tall_screen_map: 
 mov [BLMapAddressBG2],edi 
 
 test al,1 
 jz .no_wide_screen_map_2 
 add edi,32*32*2 
.no_wide_screen_map_2: 
 mov [BRMapAddressBG2],edi 
 
 pop ebx 
.no_change: 
 ret 
 
ALIGNC 
SNES_W2109: ; BG3SC 
%ifdef TRAP_BGSC 
 pusha  ;* 
 xor ebx,ebx 
 mov bl,[_Current_Line_Timing] 
 shl ebx,16 
 or ebx,0x2109 
 mov [_Map_Address],ebx ; Set up Map Address so message works! 
 mov [_Map_Byte],al     ; Set up Map Byte so message works 
 call _InvalidHWWrite   ; Unmapped hardware address! 
 popa   ;* 
%endif 
 cmp [_BG3SC],al 
 je near .no_change 
 SyncRender  ;* 
 push ebx 
 mov [_BG3SC],al 
;mov bl,al 
;and bl,3 
;mov [_BGSC3],bl 
 mov [_BGSC3],al 
 mov edi,eax 
 and edi,byte 0x7C 
 shl edi,9 
 add edi,_VRAM 
 mov [MapAddressBG3],edi 
 
 test al,1 
 jz .no_wide_screen_map 
 add edi,32*32*2 
.no_wide_screen_map: 
 mov [TRMapAddressBG3],edi 
 
 add edi,32*32*2 
 test al,2 
 jnz .tall_screen_map 
 mov edi,[TLMapAddressBG3] 
.tall_screen_map: 
 mov [BLMapAddressBG3],edi 
 
 test al,1 
 jz .no_wide_screen_map_2 
 add edi,32*32*2 
.no_wide_screen_map_2: 
 mov [BRMapAddressBG3],edi 
 
 mov edi,[_BG3VOFS] 
 mov ebx,edi 
 shl edi,3              ; divided by 8 (base tile size), 
 and edi,(0x1F << 6)    ; * 2 (16-bit words) * 32 (row) 
 and bh,1 
 push edi 
 mov ebx,[BLMapAddressBG3] 
 mov edi,[TLMapAddressBG3] 
 jnz .offset_line_in_map_bottom 
 mov ebx,edi 
.offset_line_in_map_bottom: 
 sub ebx,edi 
 pop edi 
 add edi,ebx 
 mov [OffsetChangeMap_VOffset],edi 
 
 mov edi,[_BG3VOFS] 
 mov ebx,edi 
 add edi,byte 32*2      ; next row 
 add ebx,byte 8 
 and edi,(0x1F << 6)    ; * 2 (16-bit words) * 32 (row) 
 and bh,1 
 push edi 
 mov ebx,[BLMapAddressBG3] 
 mov edi,[TLMapAddressBG3] 
 jnz .vmap_offset_line_in_map_bottom 
 mov ebx,edi 
.vmap_offset_line_in_map_bottom: 
 sub ebx,edi 
 pop edi 
 add edi,ebx 
 sub edi,[OffsetChangeMap_VOffset] 
 mov [OffsetChangeVMap_VOffset],edi 
 
 pop ebx 
 mov byte [Redo_Offset_Change],0xFF 
 
.no_change: 
 ret 
 
ALIGNC 
SNES_W210A: ; BG4SC 
%ifdef TRAP_BGSC 
 pusha  ;* 
 xor ebx,ebx 
 mov bl,[_Current_Line_Timing] 
 shl ebx,16 
 or ebx,0x210A 
 mov [_Map_Address],ebx ; Set up Map Address so message works! 
 mov [_Map_Byte],al     ; Set up Map Byte so message works 
 call _InvalidHWWrite   ; Unmapped hardware address! 
 popa   ;* 
%endif 
 cmp [_BG4SC],al 
 je .no_change 
 SyncRender  ;* 
 push ebx 
 mov [_BG4SC],al 
;mov bl,al 
;and bl,3 
;mov [_BGSC4],bl 
 mov [_BGSC4],al 
 mov edi,eax 
 and edi,byte 0x7C 
 shl edi,9 
 add edi,_VRAM 
 mov [MapAddressBG4],edi 
 
 test al,1 
 jz .no_wide_screen_map 
 add edi,32*32*2 
.no_wide_screen_map: 
 mov [TRMapAddressBG4],edi 
 
 add edi,32*32*2 
 test al,2 
 jnz .tall_screen_map 
 mov edi,[TLMapAddressBG4] 
.tall_screen_map: 
 mov [BLMapAddressBG4],edi 
 
 test al,1 
 jz .no_wide_screen_map_2 
 add edi,32*32*2 
.no_wide_screen_map_2: 
 mov [BRMapAddressBG4],edi 
 
 pop ebx 
.no_change: 
 ret 
 
ALIGNC 
SNES_W210B: ; BG12NBA 
 cmp [_BG12NBA],al 
 je .no_change 
 SyncRender  ;* 
 push ebx 
 mov [_BG12NBA],al 
 mov bl,al 
 and ebx,byte 7 
 mov edi,[NBATableBG1] 
 mov [NBABG1],bl 
 mov ebx,[edi+ebx*4] 
 mov [SetAddressBG1],ebx 
 mov bl,al 
 shr ebx,4 
 and ebx,byte 7 
 mov edi,[NBATableBG2] 
 mov [NBABG2],bl 
 mov ebx,[edi+ebx*4] 
 mov [SetAddressBG2],ebx 
 pop ebx 
.no_change: 
 ret 
 
ALIGNC 
SNES_W210C: ; BG34NBA 
 cmp [_BG34NBA],al 
 je .no_change 
 SyncRender  ;* 
 push ebx 
 mov [_BG34NBA],al 
 
 mov bl,al 
 and ebx,byte 7 
 shl ebx,12     ; * 8k * 4 (2bpl) / 8 
 mov [SetAddressBG3],ebx 
 
 mov bl,al 
 shr ebx,4 
 and ebx,byte 7 
 shl ebx,12     ; * 8k * 4 (2bpl) / 8 
 mov [SetAddressBG4],ebx 
 
 pop ebx 
.no_change: 
 ret 
 
ALIGNC 
SNES_W210D: ; BG1HOFS 
%ifdef TRAP_BGHOFS 
 pusha  ;* 
 xor ebx,ebx 
 mov bl,[_Current_Line_Timing] 
 shl ebx,16 
 or ebx,0x210D 
 mov [_Map_Address],ebx ; Set up Map Address so message works! 
 mov [_Map_Byte],al     ; Set up Map Byte so message works 
 call _InvalidHWWrite   ; Unmapped hardware address! 
 popa   ;* 
%endif 
 
 push ebx 
 mov bl,[BGOFS_Last_Write] 
 mov bh,al 
 mov [BGOFS_Last_Write],al 
 
 cmp [_BG1HOFS],bx 
 je .no_change 
 SyncRender  ;*scroll 
 mov [_BG1HOFS],ebx 
 
;shl ebx,0x13 
;sar ebx,0x13 
;mov [_M7H_13],ebx 
 mov bl,0x40    ; Recalculate H 
 or [Redo_M7],bl 
 
 mov bl,1 
 or [Redo_BGs],bl 
.no_change: 
 
 pop ebx 
 ret 
 
ALIGNC 
SNES_W210E: ; BG1VOFS 
%ifdef TRAP_BGVOFS 
 pusha  ;* 
 xor ebx,ebx 
 mov bl,[_Current_Line_Timing] 
 shl ebx,16 
 or ebx,0x210E 
 mov [_Map_Address],ebx ; Set up Map Address so message works! 
 mov [_Map_Byte],al     ; Set up Map Byte so message works 
 call _InvalidHWWrite   ; Unmapped hardware address! 
 popa   ;* 
%endif 
 
 push ebx 
 mov bl,[BGOFS_Last_Write] 
 mov bh,al 
 mov [BGOFS_Last_Write],al 
 
 cmp [_BG1VOFS],bx 
 je .no_change 
 SyncRender  ;*scroll 
 mov [_BG1VOFS],ebx 
 
;shl ebx,0x13 
;sar ebx,0x13 
;mov [_M7V_13],ebx 
 mov bl,0x80    ; Recalculate V 
 or [Redo_M7],bl 
.no_change: 
 
 pop ebx 
 ret 
 
ALIGNC 
SNES_W210F: ; BG2HOFS 
%ifdef TRAP_BGHOFS 
 pusha  ;* 
 xor ebx,ebx 
 mov bl,[_Current_Line_Timing] 
 shl ebx,16 
 or ebx,0x210F 
 mov [_Map_Address],ebx ; Set up Map Address so message works! 
 mov [_Map_Byte],al     ; Set up Map Byte so message works 
 call _InvalidHWWrite   ; Unmapped hardware address! 
 popa   ;* 
%endif 
 
 push ebx 
 mov bl,[BGOFS_Last_Write] 
 mov bh,al 
 mov [BGOFS_Last_Write],al 
 
 cmp [_BG2HOFS],bx 
 je .no_change 
 SyncRender  ;*scroll 
 mov [_BG2HOFS],ebx 
 
 mov bl,2 
 or [Redo_BGs],bl 
.no_change: 
 
 pop ebx 
 ret 
 
ALIGNC 
SNES_W2110: ; BG2VOFS 
%ifdef TRAP_BGVOFS 
 pusha  ;* 
 xor ebx,ebx 
 mov bl,[_Current_Line_Timing] 
 shl ebx,16 
 or ebx,0x2110 
 mov [_Map_Address],ebx ; Set up Map Address so message works! 
 mov [_Map_Byte],al     ; Set up Map Byte so message works 
 call _InvalidHWWrite   ; Unmapped hardware address! 
 popa   ;* 
%endif 
 
 push ebx 
 mov bl,[BGOFS_Last_Write] 
 mov bh,al 
 mov [BGOFS_Last_Write],al 
 
 cmp [_BG2VOFS],bx 
 je .no_change 
 SyncRender  ;*scroll 
 mov [_BG2VOFS],ebx 
.no_change: 
 
 pop ebx 
 ret 
 
ALIGNC 
SNES_W2111: ; BG3HOFS 
%ifdef TRAP_BGHOFS 
 pusha  ;* 
 xor ebx,ebx 
 mov bl,[_Current_Line_Timing] 
 shl ebx,16 
 or ebx,0x2111 
 mov [_Map_Address],ebx ; Set up Map Address so message works! 
 mov [_Map_Byte],al     ; Set up Map Byte so message works 
 call _InvalidHWWrite   ; Unmapped hardware address! 
 popa   ;* 
%endif 
 
 push ebx 
 mov bl,[BGOFS_Last_Write] 
 mov bh,al 
 mov [BGOFS_Last_Write],al 
 
 cmp [_BG3HOFS],bx 
 je .no_change 
 SyncRender  ;*scroll 
 mov [_BG3HOFS],ebx 
 
 mov bl,4 
 mov [Redo_Offset_Change],bl 
 or [Redo_BGs],bl 
.no_change: 
 
 pop ebx 
 ret 
 
ALIGNC 
SNES_W2112: ; BG3VOFS 
%ifdef TRAP_BGVOFS 
 pusha  ;* 
 xor ebx,ebx 
 mov bl,[_Current_Line_Timing] 
 shl ebx,16 
 or ebx,0x2112 
 mov [_Map_Address],ebx ; Set up Map Address so message works! 
 mov [_Map_Byte],al     ; Set up Map Byte so message works 
 call _InvalidHWWrite   ; Unmapped hardware address! 
 popa   ;* 
%endif 
 
 push ebx 
 mov bl,[BGOFS_Last_Write] 
 mov bh,al 
 mov [BGOFS_Last_Write],al 
 
 cmp [_BG3VOFS],bx 
 je .no_change 
 SyncRender  ;*scroll 
 mov [_BG3VOFS],ebx 
 
 mov edi,ebx 
 push ebx 
 shl edi,3          ; divided by 8 (base tile size), 
 
 and edi,(0x1F << 6)    ; * 2 (16-bit words) * 32 (row) 
 and bh,1 
 push edi 
 mov ebx,[BLMapAddressBG3] 
 mov edi,[TLMapAddressBG3] 
 jnz .offset_line_in_map_bottom 
 mov ebx,edi 
.offset_line_in_map_bottom: 
 sub ebx,edi 
 pop edi 
 add edi,ebx 
 mov [OffsetChangeMap_VOffset],edi 
 
 pop ebx 
 add edi,byte 32*2      ; next row 
 add ebx,byte 8 
 and edi,(0x1F << 6)    ; * 2 (16-bit words) * 32 (row) 
 and bh,1 
 push edi 
 mov ebx,[BLMapAddressBG3] 
 mov edi,[TLMapAddressBG3] 
 jnz .vmap_offset_line_in_map_bottom 
 mov ebx,edi 
.vmap_offset_line_in_map_bottom: 
 sub ebx,edi 
 pop edi 
 add edi,ebx 
 sub edi,[OffsetChangeMap_VOffset] 
 mov [OffsetChangeVMap_VOffset],edi 
 
 mov byte [Redo_Offset_Change],0xFF 
.no_change: 
 
 pop ebx 
 ret 
 
ALIGNC 
SNES_W2113: ; BG4HOFS 
%ifdef TRAP_BGHOFS 
 pusha  ;* 
 xor ebx,ebx 
 mov bl,[_Current_Line_Timing] 
 shl ebx,16 
 or ebx,0x2113 
 mov [_Map_Address],ebx ; Set up Map Address so message works! 
 mov [_Map_Byte],al     ; Set up Map Byte so message works 
 call _InvalidHWWrite   ; Unmapped hardware address! 
 popa   ;* 
%endif 
 
 push ebx 
 mov bl,[BGOFS_Last_Write] 
 mov bh,al 
 mov [BGOFS_Last_Write],al 
 
 cmp [_BG4HOFS],bx 
 je .no_change 
 SyncRender  ;*scroll 
 mov [_BG4HOFS],ebx 
 
 mov bl,8 
 or [Redo_BGs],bl 
.no_change: 
 
 pop ebx 
 ret 
 
ALIGNC 
SNES_W2114: ; BG4VOFS 
%ifdef TRAP_BGVOFS 
 pusha  ;* 
 xor ebx,ebx 
 mov bl,[_Current_Line_Timing] 
 shl ebx,16 
 or ebx,0x2114 
 mov [_Map_Address],ebx ; Set up Map Address so message works! 
 mov [_Map_Byte],al     ; Set up Map Byte so message works 
 call _InvalidHWWrite   ; Unmapped hardware address! 
 popa   ;* 
%endif 
 
 push ebx 
 mov bl,[BGOFS_Last_Write] 
 mov bh,al 
 mov [BGOFS_Last_Write],al 
 
 cmp [_BG4VOFS],bx 
 je .no_change 
 SyncRender  ;*scroll 
 mov [_BG4VOFS],ebx 
.no_change: 
 
 pop ebx 
 ret 
 
ALIGNC 
SNES_W2115_Before_VRAM_read: ; VMAIN 
 mov [_VMAIN],al    ; Get our copy of this 
 and al,0x0C 
 jz near .no_full 
 Set_21_Write 0x18,SNES_W2118_FULL 
 Set_21_Write 0x19,SNES_W2119_FULL 
 cmp al,2*4 
 je .full_64 
 ja near .full_128 
 
.full_32: 
 mov al,5 
 mov [_2118_Bitshift],al 
 mov [_2119_Bitshift],al 
 mov [_2139_Bitshift],al 
 mov [_213A_Bitshift],al 
 mov [_2118_Bitshift_First],al 
 mov [_2119_Bitshift_First],al 
 mov al,0x1F 
 mov [_2118_Bitmask],al 
 mov [_2119_Bitmask],al 
 mov [_2139_Bitmask],al 
 mov [_213A_Bitmask],al 
 mov [_2118_Bitmask_First],al 
 mov [_2119_Bitmask_First],al 
 mov edi,0x7F00 
 mov [_2118_Topmask],edi 
 mov [_2119_Topmask],edi 
 mov [_2139_Topmask],edi 
 mov [_213A_Topmask],edi 
 mov [_2118_Topmask_First],edi 
 mov [_2119_Topmask_First],edi 
 jmp .full_done 
 
ALIGNC 
.full_64: 
 mov al,6 
 mov [_2118_Bitshift],al 
 mov [_2119_Bitshift],al 
 mov [_2139_Bitshift],al 
 mov [_213A_Bitshift],al 
 mov [_2118_Bitshift_First],al 
 mov [_2119_Bitshift_First],al 
 mov al,0x3F 
 mov [_2118_Bitmask],al 
 mov [_2119_Bitmask],al 
 mov [_2139_Bitmask],al 
 mov [_213A_Bitmask],al 
 mov [_2118_Bitmask_First],al 
 mov [_2119_Bitmask_First],al 
 mov edi,0x7E00 
 mov [_2118_Topmask],edi 
 mov [_2119_Topmask],edi 
 mov [_2139_Topmask],edi 
 mov [_213A_Topmask],edi 
 mov [_2118_Topmask_First],edi 
 mov [_2119_Topmask_First],edi 
 jmp .full_done 
 
ALIGNC 
.full_128: 
 mov al,7 
 mov [_2118_Bitshift],al 
 mov [_2119_Bitshift],al 
 mov [_2139_Bitshift],al 
 mov [_213A_Bitshift],al 
 mov [_2118_Bitshift_First],al 
 mov [_2119_Bitshift_First],al 
 mov al,0x7F 
 mov [_2118_Bitmask],al 
 mov [_2119_Bitmask],al 
 mov [_2139_Bitmask],al 
 mov [_213A_Bitmask],al 
 mov [_2118_Bitmask_First],al 
 mov [_2119_Bitmask_First],al 
 mov edi,0x7C00 
 mov [_2118_Topmask],edi 
 mov [_2119_Topmask],edi 
 mov [_2139_Topmask],edi 
 mov [_213A_Topmask],edi 
 mov [_2118_Topmask_First],edi 
 mov [_2119_Topmask_First],edi 
 jmp short .full_done 
 
ALIGNC 
.no_full: 
 Set_21_Write 0x18,SNES_W2118_NORM 
 Set_21_Write 0x19,SNES_W2119_NORM 
.full_done: 
%ifndef NO_DMA_WRITE 
 call Update_DMA_PPU_Handlers 
%endif 
 mov al,[_VMAIN] 
 and al,3 
 jnz .not_1 
 
 mov byte [SCINC],1 
 mov al,[_VMAIN] 
 ret 
 
ALIGNC 
.not_1: 
 cmp al,2 
 jae .not_32 
 
 mov byte [SCINC],32 
 mov al,[_VMAIN] 
 ret 
 
ALIGNC 
.not_32: 
 ; A bug in SNES makes mode 2 = 128 
 mov byte [SCINC],128 
 mov al,[_VMAIN] 
 ret 
 
ALIGNC 
SNES_W2115: ; VMAIN 
 mov [_VMAIN],al    ; Get our copy of this 
 and al,0x0C 
 jz near .no_full 
 Set_21_Read 0x39,SNES_R2139_FULL 
 Set_21_Read 0x3A,SNES_R213A_FULL 
 Set_21_Write 0x18,SNES_W2118_FULL_First 
 Set_21_Write 0x19,SNES_W2119_FULL_First 
 cmp al,2*4 
 je .full_64 
 ja near .full_128 
 
.full_32: 
 mov al,5 
 mov [_2118_Bitshift],al 
 mov [_2119_Bitshift],al 
 mov [_2139_Bitshift],al 
 mov [_213A_Bitshift],al 
 mov [_2118_Bitshift_First],al 
 mov [_2119_Bitshift_First],al 
 mov al,0x1F 
 mov [_2118_Bitmask],al 
 mov [_2119_Bitmask],al 
 mov [_2139_Bitmask],al 
 mov [_213A_Bitmask],al 
 mov [_2118_Bitmask_First],al 
 mov [_2119_Bitmask_First],al 
 mov edi,0x7F00 
 mov [_2118_Topmask],edi 
 mov [_2119_Topmask],edi 
 mov [_2139_Topmask],edi 
 mov [_213A_Topmask],edi 
 mov [_2118_Topmask_First],edi 
 mov [_2119_Topmask_First],edi 
 jmp .full_done 
 
ALIGNC 
.full_64: 
 mov al,6 
 mov [_2118_Bitshift],al 
 mov [_2119_Bitshift],al 
 mov [_2139_Bitshift],al 
 mov [_213A_Bitshift],al 
 mov [_2118_Bitshift_First],al 
 mov [_2119_Bitshift_First],al 
 mov al,0x3F 
 mov [_2118_Bitmask],al 
 mov [_2119_Bitmask],al 
 mov [_2139_Bitmask],al 
 mov [_213A_Bitmask],al 
 mov [_2118_Bitmask_First],al 
 mov [_2119_Bitmask_First],al 
 mov edi,0x7E00 
 mov [_2118_Topmask],edi 
 mov [_2119_Topmask],edi 
 mov [_2139_Topmask],edi 
 mov [_213A_Topmask],edi 
 mov [_2118_Topmask_First],edi 
 mov [_2119_Topmask_First],edi 
 jmp .full_done 
 
ALIGNC 
.full_128: 
 mov al,7 
 mov [_2118_Bitshift],al 
 mov [_2119_Bitshift],al 
 mov [_2139_Bitshift],al 
 mov [_213A_Bitshift],al 
 mov [_2118_Bitshift_First],al 
 mov [_2119_Bitshift_First],al 
 mov al,0x7F 
 mov [_2118_Bitmask],al 
 mov [_2119_Bitmask],al 
 mov [_2139_Bitmask],al 
 mov [_213A_Bitmask],al 
 mov [_2118_Bitmask_First],al 
 mov [_2119_Bitmask_First],al 
 mov edi,0x7C00 
 mov [_2118_Topmask],edi 
 mov [_2119_Topmask],edi 
 mov [_2139_Topmask],edi 
 mov [_213A_Topmask],edi 
 mov [_2118_Topmask_First],edi 
 mov [_2119_Topmask_First],edi 
 jmp short .full_done 
 
ALIGNC 
.no_full: 
 Set_21_Read 0x39,SNES_R2139_NORM 
 Set_21_Read 0x3A,SNES_R213A_NORM 
 Set_21_Write 0x18,SNES_W2118_NORM_First 
 Set_21_Write 0x19,SNES_W2119_NORM_First 
.full_done: 
%ifndef NO_DMA_WRITE 
 call Update_DMA_PPU_Handlers 
%endif 
 mov al,[_VMAIN] 
 and al,3 
 jnz .not_1 
 
 mov byte [SCINC],1 
 mov al,[_VMAIN] 
 ret 
 
ALIGNC 
.not_1: 
 cmp al,2 
 jae .not_32 
 
 mov byte [SCINC],32 
 mov al,[_VMAIN] 
 ret 
 
ALIGNC 
.not_32: 
 ; A bug in SNES makes mode 2 = 128 
 mov byte [SCINC],128 
 mov al,[_VMAIN] 
 ret 
 
ALIGNC 
SNES_W2116: ; VMADDL 
 push eax 
 VRAM_Read_Reset check_full 
 mov [VRAMAddress],al 
 pop eax 
 ret 
 
ALIGNC 
SNES_W2117: ; VMADDH 
 push eax 
 and al,0x7F 
 VRAM_Read_Reset check_full 
 mov [VRAMAddress+1],al 
 pop eax 
 ret 
 
%macro VRAM_Cache_Check 0 
;  Check upper boundary 
;  Check within set (?) 
%ifdef Set_Based_Tile_Cache 
 mov ebx,[Tile_Recache_Set_End] 
%endif 
;shr edi,3 
 shr edi,5 
%ifdef Set_Based_Tile_Cache 
 sub ebx,edi 
 je %%end_of_set    ; Simplest case - tile is end of set 
%ifdef Check_Within_Tile_Set 
 jg %%check_within_set  ; Tile may be within set? 
%else 
 jg %%new_set 
%endif 
 ; Tile may be immediately after set? 
 cmp ebx,-1 
%ifdef Check_Within_Tile_Set 
 jne %%new_set 
 jmp short %%extend_set_one_tile 
%%check_within_set: 
 ; Tile may be within set? 
 cmp [Tile_Recache_Set_Begin],edi 
 jle %%end_of_set 
%else 
 je %%extend_set_one_tile 
%endif 
%%new_set: 
 ; New set 
 push edi 
 mov edi,[Tile_Recache_Set_End] 
 inc edi 
 js %%recache_done  ; No set to recache? 
 sub edi,[Tile_Recache_Set_Begin] 
 call Recache_Tile_Set 
%%recache_done: 
 pop edi 
 mov [Tile_Recache_Set_Begin],edi 
%%extend_set_one_tile: 
 mov [Tile_Recache_Set_End],edi 
%%end_of_set: 
%endif 
%endmacro 
 
%macro JUMP_NOT_VBLANK 1+ 
 cmp byte [HVBJOY], 0 
 js %%in_vblank 
 
 cmp byte [_INIDISP], 0 
 jns %1 
 
%%in_vblank: 
%endmacro 
 
%macro GEN_SNES_W2118_2119 0-1 0 
ALIGNC 
; VMDATAL, normal increment 
%ifidni %1,first 
SNES_W2118_NORM_First: 
%else 
SNES_W2118_NORM: 
%endif 
 push ebx 
 
 JUMP_NOT_VBLANK near .no_increment  ;.no_change 
 
 mov ebx,_VRAM 
 mov edi,[VRAMAddress] 
 cmp [ebx+edi*2],al 
 je .no_change 
 push edi 
 SyncRender  ;* 
 pop edi 
%ifdef Profile_VRAM_Writes 
 inc dword [_VMWriteL_Norm] 
%endif 
 mov [ebx+edi*2],al 
 VRAM_Cache_Check 
.no_change: 
 mov bl,[_VMAIN] 
 test bl,bl 
 js .no_increment 
 mov edi,[SCINC] 
 add edi,[VRAMAddress]  ; Always words (since <<1)! 
 and edi,0x7FFF 
 mov [VRAMAddress],edi 
.no_increment: 
 pop ebx 
%ifidni %1,first 
 Set_21_Write 0x18,SNES_W2118_NORM 
 Set_21_Write 0x19,SNES_W2119_NORM 
 VRAM_Read_Reset 
%endif 
 ret 
 
ALIGNC 
; VMDATAH, normal increment 
%ifidni %1,first 
SNES_W2119_NORM_First: 
%else 
SNES_W2119_NORM: 
%endif 
 push ebx 
 
 JUMP_NOT_VBLANK near .no_increment  ;.no_change 
 
 mov ebx,_VRAM+1 
 mov edi,[VRAMAddress] 
 cmp [ebx+edi*2],al 
 je .no_change 
 push edi 
 SyncRender  ;* 
 pop edi 
%ifdef Profile_VRAM_Writes 
 inc [_VMWriteH_Norm] 
%endif 
 mov [ebx+edi*2],al 
 VRAM_Cache_Check 
.no_change: 
 mov bl,[_VMAIN] 
 test bl,bl 
 jns .no_increment 
 mov edi,[SCINC] 
 add edi,[VRAMAddress]  ; Always words (since <<1)! 
 and edi,0x7FFF 
 mov [VRAMAddress],edi 
.no_increment: 
 pop ebx 
%ifidni %1,first 
 Set_21_Write 0x18,SNES_W2118_NORM 
 Set_21_Write 0x19,SNES_W2119_NORM 
 VRAM_Read_Reset 
%endif 
 ret 
 
ALIGNC 
; VMDATAL, full graphic increment 
%ifidni %1,first 
SNES_W2118_FULL_First: 
%else 
SNES_W2118_FULL: 
%endif 
 push ebx 
 
 JUMP_NOT_VBLANK near .no_increment  ;.no_change 
 
 mov edi,[VRAMAddress] 
 push edx 
 push eax 
 mov edx,edi 
 mov eax,edi 
 shr edx,7 
%ifidni %1,first 
_2118_Bitshift_First equ $-1 
%else 
_2118_Bitshift equ $-1 
%endif 
 and eax,byte 0x7F 
%ifidni %1,first 
_2118_Bitmask_First equ $-1 
%else 
_2118_Bitmask equ $-1 
%endif 
 and edx,byte 7 
 shl eax,3 
 and edi,0x7C00 
%ifidni %1,first 
_2118_Topmask_First equ $-4 
%else 
_2118_Topmask equ $-4 
%endif 
 or edi,eax 
 pop eax 
 or edi,edx 
 pop edx 
 mov ebx,_VRAM 
 cmp [ebx+edi*2],al 
 je .no_change 
 push edi 
 SyncRender  ;* 
 pop edi 
%ifdef Profile_VRAM_Writes 
 inc dword [_VMWriteL_Full] 
%endif 
 mov [ebx+edi*2],al 
 VRAM_Cache_Check 
.no_change: 
 mov bl,[_VMAIN] 
 test bl,bl 
 js .no_increment 
 mov edi,[SCINC] 
 add edi,[VRAMAddress]  ; Always words (since <<1)! 
 and edi,0x7FFF 
 mov [VRAMAddress],edi 
.no_increment: 
 pop ebx 
%ifidni %1,first 
 Set_21_Write 0x18,SNES_W2118_FULL 
 Set_21_Write 0x19,SNES_W2119_FULL 
 VRAM_Read_Reset 
%endif 
 ret 
 
ALIGNC 
; VMDATAH, full graphic increment 
%ifidni %1,first 
SNES_W2119_FULL_First: 
%else 
SNES_W2119_FULL: 
%endif 
 push ebx 
 
 JUMP_NOT_VBLANK near .no_increment  ;.no_change 
 
 mov edi,[VRAMAddress] 
 push edx 
 push eax 
 mov edx,edi 
 mov eax,edi 
 shr edx,7 
%ifidni %1,first 
_2119_Bitshift_First equ $-1 
%else 
_2119_Bitshift equ $-1 
%endif 
 and eax,byte 0x7F 
%ifidni %1,first 
_2119_Bitmask_First equ $-1 
%else 
_2119_Bitmask equ $-1 
%endif 
 and edx,byte 7 
 shl eax,3 
 and edi,0x7C00 
%ifidni %1,first 
_2119_Topmask_First equ $-4 
%else 
_2119_Topmask equ $-4 
%endif 
 or edi,eax 
 pop eax 
 or edi,edx 
 pop edx 
 mov ebx,_VRAM+1 
 cmp [ebx+edi*2],al 
 je .no_change 
 push edi 
 SyncRender  ;* 
 pop edi 
%ifdef Profile_VRAM_Writes 
 inc dword [_VMWriteH_Full] 
%endif 
 mov [ebx+edi*2],al 
 VRAM_Cache_Check 
.no_change: 
 mov bl,[_VMAIN] 
 test bl,bl 
 jns .no_increment 
 mov edi,[SCINC] 
 add edi,[VRAMAddress]  ; Always words (since <<1)! 
 and edi,0x7FFF 
 mov [VRAMAddress],edi 
.no_increment: 
 pop ebx 
%ifidni %1,first 
 Set_21_Write 0x18,SNES_W2118_FULL 
 Set_21_Write 0x19,SNES_W2119_FULL 
 VRAM_Read_Reset 
%endif 
 ret 
%endmacro 
 
GEN_SNES_W2118_2119 
GEN_SNES_W2118_2119 first 
 
EXPORT_EQU_C SNES_W2118_NORM,SNES_W2118_NORM 
EXPORT_EQU_C SNES_W2118_NORM_First,SNES_W2118_NORM_First 
EXPORT_EQU_C SNES_W2118_FULL,SNES_W2118_FULL 
EXPORT_EQU_C SNES_W2118_FULL_First,SNES_W2118_FULL_First 
EXPORT_EQU_C SNES_W2119_NORM,SNES_W2119_NORM 
EXPORT_EQU_C SNES_W2119_NORM_First,SNES_W2119_NORM_First 
EXPORT_EQU_C SNES_W2119_FULL,SNES_W2119_FULL 
EXPORT_EQU_C SNES_W2119_FULL_First,SNES_W2119_FULL_First 
 
; SNES_W211A: ; M7SEL in mode7.asm 
; SNES_W211B: ; M7A in mode7.asm 
; SNES_W211C: ; M7B in mode7.asm 
; SNES_W211D: ; M7C in mode7.asm 
; SNES_W211E: ; M7D in mode7.asm 
; SNES_W211F: ; M7X in mode7.asm 
; SNES_W2120: ; M7Y in mode7.asm 
 
ALIGNC 
SNES_W2121: ; CGADD 
 push ebx 
 xor ebx,ebx 
 mov [CGAddress],al 
 mov [CGHigh],bl 
 mov [CGReadHigh],bl 
 pop ebx 
 ret 
 
ALIGNC 
SNES_W2122: ; CGDATA 
 ; Palette should be set even if just lo byte set! 
 ; We now set the palette in CGRAM 
 
;SyncRender  ;*16-bit rendering only 
 push edx 
 push ebx 
 push eax 
 xor ebx,ebx 
 mov bl,[CGHigh] 
 mov edx,[CGAddress] 
 test ebx,ebx 
 jnz .hi_byte 
 
 cmp al,[_Real_SNES_Palette+ebx+edx*2] 
 jz .no_change 
 mov byte [_PaletteChanged],1 
 mov [_Real_SNES_Palette+ebx+edx*2],al 
.no_change: 
 mov bl,1 
 pop eax 
 mov [CGHigh],bl 
 pop ebx 
 pop edx 
 ret 
 
.hi_byte: 
 and al,0x7F 
 cmp al,[_Real_SNES_Palette+ebx+edx*2] 
 jz .no_change_hi 
 mov byte [_PaletteChanged],1 
 mov [_Real_SNES_Palette+ebx+edx*2],al 
 
.no_change_hi: 
 inc edx 
 mov bl,0 
 pop eax 
 mov [CGHigh],bl 
 pop ebx 
 mov [CGAddress],dl ; Chop address for wrap 
 pop edx 
 ret 
 
ALIGNC 
SNES_W2123: ; W12SEL 
 cmp al,[_W12SEL] 
 je .no_change 
;SyncRender  ;*windowing only 
 call _Recalc_Window_Effects 
 mov [_W12SEL],al 
.no_change: 
 ret 
 
ALIGNC 
SNES_W2124: ; W34SEL 
 cmp al,[_W34SEL] 
 je .no_change 
;SyncRender  ;*windowing only 
 call _Recalc_Window_Effects 
 mov [_W34SEL],al 
.no_change: 
 ret 
 
ALIGNC 
SNES_W2125: ; WOBJSEL 
 cmp al,[_WOBJSEL] 
 je .no_change 
;SyncRender  ;*windowing only 
 call _Recalc_Window_Effects 
 mov [_WOBJSEL],al 
.no_change: 
 ret 
 
ALIGNC 
SNES_W2126: ; WH0 
 cmp al,[_WH0] 
 je .no_change 
;SyncRender  ;*windowing only 
 call _Recalc_Window_Effects 
 mov [_WH0],al 
.no_change: 
 ret 
 
ALIGNC 
SNES_W2127: ; WH1 
 cmp al,[_WH1] 
 je .no_change 
;SyncRender  ;*windowing only 
 call _Recalc_Window_Effects 
 inc eax 
 mov [_WH1],al 
 dec eax 
.no_change: 
 ret 
 
ALIGNC 
SNES_W2128: ; WH2 
 cmp al,[_WH2] 
 je .no_change 
;SyncRender  ;*windowing only 
 call _Recalc_Window_Effects 
 mov [_WH2],al 
.no_change: 
 ret 
 
ALIGNC 
SNES_W2129: ; WH3 
 cmp al,[_WH3] 
 je .no_change 
;SyncRender  ;*windowing only 
 call _Recalc_Window_Effects 
 inc eax 
 mov [_WH3],al 
 dec eax 
.no_change: 
 ret 
 
ALIGNC 
SNES_W212A: ; WBGLOG 
 cmp al,[_WBGLOG] 
 je .no_change 
;SyncRender  ;*windowing only 
 call _Recalc_Window_Effects 
 mov [_WBGLOG],al 
.no_change: 
 ret 
 
ALIGNC 
SNES_W212B: ; WOBJLOG 
 cmp al,[_WOBJLOG] 
 je .no_change 
;SyncRender  ;*windowing only 
 call _Recalc_Window_Effects 
 mov [_WOBJLOG],al 
.no_change: 
 ret 
 
ALIGNC 
EXPORT_C Update_Layering 
 pusha 
 cmp byte [_Layering_Mode],1 
 je .Update_Layering_1 
 ja near .Update_Layering_2 
.Update_Layering_0: 
 mov dword [Render_Select],_Render_Layering_Option_0 
 mov al,[_TM]   ; layering option 0: main-on-sub 
 and al,[BGMODE_Allowed_Layer_Mask] 
 and al,[_Layer_Disable_Mask] 
 mov [SCR_TM],al 
 xor al,0xFF 
 and al,[_TS] 
 and al,[BGMODE_Allowed_Layer_Mask] 
 and al,[_Layer_Disable_Mask] 
 mov [SCR_TS],al 
 popa 
 ret 
ALIGNC 
.Update_Layering_1: 
 mov dword [Render_Select],_Render_Layering_Option_1 
 mov al,[_TS]   ; layering option 1: sub-on-main 
 and al,[BGMODE_Allowed_Layer_Mask] 
 and al,[_Layer_Disable_Mask] 
 mov [SCR_TS],al 
 xor al,0xFF 
 and al,[_TM] 
 and al,[BGMODE_Allowed_Layer_Mask] 
 and al,[_Layer_Disable_Mask] 
 mov [SCR_TM],al 
 popa 
 ret 
ALIGNC 
.Update_Layering_2: 
 mov dword [Render_Select],_Render_Layering_Option_2 
 mov al,[_TM]   ; layering option 2: main-with-sub 
 or al,[_TS] 
 and al,[BGMODE_Allowed_Layer_Mask] 
 and al,[_Layer_Disable_Mask] 
 mov byte [SCR_TS],0 
 mov [SCR_TM],al 
 popa 
 ret 
 
ALIGNC 
SNES_W212C: ; TM 
 cmp al,[_TM] 
 je .no_change 
 SyncRender  ;* 
 call _Recalc_Window_Effects 
 mov [_TM],al 
 call _Update_Layering 
 mov al,[_TM] 
.no_change: 
 ret 
 
ALIGNC 
SNES_W212D: ; TS 
 cmp al,[_TS] 
 je .no_change 
 SyncRender  ;* 
 call _Recalc_Window_Effects 
 mov [_TS],al 
 call _Update_Layering 
 mov al,[_TS] 
.no_change: 
 ret 
 
ALIGNC 
SNES_W212E: ; TMW 
 cmp al,[_TMW] 
 je .no_change 
;SyncRender  ;*windowing only 
 call _Recalc_Window_Effects 
 mov [_TMW],al 
.no_change: 
 ret 
 
ALIGNC 
SNES_W212F: ; TSW 
 cmp al,[_TSW] 
 je .no_change 
;SyncRender  ;*windowing only 
 call _Recalc_Window_Effects 
 mov [_TSW],al 
.no_change: 
 ret 
 
ALIGNC 
SNES_W2130: ; CGWSEL 
 cmp al,[_CGWSEL] 
 je .no_change 
;SyncRender  ;*windowing only 
 call _Recalc_Window_Effects 
 mov [_CGWSEL],al 
.no_change: 
 ret 
 
ALIGNC 
SNES_W2131: ; CGADSUB 
 cmp al,[_CGADSUB] 
 je .no_change 
;SyncRender  ;*16-bit rendering only 
 call _Recalc_Window_Effects 
 push eax 
 mov [_CGADSUB],al 
 
 test al,0xA0   ; Hack for back area color addition 
 mov ah,0 
 jz .no_add_hack 
 js .no_add_hack 
 mov ah,-1 
.no_add_hack: 
 mov [_fixedpalettecheck],ah 
 pop eax 
.no_change: 
 ret 
 
ALIGNC 
SNES_W2132: ; COLDATA 
;SyncRender  ;*16-bit rendering only 
 call _Recalc_Window_Effects 
 push ebx 
 mov edi,[_COLDATA] 
 
 test al,0xC0 
 jns .no_blue 
 
 mov bl,al 
 and ebx,0x1F 
 shl ebx,10 
 
 and edi,0x3FF 
 or edi,ebx 
 
 test al,0x40 
.no_blue: 
 jz .no_green 
 
 mov bl,al 
 and ebx,0x1F 
 shl ebx,5 
 
 and edi,0x7C1F 
 or edi,ebx 
 
.no_green: 
 test al,0x20 
 jz .no_red 
 
 mov bl,al 
 and ebx,0x1F 
 
 and edi,0x7FE0 
 or edi,ebx 
 
.no_red: 
 mov [_COLDATA],edi 
 pop ebx 
 
 ret 
 
ALIGNC 
SNES_W2133: ; SETINI 
 cmp al,[_SETINI] 
 je .no_change 
 SyncRender  ;*interlaced etc. not yet supported 
 test al,4 
 mov [_SETINI],al 
 mov edi,239 
 jnz .tall_screen 
 mov edi,223 
.tall_screen: 
 mov [_LastRenderLine],edi 
 
 mov al,[_SETINI] 
.no_change: 
 ret 
 
; SNES_W2140_SKIP: ; APUI00 in APUskip.asm 
; SNES_W2141_SKIP: ; APUI01 in APUskip.asm 
; SNES_W2142_SKIP: ; APUI02 in APUskip.asm 
; SNES_W2143_SKIP: ; APUI03 in APUskip.asm 
 
; SNES_W2140_SPC:  ; APUI00 in spc700.asm 
; SNES_W2141_SPC:  ; APUI01 in spc700.asm 
; SNES_W2142_SPC:  ; APUI02 in spc700.asm 
; SNES_W2143_SPC:  ; APUI03 in spc700.asm 
 
ALIGNC 
SNES_W2180: ; WMDATA 
 mov edi,[WMADDL] 
 mov [_WRAM+edi],al 
 inc edi 
 and edi,0x01FFFF 
 mov [WMADDL],edi 
 ret 
 
ALIGNC 
SNES_W2181: ; WMADDL 
 mov [WMADDL],al 
 ret 
 
ALIGNC 
SNES_W2182: ; WMADDM 
 mov [WMADDM],al 
 ret 
 
ALIGNC 
SNES_W2183: ; WMADDH 
 push eax 
 and al,1 
 mov [WMADDH],al 
 pop eax 
 ret 
 
; Write to 40xx handlers 
ALIGNC 
SNES_W4016: ; JOYC1 
 test al,1 
 jz .no_ready_reset 
 push eax 
 mov al,[JOYC1] 
 test al,1 
 jnz .no_reset 
 mov al,16 
 mov [Controller1_Pos],al 
 mov [Controller23_Pos],al 
 mov [Controller45_Pos],al 
.no_reset: 
 pop eax 
.no_ready_reset: 
 mov [JOYC1],al 
 ret 
 
ALIGNC 
SNES_W4017: ; JOYC2 
 ret 
 
; Write to 42xx handlers 
 
; SNES_W4200: ; NMITIMEN in timing.ni 
 
ALIGNC 
SNES_W4201: ; WRIO 
 call UNSUPPORTED_WRITE 
 ret 
 
ALIGNC 
SNES_W4202: ; WRMPYA 
 mov [WRMPYA],al 
 ret 
 
ALIGNC 
SNES_W4203: ; WRMPYB 
 push eax 
 mov [WRMPYB],al 
 mul byte [WRMPYA]  ; Do the multiplication 
 mov [RDMPYL],al 
 mov [RDMPYH],ah 
 pop eax 
 ret 
 
ALIGNC 
SNES_W4204: ; WRDIVL 
 mov [WRDIVL],al 
 ret 
 
ALIGNC 
SNES_W4205: ; WRDIVH 
 mov [WRDIVH],al 
 ret 
 
ALIGNC 
SNES_W4206: ; WRDIVB 
 push eax 
 push ebx 
 push edx 
 test al,al 
 jz .divide_by_zero 
 xor ebx,ebx 
 xor edx,edx    ; Divide uses DX:AX / BX 
 mov bl,al 
 mov al,[WRDIVL] 
 mov ah,[WRDIVH] 
 div bx         ; Result is ax=quotient,dx=remainder 
 mov [RDDIVL],al 
 mov [RDDIVH],ah 
 mov [RDMPYL],dl 
 mov [RDMPYH],dh 
.divide_by_zero: 
 pop edx 
 pop ebx 
 pop eax 
 ret 
 
; SNES_W4207: ; HTIMEL in timing.ni 
; SNES_W4208: ; HTIMEH in timing.ni 
; SNES_W4209: ; VTIMEL in timing.ni 
; SNES_W420A: ; VTIMEH in timing.ni 
; SNES_W420B: ; MDMAEN in DMA.asm 
; SNES_W420C: ; HDMAEN in DMA.asm 
; SNES_W420D: ; MEMSEL in timing.ni 
; SNES_W4210: ; RDNMI in timing.ni 
; SNES_W4211: ; TIMEUP in timing.ni 
 
; Write to 43xx handlers 
; SNES_W43xx: ; in DMA.asm 
 
section .text 
ALIGNC 
section .data 
ALIGND 
section .bss 
ALIGNB