@@ -0,0 +1,64 @@
#include "stdafx.h"
#include "Display.h"


Display::Display()
{
display = new byte *[height];
for (int i = 0; i < height; i++) {
display[i] = new byte[width];
}

for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
display[i][j] = 0;
}
}
}

void Display::setPixel(int x, int y, byte pixel)
{
if ((x >= width || x < 0) || (y >= height || y < 0)) return;

byte mask = 0x80;
int offset = 0;
for (int i = x; (i < width) && (offset < 8); i++, offset++) {
display[y][i] ^= pixel & (mask >> offset);
}
}

void Display::clear()
{
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
display[i][j] = 0;
}
}
}

bool Display::compare(int x, int y, byte pixel)
{
byte mask = 0x80;
int offset = 0;
for (int i = x; i < width || offset < 8; i++) {
if(display[y][i] ^ (pixel & (mask >> offset))) return true;
offset++;
}
return false;
}

int Display::getHeight()
{
return height;
}

int Display::getWidth()
{
return width;
}

byte ** Display::getDisplay()
{
return display;
}

@@ -0,0 +1,23 @@
#pragma once

#include "types.h"

class Display
{
byte **display;
const int height = 32;
const int width = 64;
public:
Display();

void setPixel(int x, int y, byte pixel);
void clear();

bool compare(int x, int y, byte pixel);

int getHeight();
int getWidth();

byte ** getDisplay();
};

@@ -0,0 +1,80 @@
#include "stdafx.h"
#include "Memory.h"


Memory::Memory()
{
for (int i = 0; i < 4096; i++) RAM[i] = 0;

for (int i = 0; i < 80; i++) RAM[i] = fontset[i];

SP = 0xEA0;
SPoffset = 0;
}

byte Memory::read(word addr)
{
return RAM[addr];
}

word Memory::readWord(word addr)
{
word result = RAM[addr];
addr++;
result <<= 8;
result |= RAM[addr];
return result;
}

void Memory::write(word addr, byte value)
{
RAM[addr] = value;
}

void Memory::push(word value)
{
RAM[SP + SPoffset] = value & 0x00FF;
SPoffset++;
RAM[SP + SPoffset] = (value & 0xFF00) >> 8;
SPoffset++;
}

word Memory::pop()
{
SPoffset--;
word result = RAM[SP + SPoffset];
SPoffset--;
result <<= 8;
result |= RAM[SP + SPoffset];
return result;
}

void Memory::loadProgram(byte * bytes, int size)
{
const word PC = 0x200;
for (int i = 0; i < size; i++) RAM[PC + i] = bytes[i];
}

word Memory::getFontAddress(byte value)
{
switch (value)
{
case 0: return 0;
case 1: return 5;
case 2: return 10;
case 3: return 15;
case 4: return 20;
case 5: return 25;
case 6: return 30;
case 7: return 35;
case 8: return 40;
case 9: return 45;
case 0xA: return 50;
case 0xB: return 55;
case 0xC: return 60;
case 0xD: return 65;
case 0xE: return 70;
case 0xF: return 75;
}
return 0;
}
@@ -0,0 +1,46 @@
#pragma once

#include "types.h"

class Memory
{
byte fontset[80] =
{
0xF0, 0x90, 0x90, 0x90, 0xF0, // 0
0x20, 0x60, 0x20, 0x20, 0x70, // 1
0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2
0xF0, 0x10, 0xF0, 0x10, 0xF0, // 3
0x90, 0x90, 0xF0, 0x10, 0x10, // 4
0xF0, 0x80, 0xF0, 0x10, 0xF0, // 5
0xF0, 0x80, 0xF0, 0x90, 0xF0, // 6
0xF0, 0x10, 0x20, 0x40, 0x40, // 7
0xF0, 0x90, 0xF0, 0x90, 0xF0, // 8
0xF0, 0x90, 0xF0, 0x10, 0xF0, // 9
0xF0, 0x90, 0xF0, 0x90, 0x90, // A
0xE0, 0x90, 0xE0, 0x90, 0xE0, // B
0xF0, 0x80, 0x80, 0x80, 0xF0, // C
0xE0, 0x90, 0x90, 0x90, 0xE0, // D
0xF0, 0x80, 0xF0, 0x80, 0xF0, // E
0xF0, 0x80, 0xF0, 0x80, 0x80 // F
};

byte RAM[4096];

word SP; // stack pointer
int SPoffset;

public:
Memory();

byte read(word addr);
word readWord(word addr);
void write(word addr, byte value);

void push(word value);
word pop();

void loadProgram(byte *bytes, int size);

word getFontAddress(byte value);
};

@@ -0,0 +1,35 @@
#include "Timers.h"



Timers::Timers()
{
delayTimer = 0;
soundTimer = 0;
}

