// Below is an example of how to load the "Hello, world" resource off
// the cart by submitting an I/O request and blocking until completion.
struct libn64_mq *mq = libn64_mq_create();
- struct libn64_io_request *req = (struct libn64_io_request *)
+ struct libn64_pi_request *req = (struct libn64_pi_request *)
libn64_mq_alloc();
- libn64_io_pack_request(req, mq, 0x100000, // @ 1MB
+ libn64_pi_pack_request(req, mq, 0x100000, // @ 1MB
CART_OFFS_DATA_TXT, CART_SIZE_DATA_TXT);
- libn64_io_submit(LIBN64_IO_CMD_CART2RAM, req);
+ libn64_pi_submit(LIBN64_PI_CMD_FILESYSTEM_LOAD, req);
libn64_recv_message(mq, LIBN64_BLOCK);
libn64_mq_free(req);
CFILES = $(call FIXPATH,\
io/init.c \
- io/thread.c \
+ io/pi_thread.c \
os/fbtext.c \
os/main.c \
os/mm.c \
#include <mq.h>
#include <syscall.h>
-enum libn64_io_command {
- LIBN64_IO_CMD_CART2RAM = 1,
- LIBN64_IO_RCP_INTERRUPT = -3,
- LIBN64_IO_CMD_INVALID = -1,
+enum libn64_pi_command {
+ LIBN64_PI_RCP_INTERRUPT = -3,
+ LIBN64_PI_CMD_FILESYSTEM_LOAD = 1,
+ LIBN64_PI_CMD_INVALID = -1,
};
enum libn64_io_response {
LIBN64_IO_RESP_ERROR = -1,
};
-struct libn64_io_request {
+struct libn64_pi_request {
uint32_t dest_address;
uint32_t src_address;
uint32_t size;
};
libn64func
-static inline libn64_thread libn64_io_get_thread(void) {
- libn64_thread io_thread;
+static inline libn64_thread libn64_pi_get_thread(void) {
+ libn64_thread pi_thread;
__asm__ __volatile__(
".set noat\n\t"
".set gp=default\n\t"
".set at\n\t"
- : "=r" (io_thread)
+ : "=r" (pi_thread)
);
- return io_thread;
+ return pi_thread;
}
libn64func
-static inline void libn64_io_pack_request(struct libn64_io_request *req,
+static inline void libn64_pi_pack_request(struct libn64_pi_request *req,
struct libn64_mq *mq, uint32_t dest_address, uint32_t src_address,
uint32_t size) {
req->dest_address = dest_address;
}
libn64func
-static inline void libn64_io_submit(uint32_t command,
- struct libn64_io_request *request) {
- libn64_thread io_thread = libn64_io_get_thread();
- libn64_sendt_message1(io_thread, command, request);
+static inline void libn64_pi_submit(uint32_t command,
+ struct libn64_pi_request *request) {
+ libn64_thread pi_thread = libn64_pi_get_thread();
+ libn64_sendt_message1(pi_thread, command, request);
}
#endif
// 'LICENSE', which is part of this source code package.
//
-#include <io/thread.h>
+#include <io/pi_thread.h>
#include <libn64.h>
#include <stddef.h>
#include <syscall.h>
libn64func
void libn64_io_init(void) {
- libn64_thread io_thread;
+ libn64_thread pi_thread;
- io_thread = libn64_thread_create(libn64_io_thread, NULL,
+ pi_thread = libn64_thread_create(libn64_pi_thread, NULL,
LIBN64_THREAD_MAX_PRIORITY);
// Store the thread address in the global block.
".set gp=default\n\t"
".set at\n\t"
- :: "r" (io_thread)
+ :: "r" (pi_thread)
: "memory"
);
}
--- /dev/null
+//
+// libn64/io/pi_thread.c: Parallel interface (PI) I/O engine.
+//
+// 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 <io.h>
+#include <io/pi_thread.h>
+#include <libn64.h>
+#include <mq.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <syscall.h>
+
+libn64func
+static void filesystem_load(const struct libn64_pi_request *pi_req);
+
+libn64func
+static void libn64_pi_issue(struct libn64_mq *pi_backlog,
+ uint32_t command, const struct libn64_pi_request *pi_req);
+
+// Issues a request to the PI subsystem to load a file.
+void filesystem_load(const struct libn64_pi_request *pi_req) {
+ uint32_t pi_addr;
+
+ extern char _binary_filesystem_bin_start;
+ static const char *fs_ptr = &_binary_filesystem_bin_start;
+ uint32_t fs_offs;
+
+ // PI transfer lengths must be an even number of bytes.
+ // The source address must also be 2-byte aligned, but the
+ // filesystem packer takes care of doing that for us.
+ uint32_t transfer_size = (pi_req->size + 1) & ~0x1;
+
+ // Cart domain is 0x1000_0000, a 0x1000 header is stapled onto
+ // the cart by checksum, and the cart gets loaded to 0x8000_0400.
+ // Do a bit of fudging to figure out where the data is located.
+ __builtin_memcpy(&fs_offs, &fs_ptr, sizeof(fs_offs));
+ fs_offs -= 0x70000400;
+
+ __asm__ __volatile__(
+ ".set noreorder\n\t"
+
+ "lui %0, 0xA460\n\t"
+ "sw %1, 0x0(%0)\n\t"
+ "sw %2, 0x4(%0)\n\t"
+ "sw %3, 0xC(%0)\n\t"
+
+ ".set reorder\n\t"
+
+ : "=&r" (pi_addr)
+ : "r" (pi_req->dest_address),
+ "r" (pi_req->src_address + fs_offs),
+ "r" (transfer_size)
+ );
+}
+
+// Issues a command to the PI subsystem.
+void libn64_pi_issue(struct libn64_mq *pi_backlog,
+ uint32_t command, const struct libn64_pi_request *pi_req) {
+ void *opaque = NULL;
+
+ do {
+ switch (command) {
+ case LIBN64_PI_CMD_FILESYSTEM_LOAD:
+ filesystem_load(pi_req);
+ return;
+
+ default:
+ libn64_send_message1(pi_req->mq, LIBN64_IO_RESP_ERROR, opaque);
+ command = libn64_recv_message1(pi_backlog, LIBN64_NOBLOCK, &opaque);
+ pi_req = (struct libn64_pi_request *) opaque;
+ break;
+ }
+ } while (pi_req != NULL);
+}
+
+// Thread responsible for processing I/O requests.
+void libn64_pi_thread(void *opaque) {
+ struct libn64_mq *pi_backlog = libn64_mq_create();
+ struct libn64_pi_request *pi_req = NULL;
+
+ // This thread uses PI interrupts to watch for command completion.
+ libn64_thread_reg_intr(libn64_thread_self(), LIBN64_INTERRUPT_PI);
+
+ while (1) {
+ uint32_t command = libn64_recvt_message1(&opaque);
+
+ // RCP signaled an interrupt for the completion of the request.
+ // Notify the requestor of the request commpletion.
+ if ((int32_t) command == LIBN64_PI_RCP_INTERRUPT) {
+ if (pi_req != NULL) {
+ libn64_send_message1(pi_req->mq, LIBN64_IO_RESP_OK, pi_req);
+ command = libn64_recv_message1(pi_backlog, LIBN64_NOBLOCK, &opaque);
+
+ if ((pi_req = (struct libn64_pi_request *) opaque) != NULL)
+ libn64_pi_issue(pi_backlog, command, pi_req);
+ }
+ }
+
+ // A new command arrived; queue it up or issue it.
+ else {
+ if (pi_req != NULL) {
+ libn64_send_message1(pi_backlog, command, opaque);
+ continue;
+ }
+
+ else {
+ pi_req = (struct libn64_pi_request *) opaque;
+ libn64_pi_issue(pi_backlog, command, pi_req);
+ }
+ }
+ }
+}
+
+++ /dev/null
-//
-// libn64/io/thread.c: Parallel and serial I/O engine.
-//
-// 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 <io.h>
-#include <io/thread.h>
-#include <libn64.h>
-#include <mq.h>
-#include <stdint.h>
-#include <syscall.h>
-
-libn64func
-static void libn64_io_issue(const struct libn64_io_request *req);
-
-// Issues a request to the PI subsystem.
-void libn64_io_issue(const struct libn64_io_request *req) {
- uint32_t pi_addr;
-
- extern char _binary_filesystem_bin_start;
- char *fs_ptr = &_binary_filesystem_bin_start;
- uint32_t fs_offs = (uint32_t) fs_ptr - 0x70000400;
-
- __asm__ __volatile__(
- ".set noreorder\n\t"
-
- "lui %0, 0xA460\n\t"
- "sw %1, 0x0(%0)\n\t"
- "sw %2, 0x4(%0)\n\t"
- "sw %3, 0xC(%0)\n\t"
-
- ".set reorder\n\t"
-
- : "=&r" (pi_addr)
- : "r" (req->dest_address),
- "r" (req->src_address + fs_offs),
- "r" (req->size)
- );
-}
-
-// Thread responsible for processing I/O requests.
-void libn64_io_thread(void *opaque) {
- libn64_thread_reg_intr(libn64_thread_self(), LIBN64_INTERRUPT_PI);
-
- struct libn64_mq *backlog = libn64_mq_create();
- struct libn64_io_request *req = NULL;
- int pending = 0;
-
- while (1) {
- uint32_t command = libn64_recvt_message1(&opaque);
-
- switch (command) {
- case LIBN64_IO_RCP_INTERRUPT:
-
- // RCP signaled an interrupt for the completion of the request.
- // Notify the requestor of the commpletion. If the backlog is
- // not empty, issue the next request to the PI subsystem.
- if (req != NULL) {
- libn64_send_message1(req->mq, LIBN64_IO_RESP_OK, req);
-
- if (pending) {
- pending--;
-
- libn64_recv_message1(backlog, LIBN64_BLOCK, &opaque);
- req = (struct libn64_io_request *) opaque;
- libn64_io_issue(req);
- } else {
- req = NULL;
- }
- }
-
- break;
-
- case LIBN64_IO_CMD_CART2RAM:
-
- // If a request is already in progress, add it to the backlog.
- // Otherwise, begin processing the request immediately.
- if (pending) {
- libn64_send_message1(backlog, command, req);
- pending++;
- } else {
- req = (struct libn64_io_request *) opaque;
- libn64_io_issue(req);
- }
-
- break;
-
- default:
- continue;
- }
- }
-}
-
bgezl $a1, libn64_recv_block_thread
lw $k1, 0x8($a0)
lui $v0, 0x8000
+ xor $at, $at, $at
eret
libn64_recv_block_thread:
--- /dev/null
+//
+// libn64/priv_include/io/pi_thread.h: Parallel interface (PI) I/O engine.
+//
+// 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_IO_PI_THREAD_H
+#define LIBN64_PRIV_INCLUDE_IO_PI_THREAD_H
+
+#include <libn64.h>
+
+libn64func
+void libn64_pi_thread(void *opaque);
+
+#endif
+
+++ /dev/null
-//
-// libn64/priv_include/io/thread.h: Parallel and serial I/O engine.
-//
-// 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_IO_THREAD_H
-#define LIBN64_PRIV_INCLUDE_IO_THREAD_H
-
-#include <libn64.h>
-
-libn64func
-void libn64_io_thread(void *opaque);
-
-#endif
-