Filesystem creation tool and message queues. (#2)
authorTyler Stachecki <tj90241@users.noreply.github.com>
Mon, 18 Dec 2017 03:44:10 +0000 (22:44 -0500)
committerGitHub <noreply@github.com>
Mon, 18 Dec 2017 03:44:10 +0000 (22:44 -0500)
* Faciliate loading of external assets.

Maintain a 'filesystem' directory placed within the
the source directory - whenever make is run, files in
this directory are assembled into the final image.

An external header file is generated with offsets and
sizes of each of the files packaged. This allows for
compile-time resolution of addresses within the cart
(as opposed to doing costly dynamic lookups based on
directory and filename).

Signed-off-by: Tyler J. Stachecki <stachecki.tyler@gmail.com>
* Add message allocator interfaces.

The message allocators were already in-use within the
kernel - these syscalls just crack them open for direct
allocations.

The message allocators provide a means for quickly
obtaining 16-byte structures that are aligned on data
cache line boundaries.

Signed-off-by: Tyler J. Stachecki <stachecki.tyler@gmail.com>
* Add support for "unbound" MP-SC message queues.

Add support for message queues which are not directly tied
to a thread and can be passed around as needed. Any thread
can send a message on a message queue, but only the thread
which created the message queue can recv on it.

Unlike thread-specific message queues, the receiver can opt
to block on the queue (suspend himself if no message are
available) or block until a message becomes available on
the queue.

Signed-off-by: Tyler J. Stachecki <stachecki.tyler@gmail.com>
16 files changed:
.gitignore
helloworld/Makefile
helloworld/filesystem/data.txt [new file with mode: 0644]
helloworld/src/main.c
libn64/include/mq.h [new file with mode: 0644]
libn64/include/syscall.h
libn64/os/asm/exception.s
libn64/os/asm/syscall.s
libn64/priv_include/os/message.h [deleted file]
libn64/priv_include/os/thread.h
libn64/rom.ld
threadtest/Makefile
threadtest/src/main.c
tools/build-linux64-toolchain.sh
tools/build-win64-toolchain.sh
tools/mkfs.c [new file with mode: 0644]

index 65638d3..e245c13 100644 (file)
@@ -14,6 +14,7 @@
 /tools/libexec
 /tools/mips64-elf
 /tools/share
+/tools/*-linux-gnu
 
 *.a
 *.bin
@@ -21,7 +22,9 @@
 *.elf
 *.map
 *.o
+*.obj
 *.z64
 
+/*/filesystem.h
 *.swp
 
index a81af0e..6acfd78 100644 (file)
@@ -22,6 +22,7 @@ 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)
 MAKE = $(call FIXPATH,$(CURDIR)/../tools/bin/make)
+MKFS = $(call FIXPATH,$(CURDIR)/../tools/bin/mkfs)
 OBJCOPY = $(call FIXPATH,$(CURDIR)/../tools/bin/mips64-elf-objcopy)
 
 CHECKSUM = $(call FIXPATH,$(CURDIR)/../tools/bin/checksum)
@@ -62,21 +63,33 @@ $(ROM_NAME).z64: $(ROM_NAME).elf
        @$(OBJCOPY) -O binary $< $@
        @$(CHECKSUM) $(call FIXPATH,../libn64/header.bin) $@
 
-$(ROM_NAME).elf: libn64 $(OBJFILES) $(UCODEBINS)
+$(ROM_NAME).elf: libn64 $(OBJFILES) filesystem.obj $(UCODEBINS)
        @echo $(call FIXPATH,"Building: $(ROM_NAME)/$@")
        @$(CC) $(CFLAGS) $(OPTFLAGS) -Wl,-Map=$(ROM_NAME).map -nostdlib \
-               -T$(call FIXPATH,../libn64/rom.ld) -o $@ $(OBJFILES) \
+               -T$(call FIXPATH,../libn64/rom.ld) -o $@ $(OBJFILES) filesystem.obj \
                -L$(call FIXPATH,../libn64) -ln64
 
 #
