1804 lines
No EOL
60 KiB
C
1804 lines
No EOL
60 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h> // for the uintx_t's
|
|
#include <unistd.h> // for sleep
|
|
#include <gtk/gtk.h>
|
|
#include <pthread.h>
|
|
|
|
// gcc emulator_shell.c `pkg-config --cflags --libs gtk+-3.0` -lpthread -o emulator
|
|
|
|
//===== EMULATOR SETUP =====
|
|
|
|
//GLOBAL VARIABLES
|
|
uint8_t in_port;
|
|
char which_interrupt;
|
|
uint8_t upscaleFactor = 2;
|
|
|
|
//GLOBAL GRAPHICS
|
|
uint8_t *bitmap = NULL;
|
|
|
|
|
|
|
|
typedef struct ConditionCodes {
|
|
//bitfields for condition codes
|
|
//the number following the variables sets the number of bits
|
|
uint8_t z:1;
|
|
uint8_t s:1;
|
|
uint8_t p:1;
|
|
uint8_t cy:1;
|
|
uint8_t ac:1;
|
|
uint8_t pad:3;
|
|
} ConditionCodes;
|
|
|
|
|
|
typedef struct State8080 {
|
|
uint8_t a;
|
|
uint8_t b;
|
|
uint8_t c;
|
|
uint8_t d;
|
|
uint8_t e;
|
|
uint8_t h;
|
|
uint8_t l;
|
|
uint16_t sp;
|
|
uint16_t pc;
|
|
uint8_t *memory;
|
|
ConditionCodes cc;
|
|
uint8_t int_enable;
|
|
uint8_t shift_offset;
|
|
uint8_t shift0;
|
|
uint8_t shift1;
|
|
} State8080;
|
|
|
|
unsigned char cycles8080[] = {
|
|
4, 10, 7, 5, 5, 5, 7, 4, 4, 10, 7, 5, 5, 5, 7, 4, //0x00..0x0f
|
|
4, 10, 7, 5, 5, 5, 7, 4, 4, 10, 7, 5, 5, 5, 7, 4, //0x10..0x1f
|
|
4, 10, 16, 5, 5, 5, 7, 4, 4, 10, 16, 5, 5, 5, 7, 4, //etc
|
|
4, 10, 13, 5, 10, 10, 10, 4, 4, 10, 13, 5, 5, 5, 7, 4,
|
|
|
|
5, 5, 5, 5, 5, 5, 7, 5, 5, 5, 5, 5, 5, 5, 7, 5, //0x40..0x4f
|
|
5, 5, 5, 5, 5, 5, 7, 5, 5, 5, 5, 5, 5, 5, 7, 5,
|
|
5, 5, 5, 5, 5, 5, 7, 5, 5, 5, 5, 5, 5, 5, 7, 5,
|
|
7, 7, 7, 7, 7, 7, 7, 7, 5, 5, 5, 5, 5, 5, 7, 5,
|
|
|
|
4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, //0x80..8x4f
|
|
4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4,
|
|
4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4,
|
|
4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4,
|
|
|
|
11, 10, 10, 10, 17, 11, 7, 11, 11, 10, 10, 10, 10, 17, 7, 11, //0xc0..0xcf
|
|
11, 10, 10, 10, 17, 11, 7, 11, 11, 10, 10, 10, 10, 17, 7, 11,
|
|
11, 10, 10, 18, 17, 11, 7, 11, 11, 5, 10, 5, 17, 17, 7, 11,
|
|
11, 10, 10, 4, 17, 11, 7, 11, 11, 5, 10, 4, 17, 17, 7, 11,
|
|
};
|
|
|
|
//===== GTK SETUP =====
|
|
|
|
void keyDown(uint8_t key)
|
|
{
|
|
switch(key) {
|
|
case 0: // COIN
|
|
in_port |= 0x1;
|
|
break;
|
|
case 1: // LEFT
|
|
in_port |= 0x20;
|
|
break;
|
|
case 2: // RIGHT
|
|
in_port |= 0x40;
|
|
break;
|
|
case 3: // FIRE
|
|
in_port |= 0x10;
|
|
break;
|
|
case 4: // START
|
|
in_port |= 0x04;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void keyUp(uint8_t key)
|
|
{
|
|
switch(key) {
|
|
case 0: // COIN
|
|
in_port &= ~0x1;
|
|
break;
|
|
case 1: // LEFT
|
|
in_port &= ~0x20;
|
|
break;
|
|
case 2: // RIGHT
|
|
in_port &= ~0x40;
|
|
break;
|
|
case 3: // FIRE
|
|
in_port &= ~0x10;
|
|
break;
|
|
case 4: // START
|
|
in_port &= ~0x04;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static gboolean on_key_press(GtkWidget *widget, GdkEventKey *event, gpointer user_data)
|
|
{
|
|
switch (event->keyval)
|
|
{
|
|
case GDK_KEY_a:
|
|
// Code to be executed when the left arrow key is pressed down
|
|
g_print("Left arrow key pressed down\n");
|
|
keyDown(1);
|
|
break;
|
|
case GDK_KEY_d:
|
|
// Code to be executed when the right arrow key is pressed down
|
|
g_print("Right arrow key pressed down\n");
|
|
keyDown(2);
|
|
break;
|
|
case GDK_KEY_w:
|
|
// Code to be executed when the space bar is pressed down
|
|
g_print("w down\n");
|
|
keyDown(3);
|
|
break;
|
|
case GDK_KEY_c:
|
|
// Code to be executed when the 'c' key is pressed down
|
|
g_print("'c' key pressed down\n");
|
|
keyDown(0);
|
|
break;
|
|
case GDK_KEY_s:
|
|
// Code to be executed when the 's' key is pressed down
|
|
g_print("'s' key pressed down\n");
|
|
keyDown(4);
|
|
break;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean on_key_release(GtkWidget *widget, GdkEventKey *event, gpointer user_data)
|
|
{
|
|
switch (event->keyval)
|
|
{
|
|
case GDK_KEY_a:
|
|
// Code to be executed when the left arrow key is released
|
|
g_print("Left arrow key released\n");
|
|
keyUp(1);
|
|
break;
|
|
case GDK_KEY_d:
|
|
// Code to be executed when the right arrow key is released
|
|
g_print("Right arrow key released\n");
|
|
keyUp(2);
|
|
break;
|
|
case GDK_KEY_w:
|
|
// Code to be executed when the space bar is pressed down
|
|
g_print("w released\n");
|
|
keyUp(3);
|
|
break;
|
|
case GDK_KEY_c:
|
|
// Code to be executed when the 'c' key is pressed down
|
|
g_print("'c' key released\n");
|
|
keyUp(0);
|
|
break;
|
|
case GDK_KEY_s:
|
|
// Code to be executed when the 's' key is pressed down
|
|
g_print("'s' key released\n");
|
|
keyUp(4);
|
|
break;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
//===== EMULATOR FUNCTIONS =====
|
|
|
|
int disassemble8080(unsigned char* code) {
|
|
int opbytes = 1;
|
|
switch (*code) {
|
|
case 0x00: printf("NOP"); break;
|
|
|
|
case 0x01: printf("LXI B,#$%02x%02x", code[2], code[1]); opbytes = 3; break;
|
|
case 0x02: printf("STAX B"); break;
|
|
case 0x03: printf("INX B"); break;
|
|
case 0x04: printf("INR B"); break;
|
|
case 0x05: printf("DCR B"); break;
|
|
case 0x06: printf("MVI B,#$%02x", code[1]); opbytes = 2; break;
|
|
case 0x07: printf("RLC"); break;
|
|
case 0x08: printf("NOP"); break;
|
|
|
|
case 0x09: printf("DAD B"); break;
|
|
case 0x0a: printf("LDAX B"); break;
|
|
case 0x0b: printf("DCX B"); break;
|
|
case 0x0c: printf("INR C"); break;
|
|
case 0x0d: printf("DCR C"); break;
|
|
case 0x0e: printf("MVI C,#$%02x", code[1]); opbytes = 2; break;
|
|
case 0x0f: printf("RRC"); break;
|
|
case 0x10: printf("NOP"); break;
|
|
|
|
case 0x11: printf("LXI D,#$%02x%02x", code[2], code[1]); opbytes = 3; break;
|
|
case 0x12: printf("STAX D"); break;
|
|
case 0x13: printf("INX D"); break;
|
|
case 0x14: printf("INR D"); break;
|
|
case 0x15: printf("DCR D"); break;
|
|
case 0x16: printf("MVI D,#$%02x", code[1]); opbytes = 2; break;
|
|
case 0x17: printf("RAL"); break;
|
|
case 0x18: printf("NOP"); break;
|
|
|
|
case 0x19: printf("DAD D"); break;
|
|
case 0x1a: printf("LDAX D"); break;
|
|
case 0x1b: printf("DCX D"); break;
|
|
case 0x1c: printf("INR E"); break;
|
|
case 0x1d: printf("DCR E"); break;
|
|
case 0x1e: printf("MVI E,#$%02x", code[1]); opbytes = 2; break;
|
|
case 0x1f: printf("RAR"); break;
|
|
case 0x20: printf("NOP"); break;
|
|
|
|
case 0x21: printf("LXI H,#$%02x%02x", code[2], code[1]); opbytes = 3; break;
|
|
case 0x22: printf("SHLD $%02x%02x", code[2], code[1]); opbytes = 3; break;
|
|
case 0x23: printf("INX H"); break;
|
|
case 0x24: printf("INR H"); break;
|
|
case 0x25: printf("DCR H"); break;
|
|
case 0x26: printf("MVI H,#$%02x", code[1]); opbytes = 2; break;
|
|
case 0x27: printf("DAA"); break;
|
|
case 0x28: printf("NOP"); break;
|
|
|
|
case 0x29: printf("DAD H"); break;
|
|
case 0x2a: printf("LHLD $%02x%02x", code[2], code[1]); opbytes = 3; break;
|
|
case 0x2b: printf("DCX H"); break;
|
|
case 0x2c: printf("INR L"); break;
|
|
case 0x2d: printf("DCR L"); break;
|
|
case 0x2e: printf("MVI L,#$%02x", code[1]); opbytes = 2; break;
|
|
case 0x2f: printf("CMA"); break;
|
|
case 0x30: printf("NOP"); break;
|
|
|
|
case 0x31: printf("LXI SP,#$%02x%02x", code[2], code[1]); opbytes = 3; break;
|
|
case 0x32: printf("STA $%02x%02x", code[2], code[1]); opbytes = 3; break;
|
|
case 0x33: printf("INX SP"); break;
|
|
case 0x34: printf("INR M"); break;
|
|
case 0x35: printf("DCR M"); break;
|
|
case 0x36: printf("MVI M,#$%02x", code[1]); opbytes = 2; break;
|
|
case 0x37: printf("STC"); break;
|
|
case 0x38: printf("NOP"); break;
|
|
|
|
case 0x39: printf("DAD SP"); break;
|
|
case 0x3a: printf("LDA $%02x%02x", code[2], code[1]); opbytes = 3; break;
|
|
case 0x3b: printf("DCX SP"); break;
|
|
case 0x3c: printf("INR A"); break;
|
|
case 0x3d: printf("DCR A"); break;
|
|
case 0x3e: printf("MVI A,#$%02x", code[1]); opbytes = 2; break;
|
|
case 0x3f: printf("CMC"); break;
|
|
|
|
case 0x40: printf("MOV B,B"); break;
|
|
case 0x41: printf("MOV B,C"); break;
|
|
case 0x42: printf("MOV B,D"); break;
|
|
case 0x43: printf("MOV B,E"); break;
|
|
case 0x44: printf("MOV B,H"); break;
|
|
case 0x45: printf("MOV B,L"); break;
|
|
case 0x46: printf("MOV B,M"); break;
|
|
case 0x47: printf("MOV B,A"); break;
|
|
|
|
case 0x48: printf("MOV C,B"); break;
|
|
case 0x49: printf("MOV C,C"); break;
|
|
case 0x4a: printf("MOV C,D"); break;
|
|
case 0x4b: printf("MOV C,E"); break;
|
|
case 0x4c: printf("MOV C,H"); break;
|
|
case 0x4d: printf("MOV C,L"); break;
|
|
case 0x4e: printf("MOV C,M"); break;
|
|
case 0x4f: printf("MOV C,A"); break;
|
|
|
|
case 0x50: printf("MOV D,B"); break;
|
|
case 0x51: printf("MOV D,C"); break;
|
|
case 0x52: printf("MOV D,D"); break;
|
|
case 0x53: printf("MOV D,E"); break;
|
|
case 0x54: printf("MOV D,H"); break;
|
|
case 0x55: printf("MOV D,L"); break;
|
|
case 0x56: printf("MOV D,M"); break;
|
|
case 0x57: printf("MOV D,A"); break;
|
|
|
|
case 0x58: printf("MOV E,B"); break;
|
|
case 0x59: printf("MOV E,C"); break;
|
|
case 0x5a: printf("MOV E,D"); break;
|
|
case 0x5b: printf("MOV E,E"); break;
|
|
case 0x5c: printf("MOV E,H"); break;
|
|
case 0x5d: printf("MOV E,L"); break;
|
|
case 0x5e: printf("MOV E,M"); break;
|
|
case 0x5f: printf("MOV E,A"); break;
|
|
|
|
case 0x60: printf("MOV H,B"); break;
|
|
case 0x61: printf("MOV H,C"); break;
|
|
case 0x62: printf("MOV H,D"); break;
|
|
case 0x63: printf("MOV H,E"); break;
|
|
case 0x64: printf("MOV H,H"); break;
|
|
case 0x65: printf("MOV H,L"); break;
|
|
case 0x66: printf("MOV H,M"); break;
|
|
case 0x67: printf("MOV H,A"); break;
|
|
|
|
case 0x68: printf("MOV L,B"); break;
|
|
case 0x69: printf("MOV L,C"); break;
|
|
case 0x6a: printf("MOV L,D"); break;
|
|
case 0x6b: printf("MOV L,E"); break;
|
|
case 0x6c: printf("MOV L,H"); break;
|
|
case 0x6d: printf("MOV L,L"); break;
|
|
case 0x6e: printf("MOV L,M"); break;
|
|
case 0x6f: printf("MOV L,A"); break;
|
|
|
|
case 0x70: printf("MOV M,B"); break;
|
|
case 0x71: printf("MOV M,C"); break;
|
|
case 0x72: printf("MOV M,D"); break;
|
|
case 0x73: printf("MOV M,E"); break;
|
|
case 0x74: printf("MOV M,H"); break;
|
|
case 0x75: printf("MOV M,L"); break;
|
|
case 0x76: printf("HLT"); break;
|
|
case 0x77: printf("MOV M,A"); break;
|
|
|
|
case 0x78: printf("MOV A,B"); break;
|
|
case 0x79: printf("MOV A,C"); break;
|
|
case 0x7a: printf("MOV A,D"); break;
|
|
case 0x7b: printf("MOV A,E"); break;
|
|
case 0x7c: printf("MOV A,H"); break;
|
|
case 0x7d: printf("MOV A,L"); break;
|
|
case 0x7e: printf("MOV A,M"); break;
|
|
case 0x7f: printf("MOV A,A"); break;
|
|
|
|
case 0x80: printf("ADD B"); break;
|
|
case 0x81: printf("ADD C"); break;
|
|
case 0x82: printf("ADD D"); break;
|
|
case 0x83: printf("ADD E"); break;
|
|
case 0x84: printf("ADD H"); break;
|
|
case 0x85: printf("ADD L"); break;
|
|
case 0x86: printf("ADD M"); break;
|
|
case 0x87: printf("ADD A"); break;
|
|
|
|
case 0x88: printf("ADC B"); break;
|
|
case 0x89: printf("ADC C"); break;
|
|
case 0x8a: printf("ADC D"); break;
|
|
case 0x8b: printf("ADC E"); break;
|
|
case 0x8c: printf("ADC H"); break;
|
|
case 0x8d: printf("ADC L"); break;
|
|
case 0x8e: printf("ADC M"); break;
|
|
case 0x8f: printf("ADC A"); break;
|
|
|
|
case 0x90: printf("SUB B"); break;
|
|
case 0x91: printf("SUB C"); break;
|
|
case 0x92: printf("SUB D"); break;
|
|
case 0x93: printf("SUB E"); break;
|
|
case 0x94: printf("SUB H"); break;
|
|
case 0x95: printf("SUB L"); break;
|
|
case 0x96: printf("SUB M"); break;
|
|
case 0x97: printf("SUB A"); break;
|
|
|
|
case 0x98: printf("SBB B"); break;
|
|
case 0x99: printf("SBB C"); break;
|
|
case 0x9a: printf("SBB D"); break;
|
|
case 0x9b: printf("SBB E"); break;
|
|
case 0x9c: printf("SBB H"); break;
|
|
case 0x9d: printf("SBB L"); break;
|
|
case 0x9e: printf("SBB M"); break;
|
|
case 0x9f: printf("SBB A"); break;
|
|
|
|
case 0xa0: printf("ANA B"); break;
|
|
case 0xa1: printf("ANA C"); break;
|
|
case 0xa2: printf("ANA D"); break;
|
|
case 0xa3: printf("ANA E"); break;
|
|
case 0xa4: printf("ANA H"); break;
|
|
case 0xa5: printf("ANA L"); break;
|
|
case 0xa6: printf("ANA M"); break;
|
|
case 0xa7: printf("ANA A"); break;
|
|
|
|
case 0xa8: printf("XRA B"); break;
|
|
case 0xa9: printf("XRA C"); break;
|
|
case 0xaa: printf("XRA D"); break;
|
|
case 0xab: printf("XRA E"); break;
|
|
case 0xac: printf("XRA H"); break;
|
|
case 0xad: printf("XRA L"); break;
|
|
case 0xae: printf("XRA M"); break;
|
|
case 0xaf: printf("XRA A"); break;
|
|
|
|
case 0xb0: printf("ORA B"); break;
|
|
case 0xb1: printf("ORA C"); break;
|
|
case 0xb2: printf("ORA D"); break;
|
|
case 0xb3: printf("ORA E"); break;
|
|
case 0xb4: printf("ORA H"); break;
|
|
case 0xb5: printf("ORA L"); break;
|
|
case 0xb6: printf("ORA M"); break;
|
|
case 0xb7: printf("ORA A"); break;
|
|
|
|
case 0xb8: printf("CMP B"); break;
|
|
case 0xb9: printf("CMP C"); break;
|
|
case 0xba: printf("CMP D"); break;
|
|
case 0xbb: printf("CMP E"); break;
|
|
case 0xbc: printf("CMP H"); break;
|
|
case 0xbd: printf("CMP L"); break;
|
|
case 0xbe: printf("CMP M"); break;
|
|
case 0xbf: printf("CMP A"); break;
|
|
|
|
case 0xc0: printf("RNZ"); break;
|
|
case 0xc1: printf("POP B"); break;
|
|
case 0xc2: printf("JNZ $%02x%02x",code[2],code[1]); opbytes = 3; break;
|
|
case 0xc3: printf("JMP $%02x%02x",code[2],code[1]); opbytes = 3; break;
|
|
case 0xc4: printf("CNZ $%02x%02x",code[2],code[1]); opbytes = 3; break;
|
|
case 0xc5: printf("PUSH B"); break;
|
|
case 0xc6: printf("ADI,#$%02x", code[1]); opbytes = 2; break;
|
|
case 0xc7: printf("RST 0"); break;
|
|
|
|
case 0xc8: printf("RZ"); break;
|
|
case 0xc9: printf("RET"); break;
|
|
case 0xca: printf("JZ $%02x%02x",code[2],code[1]); opbytes = 3; break;
|
|
case 0xcb: printf("NOP"); break;
|
|
case 0xcc: printf("CZ $%02x%02x",code[2],code[1]); opbytes = 3; break;
|
|
case 0xcd: printf("CALL $%02x%02x",code[2],code[1]); opbytes = 3; break;
|
|
case 0xce: printf("ACI,#$%02x", code[1]); opbytes = 2; break;
|
|
case 0xcf: printf("RST 1"); break;
|
|
|
|
case 0xd0: printf("RNC"); break;
|
|
case 0xd1: printf("POP D"); break;
|
|
case 0xd2: printf("JNC $%02x%02x",code[2],code[1]); opbytes = 3; break;
|
|
case 0xd3: printf("OUT #$%02x", code[1]); opbytes = 2; break;
|
|
case 0xd4: printf("CNC $%02x%02x",code[2],code[1]); opbytes = 3; break;
|
|
case 0xd5: printf("PUSH D"); break;
|
|
case 0xd6: printf("SUI,#$%02x", code[1]); opbytes = 2; break;
|
|
case 0xd7: printf("RST 2"); break;
|
|
|
|
case 0xd8: printf("RC"); break;
|
|
case 0xd9: printf("NOP"); break;
|
|
case 0xda: printf("JC $%02x%02x",code[2],code[1]); opbytes = 3; break;
|
|
case 0xdb: printf("IN #$%02x", code[1]); opbytes = 2; break;
|
|
case 0xdc: printf("CC $%02x%02x",code[2],code[1]); opbytes = 3; break;
|
|
case 0xdd: printf("NOP"); break;
|
|
case 0xde: printf("SBI,#$%02x", code[1]); opbytes = 2; break;
|
|
case 0xdf: printf("RST 3"); break;
|
|
|
|
case 0xe0: printf("RPO"); break;
|
|
case 0xe1: printf("POP H"); break;
|
|
case 0xe2: printf("JPO $%02x%02x",code[2],code[1]); opbytes = 3; break;
|
|
case 0xe3: printf("XTHL"); break;
|
|
case 0xe4: printf("CPO $%02x%02x",code[2],code[1]); opbytes = 3; break;
|
|
case 0xe5: printf("PUSH H"); break;
|
|
case 0xe6: printf("ANI,#$%02x", code[1]); opbytes = 2; break;
|
|
case 0xe7: printf("RST 4"); break;
|
|
|
|
case 0xe8: printf("RPE"); break;
|
|
case 0xe9: printf("PCHL"); break;
|
|
case 0xea: printf("JPE $%02x%02x",code[2],code[1]); opbytes = 3; break;
|
|
case 0xeb: printf("XCHG"); break;
|
|
case 0xec: printf("CPE $%02x%02x",code[2],code[1]); opbytes = 3; break;
|
|
case 0xed: printf("NOP"); break;
|
|
case 0xee: printf("XRI,#$%02x", code[1]); opbytes = 2; break;
|
|
case 0xef: printf("RST 5"); break;
|
|
|
|
case 0xf0: printf("RP"); break;
|
|
case 0xf1: printf("POP PSW"); break;
|
|
case 0xf2: printf("JP $%02x%02x",code[2],code[1]); opbytes = 3; break;
|
|
case 0xf3: printf("DI"); break;
|
|
case 0xf4: printf("CP $%02x%02x",code[2],code[1]); opbytes = 3; break;
|
|
case 0xf5: printf("PUSH PSW"); break;
|
|
case 0xf6: printf("ORI,#$%02x", code[1]); opbytes = 2; break;
|
|
case 0xf7: printf("RST 6"); break;
|
|
|
|
case 0xf8: printf("RM"); break;
|
|
case 0xf9: printf("SPHL"); break;
|
|
case 0xfa: printf("JM $%02x%02x",code[2],code[1]); opbytes = 3; break;
|
|
case 0xfb: printf("EI"); break;
|
|
case 0xfc: printf("CM $%02x%02x",code[2],code[1]); opbytes = 3; break;
|
|
case 0xfd: printf("NOP"); break;
|
|
case 0xfe: printf("CPI,#$%02x", code[1]); opbytes = 2; break;
|
|
case 0xff: printf("RST 7"); break;
|
|
|
|
}
|
|
printf("\n");
|
|
return opbytes;
|
|
}
|
|
|
|
|
|
void UnimplementedInstruction(State8080* state) {
|
|
printf("Error: Unknown instruction");
|
|
return exit(1);
|
|
}
|
|
|
|
int parity(int x, int size)
|
|
{
|
|
int i;
|
|
int p = 0;
|
|
x = (x & ((1<<size)-1));
|
|
for (i=0; i<size; i++)
|
|
{
|
|
if (x & 0x1) p++;
|
|
x = x >> 1;
|
|
}
|
|
return (0 == (p & 0x1));
|
|
}
|
|
|
|
void printState(State8080* state) {
|
|
printf("\tC=%d,P=%d,S=%d,Z=%d,Enable=%d\n", state->cc.cy, state->cc.p,
|
|
state->cc.s, state->cc.z, state->int_enable);
|
|
printf("\tA $%02x B $%02x C $%02x D $%02x E $%02x H $%02x L $%02x SP %04x\n",
|
|
state->a, state->b, state->c, state->d,
|
|
state->e, state->h, state->l, state->sp);
|
|
}
|
|
|
|
uint8_t machineIn(State8080* state, uint8_t port) {
|
|
uint8_t a;
|
|
switch(port) {
|
|
case 0:
|
|
a = 0xf;
|
|
break;
|
|
case 1:
|
|
a = in_port;
|
|
break;
|
|
case 2:
|
|
a = 0;
|
|
break;
|
|
case 3:
|
|
{
|
|
uint16_t v = (state->shift1<<8) | (state->shift0);
|
|
a = ((v >> (8-state->shift_offset)) & 0xff);
|
|
}
|
|
break;
|
|
}
|
|
return a;
|
|
}
|
|
|
|
void machineOut(State8080* state, uint8_t port, uint8_t value) {
|
|
switch(port) {
|
|
case 2:
|
|
{
|
|
state->shift_offset = value & 0x7;
|
|
} break;
|
|
case 4:
|
|
{
|
|
state->shift0 = state->shift1;
|
|
state->shift1 = value;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// Due to problems in code, many commands were replaced by the ones from the guide
|
|
|
|
static void LogicFlagsA(State8080 *state)
|
|
{
|
|
state->cc.cy = state->cc.ac = 0;
|
|
state->cc.z = (state->a == 0);
|
|
state->cc.s = (0x80 == (state->a & 0x80));
|
|
state->cc.p = parity(state->a, 8);
|
|
}
|
|
|
|
static void ArithFlagsA(State8080 *state, uint16_t res)
|
|
{
|
|
state->cc.cy = (res > 0xff);
|
|
state->cc.z = ((res&0xff) == 0);
|
|
state->cc.s = (0x80 == (res & 0x80));
|
|
state->cc.p = parity(res&0xff, 8);
|
|
}
|
|
|
|
static void WriteMem(State8080* state, uint16_t address, uint8_t value)
|
|
{
|
|
if (address < 0x2000)
|
|
{
|
|
// printf("Writing ROM not allowed %x\n", address);
|
|
return;
|
|
}
|
|
if (address >=0x4000)
|
|
{
|
|
// printf("Writing out of Space Invaders RAM not allowed %x\n", address);
|
|
return;
|
|
}
|
|
|
|
state->memory[address] = value;
|
|
}
|
|
|
|
static uint8_t ReadFromHL(State8080* state)
|
|
{
|
|
uint16_t offset = (state->h << 8) | state->l;
|
|
return state->memory[offset];
|
|
}
|
|
|
|
static void WriteToHL(State8080* state, uint8_t value)
|
|
{
|
|
uint16_t offset = (state->h << 8) | state->l;
|
|
WriteMem(state, offset, value);
|
|
}
|
|
|
|
static void Push(State8080* state, uint8_t high, uint8_t low)
|
|
{
|
|
WriteMem(state, state->sp-1, high);
|
|
WriteMem(state, state->sp-2, low);
|
|
state->sp = state->sp - 2;
|
|
// printf ("%04x %04x\n", state->pc, state->sp);
|
|
}
|
|
|
|
static void Pop(State8080* state, uint8_t *high, uint8_t *low)
|
|
{
|
|
*low = state->memory[state->sp];
|
|
*high = state->memory[state->sp+1];
|
|
state->sp += 2;
|
|
// printf ("%04x %04x pop\n", state->pc, state->sp);
|
|
}
|
|
|
|
static void FlagsZSP(State8080 *state, uint8_t value)
|
|
{
|
|
state->cc.z = (value == 0);
|
|
state->cc.s = (0x80 == (value & 0x80));
|
|
state->cc.p = parity(value, 8);
|
|
}
|
|
|
|
|
|
|
|
int emulate8080(State8080* state) {
|
|
unsigned char *opcode = &(state->memory[state->pc]);
|
|
|
|
//printf("%04x ", state->pc);
|
|
|
|
//disassemble8080(opcode);
|
|
state->pc++;
|
|
|
|
switch(*opcode) {
|
|
case 0x00: break; //NOP
|
|
case 0x01: //LXI B,word
|
|
state->c = opcode[1];
|
|
state->b = opcode[2];
|
|
state->pc += 2;
|
|
break;
|
|
case 0x02: //STAX B
|
|
{
|
|
uint16_t offset=(state->b<<8) | state->c;
|
|
WriteMem(state, offset, state->a);
|
|
}
|
|
break;
|
|
case 0x03: //INX B
|
|
state->c++;
|
|
if (state->c == 0)
|
|
state->b++;
|
|
break;
|
|
case 0x04: //INR B
|
|
state->b += 1;
|
|
FlagsZSP(state,state->b);
|
|
break;
|
|
case 0x05: //DCR B
|
|
state->b -= 1;
|
|
FlagsZSP(state,state->b);
|
|
break;
|
|
case 0x06: //MVI B,byte
|
|
state->b = opcode[1];
|
|
state->pc++;
|
|
break;
|
|
case 0x07: //RLC
|
|
{
|
|
uint8_t x = state->a;
|
|
state->a = ((x & 0x80) >> 7) | (x << 1);
|
|
state->cc.cy = (0x80 == (x&0x80));
|
|
}
|
|
break;
|
|
case 0x08: UnimplementedInstruction(state); break;
|
|
case 0x09: //DAD B
|
|
{
|
|
uint32_t hl = (state->h << 8) | state->l;
|
|
uint32_t bc = (state->b << 8) | state->c;
|
|
uint32_t res = hl + bc;
|
|
state->h = (res & 0xff00) >> 8;
|
|
state->l = res & 0xff;
|
|
state->cc.cy = ((res & 0xffff0000) != 0);
|
|
}
|
|
break;
|
|
case 0x0a: //LDAX B
|
|
{
|
|
uint16_t offset=(state->b<<8) | state->c;
|
|
state->a = state->memory[offset];
|
|
}
|
|
break;
|
|
case 0x0b: //DCX B
|
|
state->c -= 1;
|
|
if (state->c==0xff)
|
|
state->b-=1;
|
|
break;
|
|
case 0x0c: //INR C
|
|
state->c += 1;
|
|
FlagsZSP(state,state->c);
|
|
break;
|
|
|
|
case 0x0d: //DCR C
|
|
state->c -= 1;
|
|
FlagsZSP(state,state->c);
|
|
break;
|
|
case 0x0e: //MVI C,byte
|
|
state->c = opcode[1];
|
|
state->pc++;
|
|
break;
|
|
case 0x0f: //RRC
|
|
{
|
|
uint8_t x = state->a;
|
|
state->a = ((x & 1) << 7) | (x >> 1);
|
|
state->cc.cy = (1 == (x&1));
|
|
}
|
|
break;
|
|
|
|
case 0x10: UnimplementedInstruction(state); break;
|
|
case 0x11: //LXI D,word
|
|
state->e = opcode[1];
|
|
state->d = opcode[2];
|
|
state->pc += 2;
|
|
break;
|
|
case 0x12: //STAX D
|
|
{
|
|
uint16_t offset=(state->d<<8) | state->e;
|
|
WriteMem(state, offset, state->a);
|
|
}
|
|
break;
|
|
case 0x13: //INX D
|
|
state->e++;
|
|
if (state->e == 0)
|
|
state->d++;
|
|
break;
|
|
case 0x14: //INR D
|
|
state->d += 1;
|
|
FlagsZSP(state,state->d);
|
|
break;
|
|
case 0x15: //DCR D
|
|
state->d -= 1;
|
|
FlagsZSP(state,state->d);
|
|
break;
|
|
case 0x16: //MVI D,byte
|
|
state->d = opcode[1];
|
|
state->pc++;
|
|
break;
|
|
case 0x17: //RAL
|
|
{
|
|
uint8_t x = state->a;
|
|
state->a = state->cc.cy | (x << 1);
|
|
state->cc.cy = (0x80 == (x&0x80));
|
|
}
|
|
break;
|
|
case 0x18: UnimplementedInstruction(state); break;
|
|
case 0x19: //DAD D
|
|
{
|
|
uint32_t hl = (state->h << 8) | state->l;
|
|
uint32_t de = (state->d << 8) | state->e;
|
|
uint32_t res = hl + de;
|
|
state->h = (res & 0xff00) >> 8;
|
|
state->l = res & 0xff;
|
|
state->cc.cy = ((res & 0xffff0000) != 0);
|
|
}
|
|
break;
|
|
case 0x1a: //LDAX D
|
|
{
|
|
uint16_t offset=(state->d<<8) | state->e;
|
|
state->a = state->memory[offset];
|
|
}
|
|
break;
|
|
case 0x1b: //DCX D
|
|
state->e -= 1;
|
|
if (state->e==0xff)
|
|
state->d-=1;
|
|
break;
|
|
case 0x1c: //INR E
|
|
state->e += 1;
|
|
FlagsZSP(state,state->e);
|
|
break;
|
|
|
|
case 0x1d: //DCR E
|
|
state->e -= 1;
|
|
FlagsZSP(state,state->e);
|
|
break;
|
|
case 0x1e: //MVI E,byte
|
|
state->e = opcode[1];
|
|
state->pc++;
|
|
break;
|
|
case 0x1f: //RAR
|
|
{
|
|
uint8_t x = state->a;
|
|
state->a = (state->cc.cy << 7) | (x >> 1);
|
|
state->cc.cy = (1 == (x&1));
|
|
}
|
|
break;
|
|
case 0x20: UnimplementedInstruction(state); break;
|
|
case 0x21: //LXI H,word
|
|
state->l = opcode[1];
|
|
state->h = opcode[2];
|
|
state->pc += 2;
|
|
break;
|
|
case 0x22: //SHLD
|
|
{
|
|
uint16_t offset = opcode[1] | (opcode[2] << 8);
|
|
WriteMem(state, offset, state->l);
|
|
WriteMem(state, offset+1, state->h);
|
|
state->pc += 2;
|
|
}
|
|
break;
|
|
case 0x23: //INX H
|
|
state->l++;
|
|
if (state->l == 0)
|
|
state->h++;
|
|
break;
|
|
case 0x24: //INR H
|
|
state->h += 1;
|
|
FlagsZSP(state,state->h);
|
|
break;
|
|
case 0x25: //DCR H
|
|
state->h -= 1;
|
|
FlagsZSP(state,state->h);
|
|
break;
|
|
case 0x26: //MVI H,byte
|
|
state->h = opcode[1];
|
|
state->pc++;
|
|
break;
|
|
case 0x27:
|
|
if ((state->a &0xf) > 9)
|
|
state->a += 6;
|
|
if ((state->a&0xf0) > 0x90)
|
|
{
|
|
uint16_t res = (uint16_t) state->a + 0x60;
|
|
state->a = res & 0xff;
|
|
ArithFlagsA(state, res);
|
|
}
|
|
break;
|
|
case 0x28: UnimplementedInstruction(state); break;
|
|
case 0x29: //DAD H
|
|
{
|
|
uint32_t hl = (state->h << 8) | state->l;
|
|
uint32_t res = hl + hl;
|
|
state->h = (res & 0xff00) >> 8;
|
|
state->l = res & 0xff;
|
|
state->cc.cy = ((res & 0xffff0000) != 0);
|
|
}
|
|
break;
|
|
case 0x2a: //LHLD adr
|
|
{
|
|
uint16_t offset = opcode[1] | (opcode[2] << 8);
|
|
state->l = state->memory[offset];
|
|
state->h = state->memory[offset+1];
|
|
state->pc += 2;
|
|
}
|
|
break;
|
|
case 0x2b: //DCX H
|
|
state->l -= 1;
|
|
if (state->l==0xff)
|
|
state->h-=1;
|
|
break;
|
|
case 0x2c: //INR L
|
|
state->l += 1;
|
|
FlagsZSP(state,state->l);
|
|
break;
|
|
case 0x2d: //DCR L
|
|
state->l -= 1;
|
|
FlagsZSP(state,state->l);
|
|
break;
|
|
case 0x2e: //MVI L,byte
|
|
state->l = opcode[1];
|
|
state->pc++;
|
|
break;
|
|
case 0x2f:
|
|
state->a = ~state->a; //CMA
|
|
break;
|
|
case 0x30: UnimplementedInstruction(state); break;
|
|
case 0x31: //LXI SP,word
|
|
state->sp = (opcode[2]<<8) | opcode[1];
|
|
state->pc += 2;
|
|
break;
|
|
case 0x32: //STA (word)
|
|
{
|
|
uint16_t offset = (opcode[2]<<8) | (opcode[1]);
|
|
WriteMem(state, offset, state->a);
|
|
state->pc += 2;
|
|
}
|
|
break;
|
|
case 0x33: //INX SP
|
|
state->sp++;
|
|
break;
|
|
case 0x34: //INR M
|
|
{
|
|
uint8_t res = ReadFromHL(state) + 1;
|
|
FlagsZSP(state, res);
|
|
WriteToHL(state, res);
|
|
}
|
|
break;
|
|
case 0x35: //DCR M
|
|
{
|
|
uint8_t res = ReadFromHL(state) - 1;
|
|
FlagsZSP(state, res);
|
|
WriteToHL(state, res);
|
|
}
|
|
break;
|
|
case 0x36: //MVI M,byte
|
|
{
|
|
WriteToHL(state, opcode[1]);
|
|
state->pc++;
|
|
}
|
|
break;
|
|
case 0x37: state->cc.cy = 1; break;
|
|
case 0x38: UnimplementedInstruction(state); break;
|
|
case 0x39: //DAD SP
|
|
{
|
|
uint32_t hl = (state->h << 8) | state->l;
|
|
uint32_t res = hl + state->sp;
|
|
state->h = (res & 0xff00) >> 8;
|
|
state->l = res & 0xff;
|
|
state->cc.cy = ((res & 0xffff0000) > 0);
|
|
}
|
|
break;
|
|
case 0x3a: //LDA (word)
|
|
{
|
|
uint16_t offset = (opcode[2]<<8) | (opcode[1]);
|
|
state->a = state->memory[offset];
|
|
state->pc+=2;
|
|
}
|
|
break;
|
|
case 0x3b: //DCX SP
|
|
state->sp -= 1;
|
|
break;
|
|
case 0x3c: //INR A
|
|
state->a += 1;
|
|
FlagsZSP(state,state->a);
|
|
break;
|
|
case 0x3d: //DCR A
|
|
state->a -= 1;
|
|
FlagsZSP(state,state->a);
|
|
break;
|
|
case 0x3e: //MVI A,byte
|
|
state->a = opcode[1];
|
|
state->pc++;
|
|
break;
|
|
case 0x3f: state->cc.cy = 0; break;
|
|
|
|
case 0x40: state->b = state->b; break;
|
|
case 0x41: state->b = state->c; break;
|
|
case 0x42: state->b = state->d; break;
|
|
case 0x43: state->b = state->e; break;
|
|
case 0x44: state->b = state->h; break;
|
|
case 0x45: state->b = state->l; break;
|
|
case 0x46: state->b = ReadFromHL(state); break;
|
|
case 0x47: state->b = state->a; break;
|
|
|
|
case 0x48: state->c = state->b; break;
|
|
case 0x49: state->c = state->c; break;
|
|
case 0x4a: state->c = state->d; break;
|
|
case 0x4b: state->c = state->e; break;
|
|
case 0x4c: state->c = state->h; break;
|
|
case 0x4d: state->c = state->l; break;
|
|
case 0x4e: state->c = ReadFromHL(state); break;
|
|
case 0x4f: state->c = state->a; break;
|
|
|
|
case 0x50: state->d = state->b; break;
|
|
case 0x51: state->d = state->c; break;
|
|
case 0x52: state->d = state->d; break;
|
|
case 0x53: state->d = state->e; break;
|
|
case 0x54: state->d = state->h; break;
|
|
case 0x55: state->d = state->l; break;
|
|
case 0x56: state->d = ReadFromHL(state); break;
|
|
case 0x57: state->d = state->a; break;
|
|
|
|
case 0x58: state->e = state->b; break;
|
|
case 0x59: state->e = state->c; break;
|
|
case 0x5a: state->e = state->d; break;
|
|
case 0x5b: state->e = state->e; break;
|
|
case 0x5c: state->e = state->h; break;
|
|
case 0x5d: state->e = state->l; break;
|
|
case 0x5e: state->e = ReadFromHL(state); break;
|
|
case 0x5f: state->e = state->a; break;
|
|
|
|
case 0x60: state->h = state->b; break;
|
|
case 0x61: state->h = state->c; break;
|
|
case 0x62: state->h = state->d; break;
|
|
case 0x63: state->h = state->e; break;
|
|
case 0x64: state->h = state->h; break;
|
|
case 0x65: state->h = state->l; break;
|
|
case 0x66: state->h = ReadFromHL(state); break;
|
|
case 0x67: state->h = state->a; break;
|
|
|
|
case 0x68: state->l = state->b; break;
|
|
case 0x69: state->l = state->c; break;
|
|
case 0x6a: state->l = state->d; break;
|
|
case 0x6b: state->l = state->e; break;
|
|
case 0x6c: state->l = state->h; break;
|
|
case 0x6d: state->l = state->l; break;
|
|
case 0x6e: state->l = ReadFromHL(state); break;
|
|
case 0x6f: state->l = state->a; break;
|
|
|
|
case 0x70: WriteToHL(state, state->b); break; //MOV M,B
|
|
case 0x71: WriteToHL(state, state->c); break; //MOV M,C
|
|
case 0x72: WriteToHL(state, state->d); break; //MOV M,D
|
|
case 0x73: WriteToHL(state, state->e); break; //MOV M,E
|
|
case 0x74: WriteToHL(state, state->h); break; //MOV M,H
|
|
case 0x75: WriteToHL(state, state->l); break; //MOV M,L
|
|
case 0x76: break; //HLT
|
|
case 0x77: WriteToHL(state, state->a); break; //MOV M,A
|
|
|
|
case 0x78: state->a = state->b; break;
|
|
case 0x79: state->a = state->c; break;
|
|
case 0x7a: state->a = state->d; break;
|
|
case 0x7b: state->a = state->e; break;
|
|
case 0x7c: state->a = state->h; break;
|
|
case 0x7d: state->a = state->l; break;
|
|
case 0x7e: state->a = ReadFromHL(state); break;
|
|
case 0x7f: break;
|
|
|
|
case 0x80: { uint16_t res = (uint16_t) state->a + (uint16_t) state->b; ArithFlagsA(state, res); state->a=(res&0xff); } break; //ADD B
|
|
case 0x81: { uint16_t res = (uint16_t) state->a + (uint16_t) state->c; ArithFlagsA(state, res); state->a=(res&0xff); } break; //ADD C
|
|
case 0x82: { uint16_t res = (uint16_t) state->a + (uint16_t) state->d; ArithFlagsA(state, res); state->a=(res&0xff); } break; //ADD D
|
|
case 0x83: { uint16_t res = (uint16_t) state->a + (uint16_t) state->e; ArithFlagsA(state, res); state->a=(res&0xff); } break; //ADD E
|
|
case 0x84: { uint16_t res = (uint16_t) state->a + (uint16_t) state->h; ArithFlagsA(state, res); state->a=(res&0xff); } break; //ADD H
|
|
case 0x85: { uint16_t res = (uint16_t) state->a + (uint16_t) state->l; ArithFlagsA(state, res); state->a=(res&0xff); } break; //ADD L
|
|
case 0x86: //ADD M
|
|
{
|
|
uint16_t res = (uint16_t) state->a + (uint16_t) ReadFromHL(state);
|
|
ArithFlagsA(state, res);
|
|
state->a=(res&0xff);
|
|
} break;
|
|
case 0x87: { uint16_t res = (uint16_t) state->a + (uint16_t) state->a; ArithFlagsA(state, res); state->a=(res&0xff); } break; //ADC A
|
|
|
|
case 0x88: { uint16_t res = (uint16_t) state->a + (uint16_t) state->b + state->cc.cy; ArithFlagsA(state, res); state->a=(res&0xff); } break; //ADC B
|
|
case 0x89: { uint16_t res = (uint16_t) state->a + (uint16_t) state->c + state->cc.cy; ArithFlagsA(state, res); state->a=(res&0xff); } break; //ADC C
|
|
case 0x8a: { uint16_t res = (uint16_t) state->a + (uint16_t) state->d + state->cc.cy; ArithFlagsA(state, res); state->a=(res&0xff); } break; //ADC D
|
|
case 0x8b: { uint16_t res = (uint16_t) state->a + (uint16_t) state->e + state->cc.cy; ArithFlagsA(state, res); state->a=(res&0xff); } break; //ADC E
|
|
case 0x8c: { uint16_t res = (uint16_t) state->a + (uint16_t) state->h + state->cc.cy; ArithFlagsA(state, res); state->a=(res&0xff); } break; //ADC H
|
|
case 0x8d: { uint16_t res = (uint16_t) state->a + (uint16_t) state->l + state->cc.cy; ArithFlagsA(state, res); state->a=(res&0xff); } break; //ADC L
|
|
case 0x8e: //ADC M
|
|
{
|
|
uint16_t res = (uint16_t) state->a + (uint16_t) ReadFromHL(state) + state->cc.cy;
|
|
ArithFlagsA(state, res);
|
|
state->a=(res&0xff);
|
|
} break;
|
|
case 0x8f: { uint16_t res = (uint16_t) state->a + (uint16_t) state->a + state->cc.cy; ArithFlagsA(state, res); state->a=(res&0xff); } break; //ADC A
|
|
|
|
|
|
case 0x90: { uint16_t res = (uint16_t) state->a - (uint16_t) state->b; ArithFlagsA(state, res); state->a=(res&0xff); } break; //SUB B
|
|
case 0x91: { uint16_t res = (uint16_t) state->a - (uint16_t) state->c; ArithFlagsA(state, res); state->a=(res&0xff); } break; //SUB C
|
|
case 0x92: { uint16_t res = (uint16_t) state->a - (uint16_t) state->d; ArithFlagsA(state, res); state->a=(res&0xff); } break; //SUB D
|
|
case 0x93: { uint16_t res = (uint16_t) state->a - (uint16_t) state->e; ArithFlagsA(state, res); state->a=(res&0xff); } break; //SUB E
|
|
case 0x94: { uint16_t res = (uint16_t) state->a - (uint16_t) state->h; ArithFlagsA(state, res); state->a=(res&0xff); } break; //SUB H
|
|
case 0x95: { uint16_t res = (uint16_t) state->a - (uint16_t) state->l; ArithFlagsA(state, res); state->a=(res&0xff); } break; //SUB L
|
|
case 0x96: //SUB M
|
|
{
|
|
uint16_t res = (uint16_t) state->a - (uint16_t) ReadFromHL(state);
|
|
ArithFlagsA(state, res);
|
|
state->a=(res&0xff);
|
|
} break;
|
|
case 0x97: { uint16_t res = (uint16_t) state->a - (uint16_t) state->a; ArithFlagsA(state, res); state->a=(res&0xff); } break; //SUB A
|
|
|
|
case 0x98: { uint16_t res = (uint16_t) state->a - (uint16_t) state->b - state->cc.cy; ArithFlagsA(state, res); state->a=(res&0xff); } break; //SBB B
|
|
case 0x99: { uint16_t res = (uint16_t) state->a - (uint16_t) state->c - state->cc.cy; ArithFlagsA(state, res); state->a=(res&0xff); } break; //SBB C
|
|
case 0x9a: { uint16_t res = (uint16_t) state->a - (uint16_t) state->d - state->cc.cy; ArithFlagsA(state, res); state->a=(res&0xff); } break; //SBB D
|
|
case 0x9b: { uint16_t res = (uint16_t) state->a - (uint16_t) state->e - state->cc.cy; ArithFlagsA(state, res); state->a=(res&0xff); } break; //SBB E
|
|
case 0x9c: { uint16_t res = (uint16_t) state->a - (uint16_t) state->h - state->cc.cy; ArithFlagsA(state, res); state->a=(res&0xff); } break; //SBB H
|
|
case 0x9d: { uint16_t res = (uint16_t) state->a - (uint16_t) state->l - state->cc.cy; ArithFlagsA(state, res); state->a=(res&0xff); } break; //SBB L
|
|
case 0x9e: //SBB M
|
|
{
|
|
uint16_t res = (uint16_t) state->a - (uint16_t) ReadFromHL(state) - state->cc.cy;
|
|
ArithFlagsA(state, res);
|
|
state->a=(res&0xff);
|
|
} break;
|
|
case 0x9f: { uint16_t res = (uint16_t) state->a - (uint16_t) state->a - state->cc.cy; ArithFlagsA(state, res); state->a=(res&0xff); } break; //SBB A
|
|
|
|
case 0xa0: state->a = state->a & state->b; LogicFlagsA(state); break;
|
|
case 0xa1: state->a = state->a & state->c; LogicFlagsA(state); break;
|
|
case 0xa2: state->a = state->a & state->d; LogicFlagsA(state); break;
|
|
case 0xa3: state->a = state->a & state->e; LogicFlagsA(state); break;
|
|
case 0xa4: state->a = state->a & state->h; LogicFlagsA(state); break;
|
|
case 0xa5: state->a = state->a & state->l; LogicFlagsA(state); break;
|
|
case 0xa6: state->a = state->a & ReadFromHL(state); LogicFlagsA(state); break;
|
|
case 0xa7: state->a = state->a & state->a; LogicFlagsA(state); break;
|
|
|
|
case 0xa8: state->a = state->a ^ state->b; LogicFlagsA(state); break;
|
|
case 0xa9: state->a = state->a ^ state->c; LogicFlagsA(state); break;
|
|
case 0xaa: state->a = state->a ^ state->d; LogicFlagsA(state); break;
|
|
case 0xab: state->a = state->a ^ state->e; LogicFlagsA(state); break;
|
|
case 0xac: state->a = state->a ^ state->h; LogicFlagsA(state); break;
|
|
case 0xad: state->a = state->a ^ state->l; LogicFlagsA(state); break;
|
|
case 0xae: state->a = state->a ^ ReadFromHL(state); LogicFlagsA(state); break;
|
|
case 0xaf: state->a = state->a ^ state->a; LogicFlagsA(state); break;
|
|
|
|
case 0xb0: state->a = state->a | state->b; LogicFlagsA(state); break;
|
|
case 0xb1: state->a = state->a | state->c; LogicFlagsA(state); break;
|
|
case 0xb2: state->a = state->a | state->d; LogicFlagsA(state); break;
|
|
case 0xb3: state->a = state->a | state->e; LogicFlagsA(state); break;
|
|
case 0xb4: state->a = state->a | state->h; LogicFlagsA(state); break;
|
|
case 0xb5: state->a = state->a | state->l; LogicFlagsA(state); break;
|
|
case 0xb6: state->a = state->a | ReadFromHL(state); LogicFlagsA(state); break;
|
|
case 0xb7: state->a = state->a | state->a; LogicFlagsA(state); break;
|
|
|
|
case 0xb8: { uint16_t res = (uint16_t) state->a - (uint16_t) state->b; ArithFlagsA(state, res); } break; //CMP B
|
|
case 0xb9: { uint16_t res = (uint16_t) state->a - (uint16_t) state->c; ArithFlagsA(state, res); } break; //CMP C
|
|
case 0xba: { uint16_t res = (uint16_t) state->a - (uint16_t) state->d; ArithFlagsA(state, res); } break; //CMP D
|
|
case 0xbb: { uint16_t res = (uint16_t) state->a - (uint16_t) state->e; ArithFlagsA(state, res); } break; //CMP E
|
|
case 0xbc: { uint16_t res = (uint16_t) state->a - (uint16_t) state->h; ArithFlagsA(state, res); } break; //CMP H
|
|
case 0xbd: { uint16_t res = (uint16_t) state->a - (uint16_t) state->l; ArithFlagsA(state, res); } break; //CMP L
|
|
case 0xbe: { uint16_t res = (uint16_t) state->a - (uint16_t) ReadFromHL(state); ArithFlagsA(state, res); } break; //CMP L
|
|
case 0xbf: { uint16_t res = (uint16_t) state->a - (uint16_t) state->a; ArithFlagsA(state, res); } break; //CMP A
|
|
|
|
case 0xc0: //RNZ
|
|
if (state->cc.z == 0)
|
|
{
|
|
state->pc = state->memory[state->sp] | (state->memory[state->sp+1]<<8);
|
|
state->sp += 2;
|
|
}
|
|
break;
|
|
|
|
case 0xc1: //POP B
|
|
Pop(state, &state->b, &state->c);
|
|
break;
|
|
case 0xc2: //JNZ address
|
|
if (0 == state->cc.z)
|
|
state->pc = (opcode[2] << 8) | opcode[1];
|
|
else
|
|
state->pc += 2;
|
|
break;
|
|
case 0xc3: //JMP address
|
|
state->pc = (opcode[2] << 8) | opcode[1];
|
|
break;
|
|
case 0xc4: //CNZ adr
|
|
if (state->cc.z == 0)
|
|
{
|
|
uint16_t ret = state->pc+2;
|
|
WriteMem(state, state->sp-1, (ret >> 8) & 0xff);
|
|
WriteMem(state, state->sp-2, (ret & 0xff));
|
|
state->sp = state->sp - 2;
|
|
state->pc = (opcode[2] << 8) | opcode[1];
|
|
}
|
|
else
|
|
state->pc += 2;
|
|
break;
|
|
|
|
case 0xc5: //PUSH B
|
|
Push(state, state->b, state->c);
|
|
break;
|
|
case 0xc6: //ADI byte
|
|
{
|
|
uint16_t x = (uint16_t) state->a + (uint16_t) opcode[1];
|
|
FlagsZSP(state, x&0xff);
|
|
state->cc.cy = (x > 0xff);
|
|
state->a = x&0xff;
|
|
state->pc++;
|
|
}
|
|
break;
|
|
case 0xc7: //RST 0
|
|
{
|
|
uint16_t ret = state->pc+2;
|
|
WriteMem(state, state->sp-1, (ret >> 8) & 0xff);
|
|
WriteMem(state, state->sp-2, (ret & 0xff));
|
|
state->sp = state->sp - 2;
|
|
state->pc = 0x0000;
|
|
}
|
|
break;
|
|
case 0xc8: //RZ
|
|
if (state->cc.z)
|
|
{
|
|
state->pc = state->memory[state->sp] | (state->memory[state->sp+1] << 8);
|
|
state->sp += 2;
|
|
}
|
|
break;
|
|
case 0xc9: //RET
|
|
state->pc = state->memory[state->sp] | (state->memory[state->sp+1] << 8);
|
|
state->sp += 2;
|
|
break;
|
|
case 0xca: //JZ adr
|
|
if (state->cc.z)
|
|
state->pc = (opcode[2] << 8) | opcode[1];
|
|
else
|
|
state->pc += 2;
|
|
break;
|
|
case 0xcb: UnimplementedInstruction(state); break;
|
|
case 0xcc: //CZ adr
|
|
if (state->cc.z == 1)
|
|
{
|
|
uint16_t ret = state->pc+2;
|
|
WriteMem(state, state->sp-1, (ret >> 8) & 0xff);
|
|
WriteMem(state, state->sp-2, (ret & 0xff));
|
|
state->sp = state->sp - 2;
|
|
state->pc = (opcode[2] << 8) | opcode[1];
|
|
}
|
|
else
|
|
state->pc += 2;
|
|
break;
|
|
case 0xcd: //CALL address
|
|
{
|
|
uint16_t ret = state->pc+2;
|
|
WriteMem(state, state->sp-1, (ret >> 8) & 0xff);
|
|
WriteMem(state, state->sp-2, (ret & 0xff));
|
|
state->sp = state->sp - 2;
|
|
state->pc = (opcode[2] << 8) | opcode[1];
|
|
}
|
|
break;
|
|
case 0xce: //ACI byte
|
|
{
|
|
uint16_t x = state->a + opcode[1] + state->cc.cy;
|
|
FlagsZSP(state, x&0xff);
|
|
state->cc.cy = (x > 0xff);
|
|
state->a = x & 0xff;
|
|
state->pc++;
|
|
}
|
|
break;
|
|
case 0xcf: //RST 1
|
|
{
|
|
uint16_t ret = state->pc+2;
|
|
WriteMem(state, state->sp-1, (ret >> 8) & 0xff);
|
|
WriteMem(state, state->sp-2, (ret & 0xff));
|
|
state->sp = state->sp - 2;
|
|
state->pc = 0x0008;
|
|
}
|
|
break;
|
|
|
|
case 0xd0: //RNC
|
|
if (state->cc.cy == 0)
|
|
{
|
|
state->pc = state->memory[state->sp] | (state->memory[state->sp+1]<<8);
|
|
state->sp += 2;
|
|
}
|
|
break;
|
|
case 0xd1: //POP D
|
|
Pop(state, &state->d, &state->e);
|
|
break;
|
|
case 0xd2: //JNC
|
|
if (state->cc.cy == 0)
|
|
state->pc = (opcode[2] << 8) | opcode[1];
|
|
else
|
|
state->pc += 2;
|
|
break;
|
|
case 0xd3: //OUT d8
|
|
machineOut(state, opcode[1], state->a);
|
|
state->pc++;
|
|
break;
|
|
case 0xd4: //CNC adr
|
|
if (state->cc.cy == 0)
|
|
{
|
|
uint16_t ret = state->pc+2;
|
|
WriteMem(state, state->sp-1, (ret >> 8) & 0xff);
|
|
WriteMem(state, state->sp-2, (ret & 0xff));
|
|
state->sp = state->sp - 2;
|
|
state->pc = (opcode[2] << 8) | opcode[1];
|
|
}
|
|
else
|
|
state->pc += 2;
|
|
break;
|
|
case 0xd5: //PUSH D
|
|
Push(state, state->d, state->e);
|
|
break;
|
|
case 0xd6: //SUI byte
|
|
{
|
|
uint8_t x = state->a - opcode[1];
|
|
FlagsZSP(state, x&0xff);
|
|
state->cc.cy = (state->a < opcode[1]);
|
|
state->a = x;
|
|
state->pc++;
|
|
}
|
|
break;
|
|
case 0xd7: //RST 2
|
|
{
|
|
uint16_t ret = state->pc+2;
|
|
WriteMem(state, state->sp-1, (ret >> 8) & 0xff);
|
|
WriteMem(state, state->sp-2, (ret & 0xff));
|
|
state->sp = state->sp - 2;
|
|
state->pc = 0x10;
|
|
}
|
|
break;
|
|
case 0xd8: //RC
|
|
if (state->cc.cy != 0)
|
|
{
|
|
state->pc = state->memory[state->sp] | (state->memory[state->sp+1]<<8);
|
|
state->sp += 2;
|
|
}
|
|
break;
|
|
case 0xd9: UnimplementedInstruction(state); break;
|
|
case 0xda: //JC
|
|
if (state->cc.cy != 0)
|
|
state->pc = (opcode[2] << 8) | opcode[1];
|
|
else
|
|
state->pc += 2;
|
|
break;
|
|
case 0xdb: //IN d8
|
|
state->a = machineIn(state, opcode[1]); //Check if this works
|
|
state->pc++;
|
|
break;
|
|
case 0xdc: //CC adr
|
|
if (state->cc.cy != 0)
|
|
{
|
|
uint16_t ret = state->pc+2;
|
|
WriteMem(state, state->sp-1, (ret >> 8) & 0xff);
|
|
WriteMem(state, state->sp-2, (ret & 0xff));
|
|
state->sp = state->sp - 2;
|
|
state->pc = (opcode[2] << 8) | opcode[1];
|
|
}
|
|
else
|
|
state->pc += 2;
|
|
break;
|
|
case 0xdd: UnimplementedInstruction(state); break;
|
|
case 0xde: //SBI byte
|
|
{
|
|
uint16_t x = state->a - opcode[1] - state->cc.cy;
|
|
FlagsZSP(state, x&0xff);
|
|
state->cc.cy = (x > 0xff);
|
|
state->a = x & 0xff;
|
|
state->pc++;
|
|
}
|
|
break;
|
|
case 0xdf: //RST 3
|
|
{
|
|
uint16_t ret = state->pc+2;
|
|
WriteMem(state, state->sp-1, (ret >> 8) & 0xff);
|
|
WriteMem(state, state->sp-2, (ret & 0xff));
|
|
state->sp = state->sp - 2;
|
|
state->pc = 0x18;
|
|
}
|
|
break;
|
|
|
|
case 0xe0: //RPO
|
|
if (state->cc.p == 0)
|
|
{
|
|
state->pc = state->memory[state->sp] | (state->memory[state->sp+1]<<8);
|
|
state->sp += 2;
|
|
}
|
|
break;
|
|
case 0xe1: //POP H
|
|
Pop(state, &state->h, &state->l);
|
|
break;
|
|
case 0xe2: //JPO
|
|
if (state->cc.p == 0)
|
|
state->pc = (opcode[2] << 8) | opcode[1];
|
|
else
|
|
state->pc += 2;
|
|
break;
|
|
case 0xe3: //XTHL
|
|
{
|
|
uint8_t h = state->h;
|
|
uint8_t l = state->l;
|
|
state->l = state->memory[state->sp];
|
|
state->h = state->memory[state->sp+1];
|
|
WriteMem(state, state->sp, l );
|
|
WriteMem(state, state->sp+1, h );
|
|
}
|
|
break;
|
|
case 0xe4: //CPO adr
|
|
if (state->cc.p == 0)
|
|
{
|
|
uint16_t ret = state->pc+2;
|
|
WriteMem(state, state->sp-1, (ret >> 8) & 0xff);
|
|
WriteMem(state, state->sp-2, (ret & 0xff));
|
|
state->sp = state->sp - 2;
|
|
state->pc = (opcode[2] << 8) | opcode[1];
|
|
}
|
|
else
|
|
state->pc += 2;
|
|
break;
|
|
case 0xe5: //PUSH H
|
|
Push(state, state->h, state->l);
|
|
break;
|
|
case 0xe6: //ANI byte
|
|
{
|
|
state->a = state->a & opcode[1];
|
|
LogicFlagsA(state);
|
|
state->pc++;
|
|
}
|
|
break;
|
|
case 0xe7: //RST 4
|
|
{
|
|
uint16_t ret = state->pc+2;
|
|
WriteMem(state, state->sp-1, (ret >> 8) & 0xff);
|
|
WriteMem(state, state->sp-2, (ret & 0xff));
|
|
state->sp = state->sp - 2;
|
|
state->pc = 0x20;
|
|
}
|
|
break;
|
|
case 0xe8: //RPE
|
|
if (state->cc.p != 0)
|
|
{
|
|
state->pc = state->memory[state->sp] | (state->memory[state->sp+1]<<8);
|
|
state->sp += 2;
|
|
}
|
|
break;
|
|
case 0xe9: //PCHL
|
|
state->pc = (state->h << 8) | state->l;
|
|
break;
|
|
case 0xea: //JPE
|
|
if (state->cc.p != 0)
|
|
state->pc = (opcode[2] << 8) | opcode[1];
|
|
else
|
|
state->pc += 2;
|
|
break;
|
|
case 0xeb: //XCHG
|
|
{
|
|
uint8_t save1 = state->d;
|
|
uint8_t save2 = state->e;
|
|
state->d = state->h;
|
|
state->e = state->l;
|
|
state->h = save1;
|
|
state->l = save2;
|
|
}
|
|
break;
|
|
case 0xec: //CPE adr
|
|
if (state->cc.p != 0)
|
|
{
|
|
uint16_t ret = state->pc+2;
|
|
WriteMem(state, state->sp-1, (ret >> 8) & 0xff);
|
|
WriteMem(state, state->sp-2, (ret & 0xff));
|
|
state->sp = state->sp - 2;
|
|
state->pc = (opcode[2] << 8) | opcode[1];
|
|
}
|
|
else
|
|
state->pc += 2;
|
|
break;
|
|
case 0xed: UnimplementedInstruction(state); break;
|
|
case 0xee: //XRI data
|
|
{
|
|
uint8_t x = state->a ^ opcode[1];
|
|
FlagsZSP(state, x);
|
|
state->cc.cy = 0; //data book says clear cy
|
|
state->a = x;
|
|
state->pc++;
|
|
}
|
|
break;
|
|
case 0xef: //RST 5
|
|
{
|
|
uint16_t ret = state->pc+2;
|
|
WriteMem(state, state->sp-1, (ret >> 8) & 0xff);
|
|
WriteMem(state, state->sp-2, (ret & 0xff));
|
|
state->sp = state->sp - 2;
|
|
state->pc = 0x28;
|
|
}
|
|
break;
|
|
|
|
case 0xf0: //RP
|
|
if (state->cc.s == 0)
|
|
{
|
|
state->pc = state->memory[state->sp] | (state->memory[state->sp+1]<<8);
|
|
state->sp += 2;
|
|
}
|
|
break;
|
|
case 0xf1: //POP PSW
|
|
Pop(state, &state->a,(unsigned char*) &state->cc);
|
|
break;
|
|
case 0xf2:
|
|
if (state->cc.s == 0)
|
|
state->pc = (opcode[2] << 8) | opcode[1];
|
|
else
|
|
state->pc += 2;
|
|
break;
|
|
case 0xf3: state->int_enable = 0; break;
|
|
case 0xf4: //CP
|
|
if (state->cc.s == 0)
|
|
{
|
|
uint16_t ret = state->pc+2;
|
|
WriteMem(state, state->sp-1, (ret >> 8) & 0xff);
|
|
WriteMem(state, state->sp-2, (ret & 0xff));
|
|
state->sp = state->sp - 2;
|
|
state->pc = (opcode[2] << 8) | opcode[1];
|
|
}
|
|
else
|
|
state->pc += 2;
|
|
break;
|
|
|
|
case 0xf5: //PUSH PSW
|
|
Push(state, state->a, *(unsigned char*)&state->cc);
|
|
break;
|
|
|
|
case 0xf6: //ORI byte
|
|
{
|
|
//AC set if lower nibble of h was zero prior to dec
|
|
uint8_t x = state->a | opcode[1];
|
|
FlagsZSP(state, x);
|
|
state->cc.cy = 0;
|
|
state->a = x;
|
|
state->pc++;
|
|
}
|
|
break;
|
|
case 0xf7: //RST 6
|
|
{
|
|
uint16_t ret = state->pc+2;
|
|
WriteMem(state, state->sp-1, (ret >> 8) & 0xff);
|
|
WriteMem(state, state->sp-2, (ret & 0xff));
|
|
state->sp = state->sp - 2;
|
|
state->pc = 0x30;
|
|
}
|
|
break;
|
|
case 0xf8: //RM
|
|
if (state->cc.s != 0)
|
|
{
|
|
state->pc = state->memory[state->sp] | (state->memory[state->sp+1]<<8);
|
|
state->sp += 2;
|
|
}
|
|
break;
|
|
case 0xf9: //SPHL
|
|
state->sp = state->l | (state->h << 8);
|
|
break;
|
|
case 0xfa: //JM
|
|
if (state->cc.s != 0)
|
|
state->pc = (opcode[2] << 8) | opcode[1];
|
|
else
|
|
state->pc += 2;
|
|
break;
|
|
case 0xfb: state->int_enable = 1; break;
|
|
case 0xfc: //CM
|
|
if (state->cc.s != 0)
|
|
{
|
|
uint16_t ret = state->pc+2;
|
|
WriteMem(state, state->sp-1, (ret >> 8) & 0xff);
|
|
WriteMem(state, state->sp-2, (ret & 0xff));
|
|
state->sp = state->sp - 2;
|
|
state->pc = (opcode[2] << 8) | opcode[1];
|
|
}
|
|
else
|
|
state->pc += 2;
|
|
break;
|
|
|
|
case 0xfd: UnimplementedInstruction(state); break;
|
|
case 0xfe: //CPI byte
|
|
{
|
|
uint8_t x = state->a - opcode[1];
|
|
FlagsZSP(state, x);
|
|
state->cc.cy = (state->a < opcode[1]);
|
|
state->pc++;
|
|
}
|
|
break;
|
|
case 0xff: //RST 7
|
|
{
|
|
uint16_t ret = state->pc+2;
|
|
WriteMem(state, state->sp-1, (ret >> 8) & 0xff);
|
|
WriteMem(state, state->sp-2, (ret & 0xff));
|
|
state->sp = state->sp - 2;
|
|
state->pc = 0x38;
|
|
}
|
|
break;
|
|
default: UnimplementedInstruction(state); break;
|
|
}
|
|
//printState(state);
|
|
return cycles8080[*opcode];
|
|
}
|
|
|
|
void generateInterrupt(State8080* state, int interrupt_num)
|
|
{
|
|
//perform "PUSH PC"
|
|
state->memory[state->sp-1] = ((state->pc & 0xFF00) >> 8);
|
|
state->memory[state->sp-2] = (state->pc & 0xff);
|
|
state->sp -= 2;
|
|
|
|
//Set the PC to the low memory vector.
|
|
|
|
state->pc = 8 * interrupt_num;
|
|
state->int_enable = 0;
|
|
}
|
|
|
|
|
|
void read_Space_Invaders_ROM(State8080* state, const char* filename, int offset) {
|
|
//Works exclusively with the memory system of Space Invaders
|
|
FILE* file = fopen(filename, "rb");
|
|
if (file == NULL) {
|
|
printf("Issue opening the file.\n");
|
|
exit(1);
|
|
}
|
|
//Get the file size and read it into a memory buffer
|
|
fseek(file, 0L, SEEK_END);
|
|
int fsize = ftell(file);
|
|
fseek(file, 0L, SEEK_SET);
|
|
|
|
uint8_t *buffer = &state->memory[offset];
|
|
|
|
fread(buffer, fsize, 1, file);
|
|
fclose(file);
|
|
}
|
|
|
|
typedef struct cpu_data {
|
|
char done;
|
|
int total_cycles_count;
|
|
GtkWidget* drawing_area;
|
|
State8080 *state;
|
|
pthread_t pid;
|
|
|
|
} cpu_data;
|
|
|
|
void *cpu(void* arg) {
|
|
cpu_data *cpu = (cpu_data*) arg;
|
|
char done = cpu->done;
|
|
State8080* state = cpu->state;
|
|
int counter;
|
|
|
|
sleep(2);
|
|
|
|
while (!done) {
|
|
counter = emulate8080(cpu->state);
|
|
cpu->total_cycles_count += counter;
|
|
printf("Total cycles: %d\n", cpu->total_cycles_count);
|
|
|
|
//usleep(1); // EMULATION OF THE PROCESSOR ŠPEED
|
|
if ((cpu->total_cycles_count > 16000) && (state->int_enable)) //1/60 second has elapsed
|
|
{
|
|
//redraw screen() will be handeled by the gtk_main() function
|
|
if (which_interrupt == 2) {
|
|
generateInterrupt(state, 2); //interrupt 2
|
|
cpu->total_cycles_count = 0;
|
|
which_interrupt = 1;
|
|
} else {
|
|
generateInterrupt(state, 1); //interrupt 1
|
|
cpu->total_cycles_count = 0;
|
|
which_interrupt = 2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//===== GTK GRAPHICS =====
|
|
|
|
static void draw_callback(GtkWidget *widget, cairo_t *cr, gpointer user_data) {
|
|
// Clear the drawing area if you want
|
|
//cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
|
|
//cairo_paint(cr);
|
|
for (int i = 0; i < 224; i++) {
|
|
for (int j = 0; j < 32; j++) {
|
|
uint8_t byte = bitmap[i*32 + j];
|
|
for (int p = 0; p < 8; p++) {
|
|
int pixel = (byte >> p) & 1; // flip the bit order within each byte
|
|
int x = i; // swap x and y
|
|
int y = 255 - (j*8 + p); // flip the y-coordinate
|
|
if (x >= 0 && x < 256 && y >= 0 && y < 256) { // Check bounds
|
|
if (pixel && (y > 191) && (y < 240)) {
|
|
cairo_set_source_rgb(cr, 0.1, 1.0, 0.1); // Set colour to green
|
|
} else if (pixel && (y < 50) && (y > 32)) {
|
|
cairo_set_source_rgb(cr, 1.0, 0.1, 0.1); // Set colour to red
|
|
} else if (pixel) {
|
|
cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); // Set colour to white
|
|
} else {
|
|
cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); // Set colour to black
|
|
}
|
|
|
|
cairo_rectangle(cr, x * upscaleFactor, y * upscaleFactor, upscaleFactor, upscaleFactor);
|
|
cairo_fill(cr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void on_about_clicked(GtkToolButton *toolbutton, gpointer user_data) {
|
|
GtkWidget *dialog = gtk_about_dialog_new();
|
|
GtkWidget *image = gtk_image_new_from_file("space.png");
|
|
|
|
GtkWidget *box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
|
|
gtk_box_pack_start(GTK_BOX(box), image, TRUE, TRUE, 0);
|
|
|
|
gtk_about_dialog_set_program_name(GTK_ABOUT_DIALOG(dialog), "Lux8080-Emulator");
|
|
gtk_about_dialog_set_comments(GTK_ABOUT_DIALOG(dialog), "This is a simple Intel 8080-Emulator for Space Invaders using C and GTK. Following the guide of @realemulator101");
|
|
gtk_about_dialog_set_logo_icon_name(GTK_ABOUT_DIALOG(dialog), "8080.ico");
|
|
|
|
GtkWidget *content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
|
|
gtk_box_pack_start(GTK_BOX(content_area), box, TRUE, TRUE, 0);
|
|
|
|
gtk_widget_show_all(box);
|
|
gtk_dialog_run(GTK_DIALOG(dialog));
|
|
gtk_widget_destroy(dialog);
|
|
}
|
|
|
|
|
|
gboolean update_ui(gpointer data) {
|
|
// Seems like GTK is not thread safe
|
|
// we fix it with update_ui using gtk_main_iteration_do(FALSE)
|
|
GtkWidget *drawing_area = (GtkWidget*)data;
|
|
gtk_widget_queue_draw(drawing_area);
|
|
while (gtk_events_pending())
|
|
gtk_main_iteration_do(FALSE);
|
|
return TRUE;
|
|
}
|
|
|
|
void on_size_changed(GtkComboBox *combo_box, gpointer user_data) {
|
|
gchar *text = gtk_combo_box_text_get_active_text(GTK_COMBO_BOX_TEXT(combo_box));
|
|
if (g_strcmp0(text, "1x") == 0) {
|
|
upscaleFactor = 1;
|
|
} else if (g_strcmp0(text, "2x") == 0) {
|
|
upscaleFactor = 2;
|
|
} else if (g_strcmp0(text, "3x") == 0) {
|
|
upscaleFactor = 3;
|
|
}
|
|
g_free(text);
|
|
}
|
|
|
|
gboolean close_splash_screen(gpointer data) {
|
|
GtkWidget *splash_screen = (GtkWidget*)data;
|
|
gtk_widget_hide(splash_screen);
|
|
return FALSE;
|
|
}
|
|
|
|
gboolean open_screen(gpointer data) {
|
|
GtkWidget *window = (GtkWidget*)data;
|
|
gtk_widget_show_all(window);
|
|
return FALSE;
|
|
}
|
|
|
|
void on_window_destroy(GtkWidget *widget, cpu_data *cpu_eco) {
|
|
pthread_cancel(cpu_eco->pid); // Assuming 'pid' is a member of 'cpu_data'
|
|
free(cpu_eco->state->memory);
|
|
free(cpu_eco->state);
|
|
free(cpu_eco);
|
|
gtk_main_quit();
|
|
}
|
|
|
|
|
|
|
|
int main (int argc, char *argv[]) {
|
|
|
|
// Emulator init
|
|
|
|
State8080 *state = (State8080*) calloc(1, sizeof(State8080)); //memset obsolte with calloc
|
|
state->cc.z = 1;
|
|
state->cc.s = 1;
|
|
state->cc.p = 1;
|
|
state->cc.cy = 0;
|
|
state->cc.ac = 1;
|
|
state->int_enable = 1;
|
|
state->pc = 0;
|
|
state->memory = calloc(0x10000, 1);
|
|
|
|
read_Space_Invaders_ROM(state, "ROM/invaders.h", 0);
|
|
read_Space_Invaders_ROM(state, "ROM/invaders.g", 0x800);
|
|
read_Space_Invaders_ROM(state, "ROM/invaders.f", 0x1000);
|
|
read_Space_Invaders_ROM(state, "ROM/invaders.e", 0x1800);
|
|
|
|
// GTK init
|
|
gtk_init(&argc, &argv);
|
|
GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
|
gtk_window_set_default_size(GTK_WINDOW(window), 448, 550);
|
|
gtk_window_set_title(GTK_WINDOW(window), "Lux8080-Emulator");
|
|
g_signal_connect(window, "key-press-event", G_CALLBACK(on_key_press), NULL);
|
|
g_signal_connect(window, "key-release-event", G_CALLBACK(on_key_release), NULL);
|
|
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
|
|
|
|
// Splashscreen
|
|
|
|
GtkWidget *splash_screen = gtk_window_new(GTK_WINDOW_POPUP);
|
|
gtk_window_set_position(GTK_WINDOW(splash_screen), GTK_WIN_POS_CENTER);
|
|
GtkWidget *image = gtk_image_new_from_file("space.png");
|
|
gtk_container_add(GTK_CONTAINER(splash_screen), image);
|
|
gtk_widget_show_all(splash_screen);
|
|
g_timeout_add(2000, close_splash_screen, splash_screen);
|
|
g_timeout_add(2002, open_screen, window);
|
|
|
|
// Toolbar init
|
|
GtkWidget *toolbar = gtk_toolbar_new();
|
|
GtkCssProvider *provider = gtk_css_provider_new();
|
|
gtk_css_provider_load_from_data(provider, "* { -GtkWidget-toolbar-style: both-horiz; }", -1, NULL);
|
|
gtk_style_context_add_provider(gtk_widget_get_style_context(toolbar), GTK_STYLE_PROVIDER(provider), GTK_STYLE_PROVIDER_PRIORITY_USER);
|
|
|
|
|
|
// Size option in the toolbar
|
|
GtkToolItem *size = gtk_tool_item_new();
|
|
GtkWidget *combo_box = gtk_combo_box_text_new();
|
|
gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo_box), "1x");
|
|
gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo_box), "2x");
|
|
gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo_box), "3x");
|
|
gtk_container_add(GTK_CONTAINER(size), combo_box);
|
|
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), size, -1);
|
|
g_signal_connect(combo_box, "changed", G_CALLBACK(on_size_changed), NULL);
|
|
|
|
// About option in the toolbar
|
|
|
|
GtkToolItem *about = gtk_tool_button_new(NULL, "About");
|
|
gtk_toolbar_insert(GTK_TOOLBAR(toolbar), about, -1);
|
|
g_signal_connect(about, "clicked", G_CALLBACK(on_about_clicked), NULL);
|
|
|
|
gtk_widget_set_can_focus(GTK_WIDGET(combo_box), FALSE);
|
|
gtk_widget_set_can_focus(GTK_WIDGET(about), FALSE);
|
|
|
|
|
|
// Drawing area init
|
|
bitmap = &(state->memory[0x2400]);
|
|
GtkWidget *drawing_area = gtk_drawing_area_new();
|
|
gtk_widget_set_app_paintable(drawing_area, TRUE);
|
|
g_signal_connect(drawing_area, "draw", G_CALLBACK(draw_callback), NULL);
|
|
|
|
// VBox to hold toolbar and drawing area
|
|
GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
|
|
gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 0);
|
|
gtk_box_pack_start(GTK_BOX(vbox), drawing_area, TRUE, TRUE, 0);
|
|
gtk_container_add(GTK_CONTAINER(window), vbox);
|
|
|
|
|
|
|
|
//gtk_widget_show_all(window);
|
|
|
|
// CPU-init
|
|
cpu_data *cpu_eco = malloc(sizeof(cpu_data));
|
|
cpu_eco->state = state;
|
|
cpu_eco->drawing_area = drawing_area;
|
|
cpu_eco->done = 0;
|
|
cpu_eco->total_cycles_count = 0;
|
|
|
|
// Pthread
|
|
pthread_create(&(cpu_eco->pid), NULL, cpu, (void*) cpu_eco);
|
|
g_signal_connect(window, "destroy", G_CALLBACK(on_window_destroy), cpu_eco);
|
|
|
|
|
|
// Main GTK Loop
|
|
g_timeout_add(50, update_ui, drawing_area);
|
|
gtk_main();
|
|
// cpu_eco->done = 1;
|
|
// pthread_join(pid, NULL);
|
|
// free(state->memory);
|
|
// free(state);
|
|
// free(cpu_eco);
|
|
return 0;
|
|
} |