tangled
alpha
login
or
join now
pluie.me
/
zesty
2
fork
atom
Zesty - a pin-accurate, cycle-accurate NES emulator written in Zig
2
fork
atom
overview
issues
pulls
pipelines
cpu: add stack operations
pluie.me
6 months ago
2f7ff9e1
617d00bb
verified
This commit was signed with the committer's
known signature
.
pluie.me
SSH Key Fingerprint:
SHA256:YtI1D7vlcZ4obaiJ4tQihtswcMhHKdfYZuc4whOX2y8=
+187
-2
4 changed files
expand all
collapse all
unified
split
src
Cpu.zig
tests
cpu
assembler.zig
stack.zig
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
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
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
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
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
424
-
// Opcodes: Load/Store & Arithmetic
425
425
-
const Dst = enum { acc, mem };
428
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
440
+
inline fn push(self: *Cpu, pins: *zesty.Pins, v: u8) void {
441
441
+
switch (self.cycle) {
442
442
+
// Dummy read
443
443
+
0 => pins.cpu_addr = self.pc,
444
444
+
1 => {
445
445
+
self.hilo = .stack(self.sp);
446
446
+
pins.cpu_addr = @bitCast(self.hilo);
447
447
+
pins.cpu_data = v;
448
448
+
pins.cpu_rw = .write;
449
449
+
self.sp -%= 1;
450
450
+
},
451
451
+
else => self.fetch(pins),
452
452
+
}
453
453
+
}
454
454
+
inline fn pull(self: *Cpu, pins: *zesty.Pins) ?u8 {
455
455
+
switch (self.cycle) {
456
456
+
// Dummy read
457
457
+
0 => pins.cpu_addr = self.pc,
458
458
+
1 => {
459
459
+
self.hilo = .stack(self.sp);
460
460
+
pins.cpu_addr = @bitCast(self.hilo);
461
461
+
self.sp +%= 1;
462
462
+
},
463
463
+
2 => {
464
464
+
self.hilo = .stack(self.sp);
465
465
+
pins.cpu_addr = @bitCast(self.hilo);
466
466
+
},
467
467
+
else => {
468
468
+
self.fetch(pins);
469
469
+
return pins.cpu_data;
470
470
+
},
471
471
+
}
472
472
+
return null;
473
473
+
}
474
474
+
inline fn pha(self: *Cpu, pins: *zesty.Pins) void {
475
475
+
self.push(pins, self.a);
476
476
+
}
477
477
+
inline fn php(self: *Cpu, pins: *zesty.Pins) void {
478
478
+
var status = self.status;
479
479
+
// BRK is always set to true here
480
480
+
status.brk = true;
481
481
+
self.push(pins, status.toByte());
482
482
+
}
483
483
+
inline fn pla(self: *Cpu, pins: *zesty.Pins) void {
484
484
+
self.a = self.pull(pins) orelse return;
485
485
+
self.setNZ(self.a);
486
486
+
}
487
487
+
inline fn plp(self: *Cpu, pins: *zesty.Pins) void {
488
488
+
const status: Status = .from(self.pull(pins) orelse return);
489
489
+
self.status = .{
490
490
+
.negative = status.negative,
491
491
+
.overflow = status.overflow,
492
492
+
.brk = self.status.brk, // Do not inherit BRK.
493
493
+
.decimal = status.decimal,
494
494
+
.irq_disabled = status.irq_disabled,
495
495
+
.zero = status.zero,
496
496
+
.carry = status.carry,
497
497
+
};
498
498
+
}
499
499
+
500
500
+
//------------------------------------------------------
501
501
+
// Opcodes: Arithmetic
502
502
+
503
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
···
9
9
_ = @import("cpu/addressing.zig");
10
10
_ = @import("cpu/alu.zig");
11
11
_ = @import("cpu/control_flow.zig");
12
12
+
_ = @import("cpu/stack.zig");
12
13
}
13
14
14
15
//------------------------------------------------------
+21
src/tests/cpu/assembler.zig
···
73
73
stx,
74
74
sty,
75
75
76
76
+
php,
77
77
+
plp,
78
78
+
pha,
79
79
+
pla,
80
80
+
76
81
ora,
77
82
@"and",
78
83
eor,
···
377
382
},
378
383
.nop => switch (opr) {
379
384
.implied => 0xea,
385
385
+
else => null,
386
386
+
},
387
387
+
.pha => switch (opr) {
388
388
+
.implied => 0x48,
389
389
+
else => null,
390
390
+
},
391
391
+
.pla => switch (opr) {
392
392
+
.implied => 0x68,
393
393
+
else => null,
394
394
+
},
395
395
+
.php => switch (opr) {
396
396
+
.implied => 0x08,
397
397
+
else => null,
398
398
+
},
399
399
+
.plp => switch (opr) {
400
400
+
.implied => 0x28,
380
401
else => null,
381
402
},
382
403
};
+96
src/tests/cpu/stack.zig
···
1
1
+
//! Stack-related tests.
2
2
+
const std = @import("std");
3
3
+
const cpu = @import("../cpu.zig");
4
4
+
5
5
+
test "PHA/PLA" {
6
6
+
var z = cpu.testZes(.{ .rom = &.{
7
7
+
.{
8
8
+
0x8000, cpu.assemble(
9
9
+
\\LDA #$89
10
10
+
\\PHA
11
11
+
\\LDA #0
12
12
+
\\PLA
13
13
+
),
14
14
+
},
15
15
+
} });
16
16
+
cpu.run(&z); // Setup A register
17
17
+
18
18
+
try std.testing.expectEqual(0xfd, z.cpu.sp);
19
19
+
cpu.run(&z); // PHA
20
20
+
try std.testing.expectEqual(0xfc, z.cpu.sp);
21
21
+
try std.testing.expectEqual(0x8003, z.cpu.pc);
22
22
+
try std.testing.expectEqual(0x89, z.ram[0x1fd]);
23
23
+
try std.testing.expect(z.cpu.status.negative);
24
24
+
try std.testing.expect(!z.cpu.status.zero);
25
25
+
26
26
+
cpu.run(&z); // Reset A register
27
27
+
try std.testing.expectEqual(0x8005, z.cpu.pc);
28
28
+
try std.testing.expectEqual(0x00, z.cpu.a);
29
29
+
try std.testing.expect(!z.cpu.status.negative);
30
30
+
try std.testing.expect(z.cpu.status.zero);
31
31
+
32
32
+
cpu.run(&z); // PLA
33
33
+
try std.testing.expectEqual(0x8006, z.cpu.pc);
34
34
+
try std.testing.expectEqual(0x89, z.cpu.a);
35
35
+
try std.testing.expectEqual(0xfd, z.cpu.sp);
36
36
+
try std.testing.expect(z.cpu.status.negative);
37
37
+
try std.testing.expect(!z.cpu.status.zero);
38
38
+
}
39
39
+
40
40
+
test "PHP/PLP" {
41
41
+
var z = cpu.testZes(.{ .rom = &.{
42
42
+
.{
43
43
+
0x8000, cpu.assemble(
44
44
+
\\CLI
45
45
+
\\SED
46
46
+
\\LDA #0
47
47
+
\\PHP
48
48
+
\\
49
49
+
\\SEI
50
50
+
\\CLD
51
51
+
\\LDA #$ff
52
52
+
\\PLP
53
53
+
),
54
54
+
},
55
55
+
} });
56
56
+
57
57
+
// Prepare flags
58
58
+
cpu.run(&z); // Clear I
59
59
+
cpu.run(&z); // Set D
60
60
+
cpu.run(&z); // Set Z
61
61
+
try std.testing.expect(!z.cpu.status.negative);
62
62
+
try std.testing.expect(z.cpu.status.zero);
63
63
+
try std.testing.expect(z.cpu.status.decimal);
64
64
+
try std.testing.expect(!z.cpu.status.irq_disabled);
65
65
+
66
66
+
try std.testing.expectEqual(0xfd, z.cpu.sp);
67
67
+
cpu.run(&z); // PHP
68
68
+
try std.testing.expectEqual(0xfc, z.cpu.sp);
69
69
+
70
70
+
try std.testing.expectEqual(0x8005, z.cpu.pc);
71
71
+
try std.testing.expectEqual(0b00111010, z.ram[0x1fd]);
72
72
+
73
73
+
// Reset flags
74
74
+
cpu.run(&z); // Set I
75
75
+
cpu.run(&z); // Clear D
76
76
+
cpu.run(&z); // Clear Z; Set N
77
77
+
try std.testing.expect(z.cpu.status.negative);
78
78
+
try std.testing.expect(!z.cpu.status.zero);
79
79
+
try std.testing.expect(!z.cpu.status.decimal);
80
80
+
try std.testing.expect(z.cpu.status.irq_disabled);
81
81
+
82
82
+
try std.testing.expectEqual(0xfc, z.cpu.sp);
83
83
+
cpu.runDebug(&z); // PLP
84
84
+
try std.testing.expectEqual(0xfd, z.cpu.sp);
85
85
+
86
86
+
try std.testing.expectEqual(0x800a, z.cpu.pc);
87
87
+
88
88
+
// Check if flags are restored
89
89
+
try std.testing.expect(!z.cpu.status.negative);
90
90
+
try std.testing.expect(z.cpu.status.zero);
91
91
+
try std.testing.expect(z.cpu.status.decimal);
92
92
+
try std.testing.expect(!z.cpu.status.irq_disabled);
93
93
+
94
94
+
// BRK should not be restored.
95
95
+
try std.testing.expect(!z.cpu.status.brk);
96
96
+
}