+# Filesystem build target.
+#
+filesystem.obj: filesystem.h
+       @echo $(call FIXPATH,"Building: $(ROM_NAME)/$@")
+       @$(OBJCOPY) -I binary -O elf32-bigmips -B mips filesystem.bin $@
+
+filesystem.h: $(wildcard filesystem/*)
+       @echo $(call FIXPATH,"Generate: $(ROM_NAME)/$@")
+       @$(MKFS) filesystem.bin filesystem.h filesystem
+
+#
 # Generic compilation/assembly targets.
 #
 $(call FIXPATH,src/graphics.o): $(call FIXPATH,src/graphics.S) $(call FIXPATH,src/graphics.bin)
-%.o: %.S
+
+%.o: %.S filesystem.h
        @echo $(call FIXPATH,"Assembling: $(ROM_NAME)/$<")
        @$(CC) $(CFLAGS) $(OPTFLAGS) -MMD -c $< -o $@
 
-%.o: %.c
+%.o: %.c filesystem.h
        @echo $(call FIXPATH,"Compiling: $(ROM_NAME)/$<")
        @$(CC) $(CFLAGS) $(OPTFLAGS) -MMD -c $< -o $@
 
@@ -95,7 +108,8 @@ libn64:
 clean:
        @echo "Cleaning $(ROM_NAME)..."
        @$(RM) $(ROM_NAME).map $(ROM_NAME).elf $(ROM_NAME).z64 \
-               $(DEPFILES) $(OBJFILES) $(UCODEBINS)
+               $(DEPFILES) $(OBJFILES) $(UCODEBINS) filesystem.obj \
+               filesystem.bin filesystem.h
 
 #
 # Use computed dependencies.
diff --git a/helloworld/filesystem/data.txt b/helloworld/filesystem/data.txt
new file mode 100644 (file)
index 0000000..a5c1966
--- /dev/null
@@ -0,0 +1 @@
+Hello, world
index d2834b3..4363696 100644 (file)
@@ -8,6 +8,7 @@
 // 'LICENSE', which is part of this source code package.
 //
 
+#include <filesystem.h>
 #include <os/fbtext.h>
 #include <rcp/vi.h>
 #include <stdint.h>
@@ -80,10 +81,13 @@ void main(void *unused __attribute__((unused))) {
     context.y = 6;
 
     // Finally, render text where the cursor is placed.
-    libn64_fbtext_puts(&context, "Hello, world!");
+    extern char _binary_filesystem_bin_start;
+    uint32_t text_offset = CART_OFFS_DATA_TXT - 0x1000;
+    char *fs_ptr = &_binary_filesystem_bin_start;
+    libn64_fbtext_puts(&context, fs_ptr + text_offset);
 
     // Block until the next VI interrupt comes in.
-    libn64_recv_message();
+    libn64_recvt_message();
   }
 }
 
diff --git a/libn64/include/mq.h b/libn64/include/mq.h
new file mode 100644 (file)
index 0000000..9fc8666
--- /dev/null
@@ -0,0 +1,55 @@
+//
+// libn64/include/mq.h: Message queues.
+//
+// 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 LIBN64_INCLUDE_MQ_H
+#define LIBN64_INCLUDE_MQ_H
+
+#include <libn64.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <syscall.h>
+
+struct libn64_message {
+  struct libn64_message *next;
+  struct libn64_message *prev;
+  uint32_t message;
+  void *data;
+} __attribute__((aligned(16)));
+
+struct libn64_mq {
+  struct libn64_message *tail;
+  struct libn64_message *head;
+  libn64_thread waiter;
+  void *arg;
+} __attribute__((aligned(16)));
+
+static inline struct libn64_mq *libn64_mq_create(void) {
+  void *opaque = libn64_mq_alloc();
+  struct libn64_mq *mq = (struct libn64_mq *) opaque;
+  __builtin_mips_cache(0xD, mq);
+
+  mq->head = mq->tail = NULL;
+  mq->waiter = libn64_thread_self();
+  return mq;
+}
+
+static inline void libn64_mq_destroy(struct libn64_mq *mq) {
+  struct libn64_message *message, *next;
+
+  for (message = mq->head; message != mq->tail; message = next) {
+    next = message->next;
+    libn64_mq_free(message);
+  }
+
+  libn64_mq_free(mq);
+}
+
+#endif
+
index 41dca63..45668c3 100644 (file)
@@ -8,8 +8,8 @@
 // 'LICENSE', which is part of this source code package.
 //
 
-#ifndef LIBN64_INCLUDE_OS_SYSCALL_H
-#define LIBN64_INCLUDE_OS_SYSCALL_H
+#ifndef LIBN64_INCLUDE_SYSCALL_H
+#define LIBN64_INCLUDE_SYSCALL_H
 
 // Syscall numbers.
 #define LIBN64_SYSCALL_THREAD_CREATE     0
 #define LIBN64_SYSCALL_PAGE_FREE         6
 #define LIBN64_SYSCALL_TIME              7
 
-#define LIBN64_SYSCALL_SEND_MESSAGE      8
-#define LIBN64_SYSCALL_RECV_MESSAGE      9
-#define LIBN64_SYSCALL_INVALID           10
+#define LIBN64_SYSCALL_SENDT_MESSAGE     8
+#define LIBN64_SYSCALL_RECVT_MESSAGE     9
+#define LIBN64_SYSCALL_SEND_MESSAGE      10
+#define LIBN64_SYSCALL_RECV_MESSAGE      11
+#define LIBN64_SYSCALL_MQ_ALLOC          12
+#define LIBN64_SYSCALL_MQ_FREE           13
+#define LIBN64_SYSCALL_INVALID           14
+
+#define LIBN64_BLOCK                     0x00000000
+#define LIBN64_NOBLOCK                   0x80000000
+#define LIBN64_NOMSGS                    LIBN64_NOBLOCK
 
 #ifndef __ASSEMBLER__
 #include <stdint.h>
@@ -38,6 +46,8 @@ enum libn64_interrupt {
   LIBN64_INTERRUPT_VI = 0x444,
 };
 
+struct libn64_mq;
+
 // Spawns a new thread with a given priority (which receives arg). If
 // the new thread has a higher priority than the current thread, a
 // context switch will result from this function call.
@@ -211,7 +221,7 @@ static inline struct timeval libn64_time(void) {
 
 // Sends a message with no parameter values to the specified thread.
 libn64func __attribute__((always_inline))
-static inline void libn64_send_message(libn64_thread thread, uint32_t message) {
+static inline void libn64_sendt_message(libn64_thread thread, uint32_t message) {
   register libn64_thread a0 __asm__("$a0") = thread;
   register uint32_t a1 __asm__("$a1") = message;
 
@@ -224,14 +234,14 @@ static inline void libn64_send_message(libn64_thread thread, uint32_t message) {
     ".set reorder\n\t"
     ".set at\n\t"
 
-    :: "r" (a0), "r" (a1), "K" (LIBN64_SYSCALL_SEND_MESSAGE)
+    :: "r" (a0), "r" (a1), "K" (LIBN64_SYSCALL_SENDT_MESSAGE)
     : "memory"
   );
 }
 
 // Sends a message with a parameter value to the specified thread.
 libn64func __attribute__((always_inline))
-static inline void libn64_send_message1(
+static inline void libn64_sendt_message1(
     libn64_thread thread, uint32_t message, uint32_t param) {
   register libn64_thread a0 __asm__("$a0") = thread;
   register uint32_t a1 __asm__("$a1") = message;
@@ -245,14 +255,14 @@ static inline void libn64_send_message1(
     ".set reorder\n\t"
     ".set at\n\t"
 
-    :: "r" (a0), "r" (a1), "r" (a2), "K" (LIBN64_SYSCALL_SEND_MESSAGE)
+    :: "r" (a0), "r" (a1), "r" (a2), "K" (LIBN64_SYSCALL_SENDT_MESSAGE)
     : "memory"
   );
 }
 
 // Blocks until a message is received. No message data is returned.
 libn64func __attribute__((always_inline))
-static inline uint32_t libn64_recv_message(void) {
+static inline uint32_t libn64_recvt_message(void) {
   register uint32_t rv __asm__("$v0");
 
   __asm__ __volatile__(
@@ -264,7 +274,7 @@ static inline uint32_t libn64_recv_message(void) {
     ".set at\n\t"
 
     : "=r" (rv)
-    : "K" (LIBN64_SYSCALL_RECV_MESSAGE)
+    : "K" (LIBN64_SYSCALL_RECVT_MESSAGE)
     : "memory"
   );
 
@@ -273,7 +283,7 @@ static inline uint32_t libn64_recv_message(void) {
 
 // Blocks until a message is received; returns message and data.
 libn64func __attribute__((always_inline))
-static inline uint64_t libn64_recv_message1(uint32_t *param) {
+static inline uint64_t libn64_recvt_message1(uint32_t *param) {
   register uint32_t rv __asm__("$v0");
   register uint32_t data __asm__("$at");
 
@@ -285,14 +295,145 @@ static inline uint64_t libn64_recv_message1(uint32_t *param) {
     ".set reorder\n\t"
     ".set at\n\t"
 
-    : "=r" (rv), "=r"(data)
-    : "K" (LIBN64_SYSCALL_RECV_MESSAGE)
+    : "=r" (rv), "=r" (data)
+    : "K" (LIBN64_SYSCALL_RECVT_MESSAGE)
   );
 
   *param = data;
   return rv;
 }
 
+// Sends a message with no parameter values on the specified queue.
+libn64func __attribute__((always_inline))
+static inline void libn64_send_message(
+    struct libn64_mq *mq, uint32_t message) {
+  register struct libn64_mq *a0 __asm__("$a0") = mq;
+  register uint32_t a1 __asm__("$a1") = message;
+
+  __asm__ __volatile__(
+    ".set noreorder\n\t"
+    ".set noat\n\t"
+    "li $at, %3\n\t"
+    "xor $a2, $a2, $a2\n\t"
+    "syscall\n\t"
+    ".set reorder\n\t"
+    ".set at\n\t"
+
+    : "=r" (a0)
+    : "0" (a0), "r" (a1), "K" (LIBN64_SYSCALL_SEND_MESSAGE)
+    : "memory"
+  );
+}
+
+// Sends a message with a parameter value to the specified queue.
+libn64func __attribute__((always_inline))
+static inline void libn64_send_message1(
+    struct libn64_mq *mq, uint32_t message, uint32_t param) {
+  register struct libn64_mq *a0 __asm__("$a0") = mq;
+  register uint32_t a1 __asm__("$a1") = message;
+  register uint32_t a2 __asm__("$a2") = param;
+
+  __asm__ __volatile__(
+    ".set noreorder\n\t"
+    ".set noat\n\t"
+    "li $at, %3\n\t"
+    "syscall\n\t"
+    ".set reorder\n\t"
+    ".set at\n\t"
+
+    :: "r" (a0), "r" (a1), "r" (a2), "K" (LIBN64_SYSCALL_SEND_MESSAGE)
+    : "memory"
+  );
+}
+
+// Blocks until a message is received. No message data is returned.
+// If not blocking and no msgs are queued, returns LIBN64_NOMSGS.
+libn64func __attribute__((always_inline))
+static inline uint32_t libn64_recv_message(
+    struct libn64_mq *mq, uint32_t flags) {
+  register struct libn64_mq *a0 __asm__("$a0") = mq;
+  register uint32_t a1 __asm__("$a1") = flags;
+  register uint32_t rv __asm__("$v0");
+
+  __asm__ __volatile__(
+    ".set noreorder\n\t"
+    ".set noat\n\t"
+    "li $at, %3\n\t"
+    "syscall\n\t"
+    ".set reorder\n\t"
+    ".set at\n\t"
+
+    : "=r" (rv)
+    : "r" (a0), "r" (a1), "K" (LIBN64_SYSCALL_RECV_MESSAGE)
+    : "memory"
+  );
+
+  return rv;
+}
+
+// Blocks until a message is received; returns message and data.
+// If not blocking and no msgs are queued, returns LIBN64_NOMSGS.
+libn64func __attribute__((always_inline))
+static inline uint64_t libn64_recv_message1(
+    struct libn64_mq *mq, uint32_t flags, uint32_t *param) {
+  register struct libn64_mq *a0 __asm__("$a0") = mq;
+  register uint32_t a1 __asm__("$a1") = flags;
+  register uint32_t data __asm__("$at");
+  register uint32_t rv __asm__("$v0");
+
+  __asm__ __volatile__(
+    ".set noreorder\n\t"
+    ".set noat\n\t"
+    "li $at, %4\n\t"
+    "syscall\n\t"
+    ".set reorder\n\t"
+    ".set at\n\t"
+
+    : "=r" (rv), "=r" (data)
+    : "r" (a0), "r" (a1), "K" (LIBN64_SYSCALL_RECV_MESSAGE)
+  );
+
+  *param = data;
+  return rv;
+}
+
+// Allocates a message (16 bytes) from the message allocator.
+libn64func __attribute__((always_inline))
+static inline void *libn64_mq_alloc(void) {
+  register void *rv __asm__("$v0");
+
+  __asm__ __volatile__(
+    ".set noreorder\n\t"
+    ".set noat\n\t"
+    "li $at, %1\n\t"
+    "syscall\n\t"
+    ".set reorder\n\t"
+    ".set at\n\t"
+
+    : "=r" (rv)
+    : "K" (LIBN64_SYSCALL_MQ_ALLOC)
+  );
+
+  return rv;
+}
+
+// Returns a message to the message allocator.
+libn64func __attribute__((always_inline))
+static inline void libn64_mq_free(void *message) {
+  register void *a0 __asm__("$a0") = message;
+
+  __asm__ __volatile__(
+    ".set noreorder\n\t"
+    ".set noat\n\t"
+    "li $at, %1\n\t"
+    "syscall\n\t"
+    ".set reorder\n\t"
+    ".set at\n\t"
+
+    :: "r" (a0), "K" (LIBN64_SYSCALL_MQ_FREE)
+  );
+}
+
 #endif
 #endif
 
index 95e0581..920f48a 100644 (file)
@@ -338,7 +338,7 @@ libn64_exception_handler_rcp_interrupt:
 #  Routine fits in 3 cache lines. There are a few load-after-use
 #  stalls, but they seem to largely be unavoidable given how few
 #  registers we have to work with. PFN of the allocated page is
-#  returned in $k0 and stored to 0x0($k1).
+#  returned in $k0.
 #
 #  Clobbers: $at, $k0
 # -------------------------------------------------------------------
index 4a2d897..245f13f 100644 (file)
@@ -394,64 +394,67 @@ libn64_syscall_time:
 .size libn64_syscall_time,.-libn64_syscall_time
 
 # -------------------------------------------------------------------
-#  libn64::send_message
+#  libn64::sendt_message
 #    $a0 = recipient
 #    $a1 = message
 #    $a2 = param
 # -------------------------------------------------------------------
-.type libn64_syscall_send_message, @function
+.type libn64_syscall_sendt_message, @function
 .align 5
-libn64_syscall_send_message:
+libn64_syscall_sendt_message:
   mtc0 $ra, $30
   jal libn64_send_message
   mtc0 $k1, $14
 
 # Check to see if we unblocked a higher priority thread.
+libn64_sendt_message_unblock:
   lui $at, 0x8000
   lw $at, 0x420($at)
   mfc0 $ra, $30
   j libn64_maybe_unblock_thread
   lw $k1, 0x8($at)
 
-.size libn64_syscall_send_message,.-libn64_syscall_send_message
+.size libn64_syscall_sendt_message,.-libn64_syscall_sendt_message
 
 # -------------------------------------------------------------------
-#  libn64::recv_message
+#  libn64::recvt_message
 # -------------------------------------------------------------------
-.global libn64_syscall_recv_message
-.type libn64_syscall_recv_message, @function
+.global libn64_syscall_recvt_message
+.type libn64_syscall_recvt_message, @function
 .align 5
-libn64_syscall_recv_message:
+libn64_syscall_recvt_message:
   mtc0 $k1, $14
+  addiu $v0, $k0, 0x8
+
+libn64_recvt_replay:
   lui $k1, 0x8000
   lw $k1, 0x420($k1)
   lw $k1, 0x8($k1)
   lw $at, 0x194($k1)
 
-# If there are no messages available, block the thread.
-libn64_recv_message_block:
-  bnel $at, $zero, libn64_recv_message_deque
+# If there is > 1 message available, extract it from the queue.
+libn64_recvt_message_block:
+  bnel $at, $zero, libn64_recvt_message_deque
   lw $k0, 0x0($at)
 
 # No messages are available; set the thread's unblock return
 # to this syscall, deque it, and schedule the next thread.
-  addu $at, $k0, 0x4
   la $k0, libn64_block_thread
   j libn64_context_save
-  mtc0 $at, $30
+  mtc0 $v0, $30
 
 # Deque the message at the head/front of the message queue.
 # If the message has a successor, make it the new queue head.
 # If there is no successor, then there is no tail; update it.
-libn64_recv_message_deque:
+libn64_recvt_message_deque:
   sw $k0, 0x194($k1)
-  bnel $k0, $zero, libn64_recv_message_after_next_update
+  bnel $k0, $zero, libn64_recvt_message_after_next_update
   sw $zero, 0x4($k0)
   sw $zero, 0x190($k1)
 
 # Return the freed message to the message cache.
 # Return the contents of the message to the caller.
-libn64_recv_message_after_next_update:
+libn64_recvt_message_after_next_update:
   lui $k1, 0x8000
   lw $k0, 0x424($k1)
 
@@ -462,9 +465,124 @@ libn64_recv_message_after_next_update:
 
   eret
 
+.size libn64_syscall_recvt_message,.-libn64_syscall_recvt_message
+
+# -------------------------------------------------------------------
+#  libn64::send_message
+# -------------------------------------------------------------------
+.global libn64_syscall_send_message
+.type libn64_syscall_send_message, @function
+.align 5
+libn64_syscall_send_message:
+  mtc0 $ra, $30
+  addiu $a0, $a0, -0x190
+  jal libn64_send_message
+  mtc0 $k1, $14
+
+# Check to see if we unblocked a higher priority thread.
+  lw $a0, 0x198($a0)
+  bne $a0, $zero, libn64_sendt_message_unblock
+  mfc0 $ra, $30
+  eret
+
+.size libn64_syscall_send_message,.-libn64_syscall_send_message
+
+# -------------------------------------------------------------------
+#  libn64::recv_message
+# -------------------------------------------------------------------
+.global libn64_syscall_recv_message
+.type libn64_syscall_recv_message, @function
+.align 5
+libn64_syscall_recv_message:
+  mtc0 $k1, $14
+  addiu $k0, $k0, 0xC
+
+libn64_recv_replay:
+  lw $at, 0x4($a0)
+
+# If there is > 1 message available, extract it from the queue.
+libn64_recv_message_block:
+  beq $at, $zero, libn64_recv_maybe_block_thread
+  addiu $k1, $a0, -0x190
+  j libn64_recvt_message_deque
+  lw $k0, 0x0($at)
+
+# No messages are available; set the thread's unblock return
+# to this syscall, deque it, and schedule the next thread.
+libn64_recv_maybe_block_thread:
+  bgezl $a1, libn64_recv_block_thread
+  lw $k1, 0x8($a0)
+  lui $v0, 0x8000
+  eret
+
+libn64_recv_block_thread:
+  la $k0, libn64_block_thread
+  j libn64_context_save
+  mtc0 $k0, $30
+
 .size libn64_syscall_recv_message,.-libn64_syscall_recv_message
 
 # -------------------------------------------------------------------
+#  libn64::mq_alloc
+# -------------------------------------------------------------------
+.global libn64_syscall_mq_alloc
+.type libn64_syscall_mq_alloc, @function
+.align 5
+libn64_syscall_mq_alloc:
+  mtc0 $k1, $14
+
+libn64_mq_alloc_replay:
+  lui $at, 0x8000
+  lw $v0, 0x424($at)
+  beql $v0, $zero, libn64_sendt_message_expand_cache
+  mtc0 $ra, $30
+
+  lw $k1, 0x0($v0)
+  sw $k1, 0x424($at)
+  eret
+
+# The message cache is dried up; expand the cache.
+libn64_sendt_message_expand_cache:
+  jal libn64_exception_handler_allocpage
+  lui $at, 0x8000
+  sll $k0, $k0, 0xC
+  or $k0, $k0, $at
+  sw $k0, 0x424($at)
+  addu $k1, $k0, $zero
+
+libn64_sendt_message_alloc_loop:
+  addiu $k0, $k0, 0x10
+  cache 0xD, -0x10($k0)
+  xor $at, $k0, $k1
+  andi $at, $at, 0x1000
+  beql $at, $zero, libn64_sendt_message_alloc_loop
+  sw $k0, -0x10($k0)
+
+# Now that the cache is populated, replay the alloc.
+  sw $zero, -0x10($k0)
+  j libn64_mq_alloc_replay
+  mfc0 $ra, $30
+
+.size libn64_syscall_mq_alloc,.-libn64_syscall_mq_alloc
+
+# -------------------------------------------------------------------
+#  libn64::mq_free
+# -------------------------------------------------------------------
+.global libn64_syscall_mq_free
+.type libn64_syscall_mq_free, @function
+.align 5
+libn64_syscall_mq_free:
+  lui $at, 0x8000
+  lw $k0, 0x424($at)
+  cache 0xD, 0x0($a0)
+  sw $k0, 0x0($a0)
+  mtc0 $k1, $14
+  sw $a0, 0x424($at)
+  eret
+
+.size libn64_syscall_mq_free,.-libn64_syscall_mq_free
+
+# -------------------------------------------------------------------
 #  System call table.
 # -------------------------------------------------------------------
 .section  .rodata
@@ -481,8 +599,12 @@ libn64_syscall_table:
 .long libn64_syscall_page_alloc
 .long libn64_syscall_page_free
 .long libn64_syscall_time
+.long libn64_syscall_sendt_message
+.long libn64_syscall_recvt_message
 .long libn64_syscall_send_message
 .long libn64_syscall_recv_message
+.long libn64_syscall_mq_alloc
+.long libn64_syscall_mq_free
 
 .size libn64_syscall_table,.-libn64_syscall_table
 
diff --git a/libn64/priv_include/os/message.h b/libn64/priv_include/os/message.h
deleted file mode 100644 (file)
index 4dc9725..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-//
-// libn64/priv_include/os/message.h: OS message definition.
-//
-// 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 LIBN64_PRIV_INCLUDE_OS_MESSAGE_H
-#define LIBN64_PRIV_INCLUDE_OS_MESSAGE_H
-
-#include <stdint.h>
-
-struct libn64_message {
-  struct libn64_message *next;
-  struct libn64_message *prev;
-  uint32_t message;
-  uint32_t data;
-} __attribute__((aligned(16)));
-
-#endif
-
index 791718a..6d29d9c 100644 (file)
@@ -12,7 +12,7 @@
 #define LIBN64_PRIV_INCLUDE_OS_THREAD_H
 
 #include <libn64.h>
-#include <os/message.h>
+#include <mq.h>
 #include <stdint.h>
 
 #define LIBN64_THREAD_MIN_PRIORITY 0
@@ -31,8 +31,8 @@ struct libn64_thread_state {
 struct libn64_thread_internal {
   struct libn64_thread_state state;
 
-  struct message *messages_head;
-  struct message *messages_tail;
+  struct libn64_message *messages_tail;
+  struct libn64_message *messages_head;
   uint32_t priority;
   uint32_t blocked;
 
index 7db8d35..dac7b86 100644 (file)
@@ -136,8 +136,6 @@ SECTIONS {
     *(.cart.libn64.*)
   }
 
-  /* TEXT */
-
   /* Everything is statically linked, so discard PLTs. */
   /DISCARD/ : { *(.rel.iplt) *(.rela.iplt) *(.rel.plt) *(.rela.plt) *(.plt) *(.iplt) }
 
index f27b86d..d2a14be 100644 (file)
@@ -22,6 +22,7 @@ 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)
 MAKE = $(call FIXPATH,$(CURDIR)/../tools/bin/make)
