+69
-2
src/Cpu.zig
+69
-2
src/Cpu.zig
···
112
112
0x01 => if (self.zpXInd(pins)) |_| self.ora(pins, v), // ORA (zp,X)
113
113
0x05 => if (self.zp(pins)) |_| self.ora(pins, v), // ORA zp
114
114
0x06 => if (self.zp(pins)) |_| self.asl(pins, .mem), // ASL zp
115
+
0x08 => self.php(pins), // PHP
115
116
0x09 => if (self.imm(pins)) |_| self.ora(pins, v), // ORA #
116
117
0x0a => if (self.imm(pins)) |_| self.asl(pins, .acc), // ASL A
117
118
0x0d => if (self.abs(pins)) |_| self.ora(pins, v), // ORA abs
···
130
131
0x24 => if (self.zp(pins)) |_| self.bit(pins), // BIT zp
131
132
0x25 => if (self.zp(pins)) |_| self._and(pins, v), // AND zp
132
133
0x26 => if (self.zp(pins)) |_| self.rol(pins, .mem), // ROL zp
134
+
0x28 => self.plp(pins), // PLP
133
135
0x29 => if (self.imm(pins)) |_| self._and(pins, v), // AND #
134
136
0x2a => if (self.imm(pins)) |_| self.rol(pins, .acc), // ROL A
135
137
0x2c => if (self.abs(pins)) |_| self.bit(pins), // BIT abs
···
149
151
0x41 => if (self.zpXInd(pins)) |_| self.eor(pins, v), // EOR (zp,X)
150
152
0x45 => if (self.zp(pins)) |_| self.eor(pins, v), // EOR zp
151
153
0x46 => if (self.zp(pins)) |_| self.lsr(pins, .mem), // LSR zp
154
+
0x48 => self.pha(pins), // PHA
152
155
0x49 => if (self.imm(pins)) |_| self.eor(pins, v), // EOR #
153
156
0x4a => if (self.imm(pins)) |_| self.lsr(pins, .acc), // LSR A
154
157
0x4d => if (self.abs(pins)) |_| self.eor(pins, v), // EOR abs
···
166
169
0x61 => if (self.zpXInd(pins)) |_| self.adc(pins, v), // ADC (zp,X)
167
170
0x65 => if (self.zp(pins)) |_| self.adc(pins, v), // ADC zp
168
171
0x66 => if (self.zp(pins)) |_| self.ror(pins, .mem), // ROR zp
172
+
0x68 => self.pla(pins), // PLA
169
173
0x69 => if (self.imm(pins)) |_| self.adc(pins, v), // ADC #
170
174
0x6a => if (self.imm(pins)) |_| self.ror(pins, .acc), // ROR A
171
175
0x6d => if (self.abs(pins)) |_| self.adc(pins, v), // ADC abs
···
421
425
}
422
426
423
427
//------------------------------------------------------
424
-
// Opcodes: Load/Store & Arithmetic
425
-
const Dst = enum { acc, mem };
428
+
// Opcodes: Load/Store
426
429
427
430
inline fn ld(self: *Cpu, pins: *zesty.Pins, to: *u8, from: u8) void {
428
431
to.* = from;
···
434
437
pins.cpu_rw = .write;
435
438
self.fetch(pins);
436
439
}
440
+
inline fn push(self: *Cpu, pins: *zesty.Pins, v: u8) void {
441
+
switch (self.cycle) {
442
+
// Dummy read
443
+
0 => pins.cpu_addr = self.pc,
444
+
1 => {
445
+
self.hilo = .stack(self.sp);
446
+
pins.cpu_addr = @bitCast(self.hilo);
447
+
pins.cpu_data = v;
448
+
pins.cpu_rw = .write;
449
+
self.sp -%= 1;
450
+
},
451
+
else => self.fetch(pins),
452
+
}
453
+
}
454
+
inline fn pull(self: *Cpu, pins: *zesty.Pins) ?u8 {
455
+
switch (self.cycle) {
456
+
// Dummy read
457
+
0 => pins.cpu_addr = self.pc,
458
+
1 => {
459
+
self.hilo = .stack(self.sp);
460
+
pins.cpu_addr = @bitCast(self.hilo);
461
+
self.sp +%= 1;
462
+
},
463
+
2 => {
464
+
self.hilo = .stack(self.sp);
465
+
pins.cpu_addr = @bitCast(self.hilo);
466
+
},
467
+
else => {
468
+
self.fetch(pins);
469
+
return pins.cpu_data;
470
+
},
471
+
}
472
+
return null;
473
+
}
474
+
inline fn pha(self: *Cpu, pins: *zesty.Pins) void {
475
+
self.push(pins, self.a);
476
+
}
477
+
inline fn php(self: *Cpu, pins: *zesty.Pins) void {
478
+
var status = self.status;
479
+
// BRK is always set to true here
480
+
status.brk = true;
481
+
self.push(pins, status.toByte());
482
+
}
483
+
inline fn pla(self: *Cpu, pins: *zesty.Pins) void {
484
+
self.a = self.pull(pins) orelse return;
485
+
self.setNZ(self.a);
486
+
}
487
+
inline fn plp(self: *Cpu, pins: *zesty.Pins) void {
488
+
const status: Status = .from(self.pull(pins) orelse return);
489
+
self.status = .{
490
+
.negative = status.negative,
491
+
.overflow = status.overflow,
492
+
.brk = self.status.brk, // Do not inherit BRK.
493
+
.decimal = status.decimal,
494
+
.irq_disabled = status.irq_disabled,
495
+
.zero = status.zero,
496
+
.carry = status.carry,
497
+
};
498
+
}
499
+
500
+
//------------------------------------------------------
501
+
// Opcodes: Arithmetic
502
+
503
+
const Dst = enum { acc, mem };
437
504
inline fn ora(self: *Cpu, pins: *zesty.Pins, v: u8) void {
438
505
self.a |= v;
439
506
self.setNZ(self.a);
+1
src/tests/cpu.zig
+1
src/tests/cpu.zig
+21
src/tests/cpu/assembler.zig
+21
src/tests/cpu/assembler.zig
···
73
73
stx,
74
74
sty,
75
75
76
+
php,
77
+
plp,
78
+
pha,
79
+
pla,
80
+
76
81
ora,
77
82
@"and",
78
83
eor,
···
379
384
.implied => 0xea,
380
385
else => null,
381
386
},
387
+
.pha => switch (opr) {
388
+
.implied => 0x48,
389
+
else => null,
390
+
},
391
+
.pla => switch (opr) {
392
+
.implied => 0x68,
393
+
else => null,
394
+
},
395
+
.php => switch (opr) {
396
+
.implied => 0x08,
397
+
else => null,
398
+
},
399
+
.plp => switch (opr) {
400
+
.implied => 0x28,
401
+
else => null,
402
+
},
382
403
};
383
404
}
384
405
};
+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.runDebug(&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
+
}