+130
src/Cpu.zig
+130
src/Cpu.zig
···
771
771
inline fn rti(self: *Cpu, pins: *zesty.Pins) void {
772
772
switch (self.cycle) {
773
773
0 => {
774
+
// Dummy read
774
775
pins.cpu_addr = self.pc;
775
776
},
776
777
1 => {
···
807
808
}
808
809
}
809
810
811
+
/// Jump (JMP)
812
+
inline fn jmp(self: *Cpu, pins: *zesty.Pins) void {
813
+
switch (self.cycle) {
814
+
0 => {
815
+
self.pc +%= 1;
816
+
pins.cpu_addr = self.pc;
817
+
},
818
+
1 => {
819
+
self.pc +%= 1;
820
+
pins.cpu_addr = self.pc;
821
+
self.hilo = .{ .lo = pins.cpu_data };
822
+
},
823
+
else => {
824
+
self.hilo.hi = pins.cpu_data;
825
+
self.fetchAt(pins, self.hilo.addr());
826
+
},
827
+
}
828
+
}
829
+
830
+
/// Jump indirect (JMP)
831
+
inline fn jmpInd(self: *Cpu, pins: *zesty.Pins) void {
832
+
switch (self.cycle) {
833
+
0 => {
834
+
self.pc +%= 1;
835
+
pins.cpu_addr = self.pc;
836
+
},
837
+
1 => {
838
+
// Fetch indirect lo
839
+
self.pc +%= 1;
840
+
pins.cpu_addr = self.pc;
841
+
self.hilo = .{ .lo = pins.cpu_data };
842
+
},
843
+
2 => {
844
+
// Fetch indirect hi
845
+
self.hilo.hi = pins.cpu_data;
846
+
pins.cpu_addr = self.hilo.addr();
847
+
},
848
+
3 => {
849
+
// Fetch target lo
850
+
self.hilo.lo +%= 1;
851
+
pins.cpu_addr = self.hilo.addr();
852
+
self.hilo = .{ .lo = pins.cpu_data };
853
+
},
854
+
else => {
855
+
// Fetch target hi
856
+
self.hilo.hi = pins.cpu_data;
857
+
self.fetchAt(pins, self.hilo.addr());
858
+
},
859
+
}
860
+
}
861
+
862
+
/// Jump to subroutine (JSR)
863
+
inline fn jsr(self: *Cpu, pins: *zesty.Pins) void {
864
+
const pc: Addr = .from(self.pc);
865
+
const stack: Addr = .stack(self.sp);
866
+
867
+
switch (self.cycle) {
868
+
0 => {
869
+
self.pc +%= 1;
870
+
pins.cpu_addr = self.pc;
871
+
},
872
+
1 => {
873
+
// Fetch target lo
874
+
self.hilo = .{ .lo = pins.cpu_data };
875
+
pins.cpu_addr = stack.addr();
876
+
},
877
+
2 => {
878
+
// Store PC hi
879
+
pins.cpu_addr = stack.addr();
880
+
pins.cpu_data = pc.hi;
881
+
pins.cpu_rw = .write;
882
+
self.sp -%= 1;
883
+
},
884
+
3 => {
885
+
// Store PC lo
886
+
pins.cpu_addr = stack.addr();
887
+
pins.cpu_data = pc.lo;
888
+
pins.cpu_rw = .write;
889
+
self.sp -%= 1;
890
+
},
891
+
4 => {
892
+
// Reposition back to PC
893
+
self.pc +%= 1;
894
+
pins.cpu_addr = self.pc;
895
+
},
896
+
else => {
897
+
// Fetch target hi
898
+
self.hilo.hi = pins.cpu_data;
899
+
self.fetchAt(pins, self.hilo.addr());
900
+
},
901
+
}
902
+
}
903
+
904
+
/// RTS (return from subroutine)
905
+
inline fn rts(self: *Cpu, pins: *zesty.Pins) void {
906
+
const stack: Addr = .stack(self.sp);
907
+
switch (self.cycle) {
908
+
0 => {
909
+
// Dummy read
910
+
pins.cpu_addr = self.pc;
911
+
},
912
+
1 => {
913
+
// Dummy read
914
+
pins.cpu_addr = stack.addr();
915
+
self.sp +%= 1;
916
+
},
917
+
2 => {
918
+
// Dummy read
919
+
pins.cpu_addr = stack.addr();
920
+
self.sp +%= 1;
921
+
},
922
+
3 => {
923
+
// Pop PC lo
924
+
self.hilo = .{ .lo = pins.cpu_data };
925
+
pins.cpu_addr = stack.addr();
926
+
},
927
+
4 => {
928
+
// Pop PC hi
929
+
self.hilo.hi = pins.cpu_data;
930
+
self.pc = self.hilo.addr();
931
+
pins.cpu_addr = self.pc;
932
+
self.pc +%= 1;
933
+
},
934
+
else => {
935
+
self.fetch(pins);
936
+
},
937
+
}
938
+
}
939
+
810
940
//------------------------------------------------------
811
941
// Helpers
812
942
+3
-5
src/tests/cpu/assembler.zig
+3
-5
src/tests/cpu/assembler.zig
···
42
42
.absolute,
43
43
.absolute_x,
44
44
.absolute_y,
45
-
.indexed,
45
+
.indirect,
46
46
=> |v| try writer.writeInt(u16, v, .little),
47
47
}
48
48
}
···
431
431
absolute_x: u16,
432
432
absolute_y: u16,
433
433
immediate: u8,
434
-
indexed: u16,
434
+
indirect: u16,
435
435
indexed_indirect: u8,
436
436
indirect_indexed: u8,
437
437
···
509
509
};
510
510
}
511
511
512
-
if (r_paren != s.len) return error.InvalidSyntax;
513
-
514
512
// (ind)
515
513
return .{
516
-
.indexed = try parseInt(u16, std.mem.trim(
514
+
.indirect = try parseInt(u16, std.mem.trim(
517
515
u8,
518
516
s[1..r_paren],
519
517
whitespace,
+92
-1
src/tests/cpu/control_flow.zig
+92
-1
src/tests/cpu/control_flow.zig
···
114
114
}
115
115
116
116
test "branches" {
117
-
// TODO: Test overflow as well
118
117
var z = cpu.testZes(.{ .rom = &.{
119
118
.{
120
119
0x8000, cpu.assemble(
···
159
158
z.stepCpu(); // BMI
160
159
z.stepCpu(); // BMI
161
160
try std.testing.expectEqual(0x800c, z.cpu.pc);
161
+
162
+
cpu.run(&z); // BIT
163
+
try std.testing.expectEqual(0x800e, z.cpu.pc);
164
+
try std.testing.expect(z.cpu.status.overflow);
165
+
166
+
z.stepCpu(); // BVS
167
+
z.stepCpu(); // BVS
168
+
z.stepCpu(); // BVS
169
+
try std.testing.expectEqual(0x8011, z.cpu.pc);
170
+
171
+
// Never hit BRK
172
+
try std.testing.expect(!z.cpu.status.brk);
173
+
}
174
+
175
+
test "subroutines" {
176
+
var z = cpu.testZes(.{ .rom = &.{
177
+
.{
178
+
0x8000, cpu.assemble(
179
+
\\jsr $8007
180
+
\\cmp $8008
181
+
\\brk
182
+
\\lda #$80
183
+
\\rts
184
+
),
185
+
},
186
+
} });
187
+
188
+
try std.testing.expectEqual(0xfd, z.cpu.sp);
189
+
cpu.run(&z); // JSR
190
+
try std.testing.expectEqual(0x8007, z.cpu.pc);
191
+
try std.testing.expectEqual(0xfb, z.cpu.sp);
192
+
try std.testing.expectEqual(0x01, z.ram[0x1fc]);
193
+
try std.testing.expectEqual(0x80, z.ram[0x1fd]);
194
+
195
+
cpu.run(&z); // LDA
196
+
try std.testing.expectEqual(0x8009, z.cpu.pc);
197
+
try std.testing.expectEqual(0x80, z.cpu.a);
198
+
try std.testing.expect(z.cpu.status.negative);
199
+
200
+
// TODO: figure out why the run harness runs two commands at once
201
+
cpu.run(&z); // RTS, CMP
202
+
try std.testing.expectEqual(0x8006, z.cpu.pc);
203
+
try std.testing.expectEqual(0xfd, z.cpu.sp);
204
+
try std.testing.expect(z.cpu.status.zero); // Should be equal
205
+
206
+
// Never hit BRK
207
+
try std.testing.expect(!z.cpu.status.brk);
208
+
}
209
+
210
+
test "JMP" {
211
+
var z = cpu.testZes(.{ .rom = &.{
212
+
.{
213
+
0x8000, cpu.assemble(
214
+
\\jmp $8004
215
+
\\brk
216
+
\\lda $8000
217
+
),
218
+
},
219
+
} });
220
+
221
+
cpu.run(&z); // JMP
222
+
try std.testing.expectEqual(0x8004, z.cpu.pc);
223
+
224
+
cpu.run(&z); // LDA
225
+
try std.testing.expectEqual(0x8007, z.cpu.pc);
226
+
try std.testing.expectEqual(0x4c, z.cpu.a);
227
+
228
+
// Never hit BRK
229
+
try std.testing.expect(!z.cpu.status.brk);
230
+
}
231
+
232
+
test "JMP indirect" {
233
+
var z = cpu.testZes(.{ .rom = &.{
234
+
.{
235
+
0x8000, cpu.assemble(
236
+
\\jmp ($aabb)
237
+
\\brk
238
+
\\lda $8000
239
+
),
240
+
},
241
+
.{ 0xaabb, &.{ 0x04, 0x80 } },
242
+
} });
243
+
244
+
cpu.run(&z); // JMP
245
+
try std.testing.expectEqual(0x8004, z.cpu.pc);
246
+
247
+
cpu.run(&z); // LDA
248
+
try std.testing.expectEqual(0x8007, z.cpu.pc);
249
+
try std.testing.expectEqual(0x6c, z.cpu.a);
250
+
251
+
// Never hit BRK
252
+
try std.testing.expect(!z.cpu.status.brk);
162
253
}