www.pudn.com > sn068s.zip > SPRITES.ASM
;
; Sprite render functions
;
; In all the sprite renderers...
; eax = (internal)
; ebx = (internal)
; cl = sprite counter for priority (internal)
; ch = sprite counter for set (internal)
; dl = sprite priority identifier (input)
; dh = (internal)
; esi = current sprite (internal)
; edi = (internal)
;
; OAM decoder generates table with plot data per sprite tile-line
; (X position, line-in-tile cache address, palette, X-flip, priority for
; each sprite scanline, and time/range flags for each scanline)
; 14b address, 2bit XYflip, 3bit palette, 2bit priority, 9bit X pos
; (may pack priority, palette, X-flip, and bit 8 X pos in 1b)
; 4b/sprite line
; 34 sprite lines/scanline max + count + time/range + priority + pri flag
; (34 * 4b = 136b + 8b)
; count = total sprites/tiles, and total sprites only including last per
; priority
; 239 lines * 145b = 34,416b (~33.61k)
; However, with this, other tables can be removed (OAM_*, 1,664b (1.625k))
; Plotters will be greatly simplified, with less redundant handling
;#define Profile_Recache_OAM
%define ALT_CLEAR
%define SNEeSe_sprites_asm
%include "misc.ni"
%include "clear.ni"
%include "tiles.ni"
%include "screen.ni"
%include "PPU.ni"
section .text
EXPORT_C sprites_text_start
section .data
EXPORT_C sprites_data_start
section .bss
EXPORT_C sprites_bss_start
extern Ready_Line_Render,_SNES_Screen8,BaseDestPtr
extern HVBJOY
section .data
ALIGND
palette_obj:
dd 0x8F8F8F8F, 0x9F9F9F9F, 0xAFAFAFAF, 0xBFBFBFBF
dd 0xCFCFCFCF, 0xDFDFDFDF, 0xEFEFEFEF, 0xFFFFFFFF
; Sprite offset tables moved to ScreenL.S
Sprite_Size_Table:
db 1, -7, 2,-15
db 1, -7, 4,-31
db 1, -7, 8,-63
db 2,-15, 4,-31
db 2,-15, 8,-63
db 4,-31, 8,-63
db 0, 0, 0, 0
db 0, 0, 0, 0
section .bss
ALIGNB
;Line counts when last OBJ of specified priority was added
EXPORT OAM_Count_Priority,skipl 240
;OBJ counts (low byte) and OBJ line counts (high byte)
EXPORT OAM_Count,skipw 240
;Time/range overflow flags
EXPORT OAM_TimeRange,skipb 240
;'Complex' priority in-use flags
EXPORT OAM_Low_Before_High,skipb 240
;Priorities for 'complex' priority detection
EXPORT OAM_Lowest_Priority,skipb 240
;Tail entry for ring buffers
EXPORT OAM_Tail,skipb 240
;239 ring buffers of 34 OBJ line descriptors (32-bit)
EXPORT OAM_Lines,skipl 34*239
EXPORT_EQU_C OAM_Lines,OAM_Lines
EXPORT_EQU_C OAM_Count_Priority,OAM_Count_Priority
EXPORT_EQU_C OAM_Count,OAM_Count
EXPORT_EQU_C OAM_TimeRange,OAM_TimeRange
EXPORT_EQU_C OAM_Low_Before_High,OAM_Low_Before_High
EXPORT_EQU_C OAM_Lowest_Priority,OAM_Lowest_Priority
; AAAA AAAA AAAA AAxx YXPP CCCX XXXX XXXX
; A - OAM sprite line-in-tile address
; YXPP CCC - bits 1-7 of OAM attribute word
; X - X position
ALIGNB
EXPORT_C OAM,skipb 512+32 ; Buffer for OAM
EXPORT SpriteCount,skipl
EXPORT_C HiSprite ,skipl
EXPORT_C HiSpriteCnt1,skipl ; First set size and bit offset
EXPORT_C HiSpriteCnt2,skipl ; Second set size and bit offset
EXPORT_C OBBASE,skipl ; VRAM location of sprite tiles 00-FF
EXPORT_C OBNAME,skipl ; VRAM location of sprite tiles 100-1FF
EXPORT_C OAMAddress,skipl
EXPORT_C OAMAddress_VBL,skipl ; Restore this at VBL
EXPORT_C HiSpriteAddr,skipl ; OAM address of sprite in 512b table
EXPORT_C HiSpriteBits,skipl ; OAM address of sprite in 32b table
ALIGNB
EXPORT Sprite_Size_Current,skipl
EXPORT_EQU sprsize_small,Sprite_Size_Current
EXPORT_EQU sprlim_small,Sprite_Size_Current+1
EXPORT_EQU sprsize_large,Sprite_Size_Current+2
EXPORT_EQU sprlim_large,Sprite_Size_Current+3
EXPORT Redo_OAM,skipb
EXPORT SPRLatch ,skipb ; Sprite Priority Rotation latch flag
EXPORT_C OBSEL ,skipb ; sssnnxbb sss=sprite size,nn=upper 4k address,bb=offset
EXPORT OAMHigh ,skipb
EXPORT OAM_Write_Low,skipb
EXPORT Pixel_Allocation_Tag,skipb
section .text
%define PS_Local_Bytes 16
%define PS_Lines esp+12
%define PS_BaseDestPtr esp+8
%define PS_Current_Line esp+4
%define PS_Priority esp
ALIGNC
EXPORT Plot_Sprites
dec ebx
push ebp
and edx,byte 0x30
push edi
push ebx
push edx
%ifdef NO_EARLY_CLEAR
mov ebx,[PS_Current_Line]
mov ebp,1
inc ebx
call Clear_Lines
%endif
mov ebx,[PS_Current_Line]
mov edi,[PS_BaseDestPtr]
mov edx,[PS_Priority]
jmp short .first_line
ALIGNC
.next_line:
%ifdef NO_EARLY_CLEAR
mov ebx,[PS_Current_Line]
mov ebp,1
add ebx,byte 2
call Clear_Lines
%endif
mov ebx,[PS_Current_Line]
mov edi,[PS_BaseDestPtr]
mov edx,[PS_Priority]
inc ebx
add edi,GfxBufferLinePitch
mov [PS_Current_Line],ebx
mov [PS_BaseDestPtr],edi
.first_line:
and edx,byte 0x30
shr edx,4
mov cl,[OAM_Count_Priority+ebx*4+edx]
shl edx,4
test cl,cl
jz near .check_line_count
mov cl,[OAM_Count+ebx*2+1]
mov al,[OAM_Low_Before_High+ebx]
test al,al
jnz near Plot_Sprites_Low_Before_High.first_line
xor eax,eax
mov al,cl
cmp cl,34
jb .zero_head
mov al,[OAM_Tail+ebx]
test al,al
jnz .zero_head
mov al,34
.zero_head
shl ebx,4
mov ch,cl
sub ch,al
sub cl,ch
shl eax,2
add ebx,[PS_Current_Line]
lea ebx,[OAM_Lines+eax+ebx*8-4]
.next_tile:
mov dh,0x30
mov eax,[ebx]
and dh,ah
sub ebx,byte 4
cmp dh,dl ; Check priority
jne near .check_tile_count
mov esi,eax
mov edi,[PS_BaseDestPtr]
shl esi,23
add edi,[_SNES_Screen8]
sar esi,23
add edi,esi ; X-adjust
mov esi,eax
mov dl,ah
shr esi,15
and edx,byte 7*2 ; Get palette
and esi,byte ~7 ; Tile line # address
mov ebp,[palette_obj+edx*2]
add esi,_TileCache4
add ah,ah
js near .flip_x
Plot_8_Paletted_Lines_noflip 1,esi,0
mov dl,[PS_Priority]
dec cl
jnz near .next_tile
jmp .check_count
ALIGNC
.flip_x:
Plot_8_Paletted_Lines_Xflip 1,esi,0
mov dl,[PS_Priority]
.check_tile_count:
dec cl
jnz near .next_tile
.check_count:
add ebx,34*4
add cl,ch
mov ch,0
jnz near .next_tile
.check_line_count:
dec dword [PS_Lines]
jnz near .next_line
.done:
add esp,byte PS_Local_Bytes
ret
ALIGNC
EXPORT_C Plot_Sprites_Low_Before_High
EXPORT Plot_Sprites_Low_Before_High
.first_line:
xor eax,eax
mov al,[OAM_Tail+ebx]
shl ebx,4
mov ch,al
shl eax,2
sub cl,ch
add ebx,[PS_Current_Line]
lea ebx,[OAM_Lines+eax+ebx*8]
.next_tile:
mov dh,0x30
mov eax,[ebx]
and dh,ah
add ebx,byte 4
push ebx
mov ebx,eax
shl ebx,23
push ecx
mov edi,[PS_BaseDestPtr+8]
sar ebx,23
add edi,[_SNES_Screen8]
mov esi,eax
cmp dh,dl ; Check priority
jnz near .bad_priority_plot
mov dh,ah
shr esi,15
add edi,ebx ; X-adjust
shl dh,3 ; Palette
and esi,byte ~7 ; Tile line # address
or dh,0x8F
add esi,_TileCache4
add ebx,DisplayZ+8
add ah,ah
mov ah,[Pixel_Allocation_Tag]
js near .flip_x
mov al,[esi]
and al,dh
jz .no_pixel_0
mov ch,[ebx]
test ch,ah
jnz .no_pixel_0
or ch,ah
mov [edi],al
mov [ebx],ch
.no_pixel_0:
mov al,[esi + 4]
and al,dh
jz .no_pixel_4
mov ch,[ebx + 4]
test ch,ah
jnz .no_pixel_4
or ch,ah
mov [edi + 4],al
mov [ebx + 4],ch
.no_pixel_4:
mov al,[esi + 1]
and al,dh
jz .no_pixel_1
mov ch,[ebx + 1]
test ch,ah
jnz .no_pixel_1
or ch,ah
mov [edi + 1],al
mov [ebx + 1],ch
.no_pixel_1:
mov al,[esi + 5]
and al,dh
jz .no_pixel_5
mov ch,[ebx + 5]
test ch,ah
jnz .no_pixel_5
or ch,ah
mov [edi + 5],al
mov [ebx + 5],ch
.no_pixel_5:
mov al,[esi + 2]
and al,dh
jz .no_pixel_2
mov ch,[ebx + 2]
test ch,ah
jnz .no_pixel_2
or ch,ah
mov [edi + 2],al
mov [ebx + 2],ch
.no_pixel_2:
mov al,[esi + 6]
and al,dh
jz .no_pixel_6
mov ch,[ebx + 6]
test ch,ah
jnz .no_pixel_6
or ch,ah
mov [edi + 6],al
mov [ebx + 6],ch
.no_pixel_6:
mov al,[esi + 3]
and al,dh
jz .no_pixel_3
mov ch,[ebx + 3]
test ch,ah
jnz .no_pixel_3
or ch,ah
mov [edi + 3],al
mov [ebx + 3],ch
.no_pixel_3:
mov al,[esi + 7]
and al,dh
jz .no_pixel_7
mov ch,[ebx + 7]
test ch,ah
jnz .no_pixel_7
or ch,ah
mov [edi + 7],al
mov [ebx + 7],ch
.no_pixel_7:
pop ecx
pop ebx
dec cl
jnz near .next_tile
jmp .check_tag
ALIGNC
.flip_x:
mov al,[esi + 7]
and al,dh
jz .no_pixel_0_x
mov ch,[ebx]
test ch,ah
jnz .no_pixel_0_x
or ch,ah
mov [edi],al
mov [ebx],ch
.no_pixel_0_x:
mov al,[esi + 3]
and al,dh
jz .no_pixel_4_x
mov ch,[ebx + 4]
test ch,ah
jnz .no_pixel_4_x
or ch,ah
mov [edi + 4],al
mov [ebx + 4],ch
.no_pixel_4_x:
mov al,[esi + 6]
and al,dh
jz .no_pixel_1_x
mov ch,[ebx + 1]
test ch,ah
jnz .no_pixel_1_x
or ch,ah
mov [edi + 1],al
mov [ebx + 1],ch
.no_pixel_1_x:
mov al,[esi + 2]
and al,dh
jz .no_pixel_5_x
mov ch,[ebx + 5]
test ch,ah
jnz .no_pixel_5_x
or ch,ah
mov [edi + 5],al
mov [ebx + 5],ch
.no_pixel_5_x:
mov al,[esi + 5]
and al,dh
jz .no_pixel_2_x
mov ch,[ebx + 2]
test ch,ah
jnz .no_pixel_2_x
or ch,ah
mov [edi + 2],al
mov [ebx + 2],ch
.no_pixel_2_x:
mov al,[esi + 1]
and al,dh
jz .no_pixel_6_x
mov ch,[ebx + 6]
test ch,ah
jnz .no_pixel_6_x
or ch,ah
mov [edi + 6],al
mov [ebx + 6],ch
.no_pixel_6_x:
mov al,[esi + 4]
and al,dh
jz .no_pixel_3_x
mov ch,[ebx + 3]
test ch,ah
jnz .no_pixel_3_x
or ch,ah
mov [edi + 3],al
mov [ebx + 3],ch
.no_pixel_3_x:
mov al,[esi]
and al,dh
jz .no_pixel_7_x
mov ch,[ebx + 7]
test ch,ah
jnz .no_pixel_7_x
or ch,ah
mov [edi + 7],al
mov [ebx + 7],ch
.no_pixel_7_x:
.check_tile_count:
pop ecx
pop ebx
dec cl
jnz near .next_tile
.check_tag:
sub ebx,34*4
add cl,ch
mov ch,0
jnz near .next_tile
rol byte [Pixel_Allocation_Tag],1
jnc .check_line_count
; Clear pixel allocation tag table
mov edi,DisplayZ+8
xor eax,eax
mov ecx,256/32
call Do_Clear
.check_line_count:
dec dword [PS_Lines]
jnz near Plot_Sprites.next_line
.done:
add esp,byte PS_Local_Bytes
ret
ALIGNC
.bad_priority_plot:
mov dh,ah
shr esi,15
add edi,ebx ; X-adjust
shl dh,3 ; Palette
and esi,byte ~7 ; Tile line # address
or dh,0x8F
add esi,_TileCache4
add ebx,DisplayZ+8
add ah,ah
mov ah,[Pixel_Allocation_Tag]
js near .bad_priority_flip_x
mov al,[esi]
and al,dh
jz .no_pixel_bp_0
mov ch,[ebx]
or ch,ah
mov [ebx],ch
.no_pixel_bp_0:
mov al,[esi + 4]
and al,dh
jz .no_pixel_bp_4
mov ch,[ebx + 4]
or ch,ah
mov [ebx + 4],ch
.no_pixel_bp_4:
mov al,[esi + 1]
and al,dh
jz .no_pixel_bp_1
mov ch,[ebx + 1]
or ch,ah
mov [ebx + 1],ch
.no_pixel_bp_1:
mov al,[esi + 5]
and al,dh
jz .no_pixel_bp_5
mov ch,[ebx + 5]
or ch,ah
mov [ebx + 5],ch
.no_pixel_bp_5:
mov al,[esi + 2]
and al,dh
jz .no_pixel_bp_2
mov ch,[ebx + 2]
or ch,ah
mov [ebx + 2],ch
.no_pixel_bp_2:
mov al,[esi + 6]
and al,dh
jz .no_pixel_bp_6
mov ch,[ebx + 6]
or ch,ah
mov [ebx + 6],ch
.no_pixel_bp_6:
mov al,[esi + 3]
and al,dh
jz .no_pixel_bp_3
mov ch,[ebx + 3]
or ch,ah
mov [ebx + 3],ch
.no_pixel_bp_3:
mov al,[esi + 7]
and al,dh
jz .no_pixel_bp_7
mov ch,[ebx + 7]
or ch,ah
mov [ebx + 7],ch
.no_pixel_bp_7:
pop ecx
pop ebx
dec cl
jnz near .next_tile
jmp .check_tag
ALIGNC
.bad_priority_flip_x:
mov al,[esi + 7]
and al,dh
jz .no_pixel_bp_0_x
mov ch,[ebx]
or ch,ah
mov [ebx],ch
.no_pixel_bp_0_x:
mov al,[esi + 3]
and al,dh
jz .no_pixel_bp_4_x
mov ch,[ebx + 4]
or ch,ah
mov [ebx + 4],ch
.no_pixel_bp_4_x:
mov al,[esi + 6]
and al,dh
jz .no_pixel_bp_1_x
mov ch,[ebx + 1]
or ch,ah
mov [ebx + 1],ch
.no_pixel_bp_1_x:
mov al,[esi + 2]
and al,dh
jz .no_pixel_bp_5_x
mov ch,[ebx + 5]
or ch,ah
mov [ebx + 5],ch
.no_pixel_bp_5_x:
mov al,[esi + 5]
and al,dh
jz .no_pixel_bp_2_x
mov ch,[ebx + 2]
or ch,ah
mov [ebx + 2],ch
.no_pixel_bp_2_x:
mov al,[esi + 1]
and al,dh
jz .no_pixel_bp_6_x
mov ch,[ebx + 6]
or ch,ah
mov [ebx + 6],ch
.no_pixel_bp_6_x:
mov al,[esi + 4]
and al,dh
jz .no_pixel_bp_3_x
mov ch,[ebx + 3]
or ch,ah
mov [ebx + 3],ch
.no_pixel_bp_3_x:
mov al,[esi]
and al,dh
jz .no_pixel_bp_7_x
mov ch,[ebx + 7]
or ch,ah
mov [ebx + 7],ch
.no_pixel_bp_7_x:
pop ecx
pop ebx
dec cl
jnz near .next_tile
jmp .check_tag
; In the precalculator...
; eax = (internal)
; edx = (internal)
; ebx = current sprite (internal)
; cl = sprite flags bit count (internal)
; ebp = (internal)
; esi = sprite tables (internal)
; edi = sprite flags (internal)
;
%macro Add_Sprite_Y_Check 0
; Check if sprite entirely offscreen
mov al,dl
mov bl,[esi+1]
shl al,3
cmp bl,239
jb %%on_screen_y
add al,bl
dec al
jns %%on_screen_y
%%off_screen:
ret
%%on_screen_y:
%endmacro
%define ASXP_Lines_Left esp
;%define ASXP_X_Position esp+1 ;planned but not yet used
%define ASXP_Total_Size esp+4
%define ASXP_Visible_Width esp+5
%define OAM_TIME_OVER (1 << 7)
%define OAM_RANGE_OVER (1 << 6)
;tiles are added to ring buffers in right-to-left order
;ring buffers handle time-overflow by spilling excess tiles
;starts with first+max-count or last (first+count-1) tile
ALIGNC
EXPORT_C Add_Sprite_X_Positive
Add_Sprite_X_Positive:
;visible width count in dh, total count in dl
;esi = OAM 512 byte subtable address
;edi = OAM 32 byte subtable address
;ecx, esi, edi must be preserved!
Add_Sprite_Y_Check
; Save visible width and total size, these won't be changing
push ecx
push edi
push edx
; Get base tile #
mov ebp,[esi]
xor ebx,ebx
shr ebp,16
mov al,[esi+3]
mov cl,dl
and eax,byte 0x40
jnz .flip_x
; If tile is X-flipped, set to last tile # instead of first
mov dl,dh
dec ebp
jmp short .adjust_x_done
.flip_x:
; Set to first tile #
sub dl,dh
.adjust_x_done:
shl cl,3 ; Convert tiles to lines
and edx,byte 0x7F ; Size will always be less than this
add ebp,edx
mov bl,[esi+1] ; Get first line
shl ebp,3
push ecx
;ebx = current line
;cl = lines left
;dl = ASXP_Total_Size * 8
;ebp = tile # (leftmost as shown)
.next_line:
;If line never displayed, ignore line
cmp bl,239
jnb near .check_count
; Check OBJ count for line (if 32, set range over and ignore OBJ)
mov al,[OAM_Count+ebx*2]
cmp al,32
jb .range_okay
or byte [OAM_TimeRange+ebx],OAM_RANGE_OVER
jmp .check_count
.range_okay:
; else increment OBJ count
inc al
mov ch,[ASXP_Visible_Width]
mov [OAM_Count+ebx*2],al
mov dl,[ASXP_Total_Size]
mov dh,[esi+3]
shl dl,3
push ebp
add dh,dh
jc .flip_y
; line in sprite = line size - lines left
sub dl,[ASXP_Lines_Left+4]
jnz .adjust_y
jmp short .adjust_y_done
.flip_y:
; line in sprite = lines left - 1
mov dl,[ASXP_Lines_Left+4]
dec dl
jz .adjust_y_done
.adjust_y:
; convert tile # in ebp to tile line #
mov edi,edx
and edx,byte 0x38 ; tile offset
shl edx,3+1 ; (lines / 8) * 16 tiles * 8 lines
and edi,byte 7 ; line in tile
add ebp,edx
add ebp,edi
.adjust_y_done:
; setup pointer to line descriptors to be used, update line count
; edx = OAM_Lines+(ebx*34+((byte) [OAM_Count+ebx*2+1]))*4
mov edx,ebx
push ebx
shl edx,5
add edx,ebx
xor eax,eax
add edx,ebx
; Check tile count for line (if 34, set time over and ignore tiles)
; If will be over 34, set time over and adjust width
mov al,[OAM_Count+ebx*2+1]
mov cl,0
cmp al,34
je .time_over
add edx,eax
add al,ch
cmp al,34
ja .time_first_over
mov [OAM_Count+ebx*2+1],al
jmp .time_okay
.time_first_over:
mov byte [OAM_Count+ebx*2+1],34
sub al,34
jmp short .list_wrap
.time_over:
mov al,[OAM_Tail+ebx]
add edx,eax
add al,ch
mov [OAM_Tail+ebx],al
sub al,34
jb .time_over_detect
je .fixup_head
.list_wrap:
mov cl,al
.fixup_head:
mov [OAM_Tail+ebx],al
sub ch,al
.time_over_detect:
or byte [OAM_TimeRange+ebx],OAM_TIME_OVER
.time_okay:
mov al,[OAM_Count+ebx*2+1]
lea edx,[OAM_Lines+edx*4]
push eax
; Determine if sprite sequence on line contains higher priorities
; after lower priorities, set new last-tile-for-priority
mov ah,[OAM_Lowest_Priority+ebx]
mov al,[esi+3]
and al,0x30
cmp ah,al
jb .low_before_high
mov [OAM_Lowest_Priority+ebx],al
jmp short .priority_done
.low_before_high:
mov byte [OAM_Low_Before_High+ebx],0xFF
.priority_done:
; setup flags & X position of tiles in line descriptor
and eax,0x30
shr eax,4
lea ebx,[OAM_Count_Priority+eax+ebx*4]
pop eax
mov [ebx],al
mov ah,0xFE
mov bh,[esi+3]
and ah,bh
and bh,0x40
mov al,[esi]
jz .Flip_None
;cl = lines left (including current)
;ch = tile count for line, dl = OBJ size in pixel width/height
;ebp = tile # (not Y adjusted)
;esi = OAM pointer
;sprite line = cl - 1 (Y flip), dl - cl (no Y flip)
call Add_Sprite_X_Positive_Flip_X
pop ebx
pop ebp
jmp short .check_count
.Flip_None:
call Add_Sprite_X_Positive_Flip_None
pop ebx
.check_count_time_over:
pop ebp
.check_count:
inc bl
dec byte [ASXP_Lines_Left]
jnz near .next_line
pop eax
pop edx
pop edi
pop ecx
ret
;starts with first or last (first+max-1) tile
ALIGNC
EXPORT_C Add_Sprite_X_Negative
Add_Sprite_X_Negative:
;visible width count in dh, total count in dl
;esi = OAM 512 byte subtable address
;edi = OAM 32 byte subtable address
;ecx, esi, edi must be preserved!
Add_Sprite_Y_Check
; Save visible width and total size, these won't be changing
push ecx
push edi
push edx
; Get base tile #
mov ebp,[esi]
xor ebx,ebx
shr ebp,16
mov al,[esi+3]
mov cl,dl
and eax,byte 0x40
jnz .flip_x
; If tile is X-flipped, set to last tile # instead of first
and edx,byte 0x7F ; Size will always be less than this
dec ebp
add ebp,edx
.flip_x:
shl cl,3 ; Convert tiles to lines
mov bl,[esi+1] ; Get first line
shl ebp,3 ; tile # * 8 lines
push ecx
;ebx = current line
;cl = lines left
;dl = ASXP_Total_Size * 8
;ebp = tile # (leftmost as shown)
.next_line:
;If line never displayed, ignore line
cmp bl,239
jnb near .check_count
; Check OBJ count for line (if 32, set range over and ignore OBJ)
mov al,[OAM_Count+ebx*2]
cmp al,32
jb .range_okay
or byte [OAM_TimeRange+ebx],OAM_RANGE_OVER
jmp .check_count
.range_okay:
; increment OBJ count
inc al
mov ch,[ASXP_Visible_Width]
mov [OAM_Count+ebx*2],al
mov dl,[ASXP_Total_Size]
mov dh,[esi+3]
shl dl,3
push ebp
add dh,dh
jc .flip_y
; line in sprite = line size - lines left
sub dl,[ASXP_Lines_Left+4]
jnz .adjust_y
jmp short .adjust_y_done
.flip_y:
; line in sprite = lines left - 1
mov dl,[ASXP_Lines_Left+4]
dec dl
jz .adjust_y_done
.adjust_y:
; convert tile # in ebp to tile line #
mov edi,edx
and edx,byte 0x38 ; tile offset
shl edx,3+1 ; (lines / 8) * 16 tiles * 8 lines
and edi,byte 7 ; line in tile
add ebp,edx
add ebp,edi
.adjust_y_done:
; setup pointer to line descriptors to be used, update line count
; edx = OAM_Lines+(ebx*34+((byte) [OAM_Count+ebx*2+1]))*4
mov edx,ebx
push ebx
shl edx,5
add edx,ebx
xor eax,eax
add edx,ebx
; Check tile count for line (if 34, set time over and ignore tiles)
; If will be over 34, set time over and adjust width
mov al,[OAM_Count+ebx*2+1]
mov cl,0
cmp al,34
je .time_over
add edx,eax
add al,ch
cmp al,34
ja .time_first_over
mov [OAM_Count+ebx*2+1],al
jmp short .time_okay
.time_first_over:
mov byte [OAM_Count+ebx*2+1],34
sub al,34
jmp short .list_wrap
.time_over:
mov al,[OAM_Tail+ebx]
add edx,eax
add al,ch
mov [OAM_Tail+ebx],al
sub al,34
jb .time_over_detect
je .fixup_head
.list_wrap:
mov cl,al
.fixup_head:
mov [OAM_Tail+ebx],al
sub ch,al
.time_over_detect:
or byte [OAM_TimeRange+ebx],OAM_TIME_OVER
.time_okay:
mov al,[OAM_Count+ebx*2+1]
lea edx,[OAM_Lines+edx*4]
push eax
; Determine if sprite sequence on line contains higher priorities
; after lower priorities, set new last-tile-for-priority
mov ah,[OAM_Lowest_Priority+ebx]
mov al,[esi+3]
and al,0x30
cmp ah,al
jb .low_before_high
mov [OAM_Lowest_Priority+ebx],al
jmp short .priority_done
.low_before_high:
mov byte [OAM_Low_Before_High+ebx],0xFF
.priority_done:
; setup flags & X position of tiles in line descriptor
and eax,0x30
shr eax,4
lea ebx,[OAM_Count_Priority+eax+ebx*4]
pop eax
mov [ebx],al
mov ah,[ASXP_Total_Size+8]
mov bh,[esi+3]
dec ah
or bh,1
mov al,[esi]
shl ah,3
add al,ah
mov ah,bh
jnc .no_carry
xor ah,1
.no_carry:
and bh,0x40
jz .Flip_None
;cl = lines left (including current)
;ch = tile count for line, dl = OBJ size in pixel width/height
;ebp = tile # (not Y adjusted)
;esi = OAM pointer
;sprite line = cl - 1 (Y flip), dl - cl (no Y flip)
call Add_Sprite_X_Negative_Flip_X
pop ebx
pop ebp
jmp short .check_count
.Flip_None:
call Add_Sprite_X_Negative_Flip_None
pop ebx
.check_count_time_over:
pop ebp
.check_count:
inc bl
dec byte [ASXP_Lines_Left]
jnz near .next_line
pop eax
pop edx
pop edi
pop ecx
ret
ALIGNC
;ebp += (count - 1) * 8; al += (count - 1) * 8;
EXPORT_C Add_Sprite_X_Positive_Flip_None
Add_Sprite_X_Positive_Flip_None:
xor ebx,ebx
mov bl,ch
add bl,cl
dec bl
shl ebx,3
add al,bl
;add ebp,ebx
.next_tile:
; compute tile line #'s and store line descriptors
push ebp
and ebp,511*8+7
cmp ebp,256*8
mov edi,[_OBNAME]
jae .name
mov edi,[_OBBASE]
.name:
and eax,0x3FFFF
add ebp,edi
shl ebp,18
or eax,ebp
pop ebp
;eax = prepared tile descriptor
mov [edx],eax
sub al,byte 8
sub ebp,byte 8
add edx,byte 4
dec ch
jnz .next_tile
sub edx,34*4
add ch,cl
mov cl,0
jnz .next_tile
ret
ALIGNC
;ebp -= (count - 1) * 8; al += (count - 1) * 8;
EXPORT_C Add_Sprite_X_Positive_Flip_X
Add_Sprite_X_Positive_Flip_X:
xor ebx,ebx
mov bl,ch
add bl,cl
dec bl
shl ebx,3
add al,bl
;sub ebp,ebx
.next_tile:
; compute tile line #'s and store line descriptors
push ebp
and ebp,511*8+7
cmp ebp,256*8
mov edi,[_OBNAME]
jae .name
mov edi,[_OBBASE]
.name:
and eax,0x3FFFF
add ebp,edi
shl ebp,18
or eax,ebp
pop ebp
;eax = prepared tile descriptor
mov [edx],eax
sub al,byte 8
add ebp,byte 8
add edx,byte 4
dec ch
jnz .next_tile
sub edx,34*4
add ch,cl
mov cl,0
jnz .next_tile
ret
ALIGNC
;ebp += (count - 1) * 8; al += (count - 1) * 8;
;if al < ((count - 1) * 8) ah ^= 1;
EXPORT_C Add_Sprite_X_Negative_Flip_None
Add_Sprite_X_Negative_Flip_None:
.next_tile:
; compute tile line #'s and store line descriptors
push ebp
and ebp,511*8+7
cmp ebp,256*8
mov edi,[_OBNAME]
jae .name
mov edi,[_OBBASE]
.name:
and eax,0x3FFFF
add ebp,edi
shl ebp,18
or eax,ebp
pop ebp
;eax = prepared tile descriptor
mov [edx],eax
sub al,byte 8
jnc .no_carry
xor ah,1
.no_carry:
sub ebp,byte 8
add edx,byte 4
dec ch
jnz .next_tile
sub edx,34*4
add ch,cl
mov cl,0
jnz .next_tile
ret
ALIGNC
;ebp -= (count - 1) * 8; al += (count - 1) * 8;
;if al < ((count - 1) * 8) ah ^= 1;
EXPORT_C Add_Sprite_X_Negative_Flip_X
Add_Sprite_X_Negative_Flip_X:
.next_tile:
; compute tile line #'s and store line descriptors
push ebp
and ebp,511*8+7
cmp ebp,256*8
mov edi,[_OBNAME]
jae .name
mov edi,[_OBBASE]
.name:
and eax,0x3FFFF
add ebp,edi
shl ebp,18
or eax,ebp
pop ebp
;eax = prepared tile descriptor
mov [edx],eax
sub al,byte 8
jnc .no_carry
xor ah,1
.no_carry:
add ebp,byte 8
add edx,byte 4
dec ch
jnz .next_tile
sub edx,34*4
add ch,cl
mov cl,0
jnz .next_tile
ret
ALIGNC
EXPORT_C Recache_OAM
EXPORT Recache_OAM
%ifdef Profile_Recache_OAM
inc dword [_Calls_Recache_OAM]
%endif
; Clear count tables
mov edi,OAM_Count_Priority
xor eax,eax
mov ecx,240*4/32
call Do_Clear
mov edi,OAM_Count
mov ecx,240*2/32
call Do_Clear
mov edi,OAM_Tail
mov ecx,224/32
call Do_Clear
mov bl,[edi]
mov [edi],eax
mov [edi+4],eax
mov [edi+8],eax
mov [edi+12],eax
mov edi,OAM_TimeRange
mov ecx,224/32
call Do_Clear
mov bl,[edi]
mov [edi],eax
mov [edi+4],eax
mov [edi+8],eax
mov [edi+12],eax
mov edi,OAM_Low_Before_High
mov ecx,224/32
call Do_Clear
mov bl,[edi]
mov [edi],eax
mov [edi+4],eax
mov [edi+8],eax
mov [edi+12],eax
mov edi,OAM_Lowest_Priority
mov ecx,224/32
dec eax
call Do_Clear
mov bl,[edi]
mov [edi],eax
mov [edi+4],eax
mov [edi+8],eax
mov [edi+12],eax
mov al,[_OBSEL]
;and al,0xE0 ;???
cmp al,0xC0 ; If invalid size selected, no sprites to plot
jnb near .done
mov ecx,[_HiSpriteCnt1] ; Size of first set and bit offset
mov eax,[_HiSpriteCnt2]
mov esi,[_HiSpriteAddr]
test ah,ah
mov edi,[_HiSpriteBits]
jz .is_zero
push byte 0
.is_zero:
push eax
jmp short .next_sprite
; Tile attribute word: YXPP CCCT TTTT TTTT
; Where:
; Y, X are vertical/horizontal flip
; P is priority
; C is color palette selector
; T is tile number
;
; AAAA AAAA AAAA AAAA YXPP CCCX XXXX XXXX
; A - OAM sprite line-in-tile address
; YXPP CCC - bits 1-7 of OAM attribute word
; X - X position
ALIGNC
.next_sprite:
;esi = OAM 512 byte subtable address OAM+(0-511)
;edi = OAM 32 byte subtable address OAM+(512-543)
;cl = variable shift count for OAM subtable (1, 3, 5, 7)
;ch = count left in current decode pass (1-128)
;Perform clipping & determine visible range
;Get size
mov ah,[edi]
mov edx,[Sprite_Size_Current]
;Shift size bit into carry
shl ah,cl
jnc .do_small
;dl = sprite size in tiles
;dh = lower limit for negative X position
shr edx,16
mov al,[esi] ; ax = X
;Get X sign bit
shr ah,8
jnc .do_positive
;Determine if sprite is entirely offscreen
cmp al,dh
jnb .do_negative
jmp short .off_screen
ALIGNC
.do_small:
;dl = sprite size in tiles
;dh = lower limit for negative X position
mov al,[esi] ; ax = X
;Get X sign bit
shr ah,8
jnc .do_positive
;Determine if sprite is entirely offscreen
cmp al,dh
jb .off_screen
.do_negative:
;# tiles -= 32 - ((X + 7) & FF) / 8 for -X
add eax,byte 7
mov dh,dl
shr eax,3
xor al,0xFF
add al,33
sub dh,al
;visible width count in dh, total count in dl
;esi = OAM 512 byte subtable address
;edi = OAM 32 byte subtable address
;ecx, esi, edi must be preserved!
call Add_Sprite_X_Negative
jmp short .off_screen
.do_positive:
;# tiles <= 32 - (X & 0xFF) / 8 for +X
shr al,3
mov dh,dl
xor al,0xFF
add al,33
;max visible count in al
;dl = min(max count(al), total count(dl))
sub al,dl
sbb bl,bl
and bl,al
add dh,bl
;visible width count in dh, total count in dl
;esi = OAM 512 byte subtable address
;edi = OAM 32 byte subtable address
;ecx, esi, edi must be preserved!
call Add_Sprite_X_Positive
jmp short .off_screen
ALIGNC
.off_screen:
sub cl,2 ; Adjust variable shift count
jnc .bits_left
and cl,7
inc edi ; Goto next sprite X-MSB/size byte
.bits_left:
add esi,byte 4 ; Goto next sprite XYAA dword
dec ch ; Check sprite list count
jnz near .next_sprite
mov ch,[1+esp] ; Get next list count
sub esi,0x200 ; Adjust pointers to sprite 127 (if we're at end)
sub edi,byte 0x20
add esp,byte 4
test ch,ch ; Continue if valid list
jnz near .next_sprite
.done:
mov byte [Redo_OAM],0
ret
ALIGNC
EXPORT_C Reset_Sprites
pusha
; Set eax to 0, as we're setting most everything to 0...
xor eax,eax
; Reset sprite renderer vars
mov byte [_HiSprite],0
mov dword [_HiSpriteAddr],_OAM+0x000
mov dword [_HiSpriteBits],_OAM+0x200
mov dword [_HiSpriteCnt1],0x8007
mov dword [_HiSpriteCnt2],0x0007
mov byte [Redo_OAM],-1
mov byte [sprsize_small],1
mov byte [sprsize_large],2
mov byte [sprlim_small],-7
mov byte [sprlim_large],-15
mov byte [Pixel_Allocation_Tag],1
mov [_OBBASE],eax
mov [_OBNAME],eax
; Reset sprite port vars
mov [_OAMAddress],eax
mov [_OAMAddress_VBL],eax
mov [OAMHigh],al
mov [OAM_Write_Low],al
mov [SPRLatch],al
mov [_OBSEL],al
; Clear pixel allocation tag table
mov edi,DisplayZ+8
xor eax,eax
mov ecx,256/32
call Do_Clear
popa
ret
ALIGNC
EXPORT SNES_R2138 ; OAMDATAREAD
mov edi,[_OAMAddress]
mov al,[OAMHigh]
cmp edi,0x110 ; if address >= 0x110
jb .no_mirror
and edi,0xFF ; ignore MSB
.no_mirror:
xor al,1
mov [OAMHigh],al
jnz .read_low
mov al,[_OAM+edi*2+1]
mov edi,[_OAMAddress]
inc edi
and edi,0x1FF ; address is 9 bits
mov [_OAMAddress],edi
ret
ALIGNC
.read_low:
mov al,[_OAM+edi*2]
ret
ALIGNC
EXPORT SNES_W2101 ; OBSEL
cmp [_OBSEL],al
je near .no_change
SyncRender ;*
push ebx
mov [_OBSEL],al ; Get our copy of this
mov ebx,eax
shr eax,5
and eax,byte 7
push edx
mov edx,[Sprite_Size_Table+eax*4]
mov [Sprite_Size_Current],edx
pop edx
mov eax,ebx
mov edi,eax
and ebx,byte 3<<3 ; Name address 0000 0000 000n n000
and edi,byte 3 ; Base address 0000 0000 0000 0xbb
;shl ebx,10 ; Name is either 0x0000,0x1000,0x2000,0x3000 words
;shl edi,14 ; Base is either 0x0000,0x2000,0x4000,0x6000 words
shl ebx,8 ; Name is either 0x0000,0x0800,0x1000,0x1800 lines
shl edi,12 ; Base is either 0x0000,0x1000,0x2000,0x3000 lines
add ebx,edi
;and edi,0xFFFF
;and ebx,0xFFFF
and edi,0x3FFF
and ebx,0x3FFF
;add edi,edi ; Convert to offsets into tile cache
;add ebx,ebx
;add edi,_TileCache4
;add ebx,_TileCache4
mov [_OBBASE],edi
mov [_OBNAME],ebx
pop ebx
mov byte [Redo_OAM],-1
.no_change:
ret
ALIGNC
EXPORT SNES_W2102 ; OAMADDL
SyncRender ;*
push ebx
xor ebx,ebx
mov [OAMHigh],bh
mov bl,al
mov [_OAMAddress],ebx
mov [_OAMAddress_VBL],ebx
cmp byte [SPRLatch],0
;if priority latch is off, do we ignore write or reset priority?
jz near .priority_no_change
.priority_update:
shr ebx,byte 1 ;convert address to OBJ #
cmp [_HiSprite],bl
je .priority_no_change
mov [_HiSprite],bl
shl ebx,2
mov byte [Redo_OAM],-1
;sub ebx,byte 4
;and ebx,0x1FC
mov [_HiSpriteAddr],ebx
shr ebx,4
add ebx,0x200
mov [_HiSpriteBits],ebx
mov bl,[_HiSprite]
mov bh,128
mov [_HiSpriteCnt2+1],bl ;
sub bh,bl
mov [_HiSpriteCnt1+1],bh ;
mov bh,7
add bl,bl
sub bh,bl
and bh,7
mov [_HiSpriteCnt1],bh ;
mov ebx,_OAM
add [_HiSpriteAddr],ebx
add [_HiSpriteBits],ebx
.priority_no_change:
pop ebx
ret
ALIGNC
EXPORT SNES_W2103 ; OAMADDH
SyncRender ;*
push ebx
xor ebx,ebx
mov [OAMHigh],bl
mov bh,1
and bh,al ; Only want MSB of address
mov [_OAMAddress_VBL+1],bh
mov [_OAMAddress+1],bh
test al,al ; Is priority rotation bit set?
js .latch_priority_rotation
mov [SPRLatch],bl
pop ebx
ret
ALIGNC
.latch_priority_rotation:
mov byte [SPRLatch],-1
mov ebx,[_OAMAddress]
jmp SNES_W2102.priority_update
ALIGNC
EXPORT SNES_W2104 ; OAMDATA
push ebx
cmp byte [HVBJOY], 0
js .in_vblank
cmp byte [_INIDISP], 0
jns near .no_increment ;.no_change
.in_vblank:
xor ebx,ebx
mov edi,[_OAMAddress]
mov bl,[OAMHigh]
cmp edi,0x100 ; if address < 0x100, word access
jb .no_mirror
cmp edi,0x110 ; if address >= 0x110, ignore MSB and use word access
jb .byte_access
and edi,0xFF ; ignore MSB
.no_mirror:
xor ebx,byte 1
mov [OAMHigh],bl
jnz .write_low
mov bl,[OAM_Write_Low]
mov bh,al
cmp [_OAM+edi*2],bx
je .no_change
push edi
SyncRender ;*
pop edi
mov byte [Redo_OAM],-1
mov [_OAM+edi*2],bx
.no_change:
mov edi,[_OAMAddress]
inc edi
and edi,0x1FF ; address is 9 bits
mov [_OAMAddress],edi
.no_increment:
.ignore_write:
pop ebx
ret
ALIGNC
.write_low:
mov [OAM_Write_Low],al
pop ebx
ret
ALIGNC
.byte_access:
cmp [_OAM+edi*2+ebx],al
je .ba_no_change
push edi
SyncRender ;*
pop edi
mov byte [Redo_OAM],-1
mov [_OAM+edi*2+ebx],al
.ba_no_change:
xor ebx,byte 1
mov [OAMHigh],bl
jnz .no_increment
inc edi
and edi,0x1FF ; address is 9 bits
mov [_OAMAddress],edi
pop ebx
ret
section .text
ALIGNC
section .data
ALIGND
section .bss
ALIGNB