diff options
author | Bad Diode <bd@badd10de.dev> | 2022-10-15 16:53:50 +0200 |
---|---|---|
committer | Bad Diode <bd@badd10de.dev> | 2022-10-15 16:53:50 +0200 |
commit | d822337f5920c1b639695ebfcf0eb9e49fd5e01a (patch) | |
tree | b9243dc0f239600ac49bc699890d2f11e5feccae | |
parent | 8864a4d8ef6a8597f351f05cfc24b596f24d19e9 (diff) | |
download | uxn64-d822337f5920c1b639695ebfcf0eb9e49fd5e01a.tar.gz uxn64-d822337f5920c1b639695ebfcf0eb9e49fd5e01a.zip |
Add minimal code for a working video output program
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Makefile | 11 | ||||
-rw-r--r-- | src/main.c | 172 | ||||
-rw-r--r-- | src/spec | 15 | ||||
-rw-r--r-- | src/types.h | 30 |
5 files changed, 222 insertions, 7 deletions
@@ -1 +1,2 @@ | |||
1 | build | 1 | build |
2 | a.out | ||
@@ -12,12 +12,8 @@ LIBULTRA := $(LIBULTRA_DIR)/usr/lib/libgultra.a | |||
12 | # Source code location and files to watch for changes. | 12 | # Source code location and files to watch for changes. |
13 | SRC_DIR := src | 13 | SRC_DIR := src |
14 | BUILD_DIR := build | 14 | BUILD_DIR := build |
15 | SRC_MAIN := $(SRC_DIR)/onetri.c \ | 15 | SRC_MAIN := $(SRC_DIR)/main.c |
16 | $(SRC_DIR)/dram_stack.c \ | 16 | SRC_OBJ := |
17 | $(SRC_DIR)/rdp_output.c | ||
18 | SRC_OBJ := $(SRC_DIR)/static.c \ | ||
19 | $(SRC_DIR)/cfb.c \ | ||
20 | $(SRC_DIR)/rsp_cfb.c | ||
21 | OBJECTS := $(patsubst $(SRC_DIR)/%.c, $(BUILD_DIR)/%.o, $(SRC_OBJ)) | 17 | OBJECTS := $(patsubst $(SRC_DIR)/%.c, $(BUILD_DIR)/%.o, $(SRC_OBJ)) |
22 | 18 | ||
23 | WATCH_SRC := $(shell find $(SRC_DIR) -name "*.c" -or -name "*.s" -or -name "*.h") | 19 | WATCH_SRC := $(shell find $(SRC_DIR) -name "*.c" -or -name "*.s" -or -name "*.h") |
@@ -74,10 +70,11 @@ $(BIN): $(ELF) $(OBJECTS) $(WATCH_SRC) | |||
74 | --cpp_command="$(SDK_BIN)/mips32-elf-gcc" \ | 70 | --cpp_command="$(SDK_BIN)/mips32-elf-gcc" \ |
75 | --ld_command="$(SDK_BIN)/mips32-elf-ld" \ | 71 | --ld_command="$(SDK_BIN)/mips32-elf-ld" \ |
76 | --objcopy_command="$(SDK_BIN)/mips32-elf-objcopy" | 72 | --objcopy_command="$(SDK_BIN)/mips32-elf-objcopy" |
73 | rm a.out | ||
77 | $(MAKEMASK) $(BIN) | 74 | $(MAKEMASK) $(BIN) |
78 | 75 | ||
79 | # Test the output .n64 in an emulator. | 76 | # Test the output .n64 in an emulator. |
80 | run: $(BIN) | 77 | run: $(BUILD_DIR) $(BIN) |
81 | # TODO: Test roms with MAME or cen64 instead of mupen64 for better accuracy. | 78 | # TODO: Test roms with MAME or cen64 instead of mupen64 for better accuracy. |
82 | mupen64plus $(BIN) | 79 | mupen64plus $(BIN) |
83 | 80 | ||
diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..57a0965 --- /dev/null +++ b/src/main.c | |||
@@ -0,0 +1,172 @@ | |||
1 | #include <ultra64.h> | ||
2 | |||
3 | #include "types.h" | ||
4 | |||
5 | // Screen size. | ||
6 | #define SCREEN_HEIGHT 240 | ||
7 | #define SCREEN_WIDTH 320 | ||
8 | |||
9 | // Get a pointer to the start (top) of a stack. | ||
10 | #define STACK_START(stack) ((stack) + sizeof((stack))) | ||
11 | |||
12 | // | ||
13 | // Threads and stacks. | ||
14 | // | ||
15 | |||
16 | #define STACK_SIZE 8 * 1024 | ||
17 | static OSThread idle_thread; | ||
18 | static OSThread main_thread; | ||
19 | static u8 idle_thread_stack[STACK_SIZE] __attribute__((aligned(8))); | ||
20 | static u8 main_thread_stack[STACK_SIZE] __attribute__((aligned(8))); | ||
21 | static u64 dram_stack[SP_DRAM_STACK_SIZE64] __attribute__((aligned(16))); | ||
22 | u64 boot_stack[STACK_SIZE / sizeof(u64)] __attribute__((aligned(8))); | ||
23 | |||
24 | // | ||
25 | // Framebuffers. | ||
26 | // | ||
27 | |||
28 | u16 framebuffers[2][SCREEN_WIDTH * SCREEN_HEIGHT]; | ||
29 | u16 rsp_framebuffer[SCREEN_WIDTH * SCREEN_HEIGHT]; | ||
30 | static int current_fb = 0; | ||
31 | |||
32 | // | ||
33 | // Message buffers and queues. | ||
34 | // | ||
35 | |||
36 | #define NUM_PI_MSGS 8 | ||
37 | static OSMesg pi_msg[NUM_PI_MSGS]; | ||
38 | static OSMesg rdp_msg_buf, retrace_msg_buf; | ||
39 | static OSMesgQueue pi_msg_queue; | ||
40 | static OSMesgQueue rdp_msg_queue; | ||
41 | static OSMesgQueue retrace_msg_queue; | ||
42 | |||
43 | // Handle for rom memory. | ||
44 | OSPiHandle *rom_handle; | ||
45 | |||
46 | static void | ||
47 | main_proc(void *arg) { | ||
48 | (void)arg; | ||
49 | |||
50 | // Setup the message queues | ||
51 | osCreateMesgQueue(&rdp_msg_queue, &rdp_msg_buf, 1); | ||
52 | osSetEventMesg(OS_EVENT_DP, &rdp_msg_queue, NULL); | ||
53 | osCreateMesgQueue(&retrace_msg_queue, &retrace_msg_buf, 1); | ||
54 | osViSetEvent(&retrace_msg_queue, NULL, 1); | ||
55 | |||
56 | // NOTE: Graphics drawing pipeline: | ||
57 | // 1. Transfer gfx data from ROM to RDRAM (CPU -- N64 OS) | ||
58 | // 2. Create display list in RDRAM from img data (CPU -- Game application) | ||
59 | // 3. Transfer display list and graphics microcode to the RSP (CPU -- N64 OS) | ||
60 | // 4. Conversion process (RSP -- GFX microcode) | ||
61 | // 5. Transfer converted data to RDP (RSP -- GFX microcode) | ||
62 | // 6. Manipulation process (RDP) | ||
63 | // 7. Transfer manipulated data to framebuffer (RDP) | ||
64 | // 8. Transfer manipulated data to the video interface (CPU -- N64 OS) | ||
65 | // 9. Trasfer digital data to DAC (Video Interface) | ||
66 | // 10. Video signal is produced. | ||
67 | |||
68 | // Main loop. | ||
69 | size_t i = 0; | ||
70 | while (true) { | ||
71 | i += 2; | ||
72 | |||
73 | // Task list. | ||
74 | OSTask tlist = (OSTask){ | ||
75 | { | ||
76 | M_GFXTASK, // task type | ||
77 | OS_TASK_DP_WAIT, // task flags | ||
78 | NULL, // boot ucode pointer (fill in later) | ||
79 | 0, // boot ucode size (fill in later) | ||
80 | NULL, // task ucode pointer (fill in later) | ||
81 | SP_UCODE_SIZE, // task ucode size | ||
82 | NULL, // task ucode data pointer (fill in later) | ||
83 | SP_UCODE_DATA_SIZE, // task ucode data size | ||
84 | &dram_stack[0], // task dram stack pointer | ||
85 | SP_DRAM_STACK_SIZE8, // task dram stack size | ||
86 | NULL, // FIFO buffer start. | ||
87 | NULL, // FIFO buffer end. | ||
88 | NULL, // task data pointer (fill in later) | ||
89 | 0, // task data size (fill in later) | ||
90 | NULL, // task yield buffer ptr (not used here) | ||
91 | 0 // task yield buffer size (not used here) | ||
92 | }, | ||
93 | }; | ||
94 | |||
95 | // Graphics command list. | ||
96 | Gfx glist[2048]; | ||
97 | |||
98 | OSTask *tlistp = &tlist; | ||
99 | Gfx *glistp = glist; | ||
100 | |||
101 | // Tell RCP where each segment is. | ||
102 | gSPSegment(glistp++, 0, 0x0); // Physical address segment | ||
103 | gSPSegment(glistp++, 2, OS_K0_TO_PHYSICAL(framebuffers[current_fb])); | ||
104 | |||
105 | // Clear color framebuffer. | ||
106 | Gfx clearcfb_dl[] = { | ||
107 | gsDPSetCycleType(G_CYC_FILL), | ||
108 | gsDPSetColorImage(G_IM_FMT_RGBA, G_IM_SIZ_16b, SCREEN_WIDTH, rsp_framebuffer), | ||
109 | gsDPSetFillColor(GPACK_RGBA5551(64, 64, 255, 1) << 16 | GPACK_RGBA5551(i, i, i, 1)), | ||
110 | gsDPFillRectangle(0, 0, SCREEN_WIDTH-1, SCREEN_HEIGHT-1), | ||
111 | gsSPEndDisplayList(), | ||
112 | }; | ||
113 | gSPDisplayList(glistp++, clearcfb_dl); | ||
114 | gDPFullSync(glistp++); | ||
115 | gSPEndDisplayList(glistp++); | ||
116 | |||
117 | // Build graphics task: | ||
118 | tlistp->t.ucode_boot = (u64 *) rspbootTextStart; | ||
119 | tlistp->t.ucode_boot_size = (u32)rspbootTextEnd - (u32)rspbootTextStart; | ||
120 | tlistp->t.ucode = (u64 *) gspF3DEX2_fifoTextStart; | ||
121 | tlistp->t.ucode_data = (u64 *) gspF3DEX2_fifoDataStart; | ||
122 | tlistp->t.data_ptr = (u64 *) glist; | ||
123 | tlistp->t.data_size = (u32)((glistp - glist) * sizeof(Gfx)); | ||
124 | |||
125 | // Start up the RSP task. | ||
126 | osSpTaskStart(tlistp); | ||
127 | |||
128 | // Wait for RDP completion. | ||
129 | osRecvMesg(&rdp_msg_queue, NULL, OS_MESG_BLOCK); | ||
130 | |||
131 | // Swap buffers. | ||
132 | osViSwapBuffer(framebuffers[current_fb]); | ||
133 | |||
134 | // Make sure there isn't an old retrace in queue | ||
135 | // (assumes queue has a depth of 1). | ||
136 | if (MQ_IS_FULL(&retrace_msg_queue)) { | ||
137 | osRecvMesg(&retrace_msg_queue, NULL, OS_MESG_BLOCK); | ||
138 | } | ||
139 | |||
140 | // Wait for Vertical retrace to finish swap buffers. | ||
141 | osRecvMesg(&retrace_msg_queue, NULL, OS_MESG_BLOCK); | ||
142 | current_fb ^= 1; | ||
143 | } | ||
144 | } | ||
145 | |||
146 | static void | ||
147 | idle_proc(void *arg) { | ||
148 | (void)arg; | ||
149 | // Initialize video. | ||
150 | osCreateViManager(OS_PRIORITY_VIMGR); | ||
151 | osViSetMode(&osViModeTable[OS_VI_NTSC_LAN1]); | ||
152 | |||
153 | // Start PI Mgr for access to cartridge. | ||
154 | osCreatePiManager((OSPri)OS_PRIORITY_PIMGR, &pi_msg_queue, pi_msg, NUM_PI_MSGS); | ||
155 | |||
156 | // Create main thread. | ||
157 | osCreateThread(&main_thread, 3, main_proc, NULL, STACK_START(main_thread_stack), 10); | ||
158 | osStartThread(&main_thread); | ||
159 | |||
160 | // Become the idle thread. | ||
161 | osSetThreadPri(0, 0); | ||
162 | for (;;); | ||
163 | } | ||
164 | |||
165 | |||
166 | void | ||
167 | boot(void) { | ||
168 | osInitialize(); | ||
169 | rom_handle = osCartRomInit(); | ||
170 | osCreateThread(&idle_thread, 1, idle_proc, NULL, STACK_START(idle_thread_stack), 10); | ||
171 | osStartThread(&idle_thread); | ||
172 | } | ||
diff --git a/src/spec b/src/spec new file mode 100644 index 0000000..4f7ab6b --- /dev/null +++ b/src/spec | |||
@@ -0,0 +1,15 @@ | |||
1 | beginseg | ||
2 | name "code" | ||
3 | flags BOOT OBJECT | ||
4 | entry boot | ||
5 | stack boot_stack + STACK_SIZE | ||
6 | include "build/blank.elf" | ||
7 | include "/opt/n64sdk/libultra/usr/lib/PR/rspboot.o" | ||
8 | include "/opt/n64sdk/libultra/usr/lib/PR/gspF3DEX2.xbus.o" | ||
9 | include "/opt/n64sdk/libultra/usr/lib/PR/gspF3DEX2.fifo.o" | ||
10 | endseg | ||
11 | |||
12 | beginwave | ||
13 | name "build/onetri" | ||
14 | include "code" | ||
15 | endwave | ||
diff --git a/src/types.h b/src/types.h new file mode 100644 index 0000000..ef9a47a --- /dev/null +++ b/src/types.h | |||
@@ -0,0 +1,30 @@ | |||
1 | #ifndef _COMMON_H | ||
2 | #define _COMMON_H | ||
3 | |||
4 | #include <stdbool.h> | ||
5 | #include <stddef.h> | ||
6 | #include <stdint.h> | ||
7 | |||
8 | typedef uint8_t u8; | ||
9 | typedef uint16_t u16; | ||
10 | typedef uint32_t u32; | ||
11 | typedef uint64_t u64; | ||
12 | typedef int8_t s8; | ||
13 | typedef int16_t s16; | ||
14 | typedef int32_t s32; | ||
15 | typedef int64_t s64; | ||
16 | typedef volatile u8 vu8; | ||
17 | typedef volatile u16 vu16; | ||
18 | typedef volatile u32 vu32; | ||
19 | typedef volatile u64 vu64; | ||
20 | typedef volatile s8 vs8; | ||
21 | typedef volatile s16 vs16; | ||
22 | typedef volatile s32 vs32; | ||
23 | typedef volatile s64 vs64; | ||
24 | |||
25 | #define KB(N) ((u64)(N) * 1024) | ||
26 | #define MB(N) ((u64)KB(N) * 1024) | ||
27 | #define GB(N) ((u64)MB(N) * 1024) | ||
28 | #define TB(N) ((u64)GB(N) * 1024) | ||
29 | |||
30 | #endif // _COMMON_H | ||