#include <cstdlib>
#include <iostream>
#include <fstream>
#include <time.h>
#include <windows.h>
#include <SDL.h> //I'm migrating to the windows API so this is temporary
#pragma comment( lib , SDL_image.lib )
#include "CPU_Timing.h"
#define MemorySize 0xfff
using namespace std;
/* Declare Windows procedure */
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);
/* Make the class name into a global variable */
char szClassName[ ] = "8 Chip F.A.I.L.";
ifstream::pos_type size;
ifstream file ("PONG", ios::in|ios::binary);
void Hash( unsigned short int, unsigned int ); //variable to be hashed, number of 'cuts'
unsigned int Power( unsigned int, unsigned int );
unsigned int ConvertDecimalToBinary( unsigned int );
void OnKeyDown(SDLKey sym, SDLMod mod, Uint16 unicode);
void DrawScene();
SDL_Event event;
//globals, I know you will want to kill me,
//but just lemme get this working, and I'll remove some of them
unsigned short OpcodeHash[3]; //we're breaking it in as many parts as needed
HBRUSH whiteBrush = CreateSolidBrush(RGB(255,255,255));
HBRUSH blackBrush = CreateSolidBrush(RGB(0,0,0));
HWND hwnd;
bool Pixels[64][32]; //1 for a white pixel 0 for a black pixel!
int WINAPI WinMain (HINSTANCE hThisInstance,
HINSTANCE hPrevInstance,
LPSTR lpszArgument,
int nFunsterStil)
{
//HWND hwnd; /* Oops it had to become global for me to access it through
//the DrawScene();
MSG messages; /* Here messages to the application are saved */
WNDCLASSEX wincl; /* Data structure for the windowclass */
srand((unsigned)time(NULL)); //Needed for the random numbers
unsigned short FullOpcode;
unsigned short X[7]; //8-bit
unsigned short Y[7]; //8-bit
unsigned int I; //memory crap
int SP = 0; //Stack Crap
unsigned int DT = 0; //Delay Timer
unsigned int ST = 0; //Sound Timer
unsigned int N; //drawing stuff
unsigned int V[0xf]; //0xf = 15 in hex...So gay to write it that way but wth
unsigned int key[0xf];
unsigned int stack[16];
int tempHash; //binary encoding
unsigned int A, B, C; //Also for the binary encoding
unsigned int xpos; //sprite xpos
unsigned int ypos; //sprite ypos
unsigned int i = 0x200; //we must start at the adress 0x200 in hex or 512 in dec
char Memory[MemorySize];
//init keys
for (int o = 0; o < 16; o++){ key[o] = 0; }
if(SDL_Init(SDL_INIT_EVERYTHING) < 0) {
return false;
}
/* The Window structure */
wincl.hInstance = hThisInstance;
wincl.lpszClassName = szClassName;
wincl.lpfnWndProc = WindowProcedure; /* This function is called by windows */
wincl.style = CS_DBLCLKS; /* Catch double-clicks */
wincl.cbSize = sizeof (WNDCLASSEX);
/* Use default icon and mouse-pointer */
wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
wincl.lpszMenuName = NULL; /* No menu */
wincl.cbClsExtra = 0; /* No extra bytes after the window class */
wincl.cbWndExtra = 0; /* structure or the window instance */
//read rom
size = file.tellg();
file.seekg (0, ios::beg);
file.read (Memory, MemorySize);
printf("Rom loaded succesfully.");
/* Black background */
wincl.hbrBackground = CreateSolidBrush(RGB(0, 0, 0));
/* Register the window class, and if it fails quit the program */
if (!RegisterClassEx (&wincl))
return 0;
/* The class is registered, let's create the program*/
hwnd = CreateWindowEx (
0, /* Extended possibilites for variation */
szClassName, /* Classname */
"8 Chip F.A.I.L.", /* Title Text */
WS_OVERLAPPEDWINDOW, /* default window */
CW_USEDEFAULT, /* Windows decides the position */
CW_USEDEFAULT, /* where the window ends up on the screen */
640, /* The programs width */
320, /* and height in pixels */
HWND_DESKTOP, /* The window is a child-window to desktop */
NULL, /* No menu */
hThisInstance, /* Program Instance handler */
NULL /* No Window Creation data */
);
/* Make the window visible on the screen */
ShowWindow (hwnd, nFunsterStil);
/* Run the message loop. It will run until GetMessage() returns 0 */
//while (GetMessage (&messages, NULL, 0, 0))
while (true)
{
start = clock(); //let the ticking begin!
//DrawScene(); Too buggy for me to put you on right now
while (PeekMessage(&messages, hwnd, 0, 0, PM_REMOVE) == TRUE) {
if (messages.message == WM_QUIT)
break;
/* Translate virtual-key messages into character messages */
TranslateMessage(&messages);
/* Send message to WindowProcedure */
DispatchMessage(&messages);
}
if(i > MemorySize){
cout << "Memory Overflow!\n"; //lol wtf?
//system("PAUSE");
//i = 0x200;
}
//First fuse'em
FullOpcode = ((Memory[i] << 8) | (Memory[i + 1])) ;
cout << FullOpcode << "\n"; //keep track of the code
/*almost all opcodes have to be broken into one 4-bit part and
another 12-bit part, and then we decide if we break the 12-bit part or not
so... to the bit grinding!!*/
//Now hash once (don't forget flour and butter)
Hash(FullOpcode, 1);
//Switch!
switch(OpcodeHash[0]){
case 0x0:
Hash(FullOpcode, 3);
if(OpcodeHash[3] == 0x0) { //Clear the Screen
for(int a = 1;a<65;a++){
for(int b = 1;b<33;b++){
Pixels[a][b] = false;
}
}
break;
}
else {
//return from sub (wtf?)
i = stack[SP] - 2;
SP--;
}
break;
case 0x1: //Get ready to JUMP!
i = (OpcodeHash[1] - 2); //NNN - 2 (it will be incremented at the end of the loop)
break;
case 0x2:
if(SP <= 15){
SP++;
stack[SP] = i;
i = OpcodeHash[1] - 2; //Set it to NNN
}
break;
case 0x3:
Hash(FullOpcode, 2);
if( V[OpcodeHash[1]] == OpcodeHash[2] ) { //VX = NN?
i += 2; //skip
}
break;
case 0x4:
Hash(FullOpcode, 2);
if( V[OpcodeHash[1]] != OpcodeHash[2] ) { //VX != NN?
i += 2; //skip
}
break;
case 0x5:
Hash(FullOpcode, 3);
if( V[OpcodeHash[1]] == V[OpcodeHash[2]] ) { //VX = VY?
i += 2; //skip
}
break;
case 0x6:
Hash(FullOpcode, 2);
V[OpcodeHash[1]] = OpcodeHash[2]; //VX = NN
break;
case 0x7:
Hash(FullOpcode, 2);
V[OpcodeHash[1]] += OpcodeHash[2]; //VX = VX + NN
break;
case 0x8:
Hash(FullOpcode, 3);
switch(OpcodeHash[3]){
case 0:
V[OpcodeHash[1]] = V[OpcodeHash[2]]; //VX = VY
break;
case 1: //or
V[OpcodeHash[1]] = (V[OpcodeHash[1]] | V[OpcodeHash[2]]); //VX or VY
break;
case 2: //and
V[OpcodeHash[1]] = (V[OpcodeHash[1]] & V[OpcodeHash[2]]); //VX and VY
break;
case 3: //xor
V[OpcodeHash[1]] = (V[OpcodeHash[1]] ^ V[OpcodeHash[2]]); //VX xor VY
break;
case 4:
if((V[OpcodeHash[1]] & V[OpcodeHash[2]]) != 0) {
//That means that if we are to sum these numbers (binary),
//there will be at least one carry!
V[0xf] = 1;
}
else
{
V[0xf] = 0;
}
V[OpcodeHash[1]] = V[OpcodeHash[1]] + V[OpcodeHash[2]];
break;
case 5:
X[0] = V[OpcodeHash[1]] & 1;
Y[0] = V[OpcodeHash[2]] & 1;
if(Y[0] > X[0]){ //must be one luck guy
V[0xf] = 0;
}
else{
for(int a = 1; a < 7; a++){
X[a] = V[OpcodeHash[1]] & Power(2, a);
Y[a] = V[OpcodeHash[2]] & Power(2, a);
if (Y[a] > X[a]){ //there's the borrow!
V[0xf] = 0;
break;
}
else
{
V[0xf] = 1;
}
}
}
V[OpcodeHash[1]] = V[OpcodeHash[1]] - V[OpcodeHash[2]];
break;
case 6:
V[0xf] = (V[OpcodeHash[1]] & 1); //VF is the least significant bit of VX
V[OpcodeHash[1]] >>= 1; //Shifts VX by 1 (right)
break;
case 7:
X[0] = V[OpcodeHash[1]] & 1;
Y[0] = V[OpcodeHash[2]] & 1;
if(X[0] > Y[0]){ //must be one luck guy
V[0xf] = 0;
}
else{
for(int a = 1; a < 7; a++){
X[a] = V[OpcodeHash[1]] & Power(2, a);
Y[a] = V[OpcodeHash[2]] & Power(2, a);
if (X[a] > Y[a]){ //there's the borrow!
V[0xf] = 0;
break;
}
else
{
V[0xf] = 1;
}
}
}
V[OpcodeHash[1]] = V[OpcodeHash[2]] - V[OpcodeHash[1]];
break;
case 0xe:
V[0xf] = V[OpcodeHash[1]] & 128; //VF is set to the most significant bit of VX
V[OpcodeHash[1]] <<= 1; //Shifts VX by 1 (left)
break;
}
break;
case 0x9:
Hash(FullOpcode, 3);
if(V[OpcodeHash[1]] != V[OpcodeHash[2]]){ //VX != VY?
i += 2; //skip
}
break;
case 0xa:
I = OpcodeHash[1]; //I = NNN
break;
case 0xb:
i = OpcodeHash[1] + V[0] - 2; //jumps to NNN + V0...Has to be decremented because it will be
//incremented at the end of the code
break;
case 0xc:
Hash(FullOpcode, 2);
V[OpcodeHash[1]] = int((double(rand())/RAND_MAX)*255); //max of 8 bit
V[OpcodeHash[1]] = V[OpcodeHash[1]] & OpcodeHash[2];
break;
case 0xd: //yay, let's draw!!! But soon, because right now we can't even plot a pixel...
/* Hash(FullOpcode, 3);
xpos = V[OpcodeHash[1]];
ypos = V[OpcodeHash[2]];
N = OpcodeHash[3];
V[0xf] = 0; //initially we assume there's no collision
for (int z = 1; z < N + 1; z++){
for (int v = 1; v < 9; v++){
if (Pixels[z + xpos][v + ypos] = false){ //No pixel! Must draw!!
Pixels[z + xpos][v + ypos] = true;
}
else
{
//flip 'em
V[0xf] = 1; //There has been a collision! Still, we must (un)draw!!
Pixels[z + xpos][v + ypos] = false;
}
}
}
break; */
case 0xe:
Hash(FullOpcode, 3);
if (OpcodeHash[2] == 9){
if (key[V[OpcodeHash[1]]] == 1){
i += 2; //skip
}
}
else{
if (key[V[OpcodeHash[1]]] != 1){ //triple array subscrit ftw
//Same shit, different day
i += 2; //skip
}
}
break;
case 0xf: //long list...again!!!!
Hash(FullOpcode, 2);
switch(OpcodeHash[2]){
case 0x07:
V[OpcodeHash[1]] = DT; //VX = DT
break;
case 0x0A: //INPUT! =O More to add soon...
while (SDL_PollEvent(&event)) {
if (event.type == SDL_KEYDOWN){
switch (event.key.keysym.sym) {
case SDLK_UP:
key[0x8] = 1;
V[OpcodeHash[1]] = 0x8;
break;
case SDLK_DOWN:
key[0x2] = 1;
V[OpcodeHash[1]] = 0x2;
break;
case SDLK_LEFT:
key[0x4] = 1;
V[OpcodeHash[1]] = 0x4;
break;
case SDLK_RIGHT:
key[0x6] = 1;
V[OpcodeHash[1]] = 0x6;
break;
case SDLK_ESCAPE:
// return 0; //quits
break;
}
}
}
break;
case 0x18:
ST = V[OpcodeHash[1]]; //ST = VX
break;
case 0x1E:
I += V[OpcodeHash[1]]; //I = I + VX
break;
case 0x29: //TODO (Unimplemented)
break;
case 0x33: //BCD - Binary Coded Decimal (255) = (0010) (0101) (0101)
//
// 2 5 5
tempHash = V[OpcodeHash[1]];
A = (tempHash / 100); //hundred
tempHash = tempHash - A*100;
B = tempHash/10; //dozen
tempHash = tempHash - B*10; //unit
C = tempHash;
// Now that the number is split, we may encode it!
Memory[I] = ConvertDecimalToBinary(A);
Memory[I + 1] = ConvertDecimalToBinary(B);
Memory[I + 2] = ConvertDecimalToBinary(C);
break;
case 0x55:
for(int h = 0; h < OpcodeHash[1] + 1; h++){
Memory[I] = V[h];
I++;
}
break;
case 0x15:
DT = V[OpcodeHash[1]]; //DT = VX
break;
case 0x65:
for(int h = 0; h < OpcodeHash[1] + 1; h++){
V[h] = Memory[I];
I++;
}
break;
}
break;
}
i += 2; //Increment 2 bytes to the CPU.
end = clock();
cpuTime= (end-start)/ (CLOCKS_PER_SEC);
delay( 1000/60 - cpuTime ); //60 hertz! No matter what pc!!
//The above makes sure that to any PC, there will be a 1/60 second stop after each
//instruction. That makes the emu run @ 60hz. Also syncs with the delay timers.
DT = DT + 1; //Increment delay timer
ST = ST + 1; //Increment sound timer
}
/* The program return-value is 0 - The value that PostQuitMessage() gave */
return messages.wParam;
}
/* This function is called by the Windows function DispatchMessage() */
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message) /* handle the messages */
{
case WM_DESTROY:
PostQuitMessage (0); /* send a WM_QUIT to the message queue */
break;
default: /* for messages that we don't deal with */
return DefWindowProc (hwnd, message, wParam, lParam);
}
file.close();
return 0;
}
/*void Hash( unsigned short int opcode, unsigned int n) {
//n ranging from 1 to 3
switch(n){
case 1:
OpcodeHash[0] = opcode >> 12;
OpcodeHash[1] = opcode << 4;
OpcodeHash[1] >>= 4;
break;
case 2:
OpcodeHash[0] = opcode >> 12;
OpcodeHash[1] = opcode << 4;
OpcodeHash[1] >>= 12;
OpcodeHash[2] = opcode << 8;
OpcodeHash[2] >>= 8;
break;
case 3:
OpcodeHash[0] = opcode >> 12;
OpcodeHash[1] = opcode << 4;
OpcodeHash[1] >>= 12;
OpcodeHash[2] = opcode << 8;
OpcodeHash[2] >>= 12;
OpcodeHash[3] = opcode << 12;
OpcodeHash[3] >>= 12;
break;
}
}*/
void Hash( unsigned short int opcode, unsigned int n) {
//n ranging from 1 to 3
switch(n){
case 1:
OpcodeHash[0] = opcode & 0xf000;
OpcodeHash[0] >>= 12;
OpcodeHash[1] = opcode & 0x0fff;
break;
case 2:
OpcodeHash[0] = opcode & 0xf000;
OpcodeHash[0] >>= 12;
OpcodeHash[1] = opcode & 0x0f00;
OpcodeHash[1] >>= 8;
OpcodeHash[2] = opcode & 0x00ff;
break;
case 3:
OpcodeHash[0] = opcode & 0xf000;
OpcodeHash[0] >>= 12;
OpcodeHash[1] = opcode & 0x0f00;
OpcodeHash[1] >>= 8;
OpcodeHash[2] = opcode & 0x00f0;
OpcodeHash[2] >>= 4;
OpcodeHash[3] = opcode & 0x000f;
break;
}
}
unsigned int Power( unsigned int power, unsigned int exponent ) {
int base = power;
int p = 1;
while(p < exponent) {
power *= base;
p++;
}
return power;
}
unsigned int ConvertDecimalToBinary( unsigned int decimal ){
int a = 0;
int b = 0;
int c = 0;
int d = 0;
unsigned int binary;
if (decimal & 1 == 1 ) { a = 1;}
decimal >>=1;
if (decimal & 1 == 1 ) { b = 1;}
decimal >>=1;
if (decimal & 1 == 1 ) { c = 1;}
decimal >>=1;
if (decimal & 1 == 1 ) { d = 1;}
decimal >>=1;
binary = a*1 + b*10 + c*100 + d*1000;
return binary;
}
void DrawScene(){
HDC hdc = GetDC(hwnd);
RECT rct[64][32];
for (int x = 1; x < 65; x++) {
for (int y = 1; y < 33; y++) {
if(Pixels[x][y] == 0) {
rct[x][y].left= x*5+ 65;
rct[x][y].right= x*5+ 60;
rct[x][y].top= y*5+ 65;
rct[x][y].bottom= y*5+ 60;
FillRect(hdc, &rct[x][y], blackBrush);
}
else
{
rct[x][y].left= x*5 + 65;
rct[x][y].right= x*5 + 60;
rct[x][y].top= y*5 + 65;
rct[x][y].bottom= y*5 + 60;
FillRect(hdc, &rct[x][y], whiteBrush);
}
}
}
}