aboutsummaryrefslogtreecommitdiff
path: root/examples/graphics/tilesasm/drawtiles.S
diff options
context:
space:
mode:
authorXavier Del Campo Romero <xavi92@disroot.org>2025-07-05 02:34:11 +0200
committerXavier Del Campo Romero <xavi92@disroot.org>2025-07-05 02:34:11 +0200
commitbeb76e4dd362374b8f42cd971d394bba1074cd8d (patch)
tree3ea4cc342737afb9225c01160c92647ba66c78bd /examples/graphics/tilesasm/drawtiles.S
parent5d9aa2d3dfc7d6e51c2eb942ab4cdbae5571a40a (diff)
downloadpsn00bsdk-fix-include.tar.gz
Replace .include with #includefix-include
For some reason, both mipsel-unknown-elf-gcc 8.2.0 and mipsel-non-elf 15.1.0 were unable to resolve .include assembler directives. As a workaround, it is still possible to use the preprocessor, and therefore the usual #include preprocessor directive. However, this requires the assembly files to use the uppercase .S file extension.
Diffstat (limited to 'examples/graphics/tilesasm/drawtiles.S')
-rw-r--r--examples/graphics/tilesasm/drawtiles.S272
1 files changed, 272 insertions, 0 deletions
diff --git a/examples/graphics/tilesasm/drawtiles.S b/examples/graphics/tilesasm/drawtiles.S
new file mode 100644
index 0000000..15a0707
--- /dev/null
+++ b/examples/graphics/tilesasm/drawtiles.S
@@ -0,0 +1,272 @@
+#
+# LibPSn00b Example Programs
+#
+# Drawing Tile-maps with Assembler Routines
+# 2022 Meido-Tek Productions / PSn00bSDK Project
+#
+# Example by John "Lameguy" Wilbert Villamor (Lameguy64)
+#
+# This file contains the assembler routine DrawTiles which can be called from
+# a C or C++ compiled module. The routine makes use of constants and assembler
+# macros written in the GNU GAS syntax.
+#
+# Assembler routines called from C-language modules can freely use registers
+# $v0-$v1, $at, $a0-$a3, $t0-$t9 without preserving through stack. Registers
+# $s0-$s9, $gp and $fp must be preserved through stack before returning and
+# registers $k0-$k1 should not be used for obvious reasons (kernel registers).
+# $sp and $ra is used as stack pointer and return address respectively.
+# $0 or $zero is constantly zero.
+#
+# A C caller always passes arguments as 32-bit values on registers $a0 to $a3
+# regardless of the data type specified in the function's C declaration.
+# Additional arguments are stored in the stack 16 bytes relative to the stack
+# pointer ($sp). To get around the stack being modified in different parts of
+# a larger routine the stack pointer would be copied to the frame pointer ($fp)
+# and the previous value of the frame pointer would be pushed into stack. This
+# way additional arguments may be read anywhere starting from $fp+20 instead.
+#
+.set noreorder # Disable GAS' annoying nop insertion
+
+.equ db, 1 # Constants for "emulating" SNASM style structs
+.equ dh, 2
+.equ dw, 4
+
+#
+# TILEDEF struct
+#
+ rs=0 # rs is used to emulate SNASM style structs
+.equ TILEDEF_uv , rs # Tile texture coordinate
+ rs=rs+dh
+.equ TILEDEF_clut , rs # Tile CLUT
+ rs=rs+dh
+.equ TILEDEF_pad , rs # Padding
+ rs=rs+dh
+.equ TILEDEF_tpage , rs # Tile tpage
+ rs=rs+dh
+.equ TILEDEF_len , rs # Entry length
+
+#
+# TILEINFO struct (to use as offsets)
+#
+ rs=0
+.equ TILEINFO_window_x , rs # \
+ rs=rs+dh # - Window coordinates
+.equ TILEINFO_window_y , rs # /
+ rs=rs+dh
+.equ TILEINFO_window_w , rs # \
+ rs=rs+dh # - Window size
+.equ TILEINFO_window_h , rs # /
+ rs=rs+dh
+.equ TILEINFO_tiles , rs # Pointer to TILEDEF entries
+ rs=rs+dw
+.equ TILEINFO_mapdata , rs # Pointer to map data
+ rs=rs+dw
+.equ TILEINFO_map_w , rs # Map width in tile units
+ rs=rs+dh
+.equ TILEINFO_map_h , rs # Map height in tile units
+ rs=rs+dh
+
+#
+# TILEPKT struct
+#
+ rs=0
+.equ TILEPKT_tag , rs # Primitive tag
+ rs=rs+dw
+.equ TILEPKT_tpage , rs # tpage packet
+ rs=rs+dw
+.equ TILEPKT_rgbc , rs # Tile color
+ rs=rs+dw
+.equ TILEPKT_x , rs # Tile screen coordinates
+ rs=rs+dh
+.equ TILEPKT_y , rs
+ rs=rs+dh
+.equ TILEPKT_uv , rs # Tile texture coordinates
+ rs=rs+dh
+.equ TILEPKT_clut , rs # Tile CLUT
+ rs=rs+dh
+.equ TILEPKT_len , rs # Packet length
+
+# addprim Macro
+#
+# Registers a primitive to a ordering table entry.
+#
+# Arguments:
+# ot - Register name of pointer to ordering table entry
+# pri - Pointer to a primitive packet
+# len - Size of packet in long words (specify as 0xnn00, ie. 0x2000)
+#
+# Destroys:
+# at, v0, v1
+#
+.macro addprim ot,pri,len
+ .set noat
+ lw $v0, 0(\ot) # Get OT entry
+ lui $at, 0x00ff # Mask out the packet length field
+ or $at, 0xffff
+ and $v0, $at
+ lui $v1, \len # Merge packet length
+ or $v1, $v0
+ sw $v1, 0(\pri) # Store updated OT entry to packet
+ lw $v0, 0(\ot) # Get OT entry
+ and \pri, $at # Mask out last 8-bits of packet address
+ lui $at, 0xff00 # Mask out OT entry's address
+ and $v0, $at
+ or $v0, \pri # Merge packet address to OT entry
+ sw $v0, 0(\ot) # Store updated OT entry
+ .set at
+.endm
+
+#
+# Start of text section
+#
+.section .text
+
+# DrawTiles Function
+#
+# Renders a tilemap by generating TILEPKT primitives (combined SPRT_16 and
+# DR_TPAGE primitives) and registering it to the specified ordering table.
+# The drawing region, tile definitions and the tilemap are specified through
+# a TILEINFO struct.
+#
+# C Declaration:
+# extern u_char *DrawTiles(int scroll_x, int scroll_y,
+# TILEINFO *info, long *ot, u_char *pri);
+#
+# Arguments:
+# scroll_x - X scrolling offset of tile-map
+# scroll_y - Y scrolling offset of tile-map
+# info - Pointer to a TILEINFO struct
+# ot - Pointer to a ordering table entry
+# pri - Pointer to next primitive (placed in stack)
+#
+# Returns:
+# New next primitive pointer value.
+#
+.global DrawTiles # Declare symbol as global
+.type DrawTiles, @function # Declare it as a function
+DrawTiles: # Symbol label of function
+
+ addiu $sp, -4 # Push frame pointer (fp) to stack
+ sw $fp, 0($sp)
+ move $fp, $sp # Copy stack pointer (sp) to fp
+
+ # Register reference:
+ #
+ # t0 - Packet address
+ # t1 - Map data address
+ # t2 - Tile X offset
+ # t3 - Tile Y offset
+ # t4 - Tile X coordinate backup
+ # t5 - Tile X loop counter
+ # t6 - Number of tiles to sort per row
+ # t7 - Number of tile rows to sort
+
+ lhu $t6, TILEINFO_window_w($a2) # Calculate size of window in tiles
+ lhu $t7, TILEINFO_window_h($a2)
+ addi $t6, 15 # So the result will be rounded-up
+ addi $t7, 15
+ srl $t6, 4 # Effectively divide by 16
+ srl $t7, 4
+
+ lw $t0, 20($sp) # Obtain next primitive pointer
+
+ srl $t2, $a0, 4 # Compute map offset in tile units
+ srl $t3, $a1, 4
+
+ bgez $a0, .Lno_neg_clip_X # Negative X clip test
+ sub $v0, $0 , $a0
+ move $t2, $0 # Force tile offset to zero
+ add $v0, 15 # Reduce number of tile columns
+ srl $v0, 4
+ sub $t6, $v0
+.Lno_neg_clip_X:
+ lhu $v1, TILEINFO_map_w($a2) # Positive X clip test
+ add $v0, $t2, $t6
+ addi $v0, 1
+ blt $v0, $v1, .Lno_pos_clip_X
+ nop
+ sub $v0, $v1 # Compute how many tiles to clip
+ sub $t6, $v0 # Reduce number of tile columns
+.Lno_pos_clip_X:
+ bgez $a1, .Lno_neg_clip_Y # Negative Y clip test
+ sub $v0, $0 , $a1
+ move $t3, $0
+ add $v0, 15
+ srl $v0, 4
+ sub $t7, $v0
+.Lno_neg_clip_Y:
+ lhu $v1, TILEINFO_map_h($a2) # Positive Y clip test
+ add $v0, $t3, $t7
+ addi $v0, 1
+ blt $v0, $v1, .Lno_pos_clip_Y
+ nop
+ sub $v0, $v1
+ sub $t7, $v0
+.Lno_pos_clip_Y:
+ bltz $t6, .Lno_draw # Exit when no tiles to draw
+ nop
+ bltz $t7, .Lno_draw
+ nop
+
+ lh $v0, TILEINFO_window_x($a2) # Compute pixel coordinates for
+ sub $a0, $v0, $a0 # tiles based on the scroll offset
+ sll $v0, $t2, 4
+ add $a0, $v0
+ lh $v0, TILEINFO_window_y($a2)
+ sub $a1, $v0, $a1
+ sll $v0, $t3, 4
+ add $a1, $v0
+ move $t4, $a0
+ sll $t2, 1
+
+.Lloop_y: # Begin of tile row loop
+ lhu $v0, TILEINFO_map_w($a2) # Get width of tilemap
+ move $t5, $t6 # n tiles to draw row
+ mult $t3, $v0 # Multiply Y offset by map width
+ lw $t1, TILEINFO_mapdata($a2) # Get tilemap address
+ nop
+ addu $t1, $t2 # Add X offset to address
+ mflo $v0 # Get Y offset result
+ sll $v0, 1 # Multiply by two
+ addu $t1, $v0 # Add to tile address
+
+.Lloop_x: # Begin of tile column loop
+ lhu $v1, 0($t1) # Load tile index
+ addiu $t1, 2 # Advance to next tile
+ beq $v1, 0xFFFF, .Lskip_tile # Skip tile if index is 0xFFFF
+ nop
+ lw $v0, TILEINFO_tiles($a2) # Get pointer to TILEDEF entries
+ sll $v1, 3 # Multiply by 8 (size of TILEDEFs)
+ addu $v1, $v0 # Adjust to tiledefs pointer
+ lw $v0, TILEDEF_uv($v1) # Obtain UV+CLUT
+ lhu $v1, TILEDEF_tpage($v1) # Obtain tpage
+ sw $v0, TILEPKT_uv($t0) # Start constructing packet
+ lui $v0, 0xE100 # tpage packet code
+ or $v0, $v1 # Merge tpage bits
+ sw $v0, TILEPKT_tpage($t0)
+ sh $a0, TILEPKT_x($t0) # Set tile screen coords
+ sh $a1, TILEPKT_y($t0)
+ li $v0, 0x7C7F7F7F # Packet code and color
+ sw $v0, TILEPKT_rgbc($t0)
+ addprim $a3, $t0, 0x0400 # Register to OT
+ addiu $t0, TILEPKT_len # Advance packet pointer
+.Lskip_tile:
+ addi $t5, -1 # Decrement and continue iterating
+ bgez $t5, .Lloop_x # if non-zero
+ addiu $a0, 16 # Advance X tile coordinate
+
+ move $a0, $t4 # Restore tile X coordinate
+ addi $t3, 1 # Increment Y offset
+ addi $t7, -1 # Decrement and continue iterating
+ bgez $t7, .Lloop_y # if non-zero
+ addiu $a1, 16 # Advance Y tile coordinate
+
+.Lno_draw:
+
+ move $v0, $t0 # Set packet pointer as return value
+
+ lw $fp, 0($sp) # Restore frame pointer and return
+ jr $ra
+ addiu $sp, 4
+ # DrawTiles
+ \ No newline at end of file