+69
-2
src/Cpu.zig
+69
-2
src/Cpu.zig
···
112
0x01 => if (self.zpXInd(pins)) |_| self.ora(pins, v), // ORA (zp,X)
113
0x05 => if (self.zp(pins)) |_| self.ora(pins, v), // ORA zp
114
0x06 => if (self.zp(pins)) |_| self.asl(pins, .mem), // ASL zp
115
0x09 => if (self.imm(pins)) |_| self.ora(pins, v), // ORA #
116
0x0a => if (self.imm(pins)) |_| self.asl(pins, .acc), // ASL A
117
0x0d => if (self.abs(pins)) |_| self.ora(pins, v), // ORA abs
···
131
0x24 => if (self.zp(pins)) |_| self.bit(pins), // BIT zp
132
0x25 => if (self.zp(pins)) |_| self._and(pins, v), // AND zp
133
0x26 => if (self.zp(pins)) |_| self.rol(pins, .mem), // ROL zp
134
0x29 => if (self.imm(pins)) |_| self._and(pins, v), // AND #
135
0x2a => if (self.imm(pins)) |_| self.rol(pins, .acc), // ROL A
136
0x2c => if (self.abs(pins)) |_| self.bit(pins), // BIT abs
···
150
0x41 => if (self.zpXInd(pins)) |_| self.eor(pins, v), // EOR (zp,X)
151
0x45 => if (self.zp(pins)) |_| self.eor(pins, v), // EOR zp
152
0x46 => if (self.zp(pins)) |_| self.lsr(pins, .mem), // LSR zp
153
0x49 => if (self.imm(pins)) |_| self.eor(pins, v), // EOR #
154
0x4a => if (self.imm(pins)) |_| self.lsr(pins, .acc), // LSR A
155
0x4c => self.jmp(pins), // JMP abs
···
169
0x61 => if (self.zpXInd(pins)) |_| self.adc(pins, v), // ADC (zp,X)
170
0x65 => if (self.zp(pins)) |_| self.adc(pins, v), // ADC zp
171
0x66 => if (self.zp(pins)) |_| self.ror(pins, .mem), // ROR zp
172
0x69 => if (self.imm(pins)) |_| self.adc(pins, v), // ADC #
173
0x6a => if (self.imm(pins)) |_| self.ror(pins, .acc), // ROR A
174
0x6c => self.jmpInd(pins), // JMP (ind)
···
425
}
426
427
//------------------------------------------------------
428
-
// Opcodes: Load/Store & Arithmetic
429
-
const Dst = enum { acc, mem };
430
431
inline fn ld(self: *Cpu, pins: *zesty.Pins, to: *u8, from: u8) void {
432
to.* = from;
···
438
pins.cpu_rw = .write;
439
self.fetch(pins);
440
}
441
inline fn ora(self: *Cpu, pins: *zesty.Pins, v: u8) void {
442
self.a |= v;
443
self.setNZ(self.a);
···
112
0x01 => if (self.zpXInd(pins)) |_| self.ora(pins, v), // ORA (zp,X)
113
0x05 => if (self.zp(pins)) |_| self.ora(pins, v), // ORA zp
114
0x06 => if (self.zp(pins)) |_| self.asl(pins, .mem), // ASL zp
115
+
0x08 => self.php(pins), // PHP
116
0x09 => if (self.imm(pins)) |_| self.ora(pins, v), // ORA #
117
0x0a => if (self.imm(pins)) |_| self.asl(pins, .acc), // ASL A
118
0x0d => if (self.abs(pins)) |_| self.ora(pins, v), // ORA abs
···
132
0x24 => if (self.zp(pins)) |_| self.bit(pins), // BIT zp
133
0x25 => if (self.zp(pins)) |_| self._and(pins, v), // AND zp
134
0x26 => if (self.zp(pins)) |_| self.rol(pins, .mem), // ROL zp
135
+
0x28 => self.plp(pins), // PLP
136
0x29 => if (self.imm(pins)) |_| self._and(pins, v), // AND #
137
0x2a => if (self.imm(pins)) |_| self.rol(pins, .acc), // ROL A
138
0x2c => if (self.abs(pins)) |_| self.bit(pins), // BIT abs
···
152
0x41 => if (self.zpXInd(pins)) |_| self.eor(pins, v), // EOR (zp,X)
153
0x45 => if (self.zp(pins)) |_| self.eor(pins, v), // EOR zp
154
0x46 => if (self.zp(pins)) |_| self.lsr(pins, .mem), // LSR zp
155
+
0x48 => self.pha(pins), // PHA
156
0x49 => if (self.imm(pins)) |_| self.eor(pins, v), // EOR #
157
0x4a => if (self.imm(pins)) |_| self.lsr(pins, .acc), // LSR A
158
0x4c => self.jmp(pins), // JMP abs
···
172
0x61 => if (self.zpXInd(pins)) |_| self.adc(pins, v), // ADC (zp,X)
173
0x65 => if (self.zp(pins)) |_| self.adc(pins, v), // ADC zp
174
0x66 => if (self.zp(pins)) |_| self.ror(pins, .mem), // ROR zp
175
+
0x68 => self.pla(pins), // PLA
176
0x69 => if (self.imm(pins)) |_| self.adc(pins, v), // ADC #
177
0x6a => if (self.imm(pins)) |_| self.ror(pins, .acc), // ROR A
178
0x6c => self.jmpInd(pins), // JMP (ind)
···
429
}
430
431
//------------------------------------------------------
432
+
// Opcodes: Load/Store
433
434
inline fn ld(self: *Cpu, pins: *zesty.Pins, to: *u8, from: u8) void {
435
to.* = from;
···
441
pins.cpu_rw = .write;
442
self.fetch(pins);
443
}
444
+
inline fn push(self: *Cpu, pins: *zesty.Pins, v: u8) void {
445
+
switch (self.cycle) {
446
+
// Dummy read
447
+
0 => pins.cpu_addr = self.pc,
448
+
1 => {
449
+
self.hilo = .stack(self.sp);
450
+
pins.cpu_addr = self.hilo.addr();
451
+
pins.cpu_data = v;
452
+
pins.cpu_rw = .write;
453
+
self.sp -%= 1;
454
+
},
455
+
else => self.fetch(pins),
456
+
}
457
+
}
458
+
inline fn pull(self: *Cpu, pins: *zesty.Pins) ?u8 {
459
+
switch (self.cycle) {
460
+
// Dummy read
461
+
0 => pins.cpu_addr = self.pc,
462
+
1 => {
463
+
self.hilo = .stack(self.sp);
464
+
pins.cpu_addr = self.hilo.addr();
465
+
self.sp +%= 1;
466
+
},
467
+
2 => {
468
+
self.hilo = .stack(self.sp);
469
+
pins.cpu_addr = self.hilo.addr();
470
+
},
471
+
else => {
472
+
self.fetch(pins);
473
+
return pins.cpu_data;
474
+
},
475
+
}
476
+
return null;
477
+
}
478
+
inline fn pha(self: *Cpu, pins: *zesty.Pins) void {
479
+
self.push(pins, self.a);
480
+
}
481
+
inline fn php(self: *Cpu, pins: *zesty.Pins) void {
482
+
var status = self.status;
483
+
// BRK is always set to true here
484
+
status.brk = true;
485
+
self.push(pins, status.toByte());
486
+
}
487
+
inline fn pla(self: *Cpu, pins: *zesty.Pins) void {
488
+
self.a = self.pull(pins) orelse return;
489
+
self.setNZ(self.a);
490
+
}
491
+
inline fn plp(self: *Cpu, pins: *zesty.Pins) void {
492
+
const status: Status = .from(self.pull(pins) orelse return);
493
+
self.status = .{
494
+
.negative = status.negative,
495
+
.overflow = status.overflow,
496
+
.brk = self.status.brk, // Do not inherit BRK.
497
+
.decimal = status.decimal,
498
+
.irq_disabled = status.irq_disabled,
499
+
.zero = status.zero,
500
+
.carry = status.carry,
501
+
};
502
+
}
503
+
504
+
//------------------------------------------------------
505
+
// Opcodes: Arithmetic
506
+
507
+
const Dst = enum { acc, mem };
508
inline fn ora(self: *Cpu, pins: *zesty.Pins, v: u8) void {
509
self.a |= v;
510
self.setNZ(self.a);
+1
src/tests/cpu.zig
+1
src/tests/cpu.zig
+34
src/tests/cpu/assembler.zig
+34
src/tests/cpu/assembler.zig
···
73
stx,
74
sty,
75
76
+
php,
77
+
plp,
78
+
pha,
79
+
pla,
80
+
81
ora,
82
@"and",
83
eor,
···
387
.implied => 0xea,
388
else => null,
389
},
390
+
.pha => switch (opr) {
391
+
.implied => 0x48,
392
+
else => null,
393
+
},
394
+
.pla => switch (opr) {
395
+
.implied => 0x68,
396
+
else => null,
397
+
},
398
+
.php => switch (opr) {
399
+
.implied => 0x08,
400
+
else => null,
401
+
},
402
+
.plp => switch (opr) {
403
+
.implied => 0x28,
404
+
else => null,
405
+
},
406
+
.jmp => switch (opr) {
407
+
.absolute => 0x4c,
408
+
.indirect => 0x6c,
409
+
else => null,
410
+
},
411
+
.jsr => switch (opr) {
412
+
.absolute => 0x20,
413
+
else => null,
414
+
},
415
+
.rts => switch (opr) {
416
+
.implied => 0x60,
417
+
else => null,
418
+
},
419
};
420
}
421
};
+96
src/tests/cpu/stack.zig
+96
src/tests/cpu/stack.zig
···
···
1
+
//! Stack-related tests.
2
+
const std = @import("std");
3
+
const cpu = @import("../cpu.zig");
4
+
5
+
test "PHA/PLA" {
6
+
var z = cpu.testZes(.{ .rom = &.{
7
+
.{
8
+
0x8000, cpu.assemble(
9
+
\\LDA #$89
10
+
\\PHA
11
+
\\LDA #0
12
+
\\PLA
13
+
),
14
+
},
15
+
} });
16
+
cpu.run(&z); // Setup A register
17
+
18
+
try std.testing.expectEqual(0xfd, z.cpu.sp);
19
+
cpu.run(&z); // PHA
20
+
try std.testing.expectEqual(0xfc, z.cpu.sp);
21
+
try std.testing.expectEqual(0x8003, z.cpu.pc);
22
+
try std.testing.expectEqual(0x89, z.ram[0x1fd]);
23
+
try std.testing.expect(z.cpu.status.negative);
24
+
try std.testing.expect(!z.cpu.status.zero);
25
+
26
+
cpu.run(&z); // Reset A register
27
+
try std.testing.expectEqual(0x8005, z.cpu.pc);
28
+
try std.testing.expectEqual(0x00, z.cpu.a);
29
+
try std.testing.expect(!z.cpu.status.negative);
30
+
try std.testing.expect(z.cpu.status.zero);
31
+
32
+
cpu.run(&z); // PLA
33
+
try std.testing.expectEqual(0x8006, z.cpu.pc);
34
+
try std.testing.expectEqual(0x89, z.cpu.a);
35
+
try std.testing.expectEqual(0xfd, z.cpu.sp);
36
+
try std.testing.expect(z.cpu.status.negative);
37
+
try std.testing.expect(!z.cpu.status.zero);
38
+
}
39
+
40
+
test "PHP/PLP" {
41
+
var z = cpu.testZes(.{ .rom = &.{
42
+
.{
43
+
0x8000, cpu.assemble(
44
+
\\CLI
45
+
\\SED
46
+
\\LDA #0
47
+
\\PHP
48
+
\\
49
+
\\SEI
50
+
\\CLD
51
+
\\LDA #$ff
52
+
\\PLP
53
+
),
54
+
},
55
+
} });
56
+
57
+
// Prepare flags
58
+
cpu.run(&z); // Clear I
59
+
cpu.run(&z); // Set D
60
+
cpu.run(&z); // Set Z
61
+
try std.testing.expect(!z.cpu.status.negative);
62
+
try std.testing.expect(z.cpu.status.zero);
63
+
try std.testing.expect(z.cpu.status.decimal);
64
+
try std.testing.expect(!z.cpu.status.irq_disabled);
65
+
66
+
try std.testing.expectEqual(0xfd, z.cpu.sp);
67
+
cpu.run(&z); // PHP
68
+
try std.testing.expectEqual(0xfc, z.cpu.sp);
69
+
70
+
try std.testing.expectEqual(0x8005, z.cpu.pc);
71
+
try std.testing.expectEqual(0b00111010, z.ram[0x1fd]);
72
+
73
+
// Reset flags
74
+
cpu.run(&z); // Set I
75
+
cpu.run(&z); // Clear D
76
+
cpu.run(&z); // Clear Z; Set N
77
+
try std.testing.expect(z.cpu.status.negative);
78
+
try std.testing.expect(!z.cpu.status.zero);
79
+
try std.testing.expect(!z.cpu.status.decimal);
80
+
try std.testing.expect(z.cpu.status.irq_disabled);
81
+
82
+
try std.testing.expectEqual(0xfc, z.cpu.sp);
83
+
cpu.run(&z); // PLP
84
+
try std.testing.expectEqual(0xfd, z.cpu.sp);
85
+
86
+
try std.testing.expectEqual(0x800a, z.cpu.pc);
87
+
88
+
// Check if flags are restored
89
+
try std.testing.expect(!z.cpu.status.negative);
90
+
try std.testing.expect(z.cpu.status.zero);
91
+
try std.testing.expect(z.cpu.status.decimal);
92
+
try std.testing.expect(!z.cpu.status.irq_disabled);
93
+
94
+
// BRK should not be restored.
95
+
try std.testing.expect(!z.cpu.status.brk);
96
+
}