<commit_before>%if 0

SNEeSe, an Open Source Super NES emulator.


Copyright (c) 1998-2003 <NAME>'.
Portions Copyright (c) 2003 <NAME>.

This is free software.  See 'LICENSE' for details.
You must read and accept the license prior to use.

%endif

; 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 MSR_PROFILING
%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_ppu_screen_asm

%include "misc.inc"
%include "clear.inc"
%include "ppu/sprites.inc"
%include "ppu/ppu.inc"
%include "ppu/tiles.inc"
%include "ppu/screen.inc"

EXTERN_C SCREEN_MODE_7
EXTERN_C SNES_Screen8
EXTERN_C Offset_Change_Disable

EXTERN_C MosaicLine,MosaicCount

EXTERN_C use_mmx,use_fpu_copies,preload_cache,preload_cache_2

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 C_LABEL(SCREEN_MODE_0)
 dd C_LABEL(SCREEN_MODE_1)
 dd C_LABEL(SCREEN_MODE_2)
 dd C_LABEL(SCREEN_MODE_3)
 dd C_LABEL(SCREEN_MODE_4)
 dd C_LABEL(SCREEN_MODE_5)
 dd C_LABEL(SCREEN_MODE_6)
 dd C_LABEL(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

ClipTableStart:
 db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
 db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
ClipLeftTable: ;ClipLeftTable[-first_pixel_offset]
 db 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
 db 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
ClipRightTable: ;ClipRightTable[-pixel_count]
 db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
 db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
;to clip both: ClipLeftTable[-first_pixel_offset] &
; ClipRightTable[-(first_pixel_offset + pixel_count)]

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
;L7 = mode-7 EXTBG low priority 1
;N7 = mode-7 EXTBG low priority 2, mode-7 w/o EXTBG
;H7 = mode-7 EXTBG high priority

;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 H7 1S N7 0S L7 BA
;Z     34 24 22 14 12 04 02 00

ALIGNB
EXPORT DisplayZ,skipb (8+256+8)
ALIGNB
EXPORT_C Current_Line_Render,skipl
EXPORT_C Last_Frame_Line,skipl
EXPORT Ready_Line_Render,skipl
EXPORT BaseDestPtr      ,skipl

EXPORT Render_Select,skipl  ; Base renderer
EXPORT Render_Mode  ,skipl  ; Mode renderer

BGLineCount:skipl
TileClip1:
TileClip1Left:skipl
TileClip1Right:skipl
TileClip2:
TileClip2Left:skipl
TileClip2Right:skipl

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
Palette_Base:skipl

; Used for offset change map addressing
EXPORT OffsetChangeMap_VOffset,skipl    ; BG3VOFS + BG3SC
EXPORT OffsetChangeVMap_VOffset,skipl   ; BG3VOFS + BG3SC (for split-table)

EXPORT Display_Needs_Update,skipb
EXPORT Tile_Layers_Enabled,skipb
EXPORT Tile_priority_bit,skipb
Tile_Priority_Used:skipb
Tile_Priority_Unused:skipb

EXPORT OffsetChangeDetect1,skipb
EXPORT OffsetChangeDetect2,skipb
EXPORT OffsetChangeDetect3,skipb
EXPORT Redo_Windowing,skipb

;YXCS 4321
;1-4 update clip window for BG 1-4
;S   update clip window for OBJ (sprites)
;C   update color window
;X   update window 1 area
;Y   update window 2 area


section .text
;ebx = first line, edi = destination base ptr, ebp = # lines
ALIGNC
EXPORT_C Render_Layering_Option_0   ; main-on-sub
 mov al,[SCR_TSW]
 mov ah,[SCR_TMW]
 shl eax,16
 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_TMW]
 mov ah,[SCR_TSW]
 shl eax,16
 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_TMW]
 shl eax,16
 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

 mov al,[Redo_Offset_Change_VOffsets]
 LOAD_BG_TABLE 3
 test al,al
 jz .no_recalc_voffsets

 mov byte [Redo_Offset_Change_VOffsets],0

;OffsetChangeMap_VOffset = ((BG3VOFS / 8) & 0x1F) * 64 +
; (BG3VOFS & 0x100 ? BLMapAddressBG3 - TLMapAddressBG3 : 0);
;OffsetChangeVMap_VOffset = ((BG3VOFS / 8 + 1) & 0x1F) * 64 +
; ((BG3VOFS + 8) & 0x100 ? BLMapAddressBG3 - TLMapAddressBG3 : 0) -
; OffsetChangeMap_VOffset;
 mov edi,[VScroll+edx]
 shl edi,3          ; divided by 8 (base tile size),
 mov ebx,[VScroll+edx]
 and edi,(0x1F << 6)    ; * 2 (16-bit words) * 32 (row)

 and bh,1
 jz .offset_line_in_map_top

 mov ebx,[BLMapAddress+edx]
 add edi,ebx
 mov ebx,[TLMapAddress+edx]
 sub edi,ebx
.offset_line_in_map_top:

 mov [OffsetChangeMap_VOffset],edi

 mov ebx,[VScroll+edx]
 add edi,byte 32*2      ; next row
 add ebx,byte 8
 and edi,(0x1F << 6)    ; * 2 (16-bit words) * 32 (row)

 and bh,1
 jz .vmap_offset_line_in_map_top

 mov ebx,[BLMapAddress+edx]
 add edi,ebx
 mov ebx,[TLMapAddress+edx]
 sub edi,ebx
.vmap_offset_line_in_map_top:

 mov ebx,[OffsetChangeMap_VOffset]
 sub edi,ebx
 mov [OffsetChangeVMap_VOffset],edi

.no_recalc_voffsets:

 ; Update BG3 position for offset change
 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 esi,[esi+edx]
 mov edi,[edi+edx]
 mov [VLMapAddress+edx],esi
 mov [VRMapAddress+edx],edi

%ifdef OFFSET_CHANGE_ELIMINATION
 xor eax,eax
 mov ebx,[OffsetChangeMap_VOffset]
 mov al,[HScroll + edx]
 mov cl,32
 shr eax,3
 add esi,ebx
 sub cl,al
 lea esi,[esi+eax*2]

 mov edx,[OffsetChangeVMap_VOffset]
 add edi,ebx

 mov ah,0
 mov bl,0

.detect_loop:
 mov ch,[esi+1]

 or ah,ch
 mov bh,[esi+1+edx]

 add esi,byte 2
 or bl,bh

 dec cl
 jnz .detect_loop

 add cl,al
 jz .detect_end

 mov al,0
 mov esi,edi
 jmp short .detect_loop

.detect_end:
 mov [OffsetChangeDetect1],ah
 or ah,bl
 mov [OffsetChangeDetect2],bl
 mov [OffsetChangeDetect3],ah
%endif

 ret

%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 Clear_Scanlines
;edi = dest address, ebp = line count
 mov al,[C_LABEL(use_fpu_copies)]
 test al,al
 jnz .clear_fpu

 mov al,[C_LABEL(use_mmx)]
 test al,al
 jnz .clear_mmx

 xor eax,eax

 mov ecx,256/4
 push es
 mov edx,ds
 mov es,edx
 cld

 cmp byte [C_LABEL(preload_cache)],0
 jnz near C_LABEL(Clear_Scanlines_Preload).clear_loop

.clear_loop:
 rep stosd

 add edi,byte GfxBufferLineSlack    ; Point screen to next line
 dec ebp
 mov ecx,256/4
 jnz .clear_loop

 pop es
 ret

ALIGNC
.clear_fpu:
 fldz
 mov ecx,256/16

.clear_fpu_loop:
 fst qword [edi+0]
 fst qword [edi+8]
 add edi,byte 16
 dec ecx
 jnz .clear_fpu_loop

 add edi,byte GfxBufferLineSlack    ; Point screen to next line
 dec ebp
 mov ecx,256/16
 jnz .clear_fpu_loop

 fstp st0
 ret

ALIGNC
.clear_mmx:
 pxor mm0,mm0
 mov ecx,256/16

.clear_mmx_loop:
 movq [edi+0],mm0
 movq [edi+8],mm0
 add edi,byte 16
 dec ecx
 jnz .clear_mmx_loop

 add edi,byte GfxBufferLineSlack    ; Point screen to next line
 dec ebp
 mov ecx,256/16
 jnz .clear_mmx_loop

 emms
 ret

ALIGNC
EXPORT_C Clear_Scanlines_Preload
;edi = dest address, ebp = line count
 cmp byte [C_LABEL(preload_cache_2)],0
 jz near C_LABEL(Clear_Scanlines)

 mov al,[C_LABEL(use_fpu_copies)]
 test al,al
 jnz .clear_fpu

 mov al,[C_LABEL(use_mmx)]
 test al,al
 jnz near .clear_mmx

 xor eax,eax

 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+32*1]
 mov bl,[edi+32*2]
 mov bl,[edi+32*3]
 mov bl,[edi+32*4]
 mov bl,[edi+32*5]
 mov bl,[edi+32*6]
 mov bl,[edi+32*7]
 mov bl,[edi+16]
 mov bl,[edi+16*3]
 mov bl,[edi+16*5]
 mov bl,[edi+16*7]
 mov bl,[edi+16*9]
 mov bl,[edi+16*11]
 mov bl,[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
 ret

ALIGNC
.clear_fpu:
 fldz
 mov ecx,256/16

.clear_fpu_next_line:
 ; Load area to clear into cache
 mov bl,[edi+0]
 mov bl,[edi+32*1]
 mov bl,[edi+32*2]
 mov bl,[edi+32*3]
 mov bl,[edi+32*4]
 mov bl,[edi+32*5]
 mov bl,[edi+32*6]
 mov bl,[edi+32*7]

.clear_fpu_loop:
 fst qword [edi+0]
 fst qword [edi+8]
 add edi,byte 16
 dec ecx
 jnz .clear_fpu_loop

 add edi,byte GfxBufferLineSlack    ; Point screen to next line
 dec ebp
 mov ecx,256/16
 jnz .clear_fpu_next_line

 fstp st0
 ret

ALIGNC
.clear_mmx:
 pxor mm0,mm0
 mov ecx,256/16

.clear_mmx_next_line:
 ; Load area to clear into cache
 mov bl,[edi+0]
 mov bl,[edi+32*1]
 mov bl,[edi+32*2]
 mov bl,[edi+32*3]
 mov bl,[edi+32*4]
 mov bl,[edi+32*5]
 mov bl,[edi+32*6]
 mov bl,[edi+32*7]

.clear_mmx_loop:
 movq [edi+0],mm0
 movq [edi+8],mm0
 add edi,byte 16
 dec ecx
 jnz .clear_mmx_next_line

 add edi,byte GfxBufferLineSlack    ; Point screen to next line
 dec ebp
 mov ecx,256/16
 jnz .clear_mmx_loop

 emms
 ret

ALIGNC
EXPORT_C Update_Display
 ; edx = number of lines to recache
 mov edx,[Ready_Line_Render]
 push eax
 sub edx,[C_LABEL(Current_Line_Render)]
 mov ah,[C_LABEL(INIDISP)]
 mov [Display_Needs_Update],dh
 test ah,ah         ; Check for screen off
 js near .screen_off
 push ebx
 push ecx
 push edi
 push ebp
 push esi
 push edx

 xor eax,eax
 mov [Priority_Used_BG1],ax
 mov [Priority_Used_BG2],ax
 mov [Priority_Used_BG3],ax
 mov [Priority_Used_BG4],ax

%ifdef WATCH_RENDER_BREAKS
EXTERN_C BreaksLast
 inc dword [C_LABEL(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 C_LABEL(Recache_OAM)
.no_oam_recache_needed:

 cmp byte [Redo_Windowing],0
 jz .no_window_recalc_needed
 call C_LABEL(Recalc_Window_Effects)
.no_window_recalc_needed:

 SORT_OFFSET_CHANGE

.no_tile_layers:

 mov ebx,[C_LABEL(Current_Line_Render)]
 mov edi,[BaseDestPtr]
 inc ebx
 mov ebp,[esp]

 call dword [Render_Select]

 pop edx
 pop esi
 pop ebp
 pop edi
 pop ecx
 pop ebx
 add [C_LABEL(Current_Line_Render)],edx

 mov eax,GfxBufferLinePitch
 imul eax,edx
 add [BaseDestPtr],eax
 pop eax
.return:
 ret

ALIGNC
.screen_off:
 push ebx
 push ecx
 push edi
 push ebp
 push esi
 push edx

 mov ebp,edx
 mov edi,[C_LABEL(SNES_Screen8)]    ; (256+16)*(239+1) framebuffer
 mov ebx,[BaseDestPtr]
 add edi,ebx

 ; Clear the framebuffer
 call C_LABEL(Clear_Scanlines)

 pop edx
 pop esi
 pop ebp
 pop edi
 pop ecx
 pop ebx
 add [C_LABEL(Current_Line_Render)],edx

 mov eax,GfxBufferLinePitch
 imul eax,edx
 add [BaseDestPtr],eax
 pop eax
 ret

;al = left edge, cl = right edge + 1
ALIGNC
EXPORT_C Recalc_Window_Bands
 test cl,cl         ; 0 = 255 (right edge)
 jz .one_inside
 cmp cl,al
 jbe .full_outside  ; if (Right < Left) full range outside window;
.one_inside:
 mov [Win_Bands_In+edx],al
 test al,al

 mov byte [Win_Count_In+edx],1  ; One band inside window (left,right)
 mov bl,WT_Left
 mov [Win_Bands_In+edx+1],cl
 jnz .not_flush_left    ; if (!Left) window flush left;
 test cl,cl
 jnz .flush_one_side    ; if (!Left && Right == 255) full range inside;
 ; Full range inside window
 mov byte [Win_Count_Out+edx],0     ; No bands outside window
 mov byte [Win_Type+edx],WT_Full    ; Full range inside window
 jmp short .done
.not_flush_left:
 ; Window not flush left (1 inside, 1 or 2 outside)
 mov bl,WT_Right
 test cl,cl
 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 [Win_Count_Out+edx],2 ; One band outside window (right+1,left-1)
 mov [Win_Bands_Out+edx+1],al
 mov byte [Win_Bands_Out+edx],0
 mov [Win_Bands_Out+edx+2],cl
 mov byte [Win_Bands_Out+edx+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 [Win_Bands_Out+edx+1],al
 mov byte [Win_Type+edx],bl     ; Window flush to one side
 mov byte [Win_Count_Out+edx],1 ; One band outside window (right+1,left-1)
 mov [Win_Bands_Out+edx],cl
 jmp short .done
.full_outside:
 ; Full range outside window (0 inside, 1 outside)
 mov byte [Win_Count_Out+edx],1     ; One band outside window
 mov byte [Win_Type+edx],WT_None    ; No range inside window
 mov dword [Win_Bands_Out+edx],0    ; Full range band
 mov byte [Win_Count_In+edx],0  ; No bands inside window
.done:
 ret

;%1 = Left,%2 = Right,%3 = Window table
%macro Recalc_Single_Window 3
 LOAD_WIN_TABLE %3
 mov al,[%1]
 mov cl,[%2]
 call C_LABEL(Recalc_Window_Bands)
%endmacro

%macro Recalc_Window_BG_Main 1
 mov al,[SCR_TM]
 LOAD_BG_TABLE %1,edi
 mov cl,[SCR_TMW]
 mov esi,BG_Win_Main
 and al,cl
 call C_LABEL(Recalc_Window_Area_BG)
%endmacro

%macro Recalc_Window_BG_Sub 1
 mov al,[SCR_TS]
 LOAD_BG_TABLE %1,edi
 mov cl,[SCR_TSW]
 mov esi,BG_Win_Sub
 and al,cl
 call C_LABEL(Recalc_Window_Area_BG)
%endmacro

ALIGNC
EXPORT_C Recalc_Window_Area_BG

 test al,[BG_Flag+edi]
 jz .no_clip

 mov ax,[WSEL+edi]  ;WSEL, WLOG
 test al,8+2
 jz .no_clip

 jpe .intersect

 add edi,esi

 LOAD_WIN_TABLE 2
 test al,2
 jz .single_window_clip_2
 LOAD_WIN_TABLE 1
 shl al,2
.single_window_clip_2:
 
 ; we want drawn areas, not window areas, so we need the inverted results...
 test al,4
 mov al,[Win_Count_Out+edx]
 jz .draw_outside
 mov al,[Win_Count_In+edx]
 add edx,byte Win_Bands_In - Win_Bands_Out
.draw_outside:

 mov [edi+BG_Win_Count],al
 test al,al
 jz .no_runs

.next_run:
 mov cx,[edx+Win_Bands_Out]
 add edx,byte 2
 mov [edi+BG_Win_Bands],cx
 add edi,byte 2
 dec al
 jnz .next_run

.no_runs:
 ret

.no_clip:
 mov byte [edi+esi+BG_Win_Count],1
 mov byte [edi+esi+BG_Win_Bands+0],0
 mov byte [edi+esi+BG_Win_Bands+1],0
 ret

; Method of generation depends on logic mode.
;  OR logic uses AND on the bands outside the window area to compute
; the areas to be drawn.  No seperate bands can end up adjacent to each
; other, so coalesence is unnecessary.
;  AND logic uses OR on the bands outside the window area to compute
; the areas to be drawn, logic code handles coalescence of adjacent
; bands.
;  XOR and XNOR logic use a sorted set of window edges, with duplicate
; edges discarded.


 ;logic - 00 = or; 01 = and; 10 = xor; 11 = xnor
 ; we want drawn areas, not window areas, so we need the inverted results...
 ; or   = and of outside
 ; and  = or of outside
 ; xor  = xor of inside 1, outside 2
 ; xnor = xor of outside both

 ; edi = BG table
 ; esi = screen offset in BG table
.intersect:
 and ah,3
 cmp ah,1
 je .intersect_and
 ja .intersect_xor

.intersect_or:
 add edi,esi

 ; or   = and of outside
 LOAD_WIN_TABLE 1
 LOAD_WIN_TABLE 2,esi
 xor ebp,ebp

 ; we want drawn areas, not window areas, so we need the inverted results...
 test al,1
 mov cl,[Win_Count_Out+edx]
 jz .or_draw_outside_1
 mov cl,[Win_Count_In+edx]
 add edx,byte Win_Bands_In - Win_Bands_Out
.or_draw_outside_1:

 test cl,cl
 jz .or_no_more_bands

 test al,4
 mov ch,[Win_Count_Out+esi]
 jz .or_draw_outside_2
 mov ch,[Win_Count_In+esi]
 add esi,byte Win_Bands_In - Win_Bands_Out
.or_draw_outside_2:

 test ch,ch
 jz .or_no_more_bands

.or_win1_loop:
 push ecx
 mov ax,[edx+Win_Bands_Out]
 dec ah
 push esi

.or_win2_loop:
 mov bx,[esi+Win_Bands_Out]
 dec bh

 cmp al,bh      ;win1left, win2right
 ja .or_no_intersect

 cmp bl,ah      ;win2left, win1right
 ja .or_no_more_intersect

 cmp bl,al
 ja .or_max_left
 mov bl,al
.or_max_left:

 mov [edi+ebp*2+BG_Win_Bands],bl

 cmp bh,ah
 jb .or_min_right
 mov bh,ah
.or_min_right:

 inc bh
 mov [edi+ebp*2+BG_Win_Bands+1],bh
 inc ebp

 add esi,byte 2
 dec ch
 jnz .or_win2_loop

.or_no_more_intersect:
 pop esi
 pop ecx

 add edx,byte 2
 dec cl
 jnz .or_win1_loop

.or_no_more_bands:
 mov eax,ebp
 mov [edi+BG_Win_Count],al
 ret

.or_no_intersect:
 add esi,byte 2
 dec ch
 mov [esp],esi
 mov [esp+4],ecx
 jnz .or_win2_loop
 add esp,byte 8
 jmp .or_no_more_bands


.intersect_and:
 add edi,esi

 ; and  = or of outside
 LOAD_WIN_TABLE 1
 LOAD_WIN_TABLE 2,esi
 xor ebp,ebp

 ; we want drawn areas, not window areas, so we need the inverted results...
 test al,1
 mov cl,[Win_Count_Out+edx]
 jz .and_draw_outside_1
 mov cl,[Win_Count_In+edx]
 add edx,byte Win_Bands_In - Win_Bands_Out
.and_draw_outside_1:

 test cl,cl
 jz .and_no_bands

 test al,4
 mov ch,[Win_Count_Out+esi]
 jz .and_draw_outside_2
 mov ch,[Win_Count_In+esi]
 add esi,byte Win_Bands_In - Win_Bands_Out
.and_draw_outside_2:

 test ch,ch
 jz .and_no_bands

.and_win1_loop:
 ; start with leftmost window bands
 mov al,[edx+Win_Bands_Out]
 mov bl,[esi+Win_Bands_Out]
 cmp al,bl
 jbe .and_no_swap
 rol cx,8
 mov ebx,edx
 mov edx,esi
 mov esi,ebx
.and_no_swap:

 mov ax,[edx+Win_Bands_Out]

.and_win2_loop:
 mov bx,[esi+Win_Bands_Out]

 ; compare left edges against right edges
 test bh,bh
 jz .and_win2right_edge

 cmp al,bh      ;win1left, win2right
 ja .and_no_intersect

.and_win2right_edge:
 test ah,ah
 jz .and_win1right_edge

 cmp bl,ah      ;win2left, win1right
 ja .and_no_intersect

.and_win1right_edge:
 cmp al,bl
 jb .and_min_left
 mov al,bl
.and_min_left:

 dec ah
 dec bh
 cmp ah,bh
 ja .and_max_right
 mov ah,bh
.and_max_right:
 inc ah

 add esi,byte 2
 dec ch
 jnz .and_win2_loop

.and_no_intersect:
 dec cl
 jz .and_last_band

 mov bx,[edx+Win_Bands_Out+2]
 add edx,byte 2

 ; compare left edges against right edges

 test bh,bh
 jz .and_win2right_edge2

 cmp al,bh      ;win1left, win2right
 ja .and_no_intersect2

.and_win2right_edge2:
 test ah,ah
 jz .and_win1right_edge2

 cmp bl,ah      ;win2left, win1right
 ja .and_no_intersect2

.and_win1right_edge2:
 cmp al,bl
 jb .and_min_left2
 mov al,bl
.and_min_left2:

 dec ah
 dec bh
 cmp ah,bh
 ja .and_max_right2
 mov ah,bh
.and_max_right2:
 inc ah

 test ch,ch
 jnz .and_win2_loop
 jmp .and_no_intersect

.and_swap_windows:
 rol cx,8
 mov ebx,edx
 mov edx,esi
 mov esi,ebx
 mov bx,[edx+Win_Bands_Out]
 dec bh

.and_no_intersect2:
 mov [edi+ebp*2+BG_Win_Bands],al
 mov [edi+ebp*2+BG_Win_Bands+1],ah
 inc ebp

 test ch,ch
 jnz .and_win1_loop
 mov ax,bx
 jmp .and_no_intersect

.and_last_band:
 test ch,ch
 jnz .and_swap_windows

 mov [edi+ebp*2+BG_Win_Bands],al
 mov [edi+ebp*2+BG_Win_Bands+1],ah
 inc ebp

.and_no_bands:
 mov eax,ebp
 mov [edi+BG_Win_Count],al
 ret

.intersect_xor:
 ;fixup for xor/xnor
 and ah,1
 xor al,ah

 add edi,esi

 LOAD_WIN_TABLE 1
 LOAD_WIN_TABLE 2,esi

 ; we want drawn areas, not window areas, so we need the inverted results...
 test al,1
 mov cl,[Win_Count_Out+edx]
 jnz .xor_draw_outside_1
 mov cl,[Win_Count_In+edx]
 add edx,byte Win_Bands_In - Win_Bands_Out
.xor_draw_outside_1:

 test al,4
 mov ch,[Win_Count_Out+esi]
 jz .xor_draw_outside_2
 mov ch,[Win_Count_In+esi]
 add esi,byte Win_Bands_In - Win_Bands_Out
.xor_draw_outside_2:

EXTERN_C xor_bands
 push edi
 xor eax,eax
 add edi,byte BG_Win_Bands
 mov al,ch
 and ecx,byte 0x7F
 push edi
 push eax
 push ecx
 push esi
 push edx
 call C_LABEL(xor_bands)
 mov edi,[esp+20]
 add esp,byte 24
 mov [edi+BG_Win_Count],al
 ret


ALIGNC
EXPORT_C Recalc_Window_Effects
 push eax
 push ecx
 push edx
 push ebx
 push ebp
 push esi
 push edi<commit_msg>Fixed a bug in dual-window AND intersect logic when one window area was empty.<commit_after>%if 0

SNEeSe, an Open Source Super NES emulator.


Copyright (c) 1998-2003 <NAME>'.
Portions Copyright (c) 2003 <NAME>.

This is free software.  See 'LICENSE' for details.
You must read and accept the license prior to use.

%endif

; 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 MSR_PROFILING
%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_ppu_screen_asm

%include "misc.inc"
%include "clear.inc"
%include "ppu/sprites.inc"
%include "ppu/ppu.inc"
%include "ppu/tiles.inc"
%include "ppu/screen.inc"

EXTERN_C SCREEN_MODE_7
EXTERN_C SNES_Screen8
EXTERN_C Offset_Change_Disable

EXTERN_C MosaicLine,MosaicCount

EXTERN_C use_mmx,use_fpu_copies,preload_cache,preload_cache_2

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 C_LABEL(SCREEN_MODE_0)
 dd C_LABEL(SCREEN_MODE_1)
 dd C_LABEL(SCREEN_MODE_2)
 dd C_LABEL(SCREEN_MODE_3)
 dd C_LABEL(SCREEN_MODE_4)
 dd C_LABEL(SCREEN_MODE_5)
 dd C_LABEL(SCREEN_MODE_6)
 dd C_LABEL(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

ClipTableStart:
 db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
 db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
ClipLeftTable: ;ClipLeftTable[-first_pixel_offset]
 db 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
 db 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
ClipRightTable: ;ClipRightTable[-pixel_count]
 db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
 db 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
;to clip both: ClipLeftTable[-first_pixel_offset] &
; ClipRightTable[-(first_pixel_offset + pixel_count)]

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
;L7 = mode-7 EXTBG low priority 1
;N7 = mode-7 EXTBG low priority 2, mode-7 w/o EXTBG
;H7 = mode-7 EXTBG high priority

;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 H7 1S N7 0S L7 BA
;Z     34 24 22 14 12 04 02 00

ALIGNB
EXPORT DisplayZ,skipb (8+256+8)
ALIGNB
EXPORT_C Current_Line_Render,skipl
EXPORT_C Last_Frame_Line,skipl
EXPORT Ready_Line_Render,skipl
EXPORT BaseDestPtr      ,skipl

EXPORT Render_Select,skipl  ; Base renderer
EXPORT Render_Mode  ,skipl  ; Mode renderer

BGLineCount:skipl
TileClip1:
TileClip1Left:skipl
TileClip1Right:skipl
TileClip2:
TileClip2Left:skipl
TileClip2Right:skipl

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
Palette_Base:skipl

; Used for offset change map addressing
EXPORT OffsetChangeMap_VOffset,skipl    ; BG3VOFS + BG3SC
EXPORT OffsetChangeVMap_VOffset,skipl   ; BG3VOFS + BG3SC (for split-table)

EXPORT Display_Needs_Update,skipb
EXPORT Tile_Layers_Enabled,skipb
EXPORT Tile_priority_bit,skipb
Tile_Priority_Used:skipb
Tile_Priority_Unused:skipb

EXPORT OffsetChangeDetect1,skipb
EXPORT OffsetChangeDetect2,skipb
EXPORT OffsetChangeDetect3,skipb
EXPORT Redo_Windowing,skipb

;YXCS 4321
;1-4 update clip window for BG 1-4
;S   update clip window for OBJ (sprites)
;C   update color window
;X   update window 1 area
;Y   update window 2 area


section .text
;ebx = first line, edi = destination base ptr, ebp = # lines
ALIGNC
EXPORT_C Render_Layering_Option_0   ; main-on-sub
 mov al,[SCR_TSW]
 mov ah,[SCR_TMW]
 shl eax,16
 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_TMW]
 mov ah,[SCR_TSW]
 shl eax,16
 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_TMW]
 shl eax,16
 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

 mov al,[Redo_Offset_Change_VOffsets]
 LOAD_BG_TABLE 3
 test al,al
 jz .no_recalc_voffsets

 mov byte [Redo_Offset_Change_VOffsets],0

;OffsetChangeMap_VOffset = ((BG3VOFS / 8) & 0x1F) * 64 +
; (BG3VOFS & 0x100 ? BLMapAddressBG3 - TLMapAddressBG3 : 0);
;OffsetChangeVMap_VOffset = ((BG3VOFS / 8 + 1) & 0x1F) * 64 +
; ((BG3VOFS + 8) & 0x100 ? BLMapAddressBG3 - TLMapAddressBG3 : 0) -
; OffsetChangeMap_VOffset;
 mov edi,[VScroll+edx]
 shl edi,3          ; divided by 8 (base tile size),
 mov ebx,[VScroll+edx]
 and edi,(0x1F << 6)    ; * 2 (16-bit words) * 32 (row)

 and bh,1
 jz .offset_line_in_map_top

 mov ebx,[BLMapAddress+edx]
 add edi,ebx
 mov ebx,[TLMapAddress+edx]
 sub edi,ebx
.offset_line_in_map_top:

 mov [OffsetChangeMap_VOffset],edi

 mov ebx,[VScroll+edx]
 add edi,byte 32*2      ; next row
 add ebx,byte 8
 and edi,(0x1F << 6)    ; * 2 (16-bit words) * 32 (row)

 and bh,1
 jz .vmap_offset_line_in_map_top

 mov ebx,[BLMapAddress+edx]
 add edi,ebx
 mov ebx,[TLMapAddress+edx]
 sub edi,ebx
.vmap_offset_line_in_map_top:

 mov ebx,[OffsetChangeMap_VOffset]
 sub edi,ebx
 mov [OffsetChangeVMap_VOffset],edi

.no_recalc_voffsets:

 ; Update BG3 position for offset change
 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 esi,[esi+edx]
 mov edi,[edi+edx]
 mov [VLMapAddress+edx],esi
 mov [VRMapAddress+edx],edi

%ifdef OFFSET_CHANGE_ELIMINATION
 xor eax,eax
 mov ebx,[OffsetChangeMap_VOffset]
 mov al,[HScroll + edx]
 mov cl,32
 shr eax,3
 add esi,ebx
 sub cl,al
 lea esi,[esi+eax*2]

 mov edx,[OffsetChangeVMap_VOffset]
 add edi,ebx

 mov ah,0
 mov bl,0

.detect_loop:
 mov ch,[esi+1]

 or ah,ch
 mov bh,[esi+1+edx]

 add esi,byte 2
 or bl,bh

 dec cl
 jnz .detect_loop

 add cl,al
 jz .detect_end

 mov al,0
 mov esi,edi
 jmp short .detect_loop

.detect_end:
 mov [OffsetChangeDetect1],ah
 or ah,bl
 mov [OffsetChangeDetect2],bl
 mov [OffsetChangeDetect3],ah
%endif

 ret

%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 Clear_Scanlines
;edi = dest address, ebp = line count
 mov al,[C_LABEL(use_fpu_copies)]
 test al,al
 jnz .clear_fpu

 mov al,[C_LABEL(use_mmx)]
 test al,al
 jnz .clear_mmx

 xor eax,eax

 mov ecx,256/4
 push es
 mov edx,ds
 mov es,edx
 cld

 cmp byte [C_LABEL(preload_cache)],0
 jnz near C_LABEL(Clear_Scanlines_Preload).clear_loop

.clear_loop:
 rep stosd

 add edi,byte GfxBufferLineSlack    ; Point screen to next line
 dec ebp
 mov ecx,256/4
 jnz .clear_loop

 pop es
 ret

ALIGNC
.clear_fpu:
 fldz
 mov ecx,256/16

.clear_fpu_loop:
 fst qword [edi+0]
 fst qword [edi+8]
 add edi,byte 16
 dec ecx
 jnz .clear_fpu_loop

 add edi,byte GfxBufferLineSlack    ; Point screen to next line
 dec ebp
 mov ecx,256/16
 jnz .clear_fpu_loop

 fstp st0
 ret

ALIGNC
.clear_mmx:
 pxor mm0,mm0
 mov ecx,256/16

.clear_mmx_loop:
 movq [edi+0],mm0
 movq [edi+8],mm0
 add edi,byte 16
 dec ecx
 jnz .clear_mmx_loop

 add edi,byte GfxBufferLineSlack    ; Point screen to next line
 dec ebp
 mov ecx,256/16
 jnz .clear_mmx_loop

 emms
 ret

ALIGNC
EXPORT_C Clear_Scanlines_Preload
;edi = dest address, ebp = line count
 cmp byte [C_LABEL(preload_cache_2)],0
 jz near C_LABEL(Clear_Scanlines)

 mov al,[C_LABEL(use_fpu_copies)]
 test al,al
 jnz .clear_fpu

 mov al,[C_LABEL(use_mmx)]
 test al,al
 jnz near .clear_mmx

 xor eax,eax

 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+32*1]
 mov bl,[edi+32*2]
 mov bl,[edi+32*3]
 mov bl,[edi+32*4]
 mov bl,[edi+32*5]
 mov bl,[edi+32*6]
 mov bl,[edi+32*7]
 mov bl,[edi+16]
 mov bl,[edi+16*3]
 mov bl,[edi+16*5]
 mov bl,[edi+16*7]
 mov bl,[edi+16*9]
 mov bl,[edi+16*11]
 mov bl,[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
 ret

ALIGNC
.clear_fpu:
 fldz
 mov ecx,256/16

.clear_fpu_next_line:
 ; Load area to clear into cache
 mov bl,[edi+0]
 mov bl,[edi+32*1]
 mov bl,[edi+32*2]
 mov bl,[edi+32*3]
 mov bl,[edi+32*4]
 mov bl,[edi+32*5]
 mov bl,[edi+32*6]
 mov bl,[edi+32*7]

.clear_fpu_loop:
 fst qword [edi+0]
 fst qword [edi+8]
 add edi,byte 16
 dec ecx
 jnz .clear_fpu_loop

 add edi,byte GfxBufferLineSlack    ; Point screen to next line
 dec ebp
 mov ecx,256/16
 jnz .clear_fpu_next_line

 fstp st0
 ret

ALIGNC
.clear_mmx:
 pxor mm0,mm0
 mov ecx,256/16

.clear_mmx_next_line:
 ; Load area to clear into cache
 mov bl,[edi+0]
 mov bl,[edi+32*1]
 mov bl,[edi+32*2]
 mov bl,[edi+32*3]
 mov bl,[edi+32*4]
 mov bl,[edi+32*5]
 mov bl,[edi+32*6]
 mov bl,[edi+32*7]

.clear_mmx_loop:
 movq [edi+0],mm0
 movq [edi+8],mm0
 add edi,byte 16
 dec ecx
 jnz .clear_mmx_next_line

 add edi,byte GfxBufferLineSlack    ; Point screen to next line
 dec ebp
 mov ecx,256/16
 jnz .clear_mmx_loop

 emms
 ret

ALIGNC
EXPORT_C Update_Display
 ; edx = number of lines to recache
 mov edx,[Ready_Line_Render]
 push eax
 sub edx,[C_LABEL(Current_Line_Render)]
 mov ah,[C_LABEL(INIDISP)]
 mov [Display_Needs_Update],dh
 test ah,ah         ; Check for screen off
 js near .screen_off
 push ebx
 push ecx
 push edi
 push ebp
 push esi
 push edx

 xor eax,eax
 mov [Priority_Used_BG1],ax
 mov [Priority_Used_BG2],ax
 mov [Priority_Used_BG3],ax
 mov [Priority_Used_BG4],ax

%ifdef WATCH_RENDER_BREAKS
EXTERN_C BreaksLast
 inc dword [C_LABEL(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 C_LABEL(Recache_OAM)
.no_oam_recache_needed:

 cmp byte [Redo_Windowing],0
 jz .no_window_recalc_needed
 call C_LABEL(Recalc_Window_Effects)
.no_window_recalc_needed:

 SORT_OFFSET_CHANGE

.no_tile_layers:

 mov ebx,[C_LABEL(Current_Line_Render)]
 mov edi,[BaseDestPtr]
 inc ebx
 mov ebp,[esp]

 call dword [Render_Select]

 pop edx
 pop esi
 pop ebp
 pop edi
 pop ecx
 pop ebx
 add [C_LABEL(Current_Line_Render)],edx

 mov eax,GfxBufferLinePitch
 imul eax,edx
 add [BaseDestPtr],eax
 pop eax
.return:
 ret

ALIGNC
.screen_off:
 push ebx
 push ecx
 push edi
 push ebp
 push esi
 push edx

 mov ebp,edx
 mov edi,[C_LABEL(SNES_Screen8)]    ; (256+16)*(239+1) framebuffer
 mov ebx,[BaseDestPtr]
 add edi,ebx

 ; Clear the framebuffer
 call C_LABEL(Clear_Scanlines)

 pop edx
 pop esi
 pop ebp
 pop edi
 pop ecx
 pop ebx
 add [C_LABEL(Current_Line_Render)],edx

 mov eax,GfxBufferLinePitch
 imul eax,edx
 add [BaseDestPtr],eax
 pop eax
 ret

;al = left edge, cl = right edge + 1
ALIGNC
EXPORT_C Recalc_Window_Bands
 test cl,cl         ; 0 = 255 (right edge)
 jz .one_inside
 cmp cl,al
 jbe .full_outside  ; if (Right < Left) full range outside window;
.one_inside:
 mov [Win_Bands_In+edx],al
 test al,al

 mov byte [Win_Count_In+edx],1  ; One band inside window (left,right)
 mov bl,WT_Left
 mov [Win_Bands_In+edx+1],cl
 jnz .not_flush_left    ; if (!Left) window flush left;
 test cl,cl
 jnz .flush_one_side    ; if (!Left && Right == 255) full range inside;
 ; Full range inside window
 mov byte [Win_Count_Out+edx],0     ; No bands outside window
 mov byte [Win_Type+edx],WT_Full    ; Full range inside window
 jmp short .done
.not_flush_left:
 ; Window not flush left (1 inside, 1 or 2 outside)
 mov bl,WT_Right
 test cl,cl
 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 [Win_Count_Out+edx],2 ; One band outside window (right+1,left-1)
 mov [Win_Bands_Out+edx+1],al
 mov byte [Win_Bands_Out+edx],0
 mov [Win_Bands_Out+edx+2],cl
 mov byte [Win_Bands_Out+edx+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 [Win_Bands_Out+edx+1],al
 mov byte [Win_Type+edx],bl     ; Window flush to one side
 mov byte [Win_Count_Out+edx],1 ; One band outside window (right+1,left-1)
 mov [Win_Bands_Out+edx],cl
 jmp short .done
.full_outside:
 ; Full range outside window (0 inside, 1 outside)
 mov byte [Win_Count_Out+edx],1     ; One band outside window
 mov byte [Win_Type+edx],WT_None    ; No range inside window
 mov dword [Win_Bands_Out+edx],0    ; Full range band
 mov byte [Win_Count_In+edx],0  ; No bands inside window
.done:
 ret

;%1 = Left,%2 = Right,%3 = Window table
%macro Recalc_Single_Window 3
 LOAD_WIN_TABLE %3
 mov al,[%1]
 mov cl,[%2]
 call C_LABEL(Recalc_Window_Bands)
%endmacro

%macro Recalc_Window_BG_Main 1
 mov al,[SCR_TM]
 LOAD_BG_TABLE %1,edi
 mov cl,[SCR_TMW]
 mov esi,BG_Win_Main
 and al,cl
 call C_LABEL(Recalc_Window_Area_BG)
%endmacro

%macro Recalc_Window_BG_Sub 1
 mov al,[SCR_TS]
 LOAD_BG_TABLE %1,edi
 mov cl,[SCR_TSW]
 mov esi,BG_Win_Sub
 and al,cl
 call C_LABEL(Recalc_Window_Area_BG)
%endmacro

ALIGNC
EXPORT_C Recalc_Window_Area_BG

 test al,[BG_Flag+edi]
 jz .no_clip

 mov ax,[WSEL+edi]  ;WSEL, WLOG
 test al,8+2
 jz .no_clip

 jpe .intersect

 add edi,esi

 LOAD_WIN_TABLE 2
 test al,2
 jz .single_window_clip_2
 LOAD_WIN_TABLE 1
 shl al,2
.single_window_clip_2:
 
 ; we want drawn areas, not window areas, so we need the inverted results...
 test al,4
 mov al,[Win_Count_Out+edx]
 jz .draw_outside
 mov al,[Win_Count_In+edx]
 add edx,byte Win_Bands_In - Win_Bands_Out
.draw_outside:

 mov [edi+BG_Win_Count],al
 test al,al
 jz .no_runs

.next_run:
 mov cx,[edx+Win_Bands_Out]
 add edx,byte 2
 mov [edi+BG_Win_Bands],cx
 add edi,byte 2
 dec al
 jnz .next_run

.no_runs:
 ret

.no_clip:
 mov byte [edi+esi+BG_Win_Count],1
 mov byte [edi+esi+BG_Win_Bands+0],0
 mov byte [edi+esi+BG_Win_Bands+1],0
 ret

; Method of generation depends on logic mode.
;  OR logic uses AND on the bands outside the window area to compute
; the areas to be drawn.  No seperate bands can end up adjacent to each
; other, so coalesence is unnecessary.
;  AND logic uses OR on the bands outside the window area to compute
; the areas to be drawn, logic code handles coalescence of adjacent
; bands.
;  XOR and XNOR logic use a sorted set of window edges, with duplicate
; edges discarded.


 ;logic - 00 = or; 01 = and; 10 = xor; 11 = xnor
 ; we want drawn areas, not window areas, so we need the inverted results...
 ; or   = and of outside
 ; and  = or of outside
 ; xor  = xor of inside 1, outside 2
 ; xnor = xor of outside both

 ; edi = BG table
 ; esi = screen offset in BG table
.intersect:
 and ah,3
 cmp ah,1
 je .intersect_and_setup
 ja .intersect_xor_check

;each intersect setup code chains to an intersect handler
;each intersect handler expects the following register state:
; edx = address of window 1 bands
; esi = address of window 2 bands
; cl = count of window 1 bands
; ch = count of window 2 bands
; ebp = 0
; edi = address for output window area (BG_WIN_DATA)

;for OR window logic, we use AND of inverted (outside) areas
.intersect_or_setup:
 add edi,esi

 LOAD_WIN_TABLE 1
 LOAD_WIN_TABLE 2,esi
 xor ebp,ebp

 ; we want drawn areas, not window areas, so we need the inverted results...
 test al,1
 mov cl,[Win_Count_Out+edx]
 jz .or_draw_outside_1
 mov cl,[Win_Count_In+edx]
 add edx,byte Win_Bands_In - Win_Bands_Out
.or_draw_outside_1:
%if Win_Bands_Out
 add edx,Win_Bands_Out
%endif

 test al,4
 mov ch,[Win_Count_Out+esi]
 jz .or_draw_outside_2
 mov ch,[Win_Count_In+esi]
 add esi,byte Win_Bands_In - Win_Bands_Out
.or_draw_outside_2:
%if Win_Bands_Out
 add esi,Win_Bands_Out
%endif

.intersect_and_entry:
 test cl,cl
 jz .and_no_more_bands

 test ch,ch
 jz .and_no_more_bands

.and_win1_loop:
 push ecx
 mov ax,[edx+Win_Bands_Out]
 dec ah
 push esi

.and_win2_loop:
 mov bx,[esi+Win_Bands_Out]
 dec bh

 cmp al,bh      ;win1left, win2right
 ja .and_no_intersect

 cmp bl,ah      ;win2left, win1right
 ja .and_no_more_intersect

 cmp bl,al
 ja .and_max_left
 mov bl,al
.and_max_left:

 mov [edi+ebp*2+BG_Win_Bands],bl

 cmp bh,ah
 jb .and_min_right
 mov bh,ah
.and_min_right:

 inc bh
 mov [edi+ebp*2+BG_Win_Bands+1],bh
 inc ebp

 add esi,byte 2
 dec ch
 jnz .and_win2_loop

.and_no_more_intersect:
 pop esi
 pop ecx

 add edx,byte 2
 dec cl
 jnz .and_win1_loop

.and_no_more_bands:
 mov eax,ebp
 mov [edi+BG_Win_Count],al
 ret

.and_no_intersect:
 add esi,byte 2
 dec ch
 mov [esp],esi
 mov [esp+4],ecx
 jnz .and_win2_loop
 add esp,byte 8
 jmp .and_no_more_bands


;for AND window logic, we use OR of inverted (outside) areas
.intersect_and_setup:
 add edi,esi

 LOAD_WIN_TABLE 1
 LOAD_WIN_TABLE 2,esi
 xor ebp,ebp

 ; we want drawn areas, not window areas, so we need the inverted results...
 test al,1
 mov cl,[Win_Count_Out+edx]
 jz .and_draw_outside_1
 mov cl,[Win_Count_In+edx]
 add edx,byte Win_Bands_In - Win_Bands_Out
.and_draw_outside_1:
%if Win_Bands_Out
 add edx,Win_Bands_Out
%endif

 test al,4
 mov ch,[Win_Count_Out+esi]
 jz .and_draw_outside_2
 mov ch,[Win_Count_In+esi]
 add esi,byte Win_Bands_In - Win_Bands_Out
.and_draw_outside_2:
%if Win_Bands_Out
 add esi,Win_Bands_Out
%endif

.intersect_or_entry:
 test cl,cl
 jz .or_copy_win2

 test ch,ch
 jz .or_copy_win1

.or_win1_loop:
 ; start with leftmost window bands
 mov al,[edx+Win_Bands_Out]
 mov bl,[esi+Win_Bands_Out]
 cmp al,bl
 jbe .or_no_swap
 rol cx,8
 mov ebx,edx
 mov edx,esi
 mov esi,ebx
.or_no_swap:

 mov ax,[edx+Win_Bands_Out]

.or_win2_loop:
 mov bx,[esi+Win_Bands_Out]

 ; compare left edges against right edges
 test bh,bh
 jz .or_win2right_edge

 cmp al,bh      ;win1left, win2right
 ja .or_no_intersect

.or_win2right_edge:
 test ah,ah
 jz .or_win1right_edge

 cmp bl,ah      ;win2left, win1right
 ja .or_no_intersect

.or_win1right_edge:
 cmp al,bl
 jb .or_min_left
 mov al,bl
.or_min_left:

 dec ah
 dec bh
 cmp ah,bh
 ja .or_max_right
 mov ah,bh
.or_max_right:
 inc ah

 add esi,byte 2
 dec ch
 jnz .or_win2_loop

.or_no_intersect:
 dec cl
 jz .or_last_band

 mov bx,[edx+Win_Bands_Out+2]
 add edx,byte 2

 ; compare left edges against right edges

 test bh,bh
 jz .or_win2right_edge2

 cmp al,bh      ;win1left, win2right
 ja .or_no_intersect2

.or_win2right_edge2:
 test ah,ah
 jz .or_win1right_edge2

 cmp bl,ah      ;win2left, win1right
 ja .or_no_intersect2

.or_win1right_edge2:
 cmp al,bl
 jb .or_min_left2
 mov al,bl
.or_min_left2:

 dec ah
 dec bh
 cmp ah,bh
 ja .or_max_right2
 mov ah,bh
.or_max_right2:
 inc ah

 test ch,ch
 jnz .or_win2_loop
 jmp .or_no_intersect

.or_swap_windows:
 rol cx,8
 mov ebx,edx
 mov edx,esi
 mov esi,ebx
 mov bx,[edx+Win_Bands_Out]
 dec bh

.or_no_intersect2:
 mov [edi+ebp*2+BG_Win_Bands],al
 mov [edi+ebp*2+BG_Win_Bands+1],ah
 inc ebp

 test ch,ch
 jnz .or_win1_loop
 mov ax,bx
 jmp .or_no_intersect

.or_last_band:
 test ch,ch
 jnz .or_swap_windows

 mov [edi+ebp*2+BG_Win_Bands],ax
 inc ebp

 mov eax,ebp
 mov [edi+BG_Win_Count],al
.or_done:
 ret

.or_copy_win2:
 mov cl,ch
 mov edx,esi
.or_copy_win1:
 mov [edi+BG_Win_Count],cl
 dec cl
 js .or_done
.or_copy_another:
 mov ax,[edx+ebp*2+Win_Bands_Out]
 mov [edi+ebp*2+BG_Win_Bands],ax
 inc ebp
 dec cl
 jns .or_copy_another
 ret


;if we're doing xor, we flip the inversion of one of the windows
.intersect_xor_check:
 ;fixup for xor/xnor
 and ah,1
 xor al,ah

.intersect_xor_setup:
 add edi,esi

 LOAD_WIN_TABLE 1
 LOAD_WIN_TABLE 2,esi

 ; we want drawn areas, not window areas, so we need the inverted results...
 test al,1
 mov cl,[Win_Count_Out+edx]
 jnz .xor_draw_outside_1
 mov cl,[Win_Count_In+edx]
 add edx,byte Win_Bands_In - Win_Bands_Out
.xor_draw_outside_1:
%if Win_Bands_Out
 add edx,Win_Bands_Out
%endif

 test al,4
 mov ch,[Win_Count_Out+esi]
 jz .xor_draw_outside_2
 mov ch,[Win_Count_In+esi]
 add esi,byte Win_Bands_In - Win_Bands_Out
.xor_draw_outside_2:
%if Win_Bands_Out
 add esi,Win_Bands_Out
%endif

.intersect_xor_entry:
EXTERN_C xor_bands
 push edi
 xor eax,eax
 add edi,byte BG_Win_Bands
 mov al,ch
 and ecx,byte 0x7F
 push edi
 push eax
 push ecx
 push esi
 push edx
 call C_LABEL(xor_bands)
 mov edi,[esp+20]
 add esp,byte 24
 mov [edi+BG_Win_Count],al
 ret


ALIGNC
EXPORT_C Recalc_Window_Effects
 push eax
 push ecx
 push edx
 push ebx
 push ebp
 push esi
 push edi
{'fraction_non_alphanumeric': 0.08872281093139989, 'fraction_numerical': 0.04878973786949247, 'mean_word_length': 4.450632295719845, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3054422', 'n_tokens_mistral': 20691, 'n_tokens_neox': 19025, 'n_words': 5053}