const std = @import("std"); pub const screen_width: u16 = 256; pub const screen_height: u16 = 240; pub const Region = enum { ntsc, pal, /// Native CPU clock frequency in Hz. pub fn cpuClockRate(self: Region) u32 { return switch (self) { .ntsc => 1_789_773, .pal => 1_662_607, }; } /// Total scanlines per frame (262 for NTSC, 312 for PAL). pub fn totalScanlines(self: Region) u16 { return switch (self) { .ntsc => 262, .pal => 312, }; } /// Scanline index of the pre-render scanline (261 for NTSC, 311 for PAL). pub fn preRenderScanline(self: Region) u16 { return switch (self) { .ntsc => 261, .pal => 311, }; } /// Number of VBlank scanlines. pub fn vblankScanlines(self: Region) u16 { return switch (self) { .ntsc => 20, .pal => 70, }; } /// CPU cycles to suppress PPU register writes after power/reset (~1 frame). pub fn powerUpSuppressionCycles(self: Region) u32 { return switch (self) { .ntsc => 29_658, .pal => 33_132, }; } }; /// Nametable mirroring mode used by PPU CIRAM and Cartridge mappers. pub const Mirroring = enum { horizontal, vertical, single_screen_lower, single_screen_upper, four_screen, }; /// Standard 8-button NES controller layout. pub const Button = enum(u8) { a = 1 << 0, b = 1 << 1, select = 1 << 2, start = 1 << 3, up = 1 << 4, down = 1 << 5, left = 1 << 6, right = 1 << 7, }; test "region properties" { const ntsc: Region = .ntsc; try std.testing.expectEqual(@as(u32, 1_789_773), ntsc.cpuClockRate()); try std.testing.expectEqual(@as(u16, 262), ntsc.totalScanlines()); try std.testing.expectEqual(@as(u16, 261), ntsc.preRenderScanline()); try std.testing.expectEqual(@as(u32, 29_658), ntsc.powerUpSuppressionCycles()); const pal: Region = .pal; try std.testing.expectEqual(@as(u32, 1_662_607), pal.cpuClockRate()); try std.testing.expectEqual(@as(u16, 312), pal.totalScanlines()); try std.testing.expectEqual(@as(u16, 311), pal.preRenderScanline()); try std.testing.expectEqual(@as(u32, 33_132), pal.powerUpSuppressionCycles()); }