Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Put literally anything on the screen |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA3-256: |
d5c698fc12d6f7036e81f3a6f96ed7e0 |
User & Date: | ryno 2025-01-30 10:29:18 |
About
Video output works!
Right now it just outputs yellow (specifically: 320×240 16-bit pixels of 31,31,0,0). There are some elements about the VI that I ain't quite certain about, like good values for the X and Y scale/offset (the n64brew wiki doesn't document them all that well, and libdragon just handles them with convoluted bitshifting and no explanation; all I know is that they're fixed-point numbers, i.e. functionally integers). Also, I'm pretty sure it borks things because there are no interrupt handlers setup yet.
Context
2025-02-04
| ||
01:05 | Add some memory-related helpers and make `print()` follow the same API as other Zig `print()`s check-in: b1f425d583 user: ryno tags: trunk | |
2025-01-30
| ||
10:29 | Put literally anything on the screen check-in: d5c698fc12 user: ryno tags: trunk | |
04:35 | Handle Zig panics check-in: 88241cf9c2 user: ryno tags: trunk | |
Changes
Added src/VI.zig.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 | const base = 0xa4400000; const AAMode = enum(u2) { EnabledAlwaysFetch = 0, Enabled = 1, Disabled = 2, DisabledNoResampling = 3, }; const PixelFormat = enum(u2) { Off = 0, Reserved = 1, Depth16 = 2, Depth32 = 3, }; pub const Pixel16 = packed struct(u16) { a: u1, b: u5, g: u5, r: u5, }; pub const Pixel32 = packed struct(u32) { r: u8, g: u8, b: u8, a: u8, }; const Control = packed struct(u32) { pixel_format: PixelFormat, enable_gamma_dither: bool, enable_gamma: bool, enable_divot: bool, enable_vbus_clock: bool = false, serrate: bool, test_mode: bool, aa_mode: AAMode, reserved1: bool = false, kill_we: bool, pixel_advance: u4, enable_dedither: bool, reserved2: u15 = 0, }; pub const control: *volatile Control = @ptrFromInt(base + 0x0); pub const origin: *volatile u32 = @ptrFromInt(base + 0x4); // u24 pub const width: *volatile u32 = @ptrFromInt(base + 0x8); // u12 pub const v_interrupt: *volatile u32 = @ptrFromInt(base + 0xc); // u10 const VCurrent = packed struct(u32) { field: bool, half_line: u9, reserved: u22, }; pub const v_current: *volatile VCurrent = @ptrFromInt(base + 0x10); const Burst = packed struct(u32) { hsync_width: u8, burst_width: u8, vsync_height: u4, burst_start: u10, reserved: u2 = 0, }; pub const burst: *volatile Burst = @ptrFromInt(base + 0x14); pub const v_total: *volatile u32 = @ptrFromInt(base + 0x18); // u10 const HTotal = packed struct(u32) { total: u12, reserved1: u4 = 0, leap: u5, reserved2: u11 = 0, }; pub const h_total: *volatile HTotal = @ptrFromInt(base + 0x1c); const HTotalLeap = packed struct(u32) { b: u12, reserved1: u4 = 0, a: u12, reserved2: u4 = 0, }; pub const h_total_leap: *volatile HTotalLeap = @ptrFromInt(base + 0x1c); const HVideo = packed struct(u32) { end: u10, reserved1: u6 = 0, start: u10, reserved2: u6 = 0, }; pub const h_video: *volatile HVideo = @ptrFromInt(base + 0x24); const VVideo = packed struct(u32) { end: u10, reserved1: u6 = 0, start: u10, reserved2: u6 = 0, }; pub const v_video: *volatile VVideo = @ptrFromInt(base + 0x28); const VBurst = packed struct(u32) { end: u10, reserved1: u6 = 0, start: u10, reserved2: u6 = 0, }; pub const v_burst: *volatile VBurst = @ptrFromInt(base + 0x2c); const XScale = packed struct(u32) { scale: u12, reserved1: u4 = 0, offset: u12, reserved2: u4 = 0, }; pub const x_scale: *volatile XScale = @ptrFromInt(base + 0x30); const YScale = packed struct(u32) { scale: u12, reserved1: u4 = 0, offset: u10, unused: u2 = 0, reserved2: u4 = 0, }; pub const y_scale: *volatile YScale = @ptrFromInt(base + 0x34); pub const test_addr: *volatile u32 = @ptrFromInt(base + 0x38); // u6 pub const staged_data: *volatile u32 = @ptrFromInt(base + 0x3c); // ----- const VideoMode = enum { NTSC, PAL, MPAL, }; const SetupOptions = struct { mode: VideoMode = .NTSC, format: PixelFormat = .Depth16, width: u12 = 320, height: u12 = 240, interlace: bool = false, antialias: bool = false, resample: bool = false, dedither: bool = false, gamma_dither: bool = false, gamma: bool = false, ique: bool = false, }; pub fn setup(opts: SetupOptions) void { // Defaults based on guidance in // https://n64brew.dev/wiki/Video_Interface control.* = .{ .pixel_format = opts.format, .enable_gamma_dither = opts.gamma_dither, .enable_gamma = opts.gamma, .enable_divot = opts.antialias, .enable_vbus_clock = false, .serrate = opts.interlace, .test_mode = false, .aa_mode = if (opts.dedither) .EnabledAlwaysFetch else .Disabled, .kill_we = false, .pixel_advance = if (opts.ique) 1 else 3, .enable_dedither = opts.dedither, }; width.* = opts.width; v_interrupt.* = 2; burst.* = switch (opts.mode) { // FIXME: does PAL-M use NTSC or PAL values here? Or a mix of // both? The n64brew wiki doesn't specify. Assuming it uses // NTSC values since it's supposed to use NTSC dimensions with // PAL colors. If any Brazilians could test this out and make // sure this works as expected, that'd be great. .NTSC, .MPAL => .{ .hsync_width = 57, .burst_width = 34, .vsync_height = 5, .burst_start = 62, }, .PAL => .{ .hsync_width = 58, .burst_width = 35, .vsync_height = 4, .burst_start = 64, }, }; v_total.* = switch (opts.mode) { .NTSC, .MPAL => if (opts.interlace) 524 else 525, .PAL => if (opts.interlace) 624 else 625, }; h_total.* = switch (opts.mode) { .NTSC => .{ .total = 3093, .leap = 0, }, .PAL => .{ .total = 3177, .leap = 0x15 }, // FIXME: should PAL-M leap be 0x15 or 0? .MPAL => .{ .total = 3090, .leap = 0, }, }; if (opts.mode == .PAL) h_total_leap.* = .{ .a = 3182, .b = 3183 }; h_video.* = switch (opts.mode) { .NTSC, .MPAL => .{ .start = 108, .end = 748 }, .PAL => .{ .start = 128, .end = 768 }, }; v_video.* = switch (opts.mode) { .NTSC, .MPAL => .{ .start = 0x025, .end = 0x1ff }, .PAL => .{ .start = 0x05f, .end = 0x239 }, }; v_burst.* = switch (opts.mode) { .NTSC, .MPAL => .{ .start = 0x00e, .end = 0x204 }, .PAL => .{ .start = 0x009, .end = 0x26b }, }; // n64brew wiki doesn't go into much detail on these... x_scale.* = .{ .scale = 0x100, .offset = 0, }; y_scale.* = .{ .scale = 0x100, .offset = 0, }; } |
Changes to src/main.zig.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | const std = @import("std"); // BEGIN MEMORY MAP STUFF const dmem: *align(4) [4096]u8 = @ptrFromInt(0xa4000000); const imem: *align(4) [4096]u8 = @ptrFromInt(0xa4001000); /// Peripheral Interface memory regions. See /// https://n64brew.dev/wiki/Peripheral_Interface for details. const PI = @import("./PI.zig"); /// SummerCart 64 memory map and helper functions. See /// https://github.com/Polprzewodnikowy/SummerCart64/blob/main/docs/01_memory_map.md /// for details. const SC64 = @import("./SC64.zig"); /// IS-Viewer memory map and helper functions. This operates in | > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | const std = @import("std"); // BEGIN MEMORY MAP STUFF const dmem: *align(4) [4096]u8 = @ptrFromInt(0xa4000000); const imem: *align(4) [4096]u8 = @ptrFromInt(0xa4001000); /// Peripheral Interface memory regions. See /// https://n64brew.dev/wiki/Peripheral_Interface for details. const PI = @import("./PI.zig"); const VI = @import("./VI.zig"); /// SummerCart 64 memory map and helper functions. See /// https://github.com/Polprzewodnikowy/SummerCart64/blob/main/docs/01_memory_map.md /// for details. const SC64 = @import("./SC64.zig"); /// IS-Viewer memory map and helper functions. This operates in |
︙ | ︙ | |||
91 92 93 94 95 96 97 98 99 100 101 102 103 104 | Debug.print("All your Nintendo 64 are belong to us.\n"); Debug.print("This is another message from Zig.\n"); const dmem_test_pat: [16]u8 align(4) = .{ 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0xba, 0xbe }; PI.writeBytes(dmem, &dmem_test_pat); while (true) {} } pub fn panic( msg: []const u8, _: ?*std.builtin.StackTrace, _: ?usize | > > > > | 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | Debug.print("All your Nintendo 64 are belong to us.\n"); Debug.print("This is another message from Zig.\n"); const dmem_test_pat: [16]u8 align(4) = .{ 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0xba, 0xbe }; PI.writeBytes(dmem, &dmem_test_pat); const pxl: VI.Pixel16 = .{ .r = 31, .g = 31, .b = 0, .a = 0 }; const test_framebuffer: [320][240]VI.Pixel16 = .{.{pxl} ** 240} ** 320; VI.origin.* = @intFromPtr(&test_framebuffer); VI.setup(.{}); while (true) {} } pub fn panic( msg: []const u8, _: ?*std.builtin.StackTrace, _: ?usize |
︙ | ︙ |