#ifndef _MOS6502_H_ #define _MOS6502_H_ #include "vm_bits.h" #include "vm_segment.h" #define MOS6502_MEMSIZE 65536 #define SET_ARITH_STATUS(v) \ cpu->P &= ~NEGATIVE; \ cpu->P &= ~ZERO; \ cpu->P &= ~CARRY; \ if ((v) == 0) cpu->P |= ZERO; \ if ((v) > 0) cpu->P |= CARRY; \ if ((v) & 0x80) cpu->P |= NEGATIVE #define SET_PC_BYTE(cpu, off, byte) \ vm_segment_set(cpu->memory, cpu->PC + off, byte) #define INIT_ADDR_MODE() \ mos6502 *cpu; \ cpu = mos6502_create() #define END_ADDR_MODE() \ mos6502_free(cpu) #define START_CPU_TEST(t) \ t *cpu; \ cpu = t##_create() #define END_CPU_TEST(t) \ t##_free(cpu) #define DEFINE_INST(inst) \ void mos6502_handle_##inst (mos6502 *cpu, vm_8bit oper) typedef struct { /* * Our memory. */ vm_segment *memory; /* * This contains the last _effective_ address we've resolved in one * of our address modes. In absolute mode, this would be the literal * operand we read from memory; in indirect mode, this will be the * address we _find_ after dereferencing the operand we read from * memory. Another way of thinking of this is, this address is where * we found the value we care about. */ vm_16bit last_addr; /* * This field contains the number of CPU cycles that the last * instruction handled should consume. In order to accurately * emulate any architecture, we must model the type of "wait" time * each instruction would cause. * * It should also be pointed out that the number of cycles is both * informed by the instruction _and_ the address mode. For example, * an instruction executed in zero-page address mode would consume * fewer cycles than one executed in absolute address mode, because * in the latter, the CPU would have to read ahead to discover a * 16-bit operand vs. the 8-bit operand in the former. */ int cycles; /* * Our program counter register; this is what we'll use to determine * where we're "at" in memory while executing opcodes. We use a * 16-bit register because our memory is 64k large. */ vm_16bit PC; /* * This is the accumulator register. It's used in most arithmetic * operations, and anything like that which you need to do will end * up storing the value here. */ vm_8bit A; /* * The X and Y registers are our index registers. They're provided * to aid looping over tables, but they can also be used for other * purposes. */ vm_8bit X, Y; /* * The P register is our status flag register. (I presume 'P' means * 'predicate'.) Each bit stands for some kind of status. */ vm_8bit P; /* * The S register is our stack counter register. It indicates how * far into the stack we've gone. */ vm_8bit S; } mos6502; /* * This is a small convenience so that we don't need to expose the * somewhat regrettable syntax for function pointers to any main source * file */ typedef vm_8bit (*mos6502_address_resolver)(mos6502 *); /* * Another convenience; this type definition is for the functions we * write to handle instruction logic. */ typedef void (*mos6502_instruction_handler)(mos6502 *, vm_8bit); extern mos6502 *mos6502_create(); extern void mos6502_free(mos6502 *); extern vm_8bit mos6502_next_byte(mos6502 *); extern void mos6502_push_stack(mos6502 *, vm_16bit); extern vm_16bit mos6502_pop_stack(mos6502 *); extern void mos6502_set_status(mos6502 *, vm_8bit); extern void mos6502_modify_status(mos6502 *, vm_8bit, vm_8bit); extern int mos6502_cycles(mos6502 *, vm_8bit); extern int mos6502_instruction(vm_8bit); extern mos6502_instruction_handler mos6502_get_instruction_handler(vm_8bit); /* * Below are some functions that are defined in mos6502.addr.c */ extern int mos6502_addr_mode(vm_8bit); extern mos6502_address_resolver mos6502_get_address_resolver(int); /* * In some address mode resolution, we must factor the carry bit into * the arithmetic we perform. In all those cases, if the carry bit is * set, we must only add 1 to the addition. The carry variable is, * therefore, the literal value we are adding, rather than a boolean * signifier. */ #define CARRY_BIT() \ vm_8bit carry = 0; \ if (cpu->P & CARRY) carry = 1 /* * A uniform way of declaring resolve functions for address modes, which * is useful in the event that we need to change the function signature. */ #define DECL_ADDR_MODE(x) \ extern vm_8bit mos6502_resolve_##x (mos6502 *) /* * Similarly, a uniform way of declaring instruction handler functions, * for the same reasons. */ #define DECL_INST(x) \ extern void mos6502_handle_##x (mos6502 *, vm_8bit) /* * All of our address modes */ DECL_ADDR_MODE(acc); DECL_ADDR_MODE(abs); DECL_ADDR_MODE(abx); DECL_ADDR_MODE(aby); DECL_ADDR_MODE(imm); DECL_ADDR_MODE(ind); DECL_ADDR_MODE(idx); DECL_ADDR_MODE(idy); DECL_ADDR_MODE(rel); DECL_ADDR_MODE(zpg); DECL_ADDR_MODE(zpx); DECL_ADDR_MODE(zpy); /* * And now, our instruction handlers; held generally in mos6502.*.c * (excepting mos6502.addr.c). */ DECL_INST(adc); DECL_INST(and); DECL_INST(asl); DECL_INST(bcc); DECL_INST(bcs); DECL_INST(beq); DECL_INST(bit); DECL_INST(bmi); DECL_INST(bne); DECL_INST(bpl); DECL_INST(brk); DECL_INST(bvc); DECL_INST(bvs); DECL_INST(clc); DECL_INST(cld); DECL_INST(cli); DECL_INST(clv); DECL_INST(cmp); DECL_INST(cpx); DECL_INST(cpy); DECL_INST(dec); DECL_INST(dex); DECL_INST(dey); DECL_INST(eor); DECL_INST(inc); DECL_INST(inx); DECL_INST(iny); DECL_INST(jmp); DECL_INST(jsr); DECL_INST(lda); DECL_INST(ldx); DECL_INST(ldy); DECL_INST(lsr); DECL_INST(nop); DECL_INST(ora); DECL_INST(pha); DECL_INST(php); DECL_INST(pla); DECL_INST(plp); DECL_INST(rol); DECL_INST(ror); DECL_INST(rti); DECL_INST(rts); DECL_INST(sbc); DECL_INST(sec); DECL_INST(sed); DECL_INST(sei); DECL_INST(sta); DECL_INST(stx); DECL_INST(sty); DECL_INST(tax); DECL_INST(tay); DECL_INST(tsx); DECL_INST(txa); DECL_INST(txs); DECL_INST(tya); #endif