-
Notifications
You must be signed in to change notification settings - Fork 27
/
functions.h
111 lines (93 loc) · 3.36 KB
/
functions.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#ifndef __6502_FUNCTIONS__
#define __6502_FUNCTIONS__
#include <stdlib.h>
#include "cpu.h"
#define ZP(x) ((uint8_t) (x))
#define STACK_PUSH(m) (m)->mem[(m)->sp-- + STACK_START]
#define STACK_POP(m) (m)->mem[++(m)->sp + STACK_START]
#ifdef DEBUG
#define debugf(...) fprintf(stderr, __VA_ARGS__)
#else
#define debugf(...) do {} while (0)
#endif
static inline size_t mem_abs(uint8_t low, uint8_t high, uint8_t off) {
return (uint16_t) off + (uint16_t) low + ((uint16_t) high << 8);
}
static inline size_t mem_indirect_index(cpu *m, uint8_t addr, uint8_t off) {
return mem_abs(m->mem[addr], m->mem[addr+1], off);
}
static inline size_t mem_indexed_indirect(cpu *m, uint8_t addr, uint8_t off) {
return mem_abs(m->mem[addr+off], m->mem[addr+off+1], 0);
}
// set arg MUST be 16 bits, not 8, so that add results can fit into set.
static inline void set_flag(cpu *m, uint8_t flag, uint16_t set) {
if (set) {
m->sr |= flag;
} else {
m->sr &= ~flag;
}
}
static inline uint8_t get_flag(cpu *m, uint8_t flag) {
return (m->sr & flag) > 0;
}
static inline uint8_t get_emu_flag(cpu *m, uint8_t flag) {
return (m->emu_flags & flag) > 0;
}
// set flags for the result of a computation. set_flags should be called on the
// result of any arithmetic operation.
static inline void set_flags(cpu *m, uint8_t val) {
set_flag(m, FLAG_ZERO, !val);
set_flag(m, FLAG_NEGATIVE, val & 0x80);
}
static inline uint8_t bcd(uint8_t val) {
// bcd is "binary coded decimal"; it treats the upper nibble and lower
// nibble of a byte each as a decimal digit, so 01011000 -> 0101 1000 -> 58.
// in other words, treat hex output as decimal output, so 0x58 is treated as
// 58. this is dumb and adds a bunch of branching to opcode interpretation
// that I Do Not Like.
return 10 * (val >> 4) + (0x0F & val);
}
static inline void add(cpu *m, uint16_t r1) {
// committing a cardinal sin for my sanity's sake. callers should initialize
// r1 to the argument to the add.
if (get_flag(m, FLAG_DECIMAL)) {
r1 = bcd(r1) + bcd(m->ac) + get_flag(m, FLAG_CARRY);
set_flag(m, FLAG_CARRY, r1 > 99);
} else {
r1 += m->ac + get_flag(m, FLAG_CARRY);
set_flag(m, FLAG_CARRY, r1 & 0xFF00);
}
set_flag(m, FLAG_OVERFLOW, (m->ac & 0x80) != (r1 & 0x80));
set_flag(m, FLAG_ZERO, r1 == 0);
set_flag(m, FLAG_NEGATIVE, r1 & 0x80);
m->ac = r1;
}
static inline void sub(cpu *m, uint16_t r1) {
if (get_flag(m, FLAG_DECIMAL)) {
r1 = bcd(m->ac) - bcd(r1) - !get_flag(m, FLAG_CARRY);
set_flag(m, FLAG_OVERFLOW, r1 > 99 || r1 < 0);
} else {
r1 = m->ac - r1 - !get_flag(m, FLAG_CARRY);
set_flag(m, FLAG_OVERFLOW, 0xFF00 & r1);
}
set_flag(m, FLAG_CARRY, !(r1 & 0x8000));
set_flag(m, FLAG_NEGATIVE, r1 & 0x80);
set_flag(m, FLAG_ZERO, r1 == 0);
m->ac = r1;
}
static inline void cmp(cpu *m, uint8_t mem, uint8_t reg) {
set_flag(m, FLAG_CARRY, reg >= mem);
set_flag(m, FLAG_ZERO, reg == mem);
set_flag(m, FLAG_NEGATIVE, 0x80 & (reg - mem));
}
// called at the start of processing an instruction to reset instruction-local
// emulator state
static inline void reset_emu_flags(cpu *m) {
m->emu_flags = 0x00;
}
// mark a memory address as dirty
static inline void mark_dirty(cpu *m, uint16_t addr) {
m->emu_flags |= EMU_FLAG_DIRTY;
m->dirty_mem_addr = addr;
}
#endif