Advertisement
otto_baynes

emu_chip.cpp

Jun 12th, 2012
908
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 13.83 KB | None | 0 0
  1. /*
  2.  *  emuChip - CHIP-8 emulator.
  3.  *  Copyright (C) 2009-2012 Boris Timofeev <mashin87@gmail.com> <http://www.emunix.org>
  4.  *
  5.  *  This program is free software: you can redistribute it and/or modify
  6.  *  it under the terms of the GNU General Public License as published by
  7.  *  the Free Software Foundation, either version 3 of the License, or
  8.  *  (at your option) any later version.
  9.  *
  10.  *  This program is distributed in the hope that it will be useful,
  11.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.  *  GNU General Public License for more details.
  14.  *
  15.  *  You should have received a copy of the GNU General Public License
  16.  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  17.  */
  18. #include "chip_emu.h"
  19. #include <stdlib.h>
  20. #include <iostream>
  21. #include <fstream>
  22. #include <time.h>
  23.  
  24. using namespace std;
  25.  
  26. ChipEmu::ChipEmu()
  27. {
  28.     init();
  29.     srand(time(NULL));
  30. }
  31.  
  32. // Clear registers, memory and stack. Load fonts and redraw screen.
  33. void ChipEmu::init()
  34. {
  35.     PC = 0x200;
  36.     SP = 0;
  37.     I = 0;
  38.     delay_timer = 0;
  39.     sound_timer = 0;
  40.  
  41.     for (int i = 0; i < 4096; i++)
  42.         memory[i] = 0;
  43.    
  44.     for (int y = 0; y < 64; y++)
  45.         for (int x = 0; x < 128; x++)
  46.             screen[x][y] = 0;
  47.  
  48.     for (int i = 0; i < 16; i++)
  49.         V[i] = 0;
  50.    
  51.     for (int i = 0; i < 16; i++)
  52.         key[i] = 0;
  53.  
  54.     for (int i = 0; i < 16; i++)
  55.         stack[i] = 0;
  56.    
  57.     unsigned char font[16*5] = {
  58.         0xF0, 0x90, 0x90, 0x90, 0xF0,   // 0
  59.         0x20, 0x60, 0x20, 0x20, 0x70,   // 1
  60.         0xF0, 0x10, 0xF0, 0x80, 0xF0,   // 2
  61.         0xF0, 0x10, 0xF0, 0x10, 0xF0,   // 3
  62.         0x90, 0x90, 0xF0, 0x10, 0x10,   // 4
  63.         0xF0, 0x80, 0xF0, 0x10, 0xF0,   // 5
  64.         0xF0, 0x80, 0xF0, 0x90, 0xF0,   // 6
  65.         0xF0, 0x10, 0x20, 0x40, 0x40,   // 7
  66.         0xF0, 0x90, 0xF0, 0x90, 0xF0,   // 8
  67.         0xF0, 0x90, 0xF0, 0x10, 0xF0,   // 9
  68.         0xF0, 0x90, 0xF0, 0x90, 0x90,   // A
  69.         0xE0, 0x90, 0xE0, 0x90, 0xE0,   // B
  70.         0xF0, 0x80, 0x80, 0x80, 0xF0,   // C
  71.         0xE0, 0x90, 0x90, 0x90, 0xE0,   // D
  72.         0xF0, 0x80, 0xF0, 0x80, 0xF0,   // E
  73.         0xF0, 0x80, 0xF0, 0x80, 0x80    // F
  74.     };
  75.     // Load font
  76.     for (int i = 0; i < 16*5; i++)
  77.         memory[i] = font[i];
  78.    
  79.     unsigned char bigfont[16*10] = {
  80.         0xFF, 0xFF, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xFF, 0xFF, // 0
  81.         0x18, 0x78, 0x78, 0x18, 0x18, 0x18, 0x18, 0x18, 0xFF, 0xFF, // 1
  82.         0xFF, 0xFF, 0x03, 0x03, 0xFF, 0xFF, 0xC0, 0xC0, 0xFF, 0xFF, // 2
  83.         0xFF, 0xFF, 0x03, 0x03, 0xFF, 0xFF, 0x03, 0x03, 0xFF, 0xFF, // 3
  84.         0xC3, 0xC3, 0xC3, 0xC3, 0xFF, 0xFF, 0x03, 0x03, 0x03, 0x03, // 4
  85.         0xFF, 0xFF, 0xC0, 0xC0, 0xFF, 0xFF, 0x03, 0x03, 0xFF, 0xFF, // 5
  86.         0xFF, 0xFF, 0xC0, 0xC0, 0xFF, 0xFF, 0xC3, 0xC3, 0xFF, 0xFF, // 6
  87.         0xFF, 0xFF, 0x03, 0x03, 0x06, 0x0C, 0x18, 0x18, 0x18, 0x18, // 7
  88.         0xFF, 0xFF, 0xC3, 0xC3, 0xFF, 0xFF, 0xC3, 0xC3, 0xFF, 0xFF, // 8
  89.         0xFF, 0xFF, 0xC3, 0xC3, 0xFF, 0xFF, 0x03, 0x03, 0xFF, 0xFF, // 9
  90.         0x7E, 0xFF, 0xC3, 0xC3, 0xC3, 0xFF, 0xFF, 0xC3, 0xC3, 0xC3, // A
  91.         0xFC, 0xFC, 0xC3, 0xC3, 0xFC, 0xFC, 0xC3, 0xC3, 0xFC, 0xFC, // B
  92.         0x3C, 0xFF, 0xC3, 0xC0, 0xC0, 0xC0, 0xC0, 0xC3, 0xFF, 0x3C, // C
  93.         0xFC, 0xFE, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xFE, 0xFC, // D
  94.         0xFF, 0xFF, 0xC0, 0xC0, 0xFF, 0xFF, 0xC0, 0xC0, 0xFF, 0xFF, // E
  95.         0xFF, 0xFF, 0xC0, 0xC0, 0xFF, 0xFF, 0xC0, 0xC0, 0xC0, 0xC0  // F
  96.     };
  97.    
  98.     // Load big font
  99.     for (int i=0; i < 16*10; i++)
  100.         memory[i+80] = bigfont[i];
  101.    
  102.     for (int i=0; i < 8; i++)
  103.         hp48_flags[i] = 0;
  104.    
  105.     stop = false;
  106.    
  107.     mode = 0;
  108. }
  109.  
  110. void ChipEmu::drawSprite(unsigned char X, unsigned char Y, unsigned char N)
  111. {  
  112.     V[0xF] = 0;
  113.     switch (mode)
  114.     {
  115.         case 0: // CHIP-8 mode
  116.             if (N == 0) N = 16;
  117.             for (int yline = 0; yline < N; yline++)
  118.             {
  119.                 unsigned char data = memory[I + yline];
  120.                 for (int xpix = 0; xpix < 8; xpix++)
  121.                 {
  122.                     if((data & (0x80 >> xpix)) != 0)
  123.                     {
  124.                         if ((V[X] + xpix) < 64 && (V[Y] + yline) < 32 && (V[X] + xpix) >= 0 && (V[Y] + yline) >= 0)
  125.                         {
  126.                             if (screen[(V[X] + xpix)*2][(V[Y] + yline)*2] == 1) V[0xF] = 1;
  127.                             screen[(V[X] + xpix)*2][(V[Y] + yline)*2] ^= 1;
  128.                             screen[(V[X] + xpix)*2 + 1][(V[Y] + yline)*2] ^= 1;
  129.                             screen[(V[X] + xpix)*2][(V[Y] + yline)*2 + 1] ^= 1;
  130.                             screen[(V[X] + xpix)*2 + 1][(V[Y] + yline)*2 + 1] ^= 1;
  131.                         }
  132.                     }
  133.                 }
  134.             }
  135.             break;
  136.            
  137.         case 1: // SCHIP mode
  138.             if (N == 0)
  139.                 for (int yline = 0; yline < 16; yline++)
  140.                 {
  141.                     unsigned char data = memory[I + yline*2];
  142.                     for (int xpix = 0; xpix < 8; xpix++)
  143.                     {
  144.                         if((data & (0x80 >> xpix)) != 0)
  145.                         {
  146.                             if ((V[X] + xpix) < 128 && (V[Y] + yline) < 64 && (V[X] + xpix) >= 0 && (V[Y] + yline) >= 0)
  147.                             {
  148.                                 if (screen[V[X] + xpix][V[Y] + yline] == 1) V[0xF] = 1;
  149.                                 screen[V[X] + xpix][V[Y] + yline] ^= 1;
  150.                             }
  151.                         }
  152.                     }
  153.                     data = memory[I + 1 + yline*2];
  154.                     for (int xpix = 0; xpix < 8; xpix++)
  155.                     {
  156.                         if((data & (0x80 >> xpix)) != 0)
  157.                         {
  158.                             if ((V[X] + xpix + 8) < 128 && (V[Y] + yline) < 64 && (V[X] + xpix + 8) >= 0 && (V[Y] + yline) >= 0)
  159.                             {
  160.                                 if (screen[V[X] + xpix + 8][V[Y] + yline] == 1) V[0xF] = 1;
  161.                                 screen[V[X] + xpix + 8][V[Y] + yline] ^= 1;
  162.                             }
  163.                         }
  164.                     }
  165.                 }
  166.             else
  167.                 for (int yline = 0; yline < N; yline++)
  168.                 {
  169.                     unsigned char data = memory[I + yline];
  170.                     for (int xpix = 0; xpix < 8; xpix++)
  171.                     {
  172.                         if((data & (0x80 >> xpix)) != 0)
  173.                         {
  174.                             if ((V[X] + xpix) < 128 && (V[Y] + yline) < 64 && (V[X] + xpix) >= 0 && (V[Y] + yline) >= 0)
  175.                             {
  176.                                 if (screen[V[X] + xpix][V[Y] + yline] == 1) V[0xF] = 1;
  177.                                 screen[V[X] + xpix][V[Y] + yline] ^= 1;
  178.                             }
  179.                         }
  180.                     }
  181.                 }
  182.             break;
  183.     }
  184. }
  185.  
  186. void ChipEmu::executeNextOpcode()
  187. {
  188.     opcode = (memory[PC]<<8) + memory[PC+1];
  189.  
  190.     PC += 2;
  191.  
  192.     switch ((opcode & 0xF000)>>12)
  193.     {
  194.         case 0x0:
  195.             if ((opcode & 0x00F0)>>4 == 0xC) // 00CN - scroll display N lines down *SCHIP*
  196.             {
  197.                 int N = opcode & 0x000F;
  198.                 for (int y = 63; y > N; y--)
  199.                     for (int x = 0; x < 128; x++)
  200.                         screen[x][y] = screen[x][y-N];
  201.                 for (int y = 0; y < N; y++)
  202.                     for (int x = 0; x < 128; x++)
  203.                         screen[x][y] = 0;
  204.                 break;
  205.             }
  206.  
  207.             switch  (opcode & 0x00FF)
  208.             {
  209.                 case 0xE0:      // 00E0 - clear the screen
  210.                     for (int y = 0; y < 64; y++)
  211.                         for (int x = 0; x < 128; x++)
  212.                             screen[x][y] = 0;
  213.                     break;
  214.  
  215.                 case 0xEE:      // 00EE - return from subroutine
  216.                     PC = stack[--SP];
  217.                     break;
  218.                
  219.                 case 0xFB:      // 00FB - scroll display 4 pixels right *SCHIP*
  220.                     for (int y = 0; y < 64; y++)
  221.                     {
  222.                         for (int x = 127; x > 3; x--)
  223.                             screen[x][y] = screen[x-4][y];
  224.                         screen[0][y] = 0;
  225.                         screen[1][y] = 0;
  226.                         screen[2][y] = 0;
  227.                         screen[3][y] = 0;
  228.                     }
  229.                     break;
  230.                
  231.                 case 0xFC:      // 00FB - scroll display 4 pixels left *SCHIP*
  232.                     for (int y = 0; y < 64; y++)
  233.                     {
  234.                         for (int x = 0; x < 124; x++)
  235.                             screen[x][y] = screen[x+4][y];
  236.                         screen[124][y] = 0;
  237.                         screen[125][y] = 0;
  238.                         screen[126][y] = 0;
  239.                         screen[127][y] = 0;
  240.                     }
  241.                     break;
  242.                
  243.                 case 0xFD:      // 00FD - Quit the emulator
  244.                     stop = true;
  245.                     cout << "Quit the emulator" << endl;
  246.                     break;
  247.                        
  248.                 case 0xFE:      // 00FE - disable extended screen mode *SCHIP*
  249.                     mode = 0;
  250.                     break;
  251.                
  252.                 case 0xFF:      // 00FF - enable extended screen mode *SCHIP*
  253.                     mode = 1;
  254.                     break;
  255.                
  256.                 default:
  257.                     cerr << "Unknown opcode: 0x" << hex << opcode <<endl;
  258.             }
  259.             break;
  260.  
  261.         case 0x1:               // 1NNN - jump to addr
  262.             PC = opcode & 0x0FFF;
  263.             break;
  264.  
  265.         case 0x2:               // 2NNN - call subroutine
  266.             stack[SP++] = PC;
  267.             PC = opcode & 0x0FFF;
  268.             break;
  269.  
  270.         case 0x3:               // 3XKK - skip next instruction if VX == Byte
  271.             if (V[((opcode & 0x0F00)>>8)] == (opcode & 0x00FF)) PC += 2;
  272.             break;
  273.  
  274.         case 0x4:               // 4XKK - skip next instruction if VX != Byte
  275.             if (V[((opcode & 0x0F00)>>8)] != (opcode & 0x00FF)) PC += 2;
  276.             break;
  277.  
  278.         case 0x5:               // 5XY0 - skip next instruction if VX == VY
  279.             if (V[((opcode & 0x0F00)>>8)] == V[((opcode & 0x00F0)>>4)]) PC += 2;
  280.             break;
  281.  
  282.         case 0x6:               // 6XKK - set VX = Byte
  283.             V[((opcode & 0x0F00)>>8)] = opcode & 0x00FF;
  284.             break;
  285.  
  286.         case 0x7:               // 7XKK - set VX = VX + Byte
  287.             V[((opcode & 0x0F00)>>8)] += opcode & 0x00FF;
  288.             break;
  289.  
  290.         case 0x8:
  291.             switch (opcode & 0x000F)
  292.             {
  293.                 case 0x0:       // 8XY0 - set VX = VY
  294.                     V[((opcode & 0x0F00)>>8)] = V[((opcode & 0x00F0)>>4)];
  295.                
  296.                     break;
  297.  
  298.                 case 0x1:       // 8XY1 - set VX = VX | VY
  299.                     V[((opcode & 0x0F00)>>8)] |= V[((opcode & 0x00F0)>>4)];
  300.                     break;
  301.  
  302.                 case 0x2:       // 8XY2 - set VX = VX & VY
  303.                     V[((opcode & 0x0F00)>>8)] &= V[((opcode & 0x00F0)>>4)];
  304.                     break;
  305.  
  306.                 case 0x3:       // 8XY3 - set VX = VX ^ VY
  307.                     V[((opcode & 0x0F00)>>8)] ^= V[((opcode & 0x00F0)>>4)];
  308.                     break;
  309.  
  310.                 case 0x4:       // 8XY4 - set VX = VX + VY, VF = carry
  311.                     int i;
  312.                     i = static_cast<int>(V[((opcode & 0x0F00)>>8)]) + static_cast<int>(V[((opcode & 0x00F0)>>4)]);
  313.  
  314.                     if (i > 255)
  315.                         V[0xF] = 1;
  316.                     else
  317.                         V[0xF] = 0;
  318.  
  319.                     V[((opcode & 0x0F00)>>8)] = i;
  320.                     break;
  321.  
  322.                 case 0x5:       // 8XY5 - set VX = VX - VY, VF = !borrow
  323.                     if (V[((opcode & 0x0F00)>>8)] >= V[((opcode & 0x00F0)>>4)])
  324.                         V[0xF] = 1;
  325.                     else
  326.                         V[0xF] = 0;
  327.  
  328.                     V[((opcode & 0x0F00)>>8)] -= V[((opcode & 0x00F0)>>4)];
  329.                     break;
  330.  
  331.                 case 0x6:       // 8XY6 - set VX = VX >> 1, VF = carry
  332.                     V[0xF] = V[((opcode & 0x0F00)>>8)] & 0x1;
  333.                     V[((opcode & 0x0F00)>>8)] >>= 1;
  334.                     break;
  335.  
  336.                 case 0x7:       // 8XY7 - set VX = VY - VX, VF = !borrow
  337.                     if (V[((opcode & 0x00F0)>>4)] >= V[((opcode & 0x0F00)>>8)])
  338.                         V[0xF] = 1;
  339.                     else
  340.                         V[0xF] = 0;
  341.  
  342.                     V[((opcode & 0x0F00)>>8)] = V[((opcode & 0x00F0)>>4)] - V[((opcode & 0x0F00)>>8)];
  343.                     break;
  344.  
  345.                 case 0xE:       // 8XYE - set VX = VX << 1, VF = carry
  346.                     V[0xF] = (V[((opcode & 0x0F00)>>8)] >> 7) & 0x01;
  347.                     V[((opcode & 0x0F00)>>8)] <<= 1;
  348.                     break;
  349.                
  350.                 default:
  351.                     cerr << "Unknown opcode: 0x" << hex << opcode <<endl;
  352.             }
  353.             break;
  354.  
  355.         case 0x9:               // 9XY0 - skip next instruction if VX != VY
  356.             if (V[((opcode & 0x0F00)>>8)] != V[((opcode & 0x00F0)>>4)]) PC += 2;
  357.             break;
  358.  
  359.         case 0xA:               // ANNN - set I = Addr
  360.             I = opcode & 0x0FFF;
  361.             break;
  362.  
  363.         case 0xB:               // BNNN - jump to Addr + V0
  364.             PC = (opcode & 0x0FFF) + V[0];
  365.             break;
  366.  
  367.         case 0xC:               // CXKK - set VX = random & Byte
  368.             V[((opcode & 0x0F00)>>8)] = (rand() % 255) & (opcode & 0x00FF);
  369.             break;
  370.  
  371.         case 0xD:               // DXYN - Draw sprite
  372.             drawSprite(((opcode & 0x0F00)>>8), ((opcode & 0x00F0)>>4), (opcode & 0x000F));
  373.             break;
  374.  
  375.         case 0xE:
  376.             switch (opcode & 0x00FF)
  377.             {
  378.                 case 0x9E:      // EX9E - skip next instruction if key VX down
  379.                     if (key[V[((opcode & 0x0F00)>>8)]] == 1)
  380.                         PC += 2;
  381.                     break;
  382.  
  383.                 case 0xA1:      // EXA1 - skip next instruction if key VX up
  384.                     if (key[V[((opcode & 0x0F00)>>8)]] == 0)
  385.                         PC += 2;
  386.                     break;
  387.                    
  388.                 default:
  389.                     cerr << "Unknown opcode: 0x" << hex << opcode <<endl;
  390.             }
  391.             break;
  392.  
  393.         case 0xF:
  394.             switch (opcode & 0x00FF)
  395.             {
  396.                 case 0x07:      // FX07 - set VX = delaytimer
  397.                     V[((opcode & 0x0F00)>>8)] = delay_timer;
  398.                     break;
  399.  
  400.                 case 0x0A:      // FX0A - set VX = key, wait for keypress
  401.                     PC -= 2;
  402.                     for (unsigned char n=0; n < 16; n++)
  403.                     {
  404.                         if (key[n] == 1)
  405.                         {
  406.                             V[((opcode & 0x0F00)>>8)] = n;
  407.                             PC += 2;
  408.                             break;
  409.                         }
  410.                     }
  411.                     break;
  412.  
  413.                 case 0x15:      // FX15 - set delaytimer = VX
  414.                     delay_timer = V[((opcode & 0x0F00)>>8)];
  415.                     break;
  416.  
  417.                 case 0x18:      // FX18 - set soundtimer = VX
  418.                     sound_timer = V[((opcode & 0x0F00)>>8)];
  419.                     break;
  420.  
  421.                 case 0x1E:      // FX1E - set I = I + VX; set VF if buffer overflow
  422.                     if ((I += V[((opcode & 0x0F00)>>8)]) > 0xfff)
  423.                         V[0xF] = 1;
  424.                     else
  425.                         V[0xF] = 0;
  426.                     break;
  427.  
  428.                 case 0x29:      // FX29 - point I to 5 byte numeric sprite for value in VX
  429.                     I = V[((opcode & 0x0F00)>>8)] * 5;
  430.                     break;
  431.                
  432.                 case 0x30:      // FX30 - point I to 10 byte numeric sprite for value in VX *SCHIP*
  433.                     I = V[((opcode & 0x0F00)>>8)] * 10 + 80;
  434.                     break;
  435.  
  436.                 case 0x33:      // FX33 - store BCD of VX in [I], [I+1], [I+2]
  437.                     int n;
  438.                     n = V[((opcode & 0x0F00)>>8)];
  439.                     memory[I] = (n - (n % 100)) / 100;
  440.                     n -= memory[I] * 100;
  441.                     memory[I+1] = (n - (n % 10)) / 10;
  442.                     n -= memory[I+1] * 10;
  443.                     memory[I+2] = n;
  444.                     break;
  445.  
  446.                 case 0x55:      // FX55 - store V0 .. VX in [I] .. [I+X]
  447.                     for (int n=0; n <= ((opcode & 0x0F00)>>8); n++)
  448.                         memory[I+n] = V[n];
  449.                     break;
  450.  
  451.                 case 0x65:      // FX65 - read V0 .. VX from [I] .. [I+X]
  452.                     for (int n=0; n <= ((opcode & 0x0F00)>>8); n++)
  453.                         V[n] = memory[I+n];
  454.                     break;
  455.                    
  456.                 case 0x75:      // FX75 - save V0...VX (X<8) in the HP48 flags *SCHIP*
  457.                     //for (int i=0; i < 8; i++)
  458.                         //hp48_flags[i] = 0;
  459.                     for (int i=0; i <= ((opcode & 0x0F00)>>8); i++)
  460.                         hp48_flags[i] = V[i];
  461.                     break;
  462.                
  463.                 case 0x85:      // FX85 - load V0...VX (X<8) from the HP48 flags *SCHIP*
  464.                     for (int i=0; i <= ((opcode & 0x0F00)>>8); i++)
  465.                         V[i] = hp48_flags[i];
  466.                     //for (int i=0; i < 8; i++)
  467.                         //hp48_flags[i] = 0;
  468.                     break;
  469.                    
  470.                 default:
  471.                     cerr << "Unknown opcode: 0x" << hex << opcode <<endl;
  472.             }
  473.             break;
  474.  
  475.         default:
  476.             cerr << "Unknown opcode: 0x" << hex << opcode <<endl;
  477.     }
  478. }
  479.  
  480. bool ChipEmu::loadGame(const char *filename)
  481. {
  482.     ifstream file(filename, ios::in | ios::binary | ios::ate);
  483.     if (file.is_open())
  484.     {
  485.         ifstream::pos_type size;
  486.         size = file.tellg();
  487.         if (size > 0x0FFF - 0x200)
  488.         {
  489.             cerr << "Error: file '" << filename << "' is too large." << endl;
  490.             return false;
  491.         }
  492.         file.seekg(0, ios::beg);
  493.         file.read(reinterpret_cast<char*>(&memory[0x200]), size);
  494.         file.close();
  495.        
  496.         cout << "File '" << filename << "' loaded." << endl;
  497.         return true;
  498.     }
  499.  
  500.     cerr << "Error: unable to open file '" << filename << "'" << endl;
  501.     return false;
  502. }
  503.  
  504. void ChipEmu::decreaseTimers()
  505. {
  506.     if(delay_timer > 0)
  507.         --delay_timer;
  508.  
  509.     if(sound_timer > 0)
  510.         --sound_timer;
  511. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement