diff options
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | apu.c | 55 | ||||
| -rw-r--r-- | apu.h | 105 | ||||
| -rw-r--r-- | cpu.c | 13 |
4 files changed, 171 insertions, 4 deletions
| @@ -1,7 +1,7 @@ | |||
| 1 | CFLAGS = -O2 -Wall -Wextra -pedantic -Wno-unused-parameter | 1 | CFLAGS = -O2 -Wall -Wextra -pedantic -Wno-unused-parameter |
| 2 | 2 | ||
| 3 | nes: | 3 | nes: |
| 4 | ${CC} ${CFLAGS} -o nes cpu.c rom.c ppu.c | 4 | ${CC} ${CFLAGS} -o nes cpu.c rom.c ppu.c apu.c |
| 5 | 5 | ||
| 6 | test: nes | 6 | test: nes |
| 7 | ./nes ${HOME}/src/other/nes-test-roms/other/nestest.nes | 7 | ./nes ${HOME}/src/other/nes-test-roms/other/nestest.nes |
| @@ -0,0 +1,55 @@ | |||
| 1 | #include <stdio.h> | ||
| 2 | #include <string.h> | ||
| 3 | |||
| 4 | #include "apu.h" | ||
| 5 | |||
| 6 | struct apu apu = {0}; | ||
| 7 | |||
| 8 | void | ||
| 9 | apu_init(void) | ||
| 10 | { | ||
| 11 | memset(&apu, 0, sizeof(apu)); | ||
| 12 | } | ||
| 13 | |||
| 14 | uint8_t | ||
| 15 | apu_read(uint16_t addr) | ||
| 16 | { | ||
| 17 | switch (addr) { | ||
| 18 | /* pulse1 */ | ||
| 19 | case 0x4000: | ||
| 20 | return (apu.pulse1.duty << 6) | (apu.pulse1.envelope << 5) | ||
| 21 | | (apu.pulse1.const_vol << 4) | apu.pulse1.envelope_vol; | ||
| 22 | case 0x4001: | ||
| 23 | return (apu.pulse1.sweep_enable << 7) | (apu.pulse1.sweep_period << 4) | ||
| 24 | | (apu.pulse1.sweep_negative << 3) | apu.pulse1.sweep_shift_count; | ||
| 25 | case 0x4002: | ||
| 26 | return apu.pulse1.timer_low; | ||
| 27 | case 0x4003: | ||
| 28 | return (apu.pulse1.length_counter_load << 3) | apu.pulse1.timer_high; | ||
| 29 | /* pulse2 */ | ||
| 30 | case 0x4004: | ||
| 31 | return (apu.pulse2.duty << 6) | (apu.pulse2.envelope << 5) | ||
| 32 | | (apu.pulse2.const_vol << 4) | apu.pulse2.envelope_vol; | ||
| 33 | case 0x4005: | ||
| 34 | return (apu.pulse1.sweep_enable << 7) | (apu.pulse1.sweep_period << 4) | ||
| 35 | | (apu.pulse1.sweep_negative << 3) | apu.pulse1.sweep_shift_count; | ||
| 36 | case 0x4006: | ||
| 37 | return apu.pulse1.timer_low; | ||
| 38 | case 0x4007: | ||
| 39 | return (apu.pulse1.length_counter_load << 3) | apu.pulse1.timer_high; | ||
| 40 | /* status */ | ||
| 41 | case 0x4015: | ||
| 42 | return (apu.status.dmc_int << 7) | (apu.status.frame_int << 6) | ||
| 43 | | (apu.status.dmc_active << 4) | (apu.status.lc_noise << 3) | ||
| 44 | | (apu.status.lc_triangle << 2) | (apu.status.lc_pulse2 << 1) | ||
| 45 | | apu.status.lc_pulse1; | ||
| 46 | default: | ||
| 47 | fprintf(stderr, "Invalid APU read at $%04X!\n", addr); | ||
| 48 | return 0; | ||
| 49 | }; | ||
| 50 | } | ||
| 51 | |||
| 52 | void | ||
| 53 | apu_write(uint16_t addr, uint8_t byte) | ||
| 54 | { | ||
| 55 | } | ||
| @@ -0,0 +1,105 @@ | |||
| 1 | #ifndef APU_H | ||
| 2 | #define APU_H | ||
| 3 | |||
| 4 | #include <stdint.h> | ||
| 5 | |||
| 6 | struct pulse { | ||
| 7 | /* $4000/$4004 */ | ||
| 8 | uint8_t duty : 2; | ||
| 9 | uint8_t envelope : 1; | ||
| 10 | uint8_t const_vol : 1; | ||
| 11 | uint8_t envelope_vol : 4; | ||
| 12 | /* $4001/$4005 */ | ||
| 13 | uint8_t sweep_enable : 1; | ||
| 14 | uint8_t sweep_period : 3; | ||
| 15 | uint8_t sweep_negative : 1; | ||
| 16 | uint8_t sweep_shift_count : 3; | ||
| 17 | /* $4002/$4006 */ | ||
| 18 | uint8_t timer_low; | ||
| 19 | /* $4003/$4007 */ | ||
| 20 | uint8_t length_counter_load : 5; | ||
| 21 | uint8_t timer_high : 3; | ||
| 22 | }; | ||
| 23 | |||
| 24 | struct triangle { | ||
| 25 | /* $4008 */ | ||
| 26 | uint8_t counter_disable : 1; | ||
| 27 | uint8_t counter_reload : 7; | ||
| 28 | /* $400A */ | ||
| 29 | uint8_t timer_low; | ||
| 30 | /* $400B */ | ||
| 31 | uint8_t counter_load : 5; | ||
| 32 | uint8_t timer_high : 3; | ||
| 33 | }; | ||
| 34 | |||
| 35 | struct noise { | ||
| 36 | /* $400C */ | ||
| 37 | uint8_t padding1 : 2; | ||
| 38 | uint8_t loop_envelope : 1; | ||
| 39 | uint8_t constant_volume : 1; | ||
| 40 | uint8_t envelope_volume : 4; | ||
| 41 | /* $400E */ | ||
| 42 | uint8_t loop_noise : 1; | ||
| 43 | uint8_t padding2 : 3; | ||
| 44 | uint8_t noise_period : 4; | ||
| 45 | /* $400F */ | ||
| 46 | uint8_t length_counter_load : 5; | ||
| 47 | uint8_t padding3 : 3; | ||
| 48 | }; | ||
| 49 | |||
| 50 | struct dmc { | ||
| 51 | /* $4010 */ | ||
| 52 | uint8_t irq_enable : 1; | ||
| 53 | uint8_t loop : 1; | ||
| 54 | uint8_t padding1 : 2; | ||
| 55 | uint8_t frequency : 4; | ||
| 56 | /* $4011 */ | ||
| 57 | uint8_t padding2 : 1; | ||
| 58 | uint8_t load_counter : 7; | ||
| 59 | /* $4012 */ | ||
| 60 | uint8_t sample_addr; | ||
| 61 | /* $4013 */ | ||
| 62 | uint8_t sample_length; | ||
| 63 | }; | ||
| 64 | |||
| 65 | struct apu { | ||
| 66 | /* $4000-$4013 (write) */ | ||
| 67 | struct pulse pulse1; | ||
| 68 | struct pulse pulse2; | ||
| 69 | struct triangle triangle; | ||
| 70 | struct noise noise; | ||
| 71 | struct dmc dmc; | ||
| 72 | /* $4015 (write) */ | ||
| 73 | struct control { | ||
| 74 | uint8_t padding : 3; | ||
| 75 | uint8_t dmc_enable : 1; | ||
| 76 | uint8_t noise_enable : 1; | ||
| 77 | uint8_t triangle_enable : 1; | ||
| 78 | uint8_t pulse2_enable : 1; | ||
| 79 | uint8_t pulse1_enable : 1; | ||
| 80 | } control; | ||
| 81 | /* $4015 (read) */ | ||
| 82 | struct status { | ||
| 83 | uint8_t dmc_int : 1; | ||
| 84 | uint8_t frame_int : 1; | ||
| 85 | uint8_t padding : 1; | ||
| 86 | uint8_t dmc_active : 1; | ||
| 87 | uint8_t lc_noise : 1; | ||
| 88 | uint8_t lc_triangle : 1; | ||
| 89 | uint8_t lc_pulse2 : 1; | ||
| 90 | uint8_t lc_pulse1 : 1; | ||
| 91 | } status; | ||
| 92 | /* $4017 (write) */ | ||
| 93 | struct frame_counter { | ||
| 94 | uint8_t mode : 1; | ||
| 95 | uint8_t irq_inhibit : 1; | ||
| 96 | uint8_t padding : 6; | ||
| 97 | } frame_counter; | ||
| 98 | }; | ||
| 99 | extern struct apu apu; | ||
| 100 | |||
| 101 | void apu_tick(void); | ||
| 102 | uint8_t apu_read(uint16_t addr); | ||
| 103 | void apu_write(uint16_t addr, uint8_t byte); | ||
| 104 | |||
| 105 | #endif /* APU_H */ | ||
| @@ -5,10 +5,11 @@ | |||
| 5 | #include <stdlib.h> | 5 | #include <stdlib.h> |
| 6 | #include <string.h> | 6 | #include <string.h> |
| 7 | 7 | ||
| 8 | #include "apu.h" | ||
| 8 | #include "cpu.h" | 9 | #include "cpu.h" |
| 9 | #include "opcodes.h" | 10 | #include "opcodes.h" |
| 10 | #include "rom.h" | ||
| 11 | #include "ppu.h" | 11 | #include "ppu.h" |
| 12 | #include "rom.h" | ||
| 12 | 13 | ||
| 13 | #define MAX(a, b) ((a > b) ? a : b) | 14 | #define MAX(a, b) ((a > b) ? a : b) |
| 14 | 15 | ||
| @@ -53,9 +54,10 @@ peek(uint16_t addr) | |||
| 53 | return rom.prg_rom[addr - 0x8000]; | 54 | return rom.prg_rom[addr - 0x8000]; |
| 54 | else | 55 | else |
| 55 | fprintf(stderr, "PRG ROG size is not 0x4000 nor 0x8000\n"), exit(1); | 56 | fprintf(stderr, "PRG ROG size is not 0x4000 nor 0x8000\n"), exit(1); |
| 56 | } else { | 57 | } else if ((addr >= 0x4000 && addr <= 0x4013) || addr == 0x4015 || addr == 0x4017) |
| 58 | return apu_read(addr); | ||
| 59 | else | ||
| 57 | return memory[addr]; | 60 | return memory[addr]; |
| 58 | } | ||
| 59 | } | 61 | } |
| 60 | 62 | ||
| 61 | static uint16_t | 63 | static uint16_t |
| @@ -68,6 +70,11 @@ peek16(uint16_t addr) | |||
| 68 | static void | 70 | static void |
| 69 | memwrite(uint16_t addr, uint8_t byte) | 71 | memwrite(uint16_t addr, uint8_t byte) |
| 70 | { | 72 | { |
| 73 | if (addr >= 0x4000 && addr <= 0x4017) { | ||
| 74 | apu_write(addr, byte); | ||
| 75 | return; | ||
| 76 | } | ||
| 77 | |||
| 71 | MEMORY_MIRROR(addr); | 78 | MEMORY_MIRROR(addr); |
| 72 | 79 | ||
| 73 | memory[addr] = byte; | 80 | memory[addr] = byte; |