+MKFS = $(call FIXPATH,$(CURDIR)/../tools/bin/mkfs)
 OBJCOPY = $(call FIXPATH,$(CURDIR)/../tools/bin/mips64-elf-objcopy)
 
 CHECKSUM = $(call FIXPATH,$(CURDIR)/../tools/bin/checksum)
@@ -29,6 +30,7 @@ RSPASM = $(call FIXPATH,$(CURDIR)/../tools/bin/rspasm)
 
 CFLAGS = -Wall -Wextra -pedantic -std=c99 -Wno-main \
        -I../libn64/include -I../libn64 -I.
+
 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
@@ -60,21 +62,33 @@ $(ROM_NAME).z64: $(ROM_NAME).elf
        @$(OBJCOPY) -O binary $< $@
        @$(CHECKSUM) $(call FIXPATH,../libn64/header.bin) $@
 
-$(ROM_NAME).elf: libn64 $(OBJFILES) $(UCODEBINS)
+$(ROM_NAME).elf: libn64 $(OBJFILES) filesystem.obj $(UCODEBINS)
        @echo $(call FIXPATH,"Building: $(ROM_NAME)/$@")
        @$(CC) $(CFLAGS) $(OPTFLAGS) -Wl,-Map=$(ROM_NAME).map -nostdlib \
