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


; screen.asm 
; Screen rendering code 
; -BGMODE specific handlers, including specialized handlers for: 
;   partial offset change in Mode 2; 
;   partial offset change in Mode 4; 
; -Basic BG scanline renderers; 
; -Line offset in tile tables. 
; 
 
%define NO_PLOTTER_PER_PRIORITY 
%define WATCH_RENDER_BREAKS 
;%define LAYERS_PER_LINE 
;%define NO_NP_RENDER 
;%define NO_OFFSET_CHANGE 
;%define NO_OFFSET_CHANGE_DISABLE 
;%define NO_EARLY_PRIORITY_ELIMINATION 
%define OFFSET_CHANGE_ELIMINATION 
 
%ifdef NO_NP_RENDER 
%define NO_EARLY_PRIORITY_ELIMINATION 
%endif 
 
%ifndef LAYERS_PER_LINE 
%define NO_EARLY_PRIORITY_ELIMINATION 
%endif 
 
%define SNEeSe_screen_asm 
 
%include "misc.ni" 
%include "clear.ni" 
%include "sprites.ni" 
%include "PPU.ni" 
%include "tiles.ni" 
%include "screen.ni" 
 
extern SCREEN_MODE_7 
extern _SNES_Screen8 
extern _Offset_Change_Disable 
 
extern _MosaicLine,_MosaicCount 
 
section .text 
EXPORT_C screen_text_start 
section .data 
EXPORT_C screen_data_start 
section .bss 
EXPORT_C screen_bss_start 
 
section .data 
ALIGND 
%macro Generate_Tile_Offset_Table 1 
 dd 16*8*(%1) 
 dd 16*8*(%1)+1 
 dd 16*8*(%1)+2 
 dd 16*8*(%1)+3 
 dd 16*8*(%1)+4 
 dd 16*8*(%1)+5 
 dd 16*8*(%1)+6 
 dd 16*8*(%1)+7 
%endmacro 
 
EXPORT Tile_Offset_Table_16_8 
Generate_Tile_Offset_Table 0 
Generate_Tile_Offset_Table 1 
 
EXPORT Screen_Mode 
 dd _SCREEN_MODE_0 
 dd _SCREEN_MODE_1 
 dd _SCREEN_MODE_2 
 dd _SCREEN_MODE_3 
 dd _SCREEN_MODE_4 
 dd _SCREEN_MODE_5 
 dd _SCREEN_MODE_6 
 dd SCREEN_MODE_7 
 
palette_2bpl: 
 dd 0x03030303, 0x07070707, 0x0B0B0B0B, 0x0F0F0F0F 
 dd 0x13131313, 0x17171717, 0x1B1B1B1B, 0x1F1F1F1F 
palette_4bpl: 
 dd 0x0F0F0F0F, 0x1F1F1F1F, 0x2F2F2F2F, 0x3F3F3F3F 
 dd 0x4F4F4F4F, 0x5F5F5F5F, 0x6F6F6F6F, 0x7F7F7F7F 
 
palette_2bpl_mmx: 
 dd 0x03030303, 0x03030303, 0x07070707, 0x07070707 
 dd 0x0B0B0B0B, 0x0B0B0B0B, 0x0F0F0F0F, 0x0F0F0F0F 
 dd 0x13131313, 0x13131313, 0x17171717, 0x17171717 
 dd 0x1B1B1B1B, 0x1B1B1B1B, 0x1F1F1F1F, 0x1F1F1F1F 
palette_4bpl_mmx: 
 dd 0x0F0F0F0F, 0x0F0F0F0F, 0x1F1F1F1F, 0x1F1F1F1F 
 dd 0x2F2F2F2F, 0x2F2F2F2F, 0x3F3F3F3F, 0x3F3F3F3F 
 dd 0x4F4F4F4F, 0x4F4F4F4F, 0x5F5F5F5F, 0x5F5F5F5F 
 dd 0x6F6F6F6F, 0x6F6F6F6F, 0x7F7F7F7F, 0x7F7F7F7F 
 
section .bss 
;Z-buffer for display 
 
;#L = tiles/pixels for layer # (low priority) 
;#H = tiles/pixels for layer # (high priority) 
;#S = sprites (# priority) 34 24 14 04 
;BA = back area 00 
 
;modes 0-1 
;layer 3H 3S 1H 2H 2S 1L 2L 1S 3H 4H 0S 3L 4L BA 
;Z     38 34 32 31 24 22 21 14 12 11 04 02 01 00 
 
;modes 2-6 
;layer 3S 1H 2S 2H 1S 1L 0S 2L BA 
;Z     34 32 24 22 14 12 04 02 00 
 
;mode 7 
;layer 3S 2S 1H 1S 1L 0S BA 
;Z     34 24 12 14 02 04 00 
 
ALIGNB 
EXPORT DisplayZ,skipb (8+256+8) 
ALIGNB 
EXPORT LineAddress ,skipl   ; Address of tileset, + offset to line in tile 
EXPORT LineAddressY,skipl   ; Same, for vertical flip 
EXPORT TileMask    ,skipl   ; Tile address mask, for tileset wrap 
 
; Used for offset change map addressing 
EXPORT OffsetChangeMap_VOffset,skipl    ; BG3VOFS + BG3SC 
EXPORT OffsetChangeVMap_VOffset,skipl   ; BG3VOFS + BG3SC (for split-table) 
 
EXPORT_C Current_Line_Render,skipl 
EXPORT Ready_Line_Render,skipl 
EXPORT BaseDestPtr      ,skipl 
 
EXPORT Render_Select,skipl  ; Base renderer 
EXPORT Render_Mode  ,skipl  ; Mode renderer 
 
Palette_Base:skipl 
 
EXPORT Tile_Layers_Enabled,skipb 
EXPORT Tile_priority_bit,skipb 
EXPORT Tile_priority_opcode,skipb 
Tile_Priority_Used:skipb 
Tile_Priority_Unused:skipb 
 
EXPORT OffsetChangeDetect1,skipb 
EXPORT OffsetChangeDetect2,skipb 
EXPORT OffsetChangeDetect3,skipb 
 