void Timers::decreaseDelayTimer()
{
if (delayTimer > 0) delayTimer--;
}

void Timers::decreaseSoundTimer()
{
if (soundTimer > 0) soundTimer--;
}

void Timers::setSoundTimer(int value)
{
soundTimer = value;
}

void Timers::setDelayTimer(int value)
{
delayTimer = value;
}

int Timers::getDelayTimer()
{
return delayTimer;
}

@@ -0,0 +1,14 @@
#pragma once
class Timers
{
int delayTimer;
int soundTimer;
public:
Timers();
void decreaseDelayTimer();
void decreaseSoundTimer();
void setSoundTimer(int value);
void setDelayTimer(int value);
int getDelayTimer();
};

361 main.cpp
@@ -0,0 +1,361 @@
#include "stdafx.h"

#include <Windows.h>
#include <ctime>
#include <fstream>

#include "Chip8.h"

#define ID_FILE_OPEN 100

const int width = 640;
const int height = 320;

Chip8 *chip;
bool loaded = false;

LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
void openFile(HWND hWnd, unsigned char *&bytes, int &size);
void drawGraphics(HDC hdc, unsigned char **pixels, int w, int h);

int WINAPI WinMain(HINSTANCE hInstance, // identifier of program
HINSTANCE hPrevInstance, //identifier of last copie of the same program
LPSTR lpCmdLine, //cmd argument
int nCmdShow) //how window looks when it has been created
{
HWND hWnd;
WNDCLASSEX wc;

ZeroMemory(&wc, sizeof(WNDCLASSEX));

wc.cbSize = sizeof(WNDCLASSEX); //structure size
wc.style = CS_HREDRAW | CS_VREDRAW; //window style
wc.lpfnWndProc = WindowProc; //event handler
wc.hInstance = hInstance;
wc.hCursor = LoadCursor(NULL, IDC_ARROW); //cursor style
wc.hbrBackground = (HBRUSH)COLOR_WINDOW; //background style
wc.lpszClassName = L"WindowClass"; //name of the class

RegisterClassEx(&wc);

RECT wr = { 0, 0, width, height }; // set the size, but not the position
AdjustWindowRect(&wr, WS_OVERLAPPEDWINDOW, FALSE); // adjust the size

hWnd = CreateWindowEx(NULL, //style stuff
L"WindowClass", //name of the class
L"CHIP-8", //tytle
WS_OVERLAPPEDWINDOW, //options of window
300, //coord x
300, //coord y
wr.right - wr.left, //width
wr.bottom - wr.top, //height
NULL, //window parent
NULL, //menu bar handler
hInstance,
NULL);

ShowWindow(hWnd, nCmdShow);

MSG msg;
chip = new Chip8();

time_t currentTime = time(0);
while (true) {
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg); //change format of msg
DispatchMessage(&msg); //send to window proc
if (msg.message == WM_QUIT) break;
}
else {
if (loaded) {
time_t now = time(0);

chip->runProgram();
if (now - currentTime > 0.016) chip->decreaseTimers(); //timer works at 60hz

if (chip->getDrawFlag()) {
Display * display = chip->getDisplay();
drawGraphics(GetDC(hWnd), (unsigned char **)display->getDisplay(), display->getWidth(), display->getHeight());
currentTime = now;
}
}
}
}
return msg.wParam;
}

LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE: {
HMENU hMenu, hSubMenu;

hMenu = CreateMenu();
hSubMenu = CreatePopupMenu();

AppendMenu(hSubMenu, MF_STRING, ID_FILE_OPEN, _T("Open"));
AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT)hSubMenu, _T("File"));

SetMenu(hWnd, hMenu);

