Lamadness: CPU Implementation Part1

· 3min · Meisterlama

I always wanted to write an emulator. For some reasons, it seemed like magic to me, and something I could never do. I tried to write a CHIP-8 emulator, and a spark happened in me : I wanted more.

Note: This article is not complete. It is still being written and
the project is not yet finished

Introduction

I started to gather info on how I could emulate this console, and i found pretty good resources like the 6502's manual with all the specifications of the NES's CPU.

I also followed a bit of bugzmanov's tutorial (itself written in rust) but I felt like some of his code was not how I imagined it. One of the only thing that comes straight from this tutorial is the iNES file parser, but I am feeling like I want to make my own, and be able to parse iNES2.0

So I started by emulating the CPU. I had to implement all the the instruction set.

For this task, I used many resources :

The 6502 doesn't have a lot of instructions per se with its 56 instructions (as far as i know, x86 have more than 400 mnemonics) but it still is a whole lot of text to type. With all the addressing modes and the illegal operations, it steps up to 256 operations to handle. Instead, I extracted all the informations needed in a CSV file, enumerating all the instructions, their addressing modes and their execution cycles. Then i parsed this file and generated every instructions bodies, and a list mapping every opcode to the correct addressing mode and function.

An item in my instruction list looks like this :

Instruction { //00
    name: "BRK",
    addr_mode: AddressingMode::Implied,
    command: CPU::brk,
    cycles: 7,
    page_boundary: false,
}

The addressing modes

The 6502 has 12 addressing modes. An addressing mode tells the cpu where to look for the data it needs to perform the operations. I had a pretty hard time grasping the concept, but in the end, it is quite like learning pointers in C. I implemented the addressing mode by having a function which return an effective memory address, based on the addressing mode used. For example LDA #$01 is the instruction LDA in immediate addressing mode. In immediate addressing, the byte that comes just after the instruction byte will be the data used by the instruction. Once assembled LDA #$01 become $A9 $01. $A9 is the instruction byte, so the data we're interested to is $01. Because we are executing the instruction $A9, this is the data read at program_counter, so the $01 is stored at program_counter + 1