diff options
Diffstat (limited to 'cpu.c')
| -rw-r--r-- | cpu.c | 64 |
1 files changed, 47 insertions, 17 deletions
| @@ -42,11 +42,25 @@ enum addressing_mode { | |||
| 42 | AM_INDIRECT_INDEXED, | 42 | AM_INDIRECT_INDEXED, |
| 43 | }; | 43 | }; |
| 44 | 44 | ||
| 45 | uint8_t memory[0x10000]; | 45 | /* 64K address space, 16bit words */ |
| 46 | uint8_t memory[0x16000]; | ||
| 46 | 47 | ||
| 47 | /* example program taken from https://bugzmanov.github.io/nes_ebook/chapter_3_1.html */ | 48 | /* example program taken from https://bugzmanov.github.io/nes_ebook/chapter_3_1.html */ |
| 48 | uint8_t program[] = { 0xa9, 0xc0, 0xaa, 0xe8, 0x00 }; | 49 | uint8_t program[] = { 0xa9, 0xc0, 0xaa, 0xe8, 0x00 }; |
| 49 | 50 | ||
| 51 | uint8_t | ||
| 52 | peek(uint16_t addr) | ||
| 53 | { | ||
| 54 | return memory[addr]; | ||
| 55 | } | ||
| 56 | |||
| 57 | uint16_t | ||
| 58 | peek16(uint16_t addr) | ||
| 59 | { | ||
| 60 | /* bytes are stored in little-endian (low then high) */ | ||
| 61 | return (uint16_t)memory[addr] | ((uint16_t)memory[addr + 1] << 8); | ||
| 62 | } | ||
| 63 | |||
| 50 | void | 64 | void |
| 51 | brk(void) | 65 | brk(void) |
| 52 | { | 66 | { |
| @@ -79,39 +93,43 @@ inx(void) | |||
| 79 | void | 93 | void |
| 80 | lda(enum addressing_mode mode) | 94 | lda(enum addressing_mode mode) |
| 81 | { | 95 | { |
| 82 | uint8_t val; | 96 | uint8_t arg, val; |
| 97 | |||
| 98 | arg = peek(regs.pc++); | ||
| 83 | 99 | ||
| 84 | switch (mode) { | 100 | switch (mode) { |
| 85 | case AM_IMMEDIATE: /* $A9 */ | 101 | case AM_IMMEDIATE: /* $A9 */ |
| 86 | val = memory[regs.pc++]; | 102 | val = arg; |
| 87 | printf("arg1 $%02X\n", val); | ||
| 88 | regs.a = val; | ||
| 89 | break; | 103 | break; |
| 90 | case AM_ZERO_PAGE: /* $A5 */ | 104 | case AM_ZERO_PAGE: /* $A5 */ |
| 91 | /* TODO */ | 105 | val = peek(arg % 256); |
| 92 | break; | ||
| 93 | case AM_ZERO_PAGE_X: /* $B5 */ | ||
| 94 | /* TODO */ | ||
| 95 | break; | 106 | break; |
| 96 | case AM_ABSOLUTE: /* $AD */ | 107 | case AM_ABSOLUTE: /* $AD */ |
| 97 | /* TODO */ | 108 | val = peek(arg); |
| 109 | break; | ||
| 110 | case AM_ZERO_PAGE_X: /* $B5 */ | ||
| 111 | val = peek((arg + regs.x) % 256); | ||
| 98 | break; | 112 | break; |
| 99 | case AM_ABSOLUTE_X: /* $BD */ | 113 | case AM_ABSOLUTE_X: /* $BD */ |
| 100 | /* TODO */ | 114 | val = peek(arg + regs.x); |
| 101 | break; | 115 | break; |
| 102 | case AM_ABSOLUTE_Y: /* $B9 */ | 116 | case AM_ABSOLUTE_Y: /* $B9 */ |
| 103 | /* TODO */ | 117 | val = peek(arg + regs.y); |
| 104 | break; | 118 | break; |
| 105 | case AM_INDIRECT_X: /* $A1 */ | 119 | case AM_INDIRECT_X: /* $A1 */ |
| 106 | /* TODO */ | 120 | val = peek(peek((arg + regs.x) % 256) + peek((arg + regs.x + 1) % 256) * 256); |
| 107 | break; | 121 | break; |
| 108 | case AM_INDIRECT_Y: /* $B1 */ | 122 | case AM_INDIRECT_Y: /* $B1 */ |
| 109 | /* TODO */ | 123 | val = peek(peek(arg) + peek((arg + 1) % 256) * 256 + regs.y); |
| 110 | break; | 124 | break; |
| 111 | default: | 125 | default: |
| 126 | regs.pc--; | ||
| 112 | return; | 127 | return; |
| 113 | } | 128 | } |
| 114 | 129 | ||
| 130 | printf("arg1 $%02X\n", arg); | ||
| 131 | regs.a = val; | ||
| 132 | |||
| 115 | STATUS_UPDATE_ZERO(regs.a); | 133 | STATUS_UPDATE_ZERO(regs.a); |
| 116 | STATUS_UPDATE_NEGATIVE(regs.a); | 134 | STATUS_UPDATE_NEGATIVE(regs.a); |
| 117 | } | 135 | } |
| @@ -122,7 +140,7 @@ interpret(void) | |||
| 122 | uint8_t opcode; | 140 | uint8_t opcode; |
| 123 | 141 | ||
| 124 | for (;;) { | 142 | for (;;) { |
| 125 | opcode = memory[regs.pc++]; | 143 | opcode = peek(regs.pc++); |
| 126 | 144 | ||
| 127 | printf("opcode: $%02X\n", opcode); | 145 | printf("opcode: $%02X\n", opcode); |
| 128 | 146 | ||
| @@ -175,9 +193,23 @@ interpret(void) | |||
| 175 | } | 193 | } |
| 176 | } | 194 | } |
| 177 | 195 | ||
| 196 | /* https://www.nesdev.org/wiki/CPU_power_up_state */ | ||
| 197 | void | ||
| 198 | cpu_init(void) | ||
| 199 | { | ||
| 200 | regs.a = regs.x = regs.y = 0; | ||
| 201 | regs.pc = 0xFFFC; | ||
| 202 | regs.sp = 0xFD; | ||
| 203 | |||
| 204 | memset(®s.status, 0, sizeof(regs.status)); | ||
| 205 | regs.status.unused = 1; | ||
| 206 | } | ||
| 207 | |||
| 178 | int | 208 | int |
| 179 | main(void) | 209 | main(void) |
| 180 | { | 210 | { |
| 211 | cpu_init(); | ||
| 212 | |||
| 181 | if (sizeof(program) > (0x10000 - 0x8000)) { | 213 | if (sizeof(program) > (0x10000 - 0x8000)) { |
| 182 | fprintf(stderr, "program is too big for memory\n"); | 214 | fprintf(stderr, "program is too big for memory\n"); |
| 183 | return 1; | 215 | return 1; |
| @@ -185,8 +217,6 @@ main(void) | |||
| 185 | memcpy(memory + 0x8000, program, sizeof(program)); | 217 | memcpy(memory + 0x8000, program, sizeof(program)); |
| 186 | regs.pc = 0x8000; | 218 | regs.pc = 0x8000; |
| 187 | 219 | ||
| 188 | regs.status.unused = 1; | ||
| 189 | |||
| 190 | printf("Initial State:\n"); | 220 | printf("Initial State:\n"); |
| 191 | printf("status: %i%i%i%i%i%i%i%i\n", regs.status.carry, | 221 | printf("status: %i%i%i%i%i%i%i%i\n", regs.status.carry, |
| 192 | regs.status.zero, regs.status.interrupt_disable, | 222 | regs.status.zero, regs.status.interrupt_disable, |
