Guest User

Chip8

a guest
Mar 28th, 2016
166
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 16.92 KB | None | 0 0
  1. #include "chip8.h"
  2.  
  3. #include <fstream>
  4. //#include "Windows.h"
  5.  
  6. unsigned char fontset[80] = {
  7.     0xF0, 0x90, 0x90, 0x90, 0xF0, // 0
  8.     0x20, 0x60, 0x20, 0x20, 0x70, // 1
  9.     0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2
  10.     0xF0, 0x10, 0xF0, 0x10, 0xF0, // 3
  11.     0x90, 0x90, 0xF0, 0x10, 0x10, // 4
  12.     0xF0, 0x80, 0xF0, 0x10, 0xF0, // 5
  13.     0xF0, 0x80, 0xF0, 0x90, 0xF0, // 6
  14.     0xF0, 0x10, 0x20, 0x40, 0x40, // 7
  15.     0xF0, 0x90, 0xF0, 0x90, 0xF0, // 8
  16.     0xF0, 0x90, 0xF0, 0x10, 0xF0, // 9
  17.     0xF0, 0x90, 0xF0, 0x90, 0x90, // A
  18.     0xE0, 0x90, 0xE0, 0x90, 0xE0, // B
  19.     0xF0, 0x80, 0x80, 0x80, 0xF0, // C
  20.     0xE0, 0x90, 0x90, 0x90, 0xE0, // D
  21.     0xF0, 0x80, 0xF0, 0x80, 0xF0, // E
  22.     0xF0, 0x80, 0xF0, 0x80, 0x80  // F
  23. };
  24.  
  25. Chip8::Chip8()
  26. {
  27.     Initialise();
  28. }
  29.  
  30. Chip8::~Chip8()
  31. {
  32. }
  33.  
  34. /*
  35. The systems memory map :
  36.  
  37. 0x000 - 0x1FF - Chip 8 interpreter(contains font set in emu)
  38. 0x050 - 0x0A0 - Used for the built in 4x5 pixel font set(0 - F)
  39. 0x200 - 0xFFF - Program ROM and work RAM
  40. */
  41. void Chip8::Initialise()
  42. {
  43.     // Initialse registers and memory
  44.     for (int i = 0; i < 4096; ++i)
  45.     {
  46.         //set the fontset at the start of the memory
  47.         if (i < 80)
  48.         {
  49.             memory[i] = fontset[i];
  50.         }
  51.         else
  52.         {
  53.             memory[i] = 0;
  54.         }
  55.     }
  56.  
  57.     for (int i = 0; i < 16; ++i)
  58.     {
  59.         Reg[i] = 0;
  60.     }
  61.  
  62.     regPc = 0x200; // program starts from this position
  63.     delay_timer = 0;
  64.     sound_timer = 0;
  65.     opcode = 0;
  66.     regI = 0;
  67.     sp = 0;
  68.     m_ScreenRefresh = true;
  69.     EmptyScreen();
  70.     for (int i = 0; i < 16; ++i)
  71.     {
  72.         stack[i] = 0;
  73.     }
  74.  
  75.     for (unsigned int i = 0; i < 16; i++) {
  76.         key[i] = false;
  77.     }
  78.  
  79.    
  80.  
  81.     //todo: fill 0x000 - 0x1ff with the needed information (like font-types)
  82. }
  83.  
  84. void Chip8::LoadGame(std::string gameName)
  85. {
  86.     //load game into memory
  87.     Initialise();
  88.     //load the data from the file in the memory starting from 0x200
  89.     U16 startPos = 0x200;
  90.  
  91.     std::ifstream romStream;
  92.     //romStream.open("c8_Games\\" + gameName, std::ios::binary);
  93.     romStream.open(gameName, std::ios::binary);
  94.     romStream.seekg(0, std::ios::end);// goes to end of the file
  95.     int size = romStream.tellg();
  96.     romStream.seekg(0, std::ios::beg);//goes back to beginning of the file
  97.     char *rom = NULL;
  98.  
  99.     //if the file size is too large, error
  100.     if (romStream.is_open())
  101.     {
  102.         if (startPos + size >= 4096)
  103.         {
  104.             //OutputDebugString(L"-----------File too large for memory-----------\n");
  105.         }
  106.         else
  107.         {
  108.             //else add to tempmemory
  109.             rom = new char[size + 1];
  110.             romStream.read(rom, size);
  111.  
  112.         }
  113.     }
  114.     else
  115.     {
  116.         //OutputDebugString(L"-----------Could  not open file-----------\n");
  117.     }
  118.     romStream.close();
  119.  
  120.     //add tempmemory to memory
  121.     if (rom != NULL)
  122.     {
  123.         for (U16 i = startPos; i < startPos + size; ++i)
  124.         {
  125.             memory[i] = (unsigned char)rom[i - startPos];
  126.         }
  127.         delete [] rom;
  128.         rom = NULL;
  129.     }
  130. }
  131.  
  132. void Chip8::NextPc()
  133.  
  134. {
  135.     regPc += 2;
  136. }
  137.  
  138. void Chip8::EmulateCycle()
  139. {
  140.     //fetch opcode
  141.     opcode = memory[regPc] << 8 | memory[regPc + 1];
  142.  
  143.  
  144.     std::cout << std::hex << opcode << std::endl;
  145.  
  146.     //decode opcode
  147.     switch (opcode & 0xF000) // takes the first byte of the opcode
  148.     {
  149.     case 0x0000:
  150.         //std::cout << "0x0000" << std::endl;
  151.         switch (opcode & 0x000F) // last bytes
  152.         {
  153.             //skipped 0x0NNN!
  154.         case 0x0000:
  155.             //clears the screen
  156.             EmptyScreen();
  157.             m_ScreenRefresh = true;
  158.             NextPc();
  159.             break;
  160.         case 0x000E:
  161.             //returns from a subroutine
  162.             regPc = stack[--sp];
  163.             NextPc();
  164.             break;
  165.         //default:
  166.             //unknown opcode;
  167.             //OutputDebugString(L"Unknown opcode [0]");
  168.         }
  169.         break;
  170.     case 0x1000:
  171.         //std::cout << "0x1000" << std::endl;
  172.         //jumps to adress NNN
  173.         regPc = opcode & 0x0FFF;
  174.         break;
  175.     case 0x2000:
  176.         //std::cout << "0x2000" << std::endl;
  177.         //calls subroutine at NNN
  178.         //we are temporarely jumping to another address(NNN) so we must store te current address in the stack;
  179.         stack[sp] = regPc;
  180.         ++sp; // increase stackCounter to avoid overwriting
  181.         //jump to address
  182.         regPc = opcode & 0x0FFF;
  183.         break;
  184.     case 0x3000:
  185.         //std::cout << "0x3000" << std::endl;
  186.         //skips the next instruction if VX equals NN
  187.         if (Reg[(opcode & 0x0F00) >> 8] == (opcode & 0x00FF))
  188.         {
  189.             NextPc();
  190.         }
  191.         NextPc();
  192.         break;
  193.     case 0x4000:
  194.         //std::cout << "0x4000" << std::endl;
  195.         //skips the next instruction if VX doesn't equal NN
  196.         if (Reg[(opcode & 0x0F00) >> 8] != (opcode & 0x00FF))
  197.         {
  198.             NextPc();
  199.         }
  200.         NextPc();
  201.         break;
  202.     case 0x5000:
  203.         //std::cout << "0x5000" << std::endl;
  204.         //skips the next instruction if VX equals VY
  205.         if (Reg[(opcode & 0x0F00) >> 8] == Reg[(opcode & 0x00F0) >> 4])
  206.         {
  207.             NextPc();
  208.         }
  209.         NextPc();
  210.         break;
  211.     case 0x6000:
  212.         //Sets VX to NN
  213.         //std::cout << "0x6000" << std::endl;
  214.         Reg[(opcode & 0x0F00) >> 8] = opcode & 0x00FF;
  215.         NextPc();
  216.         break;
  217.     case 0x7000:
  218.         //Adds NN to VX
  219.         //std::cout << "0x7000" << std::endl;
  220.         Reg[(opcode & 0x0F00) >> 8] += opcode & 0x00FF;
  221.         NextPc();
  222.         break;
  223.     case 0x8000:// I think something is going wrong here
  224.         //std::cout << "0x8000" << std::endl;
  225.         switch (opcode & 0x000F)
  226.         {
  227.         case 0x0000:
  228.             //Sets VX to te value of VY
  229.             Reg[(opcode & 0x0F00) >> 8] = Reg[(opcode & 0x00F0) >> 4];
  230.             NextPc();
  231.             break;
  232.         case 0x0001:
  233.             //Sets VX to VX or VY
  234.             Reg[(opcode & 0x0F00) >> 8] |= Reg[(opcode & 0x00F0) >> 4];
  235.             NextPc();
  236.             break;
  237.         case 0x0002:
  238.             //Sets VX to VX and VY
  239.             Reg[(opcode & 0x0F00) >> 8] &= Reg[(opcode & 0x00F0) >> 4];
  240.             NextPc();
  241.             break;
  242.         case 0x0003:
  243.             //Sets VX to VX xor VY
  244.             Reg[(opcode & 0x0F00) >> 8] ^= Reg[(opcode & 0x00F0) >> 4];
  245.             NextPc();
  246.             break;
  247.         case 0x0004: // CHECK
  248.             /*
  249.             if the sum of VX and VY is larger than 255, it can’t be stored in the register (or actually it starts counting from 0 again).
  250.             If the sum of VX and VY is larger than 255, we use the carry flag to let the system know that the total sum of both values was indeed larger than 255(0xFF).
  251.             */
  252.  
  253.             //Adds VY to VX. VF is set to 1 when there's a carry, and to 0 when there isn't
  254.             //Reg[(opcode & 0x0F00) >> 8] += Reg[(opcode & 0x00F0) >> 4];
  255.             //if (Reg[(opcode & 0x00F0) >> 4] > (0xFF - Reg[(opcode & 0x0F00) >> 8]))
  256.             //{
  257.             //  Reg[0xF] = 1; // collision
  258.             //}
  259.             //else
  260.             //{
  261.             //  Reg[0xF] = 0; // no collision
  262.             //}
  263.  
  264.             if (Reg[(opcode & 0x00F0) >> 4] > (0xFF - Reg[(opcode & 0x0F00) >> 8]))
  265.             {
  266.                 Reg[0xF] = 1;
  267.             }
  268.             else
  269.             {
  270.                 Reg[0xF] = 0;
  271.             }
  272.             Reg[(opcode & 0x0F00) >> 8] += Reg[(opcode & 0x00F0) >> 4];
  273.  
  274.             NextPc();
  275.             break;
  276.         case 0x0005:
  277.             //VY is substracted from VX. VF is set to 0 when there's a borrow, and 1 when there isn't
  278.            
  279.             if (Reg[(opcode & 0x00F0) >> 4] > (Reg[(opcode & 0x0F00) >> 8]))
  280.             {
  281.                 Reg[0xF] = 0;
  282.             }
  283.             else
  284.             {
  285.                 Reg[0xF] = 1;
  286.             }
  287.             Reg[(opcode & 0x0F00) >> 8] -= Reg[(opcode & 0x00F0) >> 4];
  288.  
  289.             NextPc();
  290.             break;
  291.         case 0x0006:
  292.             //Shifts VX right by one. VF is set to the value of the least significant bit of VX before the shifts
  293.             Reg[0xF] = Reg[(opcode & 0x0F00) >> 8] & 0x1;
  294.             Reg[(opcode & 0x0F00) >> 8] >>= 1;
  295.             NextPc();
  296.             break;
  297.         case 0x0007:
  298.             //Sets VX to VY minus VX. VF is set to 0 when there's a borrow, and 1 when there isn't
  299.             if (Reg[(opcode & 0x0F00) >> 8] > Reg[(opcode & 0x00F0) >> 4])
  300.             {
  301.                 Reg[0xF] = 0;
  302.             }
  303.             else
  304.             {
  305.                 Reg[0xF] = 1;
  306.             }
  307.             Reg[(opcode & 0x0F00) >> 8] = (Reg[(opcode & 0x00F0) >> 4] - Reg[(opcode & 0x0F00) >> 8]);
  308.  
  309.             NextPc();
  310.             break;
  311.         case 0x000E:
  312.             //Shifts VX left by one. VF is set to the value of the most significant bit of VX before the shift
  313.             Reg[0xF] = Reg[(opcode & 0x0F00) >> 8] >> 7;
  314.             Reg[(opcode & 0x0F00) >> 8] <<= 1;
  315.             NextPc();
  316.             break;
  317.         default:
  318.             //unknown opcode
  319.             //OutputDebugString(L"Unknown opcode [8]");
  320.             break;
  321.         }
  322.         break;
  323.     case 0x9000:
  324.         //std::cout << "0x9000" << std::endl;
  325.         //Skips the next instruction if VX doesn't equal VY
  326.         if (Reg[(opcode & 0x0F00) >> 8] != Reg[(opcode & 0x00F0) >> 4])
  327.         {
  328.             NextPc();
  329.         }
  330.         NextPc();
  331.         break;
  332.     case 0xA000:
  333.         //Sets I to the address NNN
  334.         //std::cout << "0xA000" << std::endl;
  335.         regI = opcode & 0x0FFF;
  336.  
  337.         NextPc(); //next memoryspace;
  338.         break;
  339.     case 0xB000:
  340.         //std::cout << "0xB000" << std::endl;
  341.         //Jumps to the adress NNN plus V0
  342.         regPc = (opcode & 0x0FFF) + Reg[0];
  343.         break;
  344.     case 0xC000:
  345.         //Sets VX to a random number(between 0 - 255) and NN
  346.         //test = (opcode & 0x0F00);
  347.         //test2 = (opcode & 0x0F00) >> 8;
  348.         //std::cout << "0xC000" << std::endl;
  349.         //Reg[(opcode & 0x0F00)] = (rand() & 0xFF) & (opcode & 0x00FF); // didn't do anything
  350.         Reg[(opcode & 0x0F00) >> 8] = (rand() % 0xFF) & (opcode & 0x00FF);
  351.  
  352.         NextPc(); //next memoryspace;
  353.         break;
  354.     case 0xD000:
  355.     {
  356.         //std::cout << "0xD000" << std::endl;
  357.         /*
  358.             Sprites stored in memory at location in index register (I), 8bits wide. Wraps around the screen.
  359.             If when drawn, clears a pixel, register VF is set to 1 otherwise it is zero. All drawing is XOR drawing (i.e. it toggles the screen pixels).
  360.             Sprites are drawn starting at position VX, VY. N is the number of 8bit rows that need to be drawn. If N is greater than 1, second line continues at position VX, VY+1, and so on.
  361.         */
  362.  
  363.         //get position and height of the sprite
  364.         U16 xPos = Reg[(opcode & 0x0F00) >> 8];
  365.         U16 yPos = Reg[(opcode & 0x00F0) >> 4];
  366.         U16 height = opcode & 0x000F;//amount of rows
  367.         U16 pixelValue;
  368.  
  369.         Reg[0xF] = 0;//reset register F
  370.         for (unsigned int row = 0; row < height; ++row)//loop over each row
  371.         {
  372.             pixelValue = memory[regI + row];//fetch pixel value
  373.  
  374.             //loop over the bits of 1 row(8 bit)
  375.             for (unsigned int bit = 0; bit < 8; ++bit)
  376.             {
  377.                 //check if the current evaluated bit is set to 1
  378.                 if ((pixelValue & (0x80 >> bit)) != 0)
  379.                 {
  380.                     //its 1
  381.                     //check if pixel on the screen is set to 1, if it is we need to register the collision by setting the VF register
  382.                     if (gfx[(xPos + bit + ((yPos + row) * SCREEN_WIDTH))] == 1)
  383.                     {
  384.                         Reg[0xF] = 1;
  385.                     }
  386.                     //set pixel value by using XOR (only if on bit is 1 and the other is 0 you will get 1 otherwise 0)
  387.                     gfx[(xPos + bit + ((yPos + row) * SCREEN_WIDTH))] ^= 1;
  388.  
  389.                 }
  390.             }
  391.         }
  392.         //set Draw screen
  393.         m_ScreenRefresh = true;
  394.         NextPc();
  395.         break;
  396.     }
  397.     case 0xE000:
  398.         //std::cout << "0xE000" << std::endl;
  399.         switch (opcode & 0x00FF)
  400.         {
  401.         case 0x009E:
  402.             //Skips the next instruction if the key stored in VX is pressed
  403.             if (key[Reg[(opcode & 0x0F00) >> 8]])
  404.             {
  405.                 NextPc();
  406.             }
  407.  
  408.             NextPc();
  409.             break;
  410.         case 0x00A1:
  411.             //Skips the next instruction if the key stored in VX isn't pressed
  412.             if (!key[Reg[(opcode & 0x0F00) >> 8]])
  413.             {
  414.                 NextPc();
  415.             }
  416.  
  417.             NextPc();
  418.             break;
  419.         default:
  420.             //unknown opcode
  421.             //OutputDebugString(L"Unknown opcode [E]");
  422.             break;
  423.         }
  424.         break;
  425.     case 0xF000:
  426.         //std::cout << "0xF000" << std::endl;
  427.         switch (opcode & 0x00FF)
  428.         {
  429.         case 0x0007:
  430.             //Sets VX to the value of the delay timer.
  431.             Reg[(opcode & 0x0F00) >> 8] = delay_timer;
  432.             NextPc();
  433.             break;
  434.         case 0x000A:
  435.             //A key pressed is awaited and then stored in VX TODO
  436.         {
  437.             bool keyIsPressed = false;
  438.  
  439.             for (int i = 0; i < 16; ++i)
  440.             {
  441.  
  442.                 if (key[i])
  443.                 {
  444.                     keyIsPressed = true;
  445.                     Reg[(opcode & 0x0F00) >> 8] = i;
  446.                     break;
  447.                 }
  448.  
  449.             }
  450.             //if you don't have a keypress don't do NextPC
  451.             if (keyIsPressed)
  452.                 NextPc();
  453.             break;
  454.         }
  455.         case 0x0015:
  456.             //Sets the delay timer to VX
  457.             delay_timer = Reg[(opcode & 0x0F00) >> 8];
  458.             NextPc();
  459.             break;
  460.         case 0x0018:
  461.             //Sets the sound timer to VX
  462.             sound_timer = Reg[(opcode & 0x0F00) >> 8];
  463.             NextPc();
  464.             break;
  465.         case 0x001E:
  466.             //Adds VX to I
  467.             regI += Reg[(opcode & 0x0F00) >> 8];
  468.             NextPc();
  469.             break;
  470.         case 0x0029:
  471.             //Sets I to the location of the sprite for the character in VX. Character 0-F are represented by a 4x5 font
  472.             regI = Reg[(opcode & 0x0F00) >> 8] * 5; //location of sprite in memory (each character has 5 spots of te memory)
  473.             NextPc();
  474.             break;
  475.         case 0x0033:
  476.             /*
  477.                 Stores the Binary-coded decimal representation of VX, with the most significant of three digits at the address in I, the middle digit at I plus 1, and the least significant digit at I plus 2.
  478.                 (In other words, take the decimal representation of VX, place the hundreds digit in memory at location in I, the tens digit at location I+1, and the ones digit at location I+2.)
  479.             */
  480.  
  481.             //store decimal representation of VX in memory at address regI, regI +1 and regI+2 (hunreds digit at regI)
  482.             memory[regI] = Reg[(opcode & 0x0F00) >> 8] /100;// example: (160 / 100) = 1;
  483.  
  484.             //tens digits in regI +1
  485.             memory[regI + 1] = (Reg[(opcode & 0x0F00) >> 8] / 10) % 10; // example: (160/10) = 16 % 10 = 6;
  486.  
  487.             //one digits at regI +2
  488.             memory[regI + 2] = (Reg[(opcode & 0X0F00) >> 8] % 100) % 10;// example: (160 % 100) = 60 % 10 = 0;
  489.             NextPc();
  490.             break;
  491.         case 0x0055:
  492.             //Stores V0 to VX in memory starting at address I
  493.             for (int i = 0; i <= ((opcode & 0x0F00) >> 8); ++i)
  494.             {
  495.                 memory[regI + i] = Reg[i];
  496.             }
  497.             NextPc();
  498.             break;
  499.         case 0x0065:
  500.             //Fills V0 to VX with values from memory starting at address I
  501.             for (int i = 0; i <= ((opcode & 0x0F00) >> 8); ++i)
  502.             {
  503.                 Reg[i] = memory[regI + i];
  504.             }
  505.             NextPc();
  506.             break;
  507.         default:
  508.             //unknown opcode
  509.             //OutputDebugString(L"Unknown opcode [F]");
  510.             break;
  511.         }
  512.         break;
  513.     default:
  514.         //unknown opcode
  515.         //OutputDebugString(L"This opcode can not exist!");
  516.         break;
  517.     }
  518.  
  519.     //if a time ris higher then 0 they will count down to 0
  520.     //update timers
  521.     if (delay_timer > 0)
  522.         --delay_timer;
  523.     if (sound_timer > 0)
  524.         --sound_timer;
  525.  
  526. }
  527.  
  528. U8 * Chip8::GetScreen()
  529. {
  530.     return gfx;
  531. }
  532.  
  533. bool Chip8::GetScreenRefresh()
  534. {
  535.     return m_ScreenRefresh;
  536. }
  537.  
  538. void Chip8::GetScreenRefreshFalse()
  539. {
  540.     m_ScreenRefresh = false;
  541. }
  542.  
  543. void Chip8::EmptyScreen()
  544. {
  545.     for (unsigned int i = 0; i < (SCREEN_WIDTH*SCREEN_HEIGHT); ++i)
  546.     {
  547.         gfx[i] = 0;
  548.     }
  549. }
  550.  
  551. void Chip8::SetKeyState(unsigned int keycode, bool state)
  552. {
  553.     key[keycode] = state;
  554. }
Add Comment
Please, Sign In to add comment