break;
} //MenuBar
case WM_COMMAND: {
switch (LOWORD(wParam))
{
case ID_FILE_OPEN: {
unsigned char *bytes = NULL;
int size;

openFile(hWnd, bytes, size);

chip->loadProgram(bytes, size);
loaded = true;

//delete[] bytes;
break;
}
}
break;
} //MenuBar handler
case WM_DESTROY: {
PostQuitMessage(0);
return 0;
}
case WM_KEYDOWN: {
switch (wParam)
{
case '1': {
chip->setKey(0);
break;
}
case '2': {
chip->setKey(1);
break;
}
case '3': {
chip->setKey(2);
break;
}
case '4': {
chip->setKey(3);
break;
}
case 'Q': {
chip->setKey(4);
break;
}
case 'W': {
chip->setKey(5);
break;
}
case 'E': {
chip->setKey(6);
break;
}
case 'R': {
chip->setKey(7);
break;
}
case 'A': {
chip->setKey(8);
break;
}
case 'S': {
chip->setKey(9);
break;
}
case 'D': {
chip->setKey(10);
break;
}
case 'F': {
chip->setKey(11);
break;
}
case 'Z': {
chip->setKey(12);
break;
}
case 'X': {
chip->setKey(13);
break;
}
case 'C': {
chip->setKey(14);
break;
}
case 'V': {
chip->setKey(15);
break;
}
default:
break;
}
break;
}
case WM_KEYUP: {
switch (wParam)
{
case '1': {
chip->resetKey(0);
break;
}
case '2': {
chip->resetKey(1);
break;
}
case '3': {
chip->resetKey(2);
break;
}
case '4': {
chip->resetKey(3);
break;
}
case 'Q': {
chip->resetKey(4);
break;
}
case 'W': {
chip->resetKey(5);
break;
}
case 'E': {
chip->resetKey(6);
break;
}
case 'R': {
chip->resetKey(7);
break;
}
case 'A': {
chip->resetKey(8);
break;
}
case 'S': {
chip->resetKey(9);
break;
}
case 'D': {
chip->resetKey(10);
break;
}
case 'F': {
chip->resetKey(11);
break;
}
case 'Z': {
chip->resetKey(12);
break;
}
case 'X': {
chip->resetKey(13);
break;
}
case 'C': {
chip->resetKey(14);
break;
}
case 'V': {
chip->resetKey(15);
break;
}
default:
break;
}
break;
}
default:
break;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}

void openFile(HWND hWnd, unsigned char *&bytes, int &size)
{
OPENFILENAME ofn;

wchar_t fileName[MAX_PATH];
fileName[0] = '\0'; //used to be init

ZeroMemory(&ofn, sizeof(ofn));

ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = hWnd;
ofn.lpstrFile = fileName;
ofn.nMaxFile = MAX_PATH;
ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_ALLOWMULTISELECT;

if (GetOpenFileName(&ofn))
{
size_t i;
char tmp[MAX_PATH];
wcstombs_s(&i, tmp, MAX_PATH, fileName, MAX_PATH);
const char *name = tmp;

FILE * file;
fopen_s(&file, name, "rb");
if (file == NULL) return;
fseek(file, 0, SEEK_END);
size = ftell(file);
fclose(file);

fopen_s(&file, name, "rb");
bytes = new unsigned char[size];
int bytes_read = fread(bytes, sizeof(unsigned char), size, file);
fclose(file);
}
}

void drawGraphics(HDC hdc, unsigned char ** pixels, int w, int h)
{
COLORREF *arr = (COLORREF*)calloc(width * height, sizeof(COLORREF));
/* Filling array here */
int k = 0;
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++) {
COLORREF color;
if (pixels[i][j]) {
color = RGB(255, 255, 255);
}
else {
color = RGB(0, 0, 0);
}

for(int l = 0 ; l < width/w; l++, k++) arr[k] = color;
}

if (k == width * height) break;

int row = k;
for (int m = 0; m < height/h; m++) {
for (int n = row - width; n < row; n++, k++) arr[k] = arr[n];
}
}

// Creating temp bitmap
HBITMAP map = CreateBitmap(width, // width.
height, // height
1, // Color Planes, unfortanutelly don't know what is it actually. Let it be 1
8*4, // Size of memory for one pixel in bits(in win32 4 bytes = 4*8 bits)
(void*)arr); // pointer to array
// Temp HDC to copy picture
HDC src = CreateCompatibleDC(hdc); // hdc - Device context for window, I've got earlier with GetDC(hWnd) or GetDC(NULL);
SelectObject(src, map); // Inserting picture into our temp HDC
// Copy image from temp HDC to window
BitBlt(hdc, // Destination
0, // x and
0, // y - upper-left corner of place, where we'd like to copy
width, // width of the region
height, // height
src, // source
0, // x and
0, // y of upper left corner of part of the source, from where we'd like to copy
SRCCOPY); // way of copy;

DeleteDC(src); // Deleting temp HDC
delete[]arr;
}
@@ -0,0 +1,14 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by Chip-8.rc

// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 101
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
@@ -0,0 +1,8 @@
// stdafx.cpp : source file that includes just the standard includes
// Chip-8.pch will be the pre-compiled header
// stdafx.obj will contain the pre-compiled type information

#include "stdafx.h"

// TODO: reference any additional headers you need in STDAFX.H
// and not in this file
@@ -0,0 +1,15 @@
// stdafx.h : include file for standard system include files,
// or project specific include files that are used frequently, but
// are changed infrequently
//

#pragma once

#include "targetver.h"

#include <stdio.h>
#include <tchar.h>



// TODO: reference additional headers your program requires here
@@ -0,0 +1,8 @@
#pragma once

// Including SDKDDKVer.h defines the highest available Windows platform.

// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and
// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.

#include <SDKDDKVer.h>
@@ -0,0 +1,4 @@
#pragma once

typedef unsigned char byte;
typedef unsigned short int word;