libgfx: Merge in very early release.
authorTyler J. Stachecki <stachecki.tyler@gmail.com>
Sat, 30 Dec 2017 16:27:49 +0000 (11:27 -0500)
committerTyler J. Stachecki <stachecki.tyler@gmail.com>
Sat, 30 Dec 2017 16:27:49 +0000 (11:27 -0500)
This additional library (that gets linked in with the
cart's ELF, rather than libn64) will provide accelerated
graphics functionality.

It currently provides a ucode which can render RDP triangle
commands from 3 vertices in the ucode's vertex buffer.

None of this would would have been possible without krom's
and GreaseMonkey's invaluable help in reverse enginering
"how to triangle" (from below). krom also provided a sample
program which I used extensively while designing the RSP
ucode.

https://forums.cen64.com/viewtopic.php?f=14&t=237

Signed-off-by: Tyler J. Stachecki <stachecki.tyler@gmail.com>
12 files changed:
.gitignore
helloworld/Makefile
libgfx/Makefile [new file with mode: 0644]
libgfx/include/libgfx/init.h [new file with mode: 0644]
libgfx/include/libgfx/rspbuf.h [new file with mode: 0644]
libgfx/include/libgfx/vertex.h [new file with mode: 0644]
libgfx/src/init.c [new file with mode: 0644]
libgfx/src/rspbuf.c [new file with mode: 0644]
libgfx/ucodes/gfx.rsp [new file with mode: 0644]
libgfx/ucodes/gfx.rsps [new file with mode: 0644]
libn64/Makefile
threadtest/Makefile

index ffeb28d..0b53f72 100644 (file)
@@ -23,6 +23,7 @@
 *.map
 *.o
 *.obj
+*.rsppch
 *.tsk
 *.z64
 
index 444ce50..76435a4 100644 (file)
@@ -31,7 +31,7 @@ RSPASM = $(call FIXPATH,$(CURDIR)/../tools/bin/rspasm)
 
 # priv_include is not "advertised", but this is just a demo...
 CFLAGS = -Wall -Wextra -pedantic -std=c99 -Wno-main \
-       -I../libn64/include -I../libn64 -I../libn64/priv_include -I.
+       -I../libn64/include -I../libgfx/include -I../libn64/priv_include -I.
 
 OPTFLAGS = -Os -march=vr4300 -mtune=vr4300 -mabi=eabi -mgp32 -mlong32 \
        -flto -ffat-lto-objects -ffunction-sections -fdata-sections \
@@ -68,11 +68,11 @@ $(ROM_NAME).z64: $(ROM_NAME).elf
        @$(OBJCOPY) -O binary $< $@
        @$(CHECKSUM) $(call FIXPATH,../libn64/header.bin) $@
 
-$(ROM_NAME).elf: libn64 $(OBJFILES) filesystem.obj
+$(ROM_NAME).elf: libn64 libgfx $(OBJFILES) filesystem.obj
        @echo $(call FIXPATH,"Building: $(ROM_NAME)/$@")
        @$(CC) $(CFLAGS) $(OPTFLAGS) -Wl,-Map=$(ROM_NAME).map -nostdlib \
                -T$(call FIXPATH,../libn64/rom.ld) -o $@ $(OBJFILES) filesystem.obj \
-               -L$(call FIXPATH,../libn64) -ln64
+               -L$(call FIXPATH,../libn64) -ln64 -L$(call FIXPATH,../libgfx) -lgfx
 
 #
 # Filesystem build target.
@@ -104,6 +104,10 @@ filesystem/%.tsk: ucodes/%.rsp
 libn64:
        @$(MAKE) -sC $(call FIXPATH,../libn64)
 
+.PHONY: libgfx
+libgfx:
+       @$(MAKE) -sC $(call FIXPATH,../libgfx)
+
 #
 # Clean project target.
 #
diff --git a/libgfx/Makefile b/libgfx/Makefile
new file mode 100644 (file)
index 0000000..88143ee
--- /dev/null
@@ -0,0 +1,82 @@
+#
+# libgfx/Makefile: Makefile for libgfx.
+#
+# n64chain: A (free) open-source N64 development toolchain.
+# Copyright 2014-16 Tyler J. Stachecki <stachecki.tyler@gmail.com>
+#
+# This file is subject to the terms and conditions defined in
+# 'LICENSE', which is part of this source code package.
+#
+
+ifdef SystemRoot
+FIXPATH = $(subst /,\,$1)
+RM = del /Q
+else
+FIXPATH = $1
+RM = rm -f
+endif
+
+AS = $(call FIXPATH,$(CURDIR)/../tools/bin/mips64-elf-as)
+AR = $(call FIXPATH,$(CURDIR)/../tools/bin/mips64-elf-gcc-ar)
+CC = $(call FIXPATH,$(CURDIR)/../tools/bin/mips64-elf-gcc)
+CPP = $(call FIXPATH,$(CURDIR)/../tools/bin/mips64-elf-cpp)
+
+RSPASM = $(call FIXPATH,$(CURDIR)/../tools/bin/rspasm)
+
+CFLAGS = -Wall -Wextra -pedantic -std=c99 -I. -Iinclude -I../libn64/include
+OPTFLAGS = -Os -march=vr4300 -mtune=vr4300 -mabi=eabi -mgp32 -mlong32 \
+       -flto -ffat-lto-objects -ffunction-sections -fdata-sections \
+       -G4 -mno-extern-sdata -mgpopt -mfix4300 -mbranch-likely \
+       -mno-check-zero-division -mno-memcpy
+
+ASMFILES = $(call FIXPATH,\
+)
+
+CFILES = $(call FIXPATH,\
+       src/init.c \
+       src/rspbuf.c \
+)
+
+UCODES = $(call FIXPATH,\
+       ucodes/gfx.rsp \
+)
+
+OBJFILES = \
+       $(CFILES:.c=.o) \
+       $(ASMFILES:.s=.o) \
+       $(UCODES:.rsp=.o)
+
+DEPFILES = $(OBJFILES:.o=.d)
+
+#
+# Primary targets.
+#
+libgfx.a: $(OBJFILES)
+       @echo $(call FIXPATH,"Building: libgfx/$@")
+       @$(AR) rcs $@ $^
+
+#
+# Generic compilation/assembly targets.
+#
+%.o: %.s
+       @echo $(call FIXPATH,"Assembling: libgfx/$<")
+       @$(CC) -x assembler-with-cpp $(CFLAGS) $(OPTFLAGS) -MMD -c $< -o $@
+
+%.o: %.c
+       @echo $(call FIXPATH,"Compiling: libgfx/$<")
+       @$(CC) $(CFLAGS) $(OPTFLAGS) -MMD -c $< -o $@
+
+%.o: %.rsp %.rsps
+       @echo $(call FIXPATH,"Assembling: $(ROM_NAME)/$@")
+       @$(CPP) -E -I../libn64/ucodes -Iucodes $< > $(<:.rsp=.rsppch)
+       @$(RSPASM) $(<:.rsp=.rsppch) -o $(<:.rsp=.bin)
+       @$(CC) -x assembler-with-cpp $(CFLAGS) $(OPTFLAGS) -MMD -c $(<:.rsp=.rsps) -o $@
+
+#
+# Clean project target.
+#
+.PHONY: clean
+clean:
+       @echo "Cleaning libgfx..."
+       @$(RM) libgfx.a $(DEPFILES) $(OBJFILES)
+
diff --git a/libgfx/include/libgfx/init.h b/libgfx/include/libgfx/init.h
new file mode 100644 (file)
index 0000000..533bc25
--- /dev/null
@@ -0,0 +1,28 @@
+//
+// libgfx/include/libgfx/init.h: Graphics initialization.
+//
+// n64chain: A (free) open-source N64 development toolchain.
+// Copyright 2014-16 Tyler J. Stachecki <stachecki.tyler@gmail.com>
+//
+// This file is subject to the terms and conditions defined in
+// 'LICENSE', which is part of this source code package.
+//
+
+#ifndef LIBGFX_INCLUDE_LIBGFX_INIT_H
+#define LIBGFX_INCLUDE_LIBGFX_INIT_H
+
+#include <rcp/sp.h>
+#include <stdint.h>
+
+// Initialize the libgfx components.
+void libgfx_init(void);
+
+// Run the microcode and wait for it to complete.
+static inline void libgfx_run(void) {
+  libn64_rsp_set_pc(0x04001000);
+  libn64_rsp_set_status(RSP_STATUS_CLEAR_HALT | RSP_STATUS_CLEAR_BROKE);
+  while ((libn64_rsp_get_status() & 0x3) != 0x3);
+}
+
+#endif
+
diff --git a/libgfx/include/libgfx/rspbuf.h b/libgfx/include/libgfx/rspbuf.h
new file mode 100644 (file)
index 0000000..a3514f6
--- /dev/null
@@ -0,0 +1,57 @@
+//
+// libgfx/include/libgfx/rspbuf.h: RSP buffer.
+//
+// n64chain: A (free) open-source N64 development toolchain.
+// Copyright 2014-16 Tyler J. Stachecki <stachecki.tyler@gmail.com>
+//
+// This file is subject to the terms and conditions defined in
+// 'LICENSE', which is part of this source code package.
+//
+
+#ifndef LIBGFX_INCLUDE_LIBGFX_RSPBUF_H
+#define LIBGFX_INCLUDE_LIBGFX_RSPBUF_H
+
+#include <libgfx/vertex.h>
+#include <stdint.h>
+#include <syscall.h>
+
+// Both the VR4300 and RSP use the first 16 bytes for "private"
+// storage. The rest of the struct is mapped 1:1 to RSP DMEM.
+struct libgfx_rspbuf {
+  uint16_t head, tail;
+  uint32_t unused[3];
+
+  uint8_t buf[0xC00 - 0x10];
+  struct libgfx_vertex vertices[0x400 / sizeof(struct libgfx_vertex)];
+};
+
+// Appends a word to the RSP buffer structure.
+// Does NOT check for overflow of the buffer.
+static inline void libgfx_rspbuf_append(
+    struct libgfx_rspbuf *rspbuf, uint32_t word) {
+  uint8_t *dest = rspbuf->buf + rspbuf->tail;
+
+  __builtin_memcpy(dest, &word, sizeof(word));
+  rspbuf->tail += sizeof(word);
+}
+
+// Allocates and initializes a new RSP buffer structure.
+static inline struct libgfx_rspbuf *libgfx_rspbuf_create(void) {
+  void *page = libn64_page_alloc();
+  struct libgfx_rspbuf *rspbuf = (struct libgfx_rspbuf *) page;
+
+  rspbuf->head = rspbuf->tail = 0;
+  return rspbuf;
+}
+
+// Frees an existing RSP buffer structure.
+static inline void libgfx_rspbuf_destroy(struct libgfx_rspbuf *rspbuf) {
+  libn64_page_free(rspbuf);
+}
+
+// Flushes the RSP buffer structure to RSP DRAM.
+void libgfx_rspbuf_flush(struct libgfx_rspbuf *rspbuf);
+void libgfx_rspbuf_flush_vertices(struct libgfx_rspbuf *rspbuf);
+
+#endif
+
diff --git a/libgfx/include/libgfx/vertex.h b/libgfx/include/libgfx/vertex.h
new file mode 100644 (file)
index 0000000..3c5703a
--- /dev/null
@@ -0,0 +1,49 @@
+//
+// libgfx/include/libgfx/vertex.h: Vertex structure.
+//
+// n64chain: A (free) open-source N64 development toolchain.
+// Copyright 2014-16 Tyler J. Stachecki <stachecki.tyler@gmail.com>
+//
+// This file is subject to the terms and conditions defined in
+// 'LICENSE', which is part of this source code package.
+//
+
+#ifndef LIBGFX_INCLUDE_VERTEX_H
+#define LIBGFX_INCLUDE_VERTEX_H
+
+#include <stdint.h>
+
+// This is how vertices are stored within the DRAM cache.
+//
+// While it would be nice to have multiple vertex formats, consider:
+//   * The RSP can only do 8-byte aligned transfers
+//   * The RSP must quickly calc vertex offset (powers of two...)
+//
+// So a vertex structure /needs/ to be either 8 or 16-bytes...
+struct libgfx_vertex {
+  int16_t x, y;         // 10.2 screen coordinates
+  int16_t s, t;         // 10.5 texture coordinates
+  int32_t z;            // 15.16 z-buffer depth
+
+  union {
+    uint32_t rgba32;
+    uint8_t rgba[4];
+  } color;
+} __attribute__((aligned(16)));
+
+// Write back the contents of the vertex (in cache) to DRAM.
+// If the line has already been flushed, this is a NO-OP.
+static inline void libgfx_vertex_flush(struct libgfx_vertex *v) {
+  __builtin_mips_cache(0x19, v);
+}
+
+// Flushes the cache line that vertex would live in if it is
+// dirty. Unconditionally flags the resulting cache line as
+// 'dirty' and sets the physical tag without loading the
+// current contents of the memory pointed to by 'v'.
+static inline void libgfx_vertex_init(struct libgfx_vertex *v) {
+  __builtin_mips_cache(0xD, v);
+}
+
+#endif
+
diff --git a/libgfx/src/init.c b/libgfx/src/init.c
new file mode 100644 (file)
index 0000000..ac88cf9
--- /dev/null
@@ -0,0 +1,30 @@
+//
+// libgfx/src/init.c: Graphics initialization.
+//
+// n64chain: A (free) open-source N64 development toolchain.
+// Copyright 2014-16 Tyler J. Stachecki <stachecki.tyler@gmail.com>
+//
+// This file is subject to the terms and conditions defined in
+// 'LICENSE', which is part of this source code package.
+//
+
+#include <libgfx/init.h>
+#include <rcp/sp.h>
+#include <stdint.h>
+
+void libgfx_init(void) {
+  extern char libn64_ucode_gfx;
+  static const char *ucode_ptr = &libn64_ucode_gfx;
+  uint32_t ucode_address;
+
+  libn64_rsp_set_status(RSP_STATUS_SET_HALT);
+
+  // DMA the initialization ucode to the RSP and spin until its done.
+  __builtin_memcpy(&ucode_address, &ucode_ptr, sizeof(ucode_address));
+  ucode_address &= 0xFFFFFF;
+
+  while (libn64_rsp_is_dma_pending());
+  libn64_rsp_dma_to_rsp(0x04001000, ucode_address + 0x1000, 0xFFF);
+  while (libn64_rsp_is_dma_pending());
+}
+
diff --git a/libgfx/src/rspbuf.c b/libgfx/src/rspbuf.c
new file mode 100644 (file)
index 0000000..3e440a3
--- /dev/null
@@ -0,0 +1,56 @@
+//
+// libgfx/src/rspbuf.c: RSP buffer.
+//
+// n64chain: A (free) open-source N64 development toolchain.
+// Copyright 2014-16 Tyler J. Stachecki <stachecki.tyler@gmail.com>
+//
+// This file is subject to the terms and conditions defined in
+// 'LICENSE', which is part of this source code package.
+//
+
+#include <libgfx/rspbuf.h>
+#include <libgfx/vertex.h>
+#include <rcp/sp.h>
+#include <stdint.h>
+
+// Flushes the RSP buffer structure to RSP DRAM.
+void libgfx_rspbuf_flush(struct libgfx_rspbuf *rspbuf) {
+  unsigned len = (rspbuf->tail - rspbuf->head + 0xF) & ~0xF;
+
+  if (len > 0) {
+    uint32_t dram_address;
+    unsigned i;
+
+    for (i = 0; i < len; i += 16)
+      __builtin_mips_cache(0x19, rspbuf->buf + i);
+
+    // Spin up an RSP DMA from RDRAM to DMEM.
+    // TODO: In the future, this should be queued up or done from RSP.
+    __builtin_memcpy(&dram_address, &rspbuf, sizeof(dram_address));
+    dram_address = (dram_address & 0xFFFFFF) + rspbuf->head + 0x10;
+
+    libn64_rsp_dma_to_rsp(0x04000010 + rspbuf->head, dram_address, len - 1);
+    while (libn64_rsp_is_dma_pending());
+
+    // Flushed everything; reset the buffer pointer;
+    rspbuf->head = rspbuf->tail = 0;
+  }
+}
+
+// Flushes the RSP vertices buffer structure to RSP DRAM.
+void libgfx_rspbuf_flush_vertices(struct libgfx_rspbuf *rspbuf) {
+  uint32_t dram_address;
+  unsigned i;
+
+  for (i = 0; i < sizeof(rspbuf->vertices) / sizeof(*rspbuf->vertices); i++)
+    libgfx_vertex_flush(rspbuf->vertices + i);
+
+  // Spin up an RSP DMA from RDRAM to DMEM.
+  // TODO: In the future, this should be queued up or done from RSP.
+  __builtin_memcpy(&dram_address, &rspbuf, sizeof(dram_address));
+  dram_address = (dram_address & 0xFFFFFF) + 0xC00;
+
+  libn64_rsp_dma_to_rsp(0x0400C00, dram_address, 0x400 - 1);
+  while (libn64_rsp_is_dma_pending());
+}
+
diff --git a/libgfx/ucodes/gfx.rsp b/libgfx/ucodes/gfx.rsp
new file mode 100644 (file)
index 0000000..a8ad251
--- /dev/null
@@ -0,0 +1,227 @@
+#include "defs.h"
+
+.set COMMAND_WORD,          $1
+.set COMMAND_POINTER,       $27
+.set OUTPUT_POINTER,        $28
+.set VERTEX_CACHE_OFFSET,   0xC00
+
+.text
+  addiu $30, $0, VERTEX_CACHE_OFFSET
+  addiu COMMAND_POINTER, $0, 0x10
+  addiu OUTPUT_POINTER, $0, 0x100
+
+loop:
+  ; Check if command is RSP processing command.
+  lw COMMAND_WORD, 0x0(COMMAND_POINTER)
+  bltz COMMAND_WORD, draw_triangle
+  addiu COMMAND_POINTER, COMMAND_POINTER, 0x4
+
+  ; Check if command is end of display list.
+  beq COMMAND_WORD, $0, finish
+  nop
+
+  ; Copy RDP command through to output stream.
+  sw COMMAND_WORD, 0x0(OUTPUT_POINTER)
+  lw COMMAND_WORD, 0x0(COMMAND_POINTER)
+  addiu COMMAND_POINTER, COMMAND_POINTER, 0x4
+  sw COMMAND_WORD, 0x4(OUTPUT_POINTER)
+  beq $0, $0, loop
+  addiu OUTPUT_POINTER, OUTPUT_POINTER, 0x8
+
+finish:
+  lui $at, 0x0400
+  ori $at, $at, 0x100
+  mtc0 $at, CMD_START
+
+  lui $at, 0x0400
+  addu $at, $at, OUTPUT_POINTER
+  mtc0 $at, CMD_END
+
+  break
+  nop
+
+; -------------------------------------------------------------------
+;  draw_triangle: Given three vertices, pack an RDP Edge Coefficients
+;                 instruction (for a triangle primitive) accordingly.
+;
+; Steps:
+;  * Sort vertices by y-coordinate (ascending).
+;  * Compute edge walker slopes and cross-product.
+;  * Utilize cross-product to determine if lft or not.
+;
+; TODO:
+;  * Handle "huge" (16-bit, heh) reciprocal numbers better?
+;  * Accumulate fractional portion of the reciprocal number?
+; -------------------------------------------------------------------
+draw_triangle:
+
+  ; Load offsets of vertices in cache into $2, $3, $4.
+  ; Pre-load the y-coordinates into $5, $6, $7.
+  .set vert1, $2
+  .set vert2, $3
+  .set vert3, $4
+  .set vert1y, $5
+  .set vert2y, $6
+  .set vert3y, $7
+  .set temp, $8
+
+  andi vert3, COMMAND_WORD, 0xFF0
+  srl vert1, COMMAND_WORD, 16
+  andi vert1, vert1, 0xFF0
+  srl vert2, COMMAND_WORD, 8
+  andi vert2, vert2, 0xFF0
+
+  addiu vert3, vert3, VERTEX_CACHE_OFFSET
+  addiu vert2, vert2, VERTEX_CACHE_OFFSET
+  addiu vert1, vert1, VERTEX_CACHE_OFFSET
+
+  ; Sort vertices based on their y-coordinates (ascending).
+  ; Effectively, this is just a space-optimized bubble sort.
+vert_reload_123:
+  lh vert1y, 2(vert1)
+
+vert_reload_23:
+  lh vert2y, 2(vert2)
+  lh vert3y, 2(vert3)
+
+  slt temp, vert2y, vert1y
+  beq temp, $0, vert_comp_23
+  slt temp, vert3y, vert2y
+  xor vert1, vert1, vert2
+  xor vert2, vert2, vert1
+  xor vert1, vert1, vert2
+
+  ; Spend extra two instructions to prevent a worst-case scenario.
+  ; it is possible we have to backwards branch /twice/ if not...
+  slt temp, vert3y, vert1y
+  bne temp, $0, vert_reload_23
+  addiu temp, $0, 0x1
+
+vert_comp_23:
+  beq temp, $0, vertsorted
+  addu temp, vert3, $0
+  addu vert3, vert2, $0
+  beq $0, $0, vert_reload_123
+  addu vert2, temp, $0
+
+  .unset vert1y
+  .unset vert2y
+  .unset vert3y
+  .unset temp
+
+  ; Vertices are y-sorted, vert1 < vert2 < vert3.
+vertsorted:
+  .set deltas, $v0
+  .set verthighmidhigh, $v1
+  .set vertlowlowmid, $v2
+  .set invipart, $v3
+  .set invfpart, $v4
+  .set vrcptemp, $v5
+
+  ; Compute deltas for edge slopes and the cross product.
+  ; Try to fill otherwise dead delay slots with VRCP work.
+  ;
+  ; BTW, if /dy is 0, the RCP calculated is the most +ive
+  ; signed number possible. And lim x->0 1/x = + infinity.
+  ; So we do not need to check/handle division by zero...
+  llv verthighmidhigh, 0x0(vert3)
+  llv vertlowlowmid, 0x0(vert1)
+  vsub vrcptemp, verthighmidhigh, vertlowlowmid
+  llv verthighmidhigh[0x4], 0x0(vert2)
+  llv vertlowlowmid[0x4], 0x0(vert1)
+  vrcp invfpart[1], vrcptemp[1] ; 1 / hdy
+  vrcph invipart[1], $v31[0x8]
+  vsub vrcptemp, verthighmidhigh, vertlowlowmid
+  llv verthighmidhigh[0x8], 0x0(vert3)
+  llv vertlowlowmid[0x8], 0x0(vert2)
+  vrcp invfpart[3], vrcptemp[3] ; 1 / mdy
+  vrcph invipart[3], $v31[0x8]
+  vsub deltas, verthighmidhigh, vertlowlowmid
+  vrcp invfpart[5], deltas[5] ; 1 / ldy
+  vrcph invipart[5], $v31[0x8]
+
+  .unset verthighmidhigh
+  .unset vertlowlowmid
+  .unset vrcptemp
+
+  ; Cross product to determine LFT/RFT (only need sign...)
+  ;   delta[0] (dxh) * delta[3] (dym)
+  ; - delta[1] (dyh) * delta[2] (dxm)
+  .set dxhdym, $v5
+  .set dyhdxm, $v6
+  vmudh dxhdym, deltas, deltas[0x7] ; 3h
+  vmudh dyhdxm, deltas, deltas[0x6] ; 2h
+  vsub dxhdym, dxhdym, dyhdxm[0x3] ; 1q
+  mfc2 $1, dxhdym[0x0]
+
+  ; Reciprocals are calculated, and, furthermore:
+  ; delta[0,1] = xh-xl, yh-yl (hdy)
+  ; delta[2,3] = xm-xl, ym-yl (mdy)
+  ; delta[4,5] = xh-xm, yh-ym (ldy)
+
+  ; Bring screen coordinates (subpixel) down to fractional form.
+  .set deltasi, $v1
+  .set deltasf, $v2
+  vmudm deltasi, deltas, $v31[0x9]
+  vmadn deltasf, $v31, $v31[0x8]
+
+  ; OK, we have the reciprocal NUMBER (x where i.e,. 1 / x). To get
+  ; that to screen coordinates, instead of multiplying by 1/4, we
+  ; multiply by 4. Oh, and since VRCP shifts the radix right by one,
+  ; we have to account for that... so 8. Confusing? Yeah...
+  ;
+  ; ... and what the heck happens to really big reciprocal numbers?
+  vmudn invfpart, invfpart, $v31[0xA]
+  vmadh invipart, invipart, $v31[0xA]
+  vmadn invfpart, $v31, $v31[0x8]
+
+  ; Finish the inverse slope calculations...
+  .set edgeslopei, $v6
+  .set edgeslopef, $v7
+  vmudl edgeslopef, deltasf, invipart[0x3] ; 1q
+  vmadm edgeslopei, deltasi, invipart[0x3] ; 1q
+  vmadn edgeslopef, $v31, $v31[0x8]
+
+  ; Pack the RDP Edge Coefficients instruction.
+  lui $5, 0x0800
+  slt $6, $1, $0
+  sll $6, $6, 23
+  or $5, $5, $6
+  lh $6, 2(vert3)
+  or $5, $5, $6
+  sw $5, 0x0(OUTPUT_POINTER)
+
+  lh $5, 2(vert2)
+  lh $6, 2(vert1)
+  sll $5, $5, 0x10
+  or $5, $5, $6
+  sw $5, 0x4(OUTPUT_POINTER)
+
+  lh $5, 0x0(vert2)
+  sll $5, $5, 0xE
+  sw $5, 0x8(OUTPUT_POINTER)
+
+  lh $5, 0x0(vert1)
+  sll $5, $5, 0xE
+  sw $5, 0x10(OUTPUT_POINTER)
+  sw $5, 0x18(OUTPUT_POINTER)
+
+  ssv edgeslopei[0x8], 0xC(OUTPUT_POINTER)
+  ssv edgeslopef[0x8], 0xE(OUTPUT_POINTER)
+  ssv edgeslopei[0x4], 0x1C(OUTPUT_POINTER)
+  ssv edgeslopef[0x4], 0x1E(OUTPUT_POINTER)
+  ssv edgeslopei[0x0], 0x14(OUTPUT_POINTER)
+  ssv edgeslopef[0x0], 0x16(OUTPUT_POINTER)
+  beq $0, $0, loop
+  addiu OUTPUT_POINTER, OUTPUT_POINTER, 0x20
+
+  ; TODO: Unset everything else...
+  ; Will likely need these still?
+  .unset vert1
+  .unset vert2
+  .unset vert3
+
+.unset COMMAND_WORD
+.unset COMMAND_POINTER
+.unset VERTEX_CACHE_OFFSET
+
diff --git a/libgfx/ucodes/gfx.rsps b/libgfx/ucodes/gfx.rsps
new file mode 100644 (file)
index 0000000..41ac037
--- /dev/null
@@ -0,0 +1,22 @@
+#
+# libgfx/ucodes/gfx.rsps: RSP graphics ucode container.
+#
+# n64chain: A (free) open-source N64 development toolchain.
+# Copyright 2014-16 Tyler J. Stachecki <stachecki.tyler@gmail.com>
+#
+# This file is subject to the terms and conditions defined in
+# 'LICENSE', which is part of this source code package.
+#
+
+#include <libn64.h>
+
+.section .text.libn64.ucode, "ax", @progbits
+
+.global libn64_ucode_gfx
+.type libn64_ucode_gfx, @object
+.align 4
+libn64_ucode_gfx:
+.incbin "ucodes/gfx.bin"
+
+.size libn64_ucode_gfx,.-libn64_ucode_gfx
+
index 81b2f33..1b181c6 100644 (file)
@@ -82,7 +82,8 @@ libn64.a: $(OBJFILES)
 
 %.o: %.rsp %.rsps
        @echo $(call FIXPATH,"Assembling: $(ROM_NAME)/$@")
-       @$(CPP) -E -Iucodes $< | $(RSPASM) -o $(<:.rsp=.bin) -
+       @$(CPP) -E -Iucodes $< > $(<:.rsp=.rsppch)
+       @$(RSPASM) $(<:.rsp=.rsppch) -o $(<:.rsp=.bin)
        @$(CC) -x assembler-with-cpp $(CFLAGS) $(OPTFLAGS) -MMD -c $(<:.rsp=.rsps) -o $@
 
 #
index 58103c0..0ed27e9 100644 (file)
@@ -30,7 +30,7 @@ CHECKSUM = $(call FIXPATH,$(CURDIR)/../tools/bin/checksum)
 RSPASM = $(call FIXPATH,$(CURDIR)/../tools/bin/rspasm)
 
 CFLAGS = -Wall -Wextra -pedantic -std=c99 -Wno-main \
-       -I../libn64/include -I../libn64 -I.
+       -I../libn64/include -I../libgfx/include -I.
 
 OPTFLAGS = -Os -march=vr4300 -mtune=vr4300 -mabi=eabi -mgp32 -mlong32 \
        -flto -ffat-lto-objects -ffunction-sections -fdata-sections \
@@ -67,11 +67,11 @@ $(ROM_NAME).z64: $(ROM_NAME).elf
        @$(OBJCOPY) -O binary $< $@
        @$(CHECKSUM) $(call FIXPATH,../libn64/header.bin) $@
 
-$(ROM_NAME).elf: libn64 $(OBJFILES) filesystem.obj
+$(ROM_NAME).elf: libn64 libgfx $(OBJFILES) filesystem.obj
        @echo $(call FIXPATH,"Building: $(ROM_NAME)/$@")
        @$(CC) $(CFLAGS) $(OPTFLAGS) -Wl,-Map=$(ROM_NAME).map -nostdlib \
                -T$(call FIXPATH,../libn64/rom.ld) -o $@ $(OBJFILES) filesystem.obj \
-               -L$(call FIXPATH,../libn64) -ln64
+               -L$(call FIXPATH,../libn64) -ln64 -L$(call FIXPATH,../libgfx) -lgfx
 
 #
 # Filesystem build target.
@@ -103,6 +103,10 @@ filesystem/%.tsk: ucodes/%.rsp
 libn64:
        @$(MAKE) -sC $(call FIXPATH,../libn64)
 
+.PHONY: libgfx
+libgfx:
+       @$(MAKE) -sC $(call FIXPATH,../libgfx)
+
 #
 # Clean project target.
 #