-               -T$(call FIXPATH,../libn64/rom.ld) -o $@ $(OBJFILES) \
+               -T$(call FIXPATH,../libn64/rom.ld) -o $@ $(OBJFILES) filesystem.obj \
                -L$(call FIXPATH,../libn64) -ln64
 
 #
+# Filesystem build target.
+#
+filesystem.obj: filesystem.h
+       @echo $(call FIXPATH,"Building: $(ROM_NAME)/$@")
+       @$(OBJCOPY) -I binary -O elf32-bigmips -B mips filesystem.bin $@
+
+filesystem.h: $(wildcard filesystem/*)
+       @echo $(call FIXPATH,"Generate: $(ROM_NAME)/$@")
+       @$(MKFS) filesystem.bin filesystem.h filesystem
+
+#
 # Generic compilation/assembly targets.
 #
 $(call FIXPATH,src/graphics.o): $(call FIXPATH,src/graphics.S) $(call FIXPATH,src/graphics.bin)
-%.o: %.S
+
+%.o: %.S filesystem.h
        @echo $(call FIXPATH,"Assembling: $(ROM_NAME)/$<")
        @$(CC) $(CFLAGS) $(OPTFLAGS) -MMD -c $< -o $@
 
-%.o: %.c
+%.o: %.c filesystem.h
        @echo $(call FIXPATH,"Compiling: $(ROM_NAME)/$<")
        @$(CC) $(CFLAGS) $(OPTFLAGS) -MMD -c $< -o $@
 
@@ -93,7 +107,8 @@ libn64:
 clean:
        @echo "Cleaning $(ROM_NAME)..."
        @$(RM) $(ROM_NAME).map $(ROM_NAME).elf $(ROM_NAME).z64 \
-               $(DEPFILES) $(OBJFILES) $(UCODEBINS)
+               $(DEPFILES) $(OBJFILES) $(UCODEBINS) filesystem.obj \
+               filesystem.bin filesystem.h
 
 #
 # Use computed dependencies.
index 9600754..91f58b6 100644 (file)
@@ -60,7 +60,7 @@ void vi_thread(void *opaque) {
     }
 
     // Block until the next VI interrupt comes in.
-    libn64_recv_message();
+    libn64_recvt_message();
   }
 }
 
@@ -78,7 +78,7 @@ void box_anim_thread(void *opaque) {
   char x_dir = args->init_x_dir;
   char y_dir = args->init_y_dir;
 
-  libn64_recv_message();
+  libn64_recvt_message();
 
   while (1) {
     // Draw the box (8x8).
@@ -127,7 +127,7 @@ void box_anim_thread(void *opaque) {
       y_dir = 1;
 
     // Block until the next VI interrupt comes in.
-    libn64_recv_message();
+    libn64_recvt_message();
   }
 }
 
index 9b1d699..c14c174 100755 (executable)
@@ -67,6 +67,8 @@ if [ ! -f stamps/binutils-install ]; then
   touch stamps/binutils-install
 fi
 
+exit 0
+
 if [ ! -f stamps/gcc-download ]; then
   wget "${GCC}" -O "tarballs/$(basename ${GCC})"
   touch stamps/gcc-download
@@ -182,6 +184,12 @@ if [ ! -f stamps/checksum-build ]; then
   touch stamps/checksum-build
 fi
 
+if [ ! -f stamps/mkfs-build ]; then
+  cc -Wall -Wextra -pedantic -std=c99 -static -O2 mkfs.c -o bin/mkfs
+
+  touch stamps/mkfs-build
+fi
+
 if [ ! -f stamps/rspasm-build ]; then
   pushd "${SCRIPT_DIR}/../rspasm"
 
index d0e4ccf..840bc0a 100755 (executable)
@@ -228,6 +228,12 @@ if [ ! -f stamps/checksum-build ]; then
   touch stamps/checksum-build
 fi
 
+if [ ! -f stamps/mkfs-build ]; then
+  x86_64-w64-mingw32-gcc -Wall -Wextra -pedantic -std=c99 -static -O2 mkfs.c -o bin/mkfs.exe
+
+  touch stamps/mkfs-build
+fi
+
 if [ ! -f stamps/rspasm-build ]; then
   pushd "${SCRIPT_DIR}/../rspasm"
 
diff --git a/tools/mkfs.c b/tools/mkfs.c
new file mode 100644 (file)
index 0000000..9a94b4f
--- /dev/null
@@ -0,0 +1,368 @@
+//
+// tools/mkfs.c: libn64 filesystem creation tool.
+//
+// n64chain: A (free) open-source N64 development toolchain.
+// Copyright 2014 Tyler J. Stachecki <tstache1@binghamton.edu>
+//
+// This file is more or less a direct rip of chksum64:
+// Copyright 1997 Andreas Sterbenz <stan@sbox.tu-graz.ac.at>
+//
+// This file is subject to the terms and conditions defined in
+// 'LICENSE', which is part of this source code package.
+//
+
+#include <ctype.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef _WIN32
+#include <windows.h>
+typedef HANDLE dir_handle_t;
+typedef WIN32_FIND_DATA file_handle_t;
+#else
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+typedef DIR *dir_handle_t;
+typedef struct dirent *file_handle_t;
+#endif
+
+struct fsinode {
+  struct fsinode *next;
+  char *local_path;
+  char *name;
+  size_t size;
+};
+
+static struct fsinode *create_fsinode(const char *path, file_handle_t *f);
+static void delete_fsinode(struct fsinode *inode);
+static int dir_close(dir_handle_t *handle);
+static const char *dir_getname(file_handle_t *handle);
+static int dir_next(const char *path, dir_handle_t *handle, file_handle_t *f);
+static int dir_open(const char *path, dir_handle_t *handle);
+static int walk_filesystem(const char *path, struct fsinode **root);
+static int write_image(FILE *fs, FILE *fslist, const struct fsinode *root);
+
+struct fsinode *create_fsinode(const char *path, file_handle_t *f) {
+  struct fsinode *inode;
+  const char *name = dir_getname(f);
+  char *local_path, *name_copy;
+
+  if ((inode = (struct fsinode *) malloc(sizeof(*inode))) == NULL) {
+    fprintf(stderr, "Failed to allocate memory for an inode.\n");
+    return NULL;
+  }
+
+  inode->next = NULL;
+
+  if ((local_path = (char *) malloc(strlen(path) + strlen(name) + 2)) == NULL) {
+    fprintf(stderr, "Failed to allocate memory for a path.\n");
+
+    free(inode);
+    return NULL;
+  }
+
+  if ((name_copy = (char *) malloc(strlen(name) + 1)) == NULL) {
+    fprintf(stderr, "Failed to allocate memory for a name.\n");
+
+    free(local_path);
+    free(inode);
+    return NULL;
+  }
+
+#ifdef _WIN32
+  sprintf(local_path, "%s\%s", path, name);
+
+  if (!(f->dwFileAttributes & FILE_ATTRIBUTE_NORMAL)) {
+    fprintf(stderr, "Only regular files are supported.\n");
+    fprintf(stderr, "Unable to handle the following: '%s'\n", local_path);
+
+    return NULL;
+  }
+
+  inode->size = (((size_t) f->nFileSizeHigh) << 32) | f->nFileSizeLow;
+#else
+  struct stat buf;
+  int status;
+
+  sprintf(local_path, "%s/%s", path, name);
+  status = stat(local_path, &buf);
+
+  if (status != 0 || !S_ISREG(buf.st_mode)) {
+    if (status != 0) {
+      fprintf(stderr, "Failed to read file attributes: '%s'\n", local_path);
+    }
+
+    else {
+      fprintf(stderr, "Only regular files are supported.\n");
+      fprintf(stderr, "Unable to handle the following: '%s'\n", local_path);
+    }
+
+    free(name_copy);
+    free(local_path);
+    free(inode);
+
+    return NULL;
+  }
+
+  inode->size = buf.st_size;
+#endif
+
+  strcpy(name_copy, name);
+
+  inode->local_path = local_path;
+  inode->name = name_copy;
+  return inode;
+}
+
+void delete_fsinode(struct fsinode *inode) {
+  struct fsinode *next;
+
+  while (inode != NULL) {
+    next = inode->next;
+
+    free(inode->local_path);
+    free(inode->name);
+    free(inode);
+
+    inode = next;
+  }
+}
+
+static int dir_close(dir_handle_t *handle) {
+#ifdef _WIN32
+  if (*handle != INVALID_HANDLE_VALUE)
+    FindClose(*handle);
+#else
+  if (*handle != NULL)
+    closedir(*handle);
+#endif
+
+  return 0;
+}
+
+const char *dir_getname(file_handle_t *handle) {
+#ifdef _WIN32
+  return handle->cFileName;
+#else
+  return (*handle)->d_name;
+#endif
+}
+
+int dir_next(const char *path, dir_handle_t *handle, file_handle_t *f) {
+#ifdef _WIN32
+  if (*handle == INVALID_HANDLE_VALUE) {
+    *handle = FindFirstFile(path, f);
+    if (*handle == INVALID_HANDLE_VALUE)
+      return -1;
+  }
+
+  else {
+    if (FindNextFile(*handle, f) == 0)
+      return GetLastError() == ERROR_NO_MORE_FILES ? 1 : -1;
+  }
+#else
+  path = path;
+  errno = 0;
+  if ((*f = readdir(*handle)) == NULL)
+    return errno == 0 ? 1 : -1;
+#endif
+
+  return 0;
+}
+
+int dir_open(const char *path, dir_handle_t *handle) {
+#ifdef _WIN32
+  DWORD status;
+
+  path = path;
+  *handle = INVALID_HANDLE_VALUE;
+  status = GetFileAttributes(path);
+
+  if (status == INVALID_FILE_ATTRIBUTES)
+    return 1;
+
+  if (status != FILE_ATTRIBUTE_DIRECTORY)
+    return -1;
+#else
+  errno = 0;
+  if ((*handle = opendir(path)) == NULL)
+    return errno == ENOENT ? 1 : -1;
+#endif
+
+  return 0;
+}
+
+int main(int argc, const char *argv[]) {
+  struct fsinode *root;
+  FILE *fs, *fslist;
+  int status;
+
+  if (argc != 4) {
+    printf("Usage: %s <fs bin path> <listing output path> <root>\n", argv[0]);
+    return EXIT_SUCCESS;
+  }
+
+  if ((fs = fopen(argv[1], "w+")) == NULL) {
+    fprintf(stderr, "Failed to open %s for writing.\n", argv[1]);
+    return EXIT_FAILURE;
+  }
+
+  if ((fslist = fopen(argv[2], "w+")) == NULL) {
+    fprintf(stderr, "Failed to open %s for writing.\n", argv[2]);
+  }
+
+  status = walk_filesystem(argv[3], &root);
+
+  if (status == EXIT_SUCCESS) {
+    status = write_image(fs, fslist, root);
+  }
+
+  delete_fsinode(root);
+  fclose(fslist);
+  fclose(fs);
+  return status;
+}
+
+int walk_filesystem(const char *path, struct fsinode **root) {
+  dir_handle_t root_handle;
+  file_handle_t file_handle;
+  int walk_status;
+
+  *root = NULL;
+  walk_status = dir_open(path, &root_handle);
+
+  if (walk_status < 0) {
+    fprintf(stderr, "Unable to list the contents of '%s'.\n", path);
+    return EXIT_FAILURE;
+  }
+
+  else if (walk_status > 0) {
+    printf("WARNING: '%s' does not exist; creating empty filesystem.\n", path);
+    return EXIT_SUCCESS;
+  }
+
+  if ((walk_status = dir_next(path, &root_handle, &file_handle)) >= 0) {
+    do {
+      const char *name = dir_getname(&file_handle);
+
+      if (strcmp(name, ".") && strcmp(name, "..")) {
+        struct fsinode *inode;
+
+        if ((inode = create_fsinode(path, &file_handle)) == NULL) {
+          delete_fsinode(*root);
+          dir_close(&root_handle);
+          *root = NULL;
+
+          return EXIT_FAILURE;
+        }
+
+        if (root != NULL) {
+          inode->next = *root;
+          *root = inode;
+        }
+
+        else {
+          *root = inode;
+        }
+      }
+
+      walk_status = dir_next(path, &root_handle, &file_handle);
+    } while(walk_status == 0);
+  }
+
+  if (walk_status < 0) {
+    fprintf(stderr, "An error occurred while listing files in '%s'\n", path);
+  }
+
+  dir_close(&root_handle);
+  return walk_status > 0 ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+int write_image(FILE *fs, FILE *fslist, const struct fsinode *root) {
+  const struct fsinode *inode;
+  size_t file_offset;
+  unsigned i;
+
+  fprintf(fslist, "// This file is auto-generated by mkfs; do not alter\n\n");
+  fprintf(fslist, "#ifndef FILESYSTEM_H\n#define FILESYSTEM_H\n\n");
+
+  if (root == NULL) {
+    fprintf(fs, "EMPTYFS!");
+    fprintf(fslist, "#endif\n");
+    return EXIT_SUCCESS;
+  }
+
+  fprintf(fslist, "#define CART_FILES");
+
+  for (inode = root; inode != NULL; inode = inode->next) {
+
+    // Convert the filenames into macro-y looking formats.
+    for (i = 0; i < strlen(inode->name); i++) {
+      if (isalpha(inode->name[i]))
+        inode->name[i] = toupper(inode->name[i]);
+      else if (!isdigit(inode->name[i]))
+        inode->name[i] = '_';
+    }
+
+    fprintf(fslist, " \\\n  X(%s)", inode->name);
+  }
+
+  fprintf(fslist, "\n\n");
+
+  for (inode = root, file_offset = 0; inode != NULL; inode = inode->next) {
+    FILE *f;
+
+    fprintf(fslist, "#define CART_OFFS_%s 0x%.8X\n", inode->name,
+        (unsigned int) (file_offset + 0x1000));
+
+    fprintf(fslist, "#define CART_SIZE_%s 0x%.8X\n", inode->name,
+        (unsigned int) inode->size);
+
+    if ((f = fopen(inode->local_path, "rb")) == NULL) {
+      fprintf(stderr, "Failed to open for reading: '%s'\n", inode->local_path);
+      return EXIT_FAILURE;
+    }
+
+    while (!feof(f)) {
+      unsigned char byte;
+
+      if (ferror(f)) {
+        fprintf(stderr, "Failed while reading: '%s'\n", inode->local_path);
+        fclose(f);
+
+        return EXIT_FAILURE;
+      }
+
+      byte = fgetc(f);
+
+      if (!feof(f) && fputc(byte, fs) == EOF) {
+        fprintf(stderr, "Failed while writing: '%s'\n", inode->local_path);
+        fclose(f);
+
+        return EXIT_FAILURE;
+      }
+    }
+
+    file_offset += inode->size;
+
+    if (file_offset & 0x1)
+      file_offset++;
+
+    fclose(f);
+  }
+
+  if (!file_offset) {
+    fprintf(fs, "EMPTYFS!");
+  }
+
+  fprintf(fslist, "\n#endif\n");
+  return EXIT_SUCCESS;
+}
+