MottZilla wrote:
There are many ways to do various parts of the emulator. Using a ReadMem type function to handle reading is one way. And yes your example seems correct.
I'm not sure what you mean about large blocks of memory.
let me show you my code:
Code:
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>
#include <conio2.h>
#include <string.h>
// DECLARACION DE DEFINES
#define LD_IMM(REG) REG = mem[++pc]; pc++; z_flag = !(REG); n_flag = REG & 0x80
#define LD_ABS(REG) REG = ReadMem((mem[pc + 2] << 8) | mem[pc + 1]); pc += 3; z_flag = !(REG); n_flag = REG & 0x80
#define LD_ZP(REG) REG = ReadMem((mem[++pc] << 8) | 0x00); pc++; z_flag = !(REG); n_flag = REG & 0x80
#define LD_ABSIND(REG,IND) REG = ReadMem(((mem[pc + 2] << 8) | mem[pc + 1]) + IND); pc += 3; z_flag = !(REG); n_flag = REG & 0x80
#define ST_ABS(REG) WriteMem((mem[pc + 2] << 8) | mem[pc + 1],REG); pc += 3;
#define IN(REG) REG++; pc++; z_flag = !(REG); n_flag = REG & 0x80
#define CP_IMM(REG) if(REG < mem[++pc]) { n_flag = 1; z_flag = c_flag = 0; }; \
if(REG == mem[pc]) { n_flag = 0; z_flag = c_flag = 1; }; \
if(REG > mem[pc]) { n_flag = z_flag = 0; c_flag = 1; };\
pc++;
#define BRANCH(FLAG,VALUE) pc += 2; if(FLAG == VALUE) { pc--; (mem[pc] > 0x7f) ? pc -= (~mem[pc] & 0x00ff) : pc += (mem[pc] & 0x00ff); }
// DECLARACION DE FUNCIONES
static inline unsigned char ReadMem(int addr);
static inline void WriteMem(int addr,int value);
static inline void WritePPU(int value);
// DECLARACION DE VARIABLES GLOBALES
unsigned char *mem,*ROM,*VRAM;
unsigned int pc;
// INICIO DEL PROGRAMA PRINCIPAL
int main(void)
{
int tecla;
char romname[50];
FILE *romfile;
unsigned long int ciclos_cpu;
int z_flag,n_flag,c_flag;
int acc,x,y;
int opcode;
int RomBanks16kb;
unsigned int InitPC;
const char tabla_ciclos_cpu[256] = // Vector que almacena los ciclos usados por cada opcode
{
7,6,2,8,3,3,5,5,3,2,2,2,4,4,6,6,2,5,2,8,4,4,6,6,2,4,2,7,5,5,7,7,
6,6,2,8,3,3,5,5,4,2,2,2,4,4,6,6,2,5,2,8,4,4,6,6,2,4,2,7,
5,5,7,7,6,6,2,8,3,3,5,5,3,2,2,2,3,4,6,6,2,5,2,8,4,4,6,6,2,4,2,7,
5,5,7,7,6,6,2,8,3,3,5,5,4,2,2,2,5,4,6,6,2,5,2,8,4,4,6,6,
2,4,2,7,5,5,7,7,2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,2,6,2,6,4,4,4,4,
2,5,2,5,5,5,5,5,2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,2,5,2,5,
4,4,4,4,2,4,2,5,4,4,4,4,2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,2,5,2,8,
4,4,6,6,2,4,2,7,5,5,7,7,2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,
2,5,2,8,4,4,6,6,2,4,2,7,5,5,7,7
};
clrscr();
strcpy(romname,"demo.nes");
romfile = fopen(romname,"rb");
mem = (unsigned char *)malloc(0x10000);
VRAM = (unsigned char *)malloc(0x4000);
memset(mem,0,0x10000);
memset(VRAM,0,0x4000);
fseek(romfile,16,0);
fread(&mem[0x8000],1,0x4000,romfile);
fseek(romfile,16,0);
fread(&mem[0xc000],1,0x4000,romfile);
fseek(romfile,16400,0);
fread(VRAM,1,0x2000,romfile);
fclose(romfile);
InitPC = (mem[0xfffd] << 8) | mem[0xfffc];
pc = InitPC;
acc = x = y = ciclos_cpu = 0;
for(;;)
{
opcode = mem[pc];
switch(opcode)
{
// LDx IMMEDIATE
case 0xa9: LD_IMM(acc); break;
case 0xa2: LD_IMM(x); break;
case 0xa0: LD_IMM(y); break;
// LDx ABSOLUTE
case 0xad: LD_ABS(acc); break;
case 0xae: LD_ABS(x); break;
case 0xac: LD_ABS(y); break;
// LDx ZERO PAGE
case 0xa5: LD_ZP(acc); break;
case 0xa6: LD_ZP(x); break;
case 0xa4: LD_ZP(y); break;
// LDx ABSOLUTE INDEXED
case 0xbd: LD_ABSIND(acc,x); break;
case 0xb9: LD_ABSIND(acc,y); break;
case 0xbe: LD_ABSIND(x,y); break;
case 0xbc: LD_ABSIND(y,x); break;
// STx ABSOLUTE
case 0x8d: ST_ABS(acc); break;
case 0x8e: ST_ABS(x); break;
case 0x8c: ST_ABS(y); break;
// INx IMPLIED
case 0xe8: IN(x); break;
case 0xc8: IN(y); break;
// CPx IMMEDIATE
case 0xc9: CP_IMM(y); break;
case 0xe0: CP_IMM(x); break;
case 0xc0: CP_IMM(y); break;
// BRANCH RELATIVE
case 0xd0: BRANCH(z_flag,0); break;
case 0xf0: BRANCH(z_flag,1); break;
case 0x90: BRANCH(c_flag,0); break;
case 0xb0: BRANCH(c_flag,1); break;
case 0x10: BRANCH(n_flag,0); break;
case 0x30: BRANCH(n_flag,1); break;
default:
printf("%X: %X\n",pc,mem[pc]);
pc++;
break;
}
ciclos_cpu = tabla_ciclos_cpu[opcode];
while(kbhit())
{
tecla = getch();
if(tecla == 27) { free(mem); free(ROM); exit(0); }
}
}
free(mem);
free(ROM);
return 0;
}
static inline unsigned char ReadMem(int addr)
{
unsigned char temp2002,temp2007;
if(addr < 0x2000) return mem[addr];
else
{
switch(addr)
{
case 0x2002:
temp2002 = VRAM[addr];
VRAM[addr] &= 0x7f; // Ponemos el bit 7 a 0
VRAM[0x2005] = VRAM[0x2006] = 0;
return temp2002;
break;
case 0x2004:
return VRAM[0x2003];
break;
case 0x2007:
temp2007 = VRAM[0x2007];
(VRAM[0x2000] & 0x4) == 0 ? VRAM[0x2006]++ : VRAM[0x2006] += 32;
return temp2007;
break;
}
}
return 0;
}
static inline void WriteMem(int addr,int value)
{
int temp2006;
static int PrimeraLectura = 1; // Valor 1 si es la primera lectura, 0 lo contrario
switch(addr)
{
case 0x2000:
case 0x2001:
VRAM[addr] = value;
break;
case 0x2006:
if(PrimeraLectura == 1)
{
temp2006 = mem[pc + 1] << 8;
PrimeraLectura = 0;
}
else
{
temp2006 |= mem[pc + 1];
PrimeraLectura = 1;
}
break;
case 0x2007:
if((VRAM[0x2002] & 0x10) == 1) break;
else WritePPU(value);
break;
}
return;
}
static inline void WritePPU(int value)
{
return;
}
that all i've done until now. as you can se i only declared 2 block of memry: one for console internal memory and one for PPU memory. code it's not commented but i think its self explanatory. any doubts just ask me! i'm wide open to suggestions too!