section .text 
;ebx = first line, edi = destination base ptr, ebp = # lines 
ALIGNC 
EXPORT_C Render_Layering_Option_0   ; main-on-sub 
 mov al,[SCR_TS]    ; Get BG status for sub screens 
 mov ah,[SCR_TM]    ; Get BG status for main screens 
 jmp dword [Render_Mode] 
 
ALIGNC 
EXPORT_C Render_Layering_Option_1   ; sub-on-main 
 mov al,[SCR_TM]    ; Get BG status for main screens 
 mov ah,[SCR_TS]    ; Get BG status for sub screens 
 jmp dword [Render_Mode] 
 
ALIGNC 
EXPORT_C Render_Layering_Option_2   ; main-with-sub 
 mov al,[SCR_TM]    ; Get BG status for main/sub screens 
 mov ah,0 
 jmp dword [Render_Mode] 
 
ALIGNC 
Update_Offset_Change: 
 mov byte [Redo_Offset_Change],0 
 
 ; Update BG3 position for offset change 
 mov al,[Redo_BGs] 
 LOAD_BG_TABLE 3 
 test al,4 
 jz .bg3_ok 
 
 and al,~4 
 mov [Redo_BGs],al 
 call Redo_8 
 
.bg3_ok: 
 
 LOAD_BG_TABLE 3 
 mov ecx,[HScroll+edx] 
 
 mov esi,TLMapAddress 
 mov edi,TRMapAddress 
 and ch,1   ;8x8 tile size 
 jz .first_tile_in_left_screen_map 
 add esi,byte (TRMapAddress-TLMapAddress) 
 add edi,byte (TLMapAddress-TRMapAddress) 
.first_tile_in_left_screen_map: 
 
 mov eax,[FirstTile+edx] 
 mov esi,[esi+edx] 
 mov edi,[edi+edx] 
 add esi,eax 
 mov [VLMapAddress+edx],esi 
 mov [VRMapAddress+edx],edi 
 
%ifdef OFFSET_CHANGE_ELIMINATION 
 mov ebx,[OffsetChangeMap_VOffset] 
 mov ah,32 
 add esi,ebx 
 mov ch,[TileCount1+edx] 
 mov edx,[OffsetChangeVMap_VOffset] 
 add edi,ebx 
 sub ah,ch 
 
 mov al,0 
 mov bl,0 
 
.detect_loop: 
 mov cl,[esi+1] 
 
 or al,cl 
 mov bh,[esi+1+edx] 
 
 add esi,byte 2 
 or bl,bh 
 
 dec ch 
 jnz .detect_loop 
 
 add ch,ah 
 jz .detect_end 
 
 mov ah,0 
 mov esi,edi 
 jmp short .detect_loop 
 
.detect_end: 
 mov [OffsetChangeDetect1],al 
 or al,bl 
 mov [OffsetChangeDetect2],bl 
 mov [OffsetChangeDetect3],al 
%endif 
 
 ret 
 
%macro SORT_SCREEN_WIDTH 1 
 test byte [Tile_Layers_Enabled],1 << (%1 - 1) 
 jz %%no_recalc 
 
 mov al,[Redo_BGs] 
 LOAD_BG_TABLE %1 
 test al,1 << (%1 - 1) 
 jz %%no_recalc 
 
 call dword [BG_Redo+edx] 
 
%%no_recalc: 
%endmacro 
 
%macro SORT_OFFSET_CHANGE 0 
 test byte [Tile_Layers_Enabled],3  ;BG1 || BG2 
 jz %%no_recalc 
 
 mov al,[BGMODE_Allowed_Offset_Change] 
 test al,al 
 jz %%no_recalc 
 
 mov al,[Redo_Offset_Change] 
 test al,al 
 jz %%no_recalc 
 
 call Update_Offset_Change 
 
%%no_recalc: 
%endmacro 
 
ALIGNC 
EXPORT_C Sync_Render 
 ; edi = number of lines to recache 
 push eax 
 mov ah,[_INIDISP] 
 test ah,ah         ; Check for screen off 
 js near .screen_off 
 push ebx 
 push ecx 
 push edx 
 push ebp 
 push esi 
 push edi 
 
 
%ifdef WATCH_RENDER_BREAKS 
extern _BreaksLast 
 inc dword [_BreaksLast] 
%endif 
 
extern BGMODE_Tile_Layer_Mask 
 mov al,[SCR_TM] 
 mov bl,[BGMODE_Tile_Layer_Mask] 
 or al,[SCR_TS] 
 and al,bl 
 mov [Tile_Layers_Enabled],al 
 jz near .no_tile_layers 
 
extern Tile_Recache_Set_End,Tile_Recache_Set_Begin,Recache_Tile_Set 
 mov edi,[Tile_Recache_Set_End] 
 inc edi 
 js .no_tile_recache_needed ; No set to recache? 
 sub edi,[Tile_Recache_Set_Begin] 
 mov byte [Redo_Offset_Change],0xFF 
 call Recache_Tile_Set 
 mov edi,-2 
 mov [Tile_Recache_Set_Begin],edi 
 mov [Tile_Recache_Set_End],edi 
.no_tile_recache_needed: 
 
 test byte [Tile_Layers_Enabled],0x10 
 jz .no_oam_recache_needed 
 mov al,[Redo_OAM] 
 test al,al 
 jz .no_oam_recache_needed 
 call Recache_OAM 
.no_oam_recache_needed: 
 
 SORT_SCREEN_WIDTH 1 
 SORT_SCREEN_WIDTH 2 
 SORT_SCREEN_WIDTH 3 
 SORT_SCREEN_WIDTH 4 
 SORT_OFFSET_CHANGE 
 
 mov al,[Tile_Layers_Enabled] 
 xor al,0xFF 
 and [Redo_BGs],al 
