www.pudn.com > sn068s.zip > MODE7.ASM
; Mode 7 matrix rendering / hardware port emulation.
%include "misc.ni"
%include "sprites.ni"
%include "screen.ni"
%include "PPU.ni"
section .text
EXPORT_C mode7_start
;%define old_sprites
extern Ready_Line_Render,BaseDestPtr,_SNES_Screen8
extern _MosaicLine
extern Tile_priority_bit
section .data
ALIGND
EXPORT M7_Handler_Table
dd M7_REPEAT,M7_REPEAT_X,M7_REPEAT_Y,M7_REPEAT_XY
dd M7_CLIP,M7_CLIP_X,M7_CLIP_Y,M7_CLIP_XY
dd M7_CLIP,M7_CLIP_X,M7_CLIP_Y,M7_CLIP_XY
dd M7_CHAR0,M7_CHAR0_X,M7_CHAR0_Y,M7_CHAR0_XY
dd M7P_REPEAT,M7P_REPEAT_X,M7P_REPEAT_Y,M7P_REPEAT_XY
dd M7P_CLIP,M7P_CLIP_X,M7P_CLIP_Y,M7P_CLIP_XY
dd M7P_CLIP,M7P_CLIP_X,M7P_CLIP_Y,M7P_CLIP_XY
dd M7P_CHAR0,M7P_CHAR0_X,M7P_CHAR0_Y,M7P_CHAR0_XY
section .bss
ALIGNB
EXPORT Mode7_AHX,skipl ; M7A * (BG1VOFS - M7X) + (M7X << 8)
EXPORT Mode7_VY ,skipl ; BG1VOFS - M7Y
EXPORT Mode7_CHXY,skipl ; M7C * (BG1HOFS - M7X) + (M7Y << 8)
EXPORT_C M7A ,skipl
EXPORT_C M7B ,skipl
EXPORT_C M7C ,skipl
EXPORT_C M7D ,skipl
EXPORT_C M7X_13 ,skipl
EXPORT_C M7Y_13 ,skipl
EXPORT_C M7H_13 ,skipl
EXPORT_C M7V_13 ,skipl
EXPORT_C M7X ,skipl
EXPORT_C M7Y ,skipl
MPYL: skipb ; Mode 7 multiplication result: low byte
MPYM: skipb ; Mode 7 multiplication result: middle byte
MPYH: skipb ; Mode 7 multiplication result: high byte
skipb
EXPORT M7_Handler,skipl
EXPORT M7_Handler_EXTBG,skipl
EXPORT_C M7SEL,skipb ; ab0000yx ab=mode 7 repetition info,y=flip vertical,x=flip horizontal
EXPORT Redo_M7,skipb ; vhyxdcba
M7_Used: skipb
M7_Unused: skipb
Redo_16x8: skipb
%define SM7_Local_Bytes 16
%define SM7_Current_Line esp+12
%define SM7_BaseDestPtr esp+8
%define SM7_Lines esp+4
%define SM7_Layers esp
section .text
ALIGNC
EXPORT SCREEN_MODE_7
push ebx
push edi
mov al,[_TM]
mov ah,[_TS]
extern _Layer_Disable_Mask
and al,[_Layer_Disable_Mask]
and ah,[_Layer_Disable_Mask]
push ebp
push eax
mov eax,[SM7_Layers]
test al,3
jnz .background_main
;Hack in sub screen mode 7 if it's enabled
and ah,3
jz .background_off
or al,ah
mov [SM7_Layers],al
.background_main:
and eax,byte 1
jnz near .background_on
; we only reach here when bit 0 clear, bit 1 set
test byte [_SETINI],0x40
jnz near .background_on
.background_off:
mov edi,[_SNES_Screen8] ; (256+16)*(240+1) framebuffer
; Clear the framebuffer
mov ebx,[SM7_BaseDestPtr]
xor eax,eax
add edi,ebx
mov ebp,[SM7_Lines]
mov ecx,256/4
push es
mov edx,ds
mov es,edx
cld
.background_off_clear_loop:
; Load area to clear into cache
mov bl,[edi+0]
mov dl,[edi+16]
mov bl,[edi+32]
mov dl,[edi+48]
mov bl,[edi+64]
mov dl,[edi+80]
mov bl,[edi+96]
mov dl,[edi+112]
mov bl,[edi+128]
mov dl,[edi+144]
mov bl,[edi+160]
mov dl,[edi+176]
mov bl,[edi+192]
mov dl,[edi+208]
mov bl,[edi+224]
mov dl,[edi+240]
rep stosd
add edi,byte GfxBufferLineSlack ; Point screen to next line
dec ebp
mov ecx,256/4
jnz .background_off_clear_loop
pop es
test dword [SM7_Layers],0x1010
jz .no_sprites
mov ebx,[SM7_Current_Line]
mov edi,[SM7_BaseDestPtr]
mov ebp,[SM7_Lines]
;inc ebx
mov dl,0x00
call Plot_Sprites
mov ebx,[SM7_Current_Line]
mov edi,[SM7_BaseDestPtr]
mov ebp,[SM7_Lines]
;inc ebx
mov dl,0x10
call Plot_Sprites
mov ebx,[SM7_Current_Line]
mov edi,[SM7_BaseDestPtr]
mov ebp,[SM7_Lines]
;inc ebx
mov dl,0x20
call Plot_Sprites
mov ebx,[SM7_Current_Line]
mov edi,[SM7_BaseDestPtr]
mov ebp,[SM7_Lines]
;inc ebx
mov dl,0x30
call Plot_Sprites
.no_sprites:
add esp,byte SM7_Local_Bytes
ret
;%1 = priority (0 = low, 1 = low/none, 2 = high)
%macro Render_Mode7_Background 1
%if %1 == 2
test byte [_SETINI],0x40
jz %%no_plot
test byte [SM7_Layers],2
jz %%no_plot
mov al,[M7_Unused]
test al,al
jz %%no_plot
%else
%if %1 == 0
test byte [SM7_Layers],1
jnz %%no_plot
%endif
%if %1 == 1
test byte [SM7_Layers],1
jz %%no_plot
%endif
mov al,0
mov [M7_Used],al
mov [M7_Unused],al
%endif
mov edi,[SM7_BaseDestPtr]
add edi,[_SNES_Screen8]
%if %1 == 2
mov byte [Tile_priority_bit],0
%else
mov byte [Tile_priority_bit],0x80
%endif
; Handle vertical mosaic
mov ebx,[SM7_Current_Line]
mov al,[MosaicBG1]
test al,al
jz %%no_mosaic
mov eax,[Mosaic_Size_Select]
mov bl,[_MosaicLine+ebx+eax]
%%no_mosaic:
; End vertical mosaic
mov ebp,256 ; Horizontal Count
%if %1 == 1
test byte [_SETINI],0x40
jz %%no_extbg
test byte [SM7_Layers],2
jz %%no_extbg
call dword [M7_Handler_EXTBG]
jmp short %%no_plot
%%no_extbg:
call dword [M7_Handler]
%else
call dword [M7_Handler_EXTBG]
%endif
%%no_plot:
%endmacro
.background_on:
call Recalc_Mode7
jmp short .first_line
.next_line:
inc dword [SM7_Current_Line]
.first_line:
mov edi,[_SNES_Screen8] ; (256+16)*(240+1) framebuffer
; Clear the framebuffer
mov ebx,[SM7_BaseDestPtr]
xor eax,eax
add edi,ebx
%if 0
mov ebp,[SM7_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 dl,[edi+16]
mov bl,[edi+32]
mov dl,[edi+48]
mov bl,[edi+64]
mov dl,[edi+80]
mov bl,[edi+96]
mov dl,[edi+112]
mov bl,[edi+128]
mov dl,[edi+144]
mov bl,[edi+160]
mov dl,[edi+176]
mov bl,[edi+192]
mov dl,[edi+208]
mov bl,[edi+224]
mov dl,[edi+240]
rep stosd
%if 0
add edi,byte GfxBufferLineSlack ; Point screen to next line
dec ebp
mov ecx,256/4
jnz .clear_loop
%endif
pop es
Render_Mode7_Background 0
test dword [SM7_Layers],0x1010
jz .no_sprites_0
mov ebx,[SM7_Current_Line]
mov edi,[SM7_BaseDestPtr]
mov ebp,1
;inc ebx
mov dl,0x00
call Plot_Sprites
.no_sprites_0:
Render_Mode7_Background 1
test dword [SM7_Layers],0x1010
jz .no_sprites_1
mov ebx,[SM7_Current_Line]
mov edi,[SM7_BaseDestPtr]
mov ebp,1
;inc ebx
mov dl,0x10
call Plot_Sprites
.no_sprites_1:
Render_Mode7_Background 2
test dword [SM7_Layers],0x1010
jz .no_sprites_23
mov ebx,[SM7_Current_Line]
mov edi,[SM7_BaseDestPtr]
mov ebp,1
;inc ebx
mov dl,0x20
call Plot_Sprites
mov ebx,[SM7_Current_Line]
mov edi,[SM7_BaseDestPtr]
mov ebp,1
;inc ebx
mov dl,0x30
call Plot_Sprites
.no_sprites_23:
mov edi,[SM7_BaseDestPtr]
add edi,GfxBufferLinePitch
dec dword [SM7_Lines]
mov [SM7_BaseDestPtr],edi ; Point screen to next line
jnz near SCREEN_MODE_7.next_line
add esp,byte SM7_Local_Bytes
ret
Recalc_Mode7:
mov dl,[Redo_M7]
test dl,0xF5 ; Need to do any recalculating?
jz near .end_recalc
test dl,0xA0 ; Recalculate V or Y?
jz .end_recalc_vy
mov eax,[_BG1VOFS]
shl eax,(32 - 13)
mov edi,[_M7Y]
shl edi,(32 - 13)
sar eax,(32 - 13)
sar edi,(32 - 13)
;mov [_M7V_13],eax
sub eax,edi
mov [_M7Y_13],edi
mov [Mode7_VY],eax ;(V - Y)
.end_recalc_vy:
test dl,0x75 ; Recalculate A, C, H, X, or Y?
jz .end_recalc
;test dl,0x50 ; Recalculate H or X?
;jnz .recalc_hx
mov eax,[_BG1HOFS]
shl eax,(32 - 13)
mov edi,[_M7X]
shl edi,(32 - 13)
sar eax,(32 - 13)
sar edi,(32 - 13)
;mov [_M7H_13],eax
sub eax,edi
;mov [_M7X_13],edi
test dl,0x51 ; Recalculate A, H, or X?
jz .recalc_c
push eax
;mov ebx,[_M7X_13]
mov ebx,edi
imul eax,[_M7A]
shl ebx,8
add eax,ebx
mov [Mode7_AHX],eax ;A * (H - X) + (X << 8)
pop eax
test dl,0x74 ; Recalculate C, H, X, or Y?
jz .end_recalc
.recalc_c:
mov ebx,[_M7Y_13]
imul eax,[_M7C]
shl ebx,8
add eax,ebx
mov [Mode7_CHXY],eax ;C * (H - X) + (Y << 8)
.end_recalc:
mov byte [Redo_M7],0 ; Done with recalculating
ret
;%1 = xflip, %3 = label, %4 = ylabel
%macro M7_PROLOG 3
ALIGNC
%3:
xor ebx,-1
add ebx,262 ;*
%2:
%if %1
add edi,255
%endif
mov eax,ebx
mov ecx,[Mode7_VY]
add eax,ecx
add ebx,ecx
imul eax,[_M7B]
imul ebx,[_M7D]
mov ecx,[Mode7_AHX]
mov edx,[Mode7_CHXY]
add eax,ecx
add ebx,edx
mov ecx,[_M7A]
mov edx,[_M7C]
jmp short .first_pixel
%endmacro
;%1 = xflip, %2 = priority, %3 = label, %4 = ylabel
%macro M7__REPEAT 4
%%return:
ret
M7_PROLOG %1,%3,%4
.pixel_loop:
add eax,ecx
add ebx,edx
.first_pixel:
push eax
push ebx
;before:
;eax = X offset
;ebx = Y offset
;during:
;ecx = (eax >> 8) & 7
;edx = (ebx >> 8) & 7
;ecx = ecx + edx * 8
;eax = (eax >> 11) & (0x3F8 >> 3)
;ebx = (ebx >> 3) & (0x3F8 << 5)
;eax = ebx + eax * 2
;after:
;eax = X tile
;ebx = Y tile
;ecx = X offset in tile
;edx = Y offset in tile
mov ecx,eax
mov edx,ebx
; Convert Screen X,Y location to SNES Pic location
; Assumes %ax is X coord 0-1023, %bx is Y 0-1023
shr ecx,7
and eax,0x3F8 << 8
shr edx,4
and ebx,0x3F8 << 8
shr eax,10 ; Get Tile Position (in 128 by 128 map)
and ecx,byte 7 << 1 ; Get pixel shift within tile
shr ebx,3 ; Tile Position*128 (words) cos thats width of map
and edx,byte 7 << 4
add ecx,edx ; Add X+Y offsets together
mov dl,[_VRAM+eax+ebx] ; Got Tile Number
shl edx,7 ; Get offset to tile data
add edx,ecx ; Add X+Y offset
%if %2
mov cl,[Tile_priority_bit]
mov al,[_VRAM+edx+1]
xor cl,al
and al,0x7F
%else
mov al,[_VRAM+edx+1]
test al,al
%endif
jz .no_pixel
%if %2
test cl,cl
jns .bad_priority
%endif
mov [edi],al
%if %2
mov [M7_Used],al
%endif
.no_pixel:
pop ebx
pop eax
mov ecx,[_M7A]
mov edx,[_M7C]
%if %1
dec edi
%else
inc edi
%endif
dec ebp
jnz .pixel_loop
ret
%if %2
ALIGNC
.bad_priority:
mov al,0xFF
pop ebx
mov [M7_Unused],al
pop eax
mov ecx,[_M7A]
mov edx,[_M7C]
%if %1
dec edi
%else
inc edi
%endif
dec ebp
jnz near .pixel_loop
ret
%endif
%endmacro
; New for v0.16, tile 0 repeat support
;%1 = xflip, %2 = priority, %3 = label, %4 = ylabel
%macro M7__CHAR0 4
%%return:
ret
M7_PROLOG %1,%3,%4
.pixel_loop:
add eax,ecx
add ebx,edx
.first_pixel:
cmp eax,0x3FFFF ; If outside screen range we use tile 0
ja near .use_tile_0
cmp ebx,0x3FFFF
ja near .use_tile_0
; Convert Screen X,Y location to SNES Pic location
; Assumes %ax is X coord 0-1023, %bx is Y 0-1023
push eax
push ebx
mov ecx,eax
mov edx,ebx
; Convert Screen X,Y location to SNES Pic location
; Assumes %ax is X coord 0-1023, %bx is Y 0-1023
shr ecx,7
and eax,0x3F8 << 8
shr edx,4
and ebx,0x3F8 << 8
shr eax,10 ; Get Tile Position (in 128 by 128 map)
and ecx,byte 7 << 1 ; Get pixel shift within tile
shr ebx,3 ; Tile Position*128 (words) cos thats width of map
and edx,byte 7 << 4
add ecx,edx ; Add X+Y offsets together
mov dl,[_VRAM+eax+ebx] ; Got Tile Number
shl edx,7 ; Get offset to tile data
add edx,ecx ; Add X+Y offset
%if %2
mov cl,[Tile_priority_bit]
mov al,[_VRAM+edx+1]
xor cl,al
and al,0x7F
%else
mov al,[_VRAM+edx+1]
test al,al
%endif
jz .no_pixel
%if %2
test cl,cl
jns .bad_priority
%endif
mov [edi],al
%if %2
mov [M7_Used],al
%endif
.no_pixel:
pop ebx
pop eax
mov ecx,[_M7A]
mov edx,[_M7C]
%if %1
dec edi
%else
inc edi
%endif
dec ebp
jnz .pixel_loop
ret
%if %2
ALIGNC
.bad_priority:
mov al,0xFF
pop ebx
mov [M7_Unused],al
pop eax
mov ecx,[_M7A]
mov edx,[_M7C]
%if %1
dec edi
%else
inc edi
%endif
dec ebp
jnz near .pixel_loop
ret
%endif
ALIGNC
.use_tile_0:
mov ecx,eax
mov edx,ebx
; Convert Screen X,Y location to SNES Pic location
; Assumes %ax is X coord 0-1023, %bx is Y 0-1023
shr ecx,7
shr edx,4
and ecx,byte 7 << 1 ; Get pixel shift within tile
and edx,byte 7 << 4
add edx,ecx ; Add X+Y offsets together
%if %2
mov cl,[Tile_priority_bit]
mov dl,[_VRAM+edx+1]
xor cl,dl
and dl,0x7F
%else
mov dl,[_VRAM+edx+1]
test dl,dl
%endif
jz .no_pixel_2
%if %2
test cl,cl
jns .bad_priority
%endif
mov [edi],dl
%if %2
mov [M7_Used],dl
%endif
.no_pixel_2:
mov ecx,[_M7A]
mov edx,[_M7C]
%if %1
dec edi
%else
inc edi
%endif
dec ebp
jnz near .pixel_loop
ret
%endmacro
;%1 = xflip, %2 = priority, %3 = label, %4 = ylabel
%macro M7__CLIP 4
%%return:
ret
M7_PROLOG %1,%3,%4
.pixel_loop:
add eax,ecx
add ebx,edx
.first_pixel:
cmp eax,0x3FFFF ; If outside screen range we simply skip the pixel
ja near .pixel_covered
cmp ebx,0x3FFFF
ja near .pixel_covered
; Convert Screen X,Y location to SNES Pic location
; Assumes %ax is X coord 0-1023, %bx is Y 0-1023
push eax
push ebx
mov ecx,eax
mov edx,ebx
; Convert Screen X,Y location to SNES Pic location
; Assumes %ax is X coord 0-1023, %bx is Y 0-1023
shr ecx,7
and eax,0x3F8 << 8
shr edx,4
and ebx,0x3F8 << 8
shr eax,10 ; Get Tile Position (in 128 by 128 map)
and ecx,byte 7 << 1 ; Get pixel shift within tile
shr ebx,3 ; Tile Position*128 (words) cos thats width of map
and edx,byte 7 << 4
add ecx,edx ; Add X+Y offsets together
mov dl,[_VRAM+eax+ebx] ; Got Tile Number
shl edx,7 ; Get offset to tile data
add edx,ecx ; Add X+Y offset
%if %2
mov cl,[Tile_priority_bit]
mov al,[_VRAM+edx+1]
xor cl,al
and al,0x7F
%else
mov al,[_VRAM+edx+1]
test al,al
%endif
jz .no_pixel
%if %2
test cl,cl
jns .bad_priority
%endif
mov [edi],al
%if %2
mov [M7_Used],al
%endif
.no_pixel:
pop ebx
pop eax
mov ecx,[_M7A]
mov edx,[_M7C]
%if %2 == 0
.pixel_covered:
%endif
%if %1
dec edi
%else
inc edi
%endif
dec ebp
jnz .pixel_loop
ret
%if %2
ALIGNC
.bad_priority:
mov al,0xFF
pop ebx
mov [M7_Unused],al
pop eax
mov ecx,[_M7A]
mov edx,[_M7C]
.pixel_covered:
%if %1
dec edi
%else
inc edi
%endif
dec ebp
jnz near .pixel_loop
ret
%endif
%endmacro
M7__REPEAT 0, 0, M7_REPEAT, M7_REPEAT_Y
M7__REPEAT 1, 0, M7_REPEAT_X, M7_REPEAT_XY
M7__CHAR0 0, 0, M7_CHAR0, M7_CHAR0_Y
M7__CHAR0 1, 0, M7_CHAR0_X, M7_CHAR0_XY
M7__CLIP 0, 0, M7_CLIP, M7_CLIP_Y
M7__CLIP 1, 0, M7_CLIP_X, M7_CLIP_XY
M7__REPEAT 0, 1, M7P_REPEAT, M7P_REPEAT_Y
M7__REPEAT 1, 1, M7P_REPEAT_X, M7P_REPEAT_XY
M7__CHAR0 0, 1, M7P_CHAR0, M7P_CHAR0_Y
M7__CHAR0 1, 1, M7P_CHAR0_X, M7P_CHAR0_XY
M7__CLIP 0, 1, M7P_CLIP, M7P_CLIP_Y
M7__CLIP 1, 1, M7P_CLIP_X, M7P_CLIP_XY
ALIGNC
EXPORT_C Reset_Mode_7
; Set eax to 0, as we're setting most everything to 0...
xor eax,eax
mov [_M7SEL],al
mov byte [Redo_M7],0xFF
mov dword [M7_Handler],M7_REPEAT
mov dword [M7_Handler_EXTBG],M7P_REPEAT
mov byte [Redo_16x8],0
mov [MPYL],eax
mov [_M7A],eax
mov [_M7B],eax
mov [_M7C],eax
mov [_M7D],eax
mov [_M7X_13],eax
mov [_M7Y_13],eax
;mov [_M7H_13],eax
;mov [_M7V_13],eax
mov [_M7X],eax
mov [_M7Y],eax
ret
Do_16x8_Multiply:
push ebx
movsx ebx,byte [_M7B+1]
mov byte [Redo_16x8],0
imul ebx,[_M7A] ; I think signed is used makes most sense!
mov [MPYL],ebx
mov al,[edi]
pop ebx
ret
ALIGNC
EXPORT SNES_R2134 ; MPYL
mov edi,MPYL
cmp byte [Redo_16x8],0
jnz Do_16x8_Multiply
mov al,[edi]
ret
ALIGNC
EXPORT SNES_R2135 ; MPYM
mov edi,MPYM
cmp byte [Redo_16x8],0
jnz Do_16x8_Multiply
mov al,[edi]
ret
ALIGNC
EXPORT SNES_R2136 ; MPYH
mov edi,MPYH
cmp byte [Redo_16x8],0
jnz Do_16x8_Multiply
mov al,[edi]
ret
ALIGNC
EXPORT SNES_W211A ; M7SEL ; New for 0.12
cmp al,[_M7SEL]
je .no_change
SyncRender ;*
push ebx
push eax
mov bl,al
mov [_M7SEL],al
shr ebx,4
and eax,3
and ebx,0x0C
add ebx,eax
mov eax,[M7_Handler_Table+ebx*4]
mov [M7_Handler],eax
mov ebx,[M7_Handler_Table+ebx*4+16*4]
pop eax
mov [M7_Handler_EXTBG],ebx
pop ebx
.no_change:
ret
ALIGNC
EXPORT SNES_W211B ; M7A
SyncRender ;*M7
; Used for matrix render and 16-bit M7A * 8-bit = 24-bit multiply
push eax
mov ah,al
mov al,[_M7A+1]
cwde
mov [_M7A],eax
mov al,0x01 ; Recalculate A
or [Redo_M7],al
mov byte [Redo_16x8],-1
pop eax
ret
ALIGNC
EXPORT SNES_W211C ; M7B
SyncRender ;*M7
; Used for matrix render and 16-bit * 8-bit M7B high byte = 24-bit multiply
push eax
mov ah,al
mov al,[_M7B+1]
cwde
mov [_M7B],eax
mov al,0x02 ; Recalculate B
or [Redo_M7],al
mov byte [Redo_16x8],-1
pop eax
ret
ALIGNC
EXPORT SNES_W211D ; M7C
SyncRender ;*M7
push eax
mov ah,al
mov al,[_M7C+1]
cwde
mov [_M7C],eax
mov al,0x04 ; Recalculate C
or [Redo_M7],al
pop eax
ret
ALIGNC
EXPORT SNES_W211E ; M7D
SyncRender ;*M7
push eax
mov ah,al
mov al,[_M7D+1]
cwde
mov [_M7D],eax
mov al,0x08 ; Recalculate D
or [Redo_M7],al
pop eax
ret
ALIGNC
EXPORT SNES_W211F ; M7X
SyncRender ;*M7
push eax
mov ah,al
mov al,[_M7X+1]
mov [_M7X],ax
;shl eax,0x13
;sar eax,0x13
;mov [_M7X_13],eax
mov al,0x10 ; Recalculate X
or [Redo_M7],al
pop eax
ret
ALIGNC
EXPORT SNES_W2120 ; M7Y
SyncRender ;*M7
push eax
mov ah,al
mov al,[_M7Y+1]
mov [_M7Y],ax
;shl eax,0x13
;sar eax,0x13
;mov [_M7Y_13],eax
mov al,0x20 ; Recalculate Y
or [Redo_M7],al
pop eax
ret
section .text
ALIGNC
section .data
ALIGND
section .bss
ALIGNB