.no_tile_layers: 
 
 mov ebx,[_Current_Line_Render] 
 mov edi,[BaseDestPtr] 
 inc ebx 
 mov ebp,[esp] 
 
 call dword [Render_Select] 
 
 pop edi 
 pop esi 
 pop ebp 
 pop edx 
 pop ecx 
 pop ebx 
 add [_Current_Line_Render],edi 
 
 mov eax,GfxBufferLinePitch 
 imul eax,edi 
 add [BaseDestPtr],eax 
 pop eax 
 ret 
 
ALIGNC 
.screen_off: 
 push ebx 
 push ecx 
 push edx 
 push ebp 
 push esi 
 push edi 
 
 mov ebp,edi 
 mov edi,[_SNES_Screen8]    ; (256+16)*(239+1) framebuffer 
 ; Clear the framebuffer 
 mov ebx,[BaseDestPtr] 
 xor eax,eax 
 add edi,ebx 
 
 mov ecx,256/4 
 push es 
 mov edx,ds 
 mov es,edx 
 cld 
 
.clear_loop: 
 ; Load area to clear into cache 
 mov bl,[edi+0] 
 mov bl,[edi+16*2] 
 mov bl,[edi+16*4] 
 mov bl,[edi+16*6] 
 mov bl,[edi+16*8] 
 mov bl,[edi+16*10] 
 mov bl,[edi+16*12] 
 mov bl,[edi+16*14] 
 mov dl,[edi+16] 
 mov bl,[edi+16*3] 
 mov dl,[edi+16*5] 
 mov bl,[edi+16*7] 
 mov dl,[edi+16*9] 
 mov bl,[edi+16*11] 
 mov dl,[edi+16*13] 
 mov bl,[edi+16*15] 
 
 rep stosd 
 
 add edi,byte GfxBufferLineSlack    ; Point screen to next line 
 dec ebp 
 mov ecx,256/4 
 jnz .clear_loop 
 
 pop es 
 
 mov edi,[esp] 
 add [_Current_Line_Render],edi 
 
 mov eax,GfxBufferLinePitch 
 imul eax,edi 
 add [BaseDestPtr],eax 
 
 pop edi 
 pop esi 
 pop ebp 
 pop edx 
 pop ecx 
 pop ebx 
 pop eax 
 ret 
 
; ~142 bytes 
;%1 = Left,%2 = Right,%3 = ICount,%4 = IBands,%5 = OCount,%6 = OBands 
%macro Recalc_Window_Bands 6 
 mov al,[%1] 
 mov dl,[%2] 
 test dl,dl         ; 0 = 255 (right edge) 
 jz %%one_inside 
 cmp dl,al 
 jbe %%full_outside ; if (Right < Left) full range outside window; 
%%one_inside: 
 mov [%4],al 
 mov [%4+1],dl 
 mov byte [%3],1    ; One band inside window (left,right) 
 test al,al 
 jnz %%not_flush_left   ; if (!Left) window flush left; 
 test dl,dl 
 jne %%flush_one_side   ; if (!Left && Right == 255) full range inside; 
 ; Full range inside window 
 mov byte [%5],0  ; No bands outside window 
 jmp short %%done 
%%not_flush_left: 
 ; Window not flush left (1 inside, 1 or 2 outside) 
 test dl,dl 
 je %%flush_one_side    ; if (Left && Right == 255) window flush right; 
 ; Window not flush left or right (1 inside, 2 outside) 
 ; Inside range is (left,right) 
 ; Outside range 1 is (0,left-1) 
 ; Outside range 2 is (right+1,255) 
;dec eax                ; Right outside edge 1 = Left inside edge - 1 
;inc edx                ; Left outside edge 2 = right inside edge + 1 
 mov byte [%5],2        ; One band outside window (right+1,left-1) 
 mov [%6+1],al 
 mov byte [%6],0 
 mov [%6+2],dl 
 mov byte [%6+3],0 
 jmp short %%done 
%%flush_one_side: 
 ; Window flush left, not flush right (1 inside, 1 outside) 
 ; Window flush right, not flush left (1 inside, 1 outside) 
 ; Inside range is (left,right), outside range is (right+1,left-1) 
;dec eax                 ; Right outside edge = Left inside edge - 1 
;inc edx                 ; Left outside edge = right inside edge + 1 
 mov [%6+1],al 
 mov byte [%5],1         ; One band outside window (right+1,left-1) 
 mov [%6],dl 
 jmp short %%done 
%%full_outside: 
 ; Full range outside window (0 inside, 1 outside) 
 mov byte [%5],1         ; One band outside window 
 mov dword [%6],0        ; Full range band 
 mov byte [%3],0         ; No bands inside window 
%%done: 
%endmacro 
 
ALIGNC 
EXPORT_C Recalc_Window_Effects 
 push eax 
 push edx 
 Recalc_Window_Bands _WH0, _WH1, _Win1_Count_In, _Win1_Bands_In, _Win1_Count_Out, _Win1_Bands_Out 
 Recalc_Window_Bands _WH2, _WH3, _Win2_Count_In, _Win2_Bands_In, _Win2_Count_Out, _Win2_Bands_Out 
 
 pop edx 
 pop eax 
 ret 
; full | any == full 
; full & any == any 
; side-flush | same-flush == same-flush (longer) 
; side-flush & same-flush == same-flush (shorter) 
; side-flush ^ same-flush == 0 (if equal) or no-flush (difference) 
; side-flush & other-flush == 0 (if no overlap) or no-flush (overlap) 
 
; windows: 
%if 0 
TMW/TSW clipping is only done inside window areas defined by 
 WH0-WH1 (window 1) and WH2-WH3 (window 2), when enabled 
 (W12SEL/W34SEL/WOBJSEL odd bits). 
 
Window areas can be inverted (W12SEL/W34SEL/WOBJSEL even bits). 
 
When specified areas of windows 1/2 overlap, final window area is determined 
 by specified logic (WBGLOG/WOBJLOG). 
 
Color arithmetic is done inside the area of the color window. 
 
w2  w1 
 /\/\  /--+ enable 
 ||||  |/-+ invert window area 
 76543210 
 \  /\  / 
 BG2 BG1 - ($2123) W12SEL 
 BG4 BG3 - ($2124) W34SEL 
 COL OBJ - ($2125) WOBJSEL 
 
 COL = Color window - related to CGWSEL ($2130) 
 
WH0-WH1 Left and right position for window 1 
WH2-WH3 Left and right position for window 2 
 if (left > right) no window range 
 
       /+-+ logic - 00 = or; 01 = and; 10 = xor; 11 = xnor 
       || 
 76543210 
 \/||||\/ 
BG4||||BG1 ($212A: WBGLOG) 
   \/\/OBJ ($212B: WOBJLOG) 
 BG3  BG2 ($212A: WBGLOG) 
      COL ($212B: WOBJLOG) 
 bits 4-7 are ignored in $212B: WOBJLOG 
 
 bits 5-7 are ignored in $212C-212F (TM, TS, TMW, TSW) 
 76543210 
 xxx||||\-+ BG1 
    |||\--+ BG2 
    ||\---+ BG3 
    |\----+ BG4 
    \-----+ OBJ 
 ($212C) TM specifies layers to be used as main screen 
 ($212D) TS specifics layers to be used as sub screen, for screen arithmetic 
 ($212E) TMW is mask to be applied with bitwise AND to TM inside window areas 
 ($212F) TSW is mask to be applied with bitwise AND to TS inside window areas 
 
 $2130 - CGWSEL 
 76543210 
 ||||xx|\-+ 1 = enable direct color mode (for BGMODEs 3,4,7) 
 ||||  \--+ 0 = arithmetic with fixed color; 1 = arithmetic with screen 
 ||\+-----+ sub screen normal display select \ 00 = on; 01 = on inside 
 \+-------+ main screen normal display select/ 10 = on outside; 11 = off 
 
 $2131 - CGADSUB 
 
 76543210 
 |||||||\-+ enable color arithmetic for BG1 
 ||||||\--+ enable color arithmetic for BG2 
 |||||\---+ enable color arithmetic for BG3 
 ||||\----+ enable color arithmetic for BG4 
 |||\-----+ enable color arithmetic for OBJ 
 ||\------+ enable color arithmetic for back area 
 |\-------+ 1 = halve-result of arithmetic (except for back area) 
 \--------+ 0 = color addition; 1 = color subtraction 
%endif 
 
;lines, startreg, reg2, reg3 
%macro Cache_Load_Lines 4 
 mov (%3),(%2) 
%ifnid %1 
%if (%1) > 1 
 mov ebp,(%1) 
%endif 
%endif 
%%CL_loop: 
 lea (%4),[(%3)+16*8] 
 mov al,[(%3)+0] 
 mov al,[(%3)+16*2] 
 mov al,[(%3)+16*4] 
 mov al,[(%3)+16*6] 
 mov al,[(%4)+0] 
 mov al,[(%4)+16*2] 
 mov al,[(%4)+16*4] 
 mov al,[(%4)+16*6] 
 mov cl,[(%3)+16] 
 mov al,[(%3)+16*3] 
 mov cl,[(%3)+16*5] 
 mov al,[(%3)+16*7] 
%ifid %1 
 lea (%3),[(%4)+GfxBufferLinePitch-(16*8)] 
%elif (%1) > 1 
 lea (%3),[(%4)+GfxBufferLinePitch-(16*8)] 
%endif 
 mov cl,[(%4)+16] 
 mov al,[(%4)+16*3] 
 mov cl,[(%4)+16*5] 
 mov al,[(%4)+16*7] 
%ifid %1 
 dec (%1) 
 jnz %%CL_loop 
%elif (%1) > 1 
 dec ebp 
 jnz %%CL_loop 
%endif 
%endmacro 
 
;Variable priority loads dh from Tile_priority_bit 
;All assume al == high byte of screen tile 
;%1 = priority - 0 = none, 1 = low, 2 = high, 3 = variable 
;%2 = branch label 
%macro Check_Tile_Priority 2 
%if %1 == 1 
 test al,0x20 
 jnz %2 
%endif 
 
%if %1 == 2 
 test al,0x20 
 jz %2 
%endif 
 
%if %1 == 3 
 mov dh,[Tile_priority_bit] 
 xor dh,al 
 and dh,0x20        ; Check tile priority 
 jz %2 
%endif 
%endmacro 
 
; esi is screen address, works cos we only plot until wraparound! 
; ch contains the X counter 
; cl contains the screen addition for palette offsetting (2bpl only) 
; edi is the address to draw to.. 
; LineAddress contains the location for the SNES tile data 
; LineAddress(Y) must be offset to the correct line for that row 
; LineAddressY is used for Y-flip 
 
%include "bg8.ni" 
%include "bg16.ni" 
 
%if 0 
 Mode 2 is a special snes mode - It is known as offset change mode, 
  basically the snes has the ability to change the horizontal and/or 
  vertical offset of each column on the screen, a pig to emulate but 
  here is the information: 
 
  BG1 & BG2 are 16 colour planes (this much was simple to find out) 
  BG3 - This does not exist but its address in VRAM is very important! 
 
  What happens is the horizontal and verticle information is written to 
  BG2 address's BG2+0 -  BG2+63, this gives 128 bytes (since VRAM is a 
  word addressed system). BG2+0 -  BG2+31 is the address for changing 
  the horizontal value BG2+32 - BG2+63 is the address for changing the 
  vertical value 
 
  There are 32 values per BG mode since there are 32 tiles horizontally across the screen! My 
  best guess is this value is immune to scrolling (otherwise it would make more sense to 
  give a 64 tile range in case of extra width modes). 
 
  Ok, the only other thing you need to know is what do the values at address BG2+x do. 
 
  Well its the same for horiz as vertical the values are encoded as : 
 
  00           | 01          | 02           | 03          |.......| 62            | 63 
  col 0 offset | col 0 flags | col 1 offset | col 1 flags |.......| col 31 offset | col 31 flags 
 
  The flags as far as I can tell are : 
 
   bit 7 6 5 4 3 2 1 0 
       ? y x ? ? ? ? ?		where	y = affect bg1 
    				x = affect bg0 
 
    			The above information came from me Savoury SnaX 
 
Addendum by TRAC (0.33 -> 0.34) 
 BG modes 2/4/6 have offset change support. 
 Offset change info is always stored at address set via BG3SC ($2109)? 
 
 Offset change info is stored one word per (width 8) tile, which appears 
  to have the format of: 
 
  FEDCBA9786543210 
  421xxxoooooooooo 
 
  4 = reserved in modes 2/6 - vertical select in mode 4 
  o = offset 
  1 = enable for first layer 
  2 = enable for second layer 
 
 The (width 8) tile on the left edge of a layer cannot have its offset 
  changed. It will always use standard H/V scroll. 
 
 The offset change map is used for the remaining width of the screen. 
  The maximum number of displayed tiles is 33, and the leftmost tile is 
  excluded, hence there is data in the table for 32 tiles. 
 
 There are one or two sets of data for the scanline. Each set is 32 words 
  (one word per tile). In BG modes 2 and 6 there are two: one for 
  horizontal, followed by one for vertical. In BG mode 4, there is one, 
  shared between horizontal and vertical. 
 
 Mode 4 stores only one word of offset change data per tile - bit 15 
  of a word entry determines if it is used for changing horizontal or 
  vertical offset (set for vertical). 
 
 When offset change is enabled for a tile, it replaces the standard offset 
  (BG#HOFS, BG#VOFS) value for that tile (horizontal offset replaces 
  BG#HOFS, vertical offset + 1 replaces BG#VOFS). 
 
 The BG3 scroll registers move the offset change map. The scrolling is 
  limited to increments of 8. 
 
 ; word offset in offset change map of first offset change entry 
 OffsetChangeMap_X = BG3HOFS >> 3; 
 ; word offset in row in offset change map to use 
 OffsetChangeMap_Y = (BG3VOFS >> 3) << 5; 
 
 Note that the offset change map does wrap rows back to themselves like 
  any other layer. 
 
%endif 
 
%include "bg8o.ni" 
%include "bg16o.ni" 
 
%include "bg16e.ni" 
 
;Sets up VLMapAddress and VRMapAddress 
 
;Uses mosaic setting, horizontal/vertical offsets, screen map size and 
;current scanline 
;eax = _Current_Line_Render 
ALIGNC 
Sort_Screen_Height_Mosaic: 
 Get_Current_Line 
Sort_Screen_Height: 
 ; Corrupts eax,ebx,ecx,esi 
 mov bl,[HScroll+1+edx] 
 mov ecx,TLMapAddress 
 mov bh,[TileWidth+edx]     ; 1 = 8x8, 2 = 16x8, 16x16 
 mov esi,TRMapAddress 
 test bl,bh 
 jz .first_tile_in_left_screen_map 
 add ecx,byte (TRMapAddress-TLMapAddress) 
 add esi,byte (TLMapAddress-TRMapAddress) 
.first_tile_in_left_screen_map: 
 mov bl,[TileHeight+edx]    ; 1 = 8x8, 16x8, 2 = 16x16 
 add eax,[VScroll+edx] 
 test ah,bl 
 jz .line_in_top_screen_map 
 
 mov eax,[edx+ecx+(BLMapAddress-TLMapAddress)] 
 mov ecx,[FirstTile+edx] 
 mov ebx,[edx+esi+(BLMapAddress-TLMapAddress)] 
 add eax,ecx 
 mov [VRMapAddress+edx],ebx 
 mov [VLMapAddress+edx],eax 
 ret 
 
ALIGNC 
.line_in_top_screen_map: 
 mov eax,[edx+ecx] 
 mov ecx,[FirstTile+edx] 
 mov ebx,[edx+esi] 
 add eax,ecx 
 mov [VRMapAddress+edx],ebx 
 mov [VLMapAddress+edx],eax 
 ret 
 
;%1 = planenum, 2 = priority 
%macro RENDER_LINE 2 
 LOAD_BG_TABLE %1 
 
%if %2 == 0 
 mov al,0 
 mov [Tile_Priority_Used],al 
 mov [Tile_Priority_Unused],al 
%else 
 mov al,[Priority_Unused+edx] 
 test al,al 
 jz %%no_plot 
 
 mov al,[Priority_Used+edx] 
 cmp al,1 
 sbb al,al 
%endif 
 
 ; set up depth 
 mov byte [Tile_priority_opcode],0x75 - (%2) 
 mov byte [Tile_priority_bit],(1 - (%2)) << (13 - 8) 
 
 ; tile size and depth selected in BGMODE write handler 
 call dword [LineRender+edx] 
%%no_plot: 
%endmacro 
 
;%1 = planenum 
%macro RENDER_LINE_NP 1 
 LOAD_BG_TABLE %1 
 
 mov al,1 
 ; tile size and depth selected in BGMODE write handler 
 call dword [LineRender+edx] 
%endmacro 
 
%if 0 
 Mode 0 - 4 background layers, 8x8 and 16x16 tile sizes 
  2bpl tile depth in all layers 
  special: each background layer has its own set of palettes 
 
 Mode 1 - 3 background layers, 8x8 and 16x16 tile sizes 
  4bpl tile depth in layers 1 and 2, 2bpl tile depth in layer 3 
  special: BG3 high priority selection 
%endif 
 
%define SM01_Local_Bytes 20 
%define SM01_Layers_Copy esp+16 
%define SM01_Current_Line esp+12 
%define SM01_BaseDestPtr esp+8 
%define SM01_Lines esp+4 
%define SM01_Layers esp 
 
%macro Check_Present_Layer 2 
 test byte %2,1 << ((%1)-1) 
%endmacro 
 
%macro Jump_Present_Layer 3 
 Check_Present_Layer (%1),%2 
 jnz (%3) 
%endmacro 
 
%macro Jump_Not_Present_Layer 3 
 Check_Present_Layer (%1),%2 
 jz (%3) 
%endmacro 
 
%macro Check_Present_OBJ 1 
 test byte %1,0x10 
%endmacro 
 
%macro Jump_Present_OBJ 2 
 Check_Present_OBJ %1 
 jnz (%2) 
%endmacro 
 
%macro Jump_Not_Present_OBJ 2 
 Check_Present_OBJ %1 
 jz (%2) 
%endmacro 
 
%macro Jump_BG3_Highest 1 
 cmp byte [_Base_BGMODE],1 
 jne %%not_highest 
 test byte [_BGMODE],8 
 jnz (%1) 
%%not_highest: 
%endmacro 
 
%macro Jump_Not_BG3_Highest 1 
 cmp byte [_Base_BGMODE],1 
 jne (%1) 
 test byte [_BGMODE],8 
 jz (%1) 
%endmacro 
 
;requires current line in ebx, uses cl 
%macro Check_Present_OBJ_Priority 1 
 mov cl,[OAM_Count_Priority+ebx*4-4+(%1)] 
 test cl,cl 
%endmacro 
 
%macro Jump_Present_OBJ_Priority 2 
 Check_Present_OBJ_Priority (%1) 
 jnz (%2) 
%endmacro 
 
%macro Jump_Not_Present_OBJ_Priority 2 
 Check_Present_OBJ_Priority (%1) 
 jz (%2) 
%endmacro 
 
%macro Render_SM01 2 
 Jump_Not_Present_Layer 4,[SM01_Layers+%1],%%bg4_lo_done 
 mov ebx,[SM01_Current_Line] 
 mov edi,[SM01_BaseDestPtr] 
 mov ebp,%2 
 
%ifndef NO_EARLY_PRIORITY_ELIMINATION 
 Jump_Present_Layer 3,[SM01_Layers+%1],%%bg4_lo_priority 
 Jump_Not_Present_OBJ [SM01_Layers+%1],%%bg4_no_priority 
 Jump_Present_OBJ_Priority 0,%%bg4_lo_priority 
 
%%bg4_no_priority: 
 RENDER_LINE_NP 4 
 and byte [SM01_Layers+%1],~8 
 jmp %%bg3_done 
 
%%bg4_lo_priority: 
%endif 
 RENDER_LINE 4,0 
 
%%bg4_lo_done: 
 
 Jump_Not_Present_Layer 3,[SM01_Layers+%1],%%bg3_lo_done 
 mov ebx,[SM01_Current_Line] 
 mov edi,[SM01_BaseDestPtr] 
 mov ebp,%2 
 
%ifndef NO_EARLY_PRIORITY_ELIMINATION 
 Jump_BG3_Highest %%bg3_lo_priority 
 Jump_Present_Layer 4,[SM01_Layers+%1],%%bg3_lo_priority 
 Jump_Not_Present_OBJ [SM01_Layers+%1],%%bg3_no_priority 
 Jump_Present_OBJ_Priority 0,%%bg3_lo_priority 
 
%%bg3_no_priority: 
 RENDER_LINE_NP 3 
 and byte [SM01_Layers+%1],~4 
 jmp %%bg3_done 
 
%%bg3_lo_priority: 
%endif 
 RENDER_LINE 3,0 
 
%%bg3_lo_done: 
 
 Jump_Not_Present_OBJ [SM01_Layers+%1],%%no_sprites_0 
 
 mov ebx,[SM01_Current_Line] 
 mov edi,[SM01_BaseDestPtr] 
 mov ebp,%2 
;inc ebx 
 mov dl,0x00 
 call Plot_Sprites 
%%no_sprites_0: 
 
 Jump_Not_Present_Layer 4,[SM01_Layers+%1],%%bg4_hi_done 
 mov ebx,[SM01_Current_Line] 
 mov edi,[SM01_BaseDestPtr] 
 mov ebp,%2 
 RENDER_LINE 4,1 
%%bg4_hi_done: 
 
 Jump_Not_Present_Layer 3,[SM01_Layers+%1],%%bg3_hi_done 
 Jump_BG3_Highest %%bg3_hi_done 
 mov ebx,[SM01_Current_Line] 
 mov edi,[SM01_BaseDestPtr] 
 mov ebp,%2 
 RENDER_LINE 3,1 
%%bg3_hi_done: 
%%bg3_done: 
 
 Jump_Not_Present_OBJ [SM01_Layers+%1],%%no_sprites_1 
 
 mov ebx,[SM01_Current_Line] 
 mov edi,[SM01_BaseDestPtr] 
 mov ebp,%2 
;inc ebx 
 mov dl,0x10 
 call Plot_Sprites 
%%no_sprites_1: 
 
 Jump_Not_Present_Layer 2,[SM01_Layers+%1],%%bg2_lo_done 
 mov ebx,[SM01_Current_Line] 
 mov edi,[SM01_BaseDestPtr] 
 mov ebp,%2 
 
%ifndef NO_EARLY_PRIORITY_ELIMINATION 
 Jump_Present_Layer 1,[SM01_Layers+%1],%%bg2_lo_priority 
 Jump_Not_Present_OBJ [SM01_Layers+%1],%%bg2_no_priority 
 Jump_Present_OBJ_Priority 2,%%bg2_lo_priority 
 
%%bg2_no_priority: 
 RENDER_LINE_NP 2 
 and byte [SM01_Layers+%1],~2 
 jmp %%bg1_done 
 
%%bg2_lo_priority: 
%endif 
 RENDER_LINE 2,0 
 
%%bg2_lo_done: 
 
 Jump_Not_Present_Layer 1,[SM01_Layers+%1],%%bg1_lo_done 
 mov ebx,[SM01_Current_Line] 
 mov edi,[SM01_BaseDestPtr] 
 mov ebp,%2 
 
%ifndef NO_EARLY_PRIORITY_ELIMINATION 
 Jump_Present_Layer 2,[SM01_Layers+%1],%%bg1_lo_priority 
 Jump_Not_Present_OBJ [SM01_Layers+%1],%%bg1_no_priority 
 Jump_Present_OBJ_Priority 2,%%bg1_lo_priority 
 
%%bg1_no_priority: 
 RENDER_LINE_NP 1 
 and byte [SM01_Layers+%1],~1 
 jmp %%bg1_done 
 
%%bg1_lo_priority: 
%endif 
 RENDER_LINE 1,0 
 
%%bg1_lo_done: 
 
 Jump_Not_Present_OBJ [SM01_Layers+%1],%%no_sprites_2 
 
 mov ebx,[SM01_Current_Line] 
 mov edi,[SM01_BaseDestPtr] 
 mov ebp,%2 
;inc ebx 
 mov dl,0x20 
 call Plot_Sprites 
%%no_sprites_2: 
 
 Jump_Not_Present_Layer 2,[SM01_Layers+%1],%%bg2_hi_done 
 mov ebx,[SM01_Current_Line] 
 mov edi,[SM01_BaseDestPtr] 
 mov ebp,%2 
 RENDER_LINE 2,1 
%%bg2_hi_done: 
 
 Jump_Not_Present_Layer 1,[SM01_Layers+%1],%%bg1_hi_done 
 mov ebx,[SM01_Current_Line] 
 mov edi,[SM01_BaseDestPtr] 
 mov ebp,%2 
 RENDER_LINE 1,1 
%%bg1_hi_done: 
%%bg1_done: 
 
 Jump_Not_Present_OBJ [SM01_Layers+%1],%%no_sprites_3 
 
 mov ebx,[SM01_Current_Line] 
 mov edi,[SM01_BaseDestPtr] 
 mov ebp,%2 
;inc ebx 
 mov dl,0x30 
 call Plot_Sprites 
%%no_sprites_3: 
 
 Jump_Not_BG3_Highest %%bg3_max_done 
 Jump_Not_Present_Layer 3,[SM01_Layers+%1],%%bg3_max_done 
 mov ebx,[SM01_Current_Line] 
 mov edi,[SM01_BaseDestPtr] 
 mov ebp,%2 
 RENDER_LINE 3,1 
%%bg3_max_done: 
 
%endmacro 
 
;* 
;Plot 8-16 lines of first layer (complete one tile and draw one tile) 
;Plot up to 8 lines of all other layers 
;* 
ALIGNC 
EXPORT_C SCREEN_MODE_0 
EXPORT_C SCREEN_MODE_1 
 
 push eax 
 push ebx 
 push edi 
 push ebp 
 push eax 
 
.next_line: 
 mov edi,[_SNES_Screen8]    ; (256+16)*(239+1) framebuffer 
 ; Clear the framebuffer 
 mov ebx,[SM01_BaseDestPtr] 
 xor eax,eax 
 add edi,ebx 
 mov ebp,1 
%ifdef LAYERS_PER_LINE 
 mov ebp,1 
%else 
 mov ebp,[SM01_Lines] 
%endif 
 
 mov ecx,256/4 
 push es 
 mov edx,ds 
 mov es,edx 
 cld 
 
.clear_loop: 
 ; Load area to clear into cache 
 mov bl,[edi+0] 
 mov bl,[edi+16*2] 
 mov bl,[edi+16*4] 
 mov bl,[edi+16*6] 
 mov bl,[edi+16*8] 
 mov bl,[edi+16*10] 
 mov bl,[edi+16*12] 
 mov bl,[edi+16*14] 
 mov dl,[edi+16] 
 mov bl,[edi+16*3] 
 mov dl,[edi+16*5] 
 mov bl,[edi+16*7] 
 mov dl,[edi+16*9] 
 mov bl,[edi+16*11] 
 mov dl,[edi+16*13] 
 mov bl,[edi+16*15] 
 
 rep stosd 
 
 add edi,byte GfxBufferLineSlack    ; Point screen to next line 
 dec ebp 
 mov ecx,256/4 
 jnz .clear_loop 
 
 pop es 
 
%ifndef LAYERS_PER_LINE 
 Render_SM01 0,[SM01_Lines] 
 Render_SM01 1,[SM01_Lines] 
%else 
 Render_SM01 0,1 
 Render_SM01 1,1 
 
 mov eax,[SM01_Layers_Copy] 
 mov [SM01_Layers],eax 
 
 mov edi,[SM01_BaseDestPtr] 
 inc dword [SM01_Current_Line] 
 add edi,GfxBufferLinePitch 
 dec dword [SM01_Lines] 
 mov [SM01_BaseDestPtr],edi 
 jnz near .next_line 
%endif 
 
 add esp,byte SM01_Local_Bytes 
 ret 
 
%if 0 
 Mode 2 - 2 background layers, 8x8 and 16x16 tile sizes 
  4bpl tile depth in layers 1 and 2 
  special: offset change data (h and v) stored in layer 3 
 
 Mode 3 - 2 background layers, 8x8 and 16x16 tile sizes 
  8bpl tile depth in layer 1, 4bpl tile depth in layer 2 
  special: direct color mode 
 
 Mode 4 - 2 background layers, 8x8 and 16x16 tile sizes 
  8bpl tile depth in layer 1, 2bpl tile depth in layer 2 
  special: direct color mode 
           offset change data (h or v?) stored in layer 3 
 
 Mode 5 - 2 background layers, 16x8 and 16x16 tile sizes 
  4bpl tile depth in layer 1, 2bpl tile depth in layer 2 
  special: 512 mode 
 
 Mode 6 - 1 background layer, 16x8 and 16x16 tile sizes 
  4bpl tile depth in layer 1 
  special: 512 mode 
           offset change data (h and v?) stored in layer 3 (?) 
%endif 
 
%define SM26_Local_Bytes 20 
%define SM26_Layers_Copy esp+16 
%define SM26_Current_Line esp+12 
%define SM26_BaseDestPtr esp+8 
%define SM26_Lines esp+4 
%define SM26_Layers esp 
 
%macro Render_SM26 2 
 Jump_Not_Present_Layer 2,[SM26_Layers+%1],%%bg2_lo_done 
 mov ebx,[SM26_Current_Line] 
 mov edi,[SM26_BaseDestPtr] 
 mov ebp,%2 
 
%ifndef NO_EARLY_PRIORITY_ELIMINATION 
 Jump_Present_Layer 1,[SM26_Layers+%1],%%bg2_lo_priority 
 Jump_Not_Present_OBJ [SM26_Layers+%1],%%bg2_no_priority 
 Jump_Present_OBJ_Priority 0,%%bg2_lo_priority 
 Jump_Present_OBJ_Priority 1,%%bg2_lo_priority 
 
%%bg2_no_priority: 
 RENDER_LINE_NP 2 
 and byte [SM26_Layers+%1],~2 
 jmp %%bg2_done 
 
%%bg2_lo_priority: 
%endif 
 RENDER_LINE 2,0 
 
%%bg2_lo_done: 
 
 Jump_Not_Present_OBJ [SM26_Layers+%1],%%no_sprites_0 
 
 mov ebx,[SM26_Current_Line] 
 mov edi,[SM26_BaseDestPtr] 
 mov ebp,%2 
;inc ebx 
 mov dl,0x00 
 call Plot_Sprites 
%%no_sprites_0: 
 
 Jump_Not_Present_Layer 1,[SM26_Layers+%1],%%bg1_lo_done 
 mov ebx,[SM26_Current_Line] 
 mov edi,[SM26_BaseDestPtr] 
 mov ebp,%2 
 
%ifndef NO_EARLY_PRIORITY_ELIMINATION 
 Jump_Present_Layer 2,[SM26_Layers+%1],%%bg1_lo_priority 
 Jump_Not_Present_OBJ [SM26_Layers+%1],%%bg1_no_priority 
 Jump_Present_OBJ_Priority 1,%%bg1_lo_priority 
 Jump_Present_OBJ_Priority 2,%%bg1_lo_priority 
 
%%bg1_no_priority: 
 RENDER_LINE_NP 1 
 and byte [SM26_Layers+%1],~1 
 jmp %%bg1_done 
 
%%bg1_lo_priority: 
%endif 
 RENDER_LINE 1,0 
 
%%bg1_lo_done: 
 
 Jump_Not_Present_OBJ [SM26_Layers+%1],%%no_sprites_1 
 
 mov ebx,[SM26_Current_Line] 
 mov edi,[SM26_BaseDestPtr] 
 mov ebp,%2 
;inc ebx 
 mov dl,0x10 
 call Plot_Sprites 
%%no_sprites_1: 
 
 Jump_Not_Present_Layer 2,[SM26_Layers+%1],%%bg2_hi_done 
 mov ebx,[SM26_Current_Line] 
 mov edi,[SM26_BaseDestPtr] 
 mov ebp,%2 
 RENDER_LINE 2,1 
%%bg2_hi_done: 
%%bg2_done: 
 
 Jump_Not_Present_OBJ [SM26_Layers+%1],%%no_sprites_2 
 
 mov ebx,[SM26_Current_Line] 
 mov edi,[SM26_BaseDestPtr] 
 mov ebp,%2 
;inc ebx 
 mov dl,0x20 
 call Plot_Sprites 
%%no_sprites_2: 
 
 Jump_Not_Present_Layer 1,[SM26_Layers+%1],%%bg1_hi_done 
 mov ebx,[SM26_Current_Line] 
 mov edi,[SM26_BaseDestPtr] 
 mov ebp,%2 
 RENDER_LINE 1,1 
%%bg1_hi_done: 
%%bg1_done: 
 
 Jump_Not_Present_OBJ [SM26_Layers+%1],%%no_sprites_3 
 
 mov ebx,[SM26_Current_Line] 
 mov edi,[SM26_BaseDestPtr] 
 mov ebp,%2 
;inc ebx 
 mov dl,0x30 
 call Plot_Sprites 
%%no_sprites_3: 
 
%endmacro 
 
EXPORT_C SCREEN_MODE_2 
EXPORT_C SCREEN_MODE_3 
EXPORT_C SCREEN_MODE_4 
EXPORT_C SCREEN_MODE_5 
EXPORT_C SCREEN_MODE_6 
 
 push eax 
 push ebx 
 push edi 
 push ebp 
 push eax 
 
.next_line: 
 mov edi,[_SNES_Screen8]    ; (256+16)*(239+1) framebuffer 
 ; Clear the framebuffer 
 mov ebx,[SM26_BaseDestPtr] 
 xor eax,eax 
 add edi,ebx 
 mov ebp,1 
%ifdef LAYERS_PER_LINE 
 mov ebp,1 
%else 
 mov ebp,[SM26_Lines] 
%endif 
  
 mov ecx,256/4 
 push es 
 mov edx,ds 
 mov es,edx 
 cld 
 
.clear_loop: 
 ; Load area to clear into cache 
 mov bl,[edi+0] 
 mov bl,[edi+16*2] 
 mov bl,[edi+16*4] 
 mov bl,[edi+16*6] 
 mov bl,[edi+16*8] 
 mov bl,[edi+16*10] 
 mov bl,[edi+16*12] 
 mov bl,[edi+16*14] 
 mov dl,[edi+16] 
 mov bl,[edi+16*3] 
 mov dl,[edi+16*5] 
 mov bl,[edi+16*7] 
 mov dl,[edi+16*9] 
 mov bl,[edi+16*11] 
 mov dl,[edi+16*13] 
 mov bl,[edi+16*15] 
 
 rep stosd 
 
 add edi,byte GfxBufferLineSlack    ; Point screen to next line 
 dec ebp 
 mov ecx,256/4 
 jnz .clear_loop 
 
 pop es 
 
%ifndef LAYERS_PER_LINE 
 Render_SM26 0,[SM26_Lines] 
 Render_SM26 1,[SM26_Lines] 
%else 
 Render_SM26 0,1 
 Render_SM26 1,1 
 
 mov eax,[SM26_Layers_Copy] 
 mov [SM26_Layers],eax 
 
 mov edi,[SM26_BaseDestPtr] 
 inc dword [SM26_Current_Line] 
 add edi,GfxBufferLinePitch 
 dec dword [SM26_Lines] 
 mov [SM26_BaseDestPtr],edi 
 jnz near .next_line 
%endif 
 
 add esp,byte SM26_Local_Bytes 
 ret 
 
section .text 
ALIGNC 
section .data 
ALIGND 
section .bss 
ALIGNB