Skip to content

Commit 206a54a

Browse files
committed
aviron: Add comments to clarify push/pop semantics
1 parent 76ab9f6 commit 206a54a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+1792
-452
lines changed

sim/aviron/build.zig

Lines changed: 248 additions & 219 deletions
Large diffs are not rendered by default.

sim/aviron/linker.ld

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ SECTIONS
1010
{
1111
KEEP(*(.vectors))
1212

13+
/* Ensure _start comes first */
14+
KEEP(*(.text._start))
15+
1316
*(.text*)
1417
} > flash
1518

sim/aviron/samples/math.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
const std = @import("std");
22

3-
pub export fn _start() callconv(.c) noreturn {
3+
export fn _start() callconv(.c) noreturn {
44
var a: usize = 1 + 2;
55

66
for (0..10) |p| {

sim/aviron/src/lib/Cpu.zig

Lines changed: 133 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ sio: SpecialIoRegisters,
5959
flash: Flash,
6060
data: DataBus,
6161
io: IOBus,
62+
sram_base: u16 = 0,
63+
sram_size: u16 = 0,
64+
eeprom_size: u16 = 0,
6265

6366
// State
6467
pc: u24 = 0,
@@ -84,6 +87,110 @@ pub const RunResult = enum {
8487
program_exit,
8588
};
8689

90+
pub fn dump_system_state(cpu: *Cpu) void {
91+
// Dump complete system state
92+
std.debug.print("\n=== SYSTEM STATE DUMP ===\n", .{});
93+
std.debug.print("PC: 0x{X:0>4}\n", .{cpu.pc});
94+
std.debug.print("SP: 0x{X:0>4}\n", .{cpu.get_sp()});
95+
std.debug.print("SREG: {f}\n", .{cpu.sreg});
96+
97+
// Dump X, Y, Z pointer registers
98+
const x_reg = (@as(u16, cpu.regs[27]) << 8) | cpu.regs[26];
99+
const y_reg = (@as(u16, cpu.regs[29]) << 8) | cpu.regs[28];
100+
const z_reg = (@as(u16, cpu.regs[31]) << 8) | cpu.regs[30];
101+
std.debug.print("\nPOINTER REGISTERS:\n", .{});
102+
std.debug.print("X (r27:r26): 0x{X:0>4}\n", .{x_reg});
103+
std.debug.print("Y (r29:r28): 0x{X:0>4}\n", .{y_reg});
104+
std.debug.print("Z (r31:r30): 0x{X:0>4}\n", .{z_reg});
105+
106+
// Dump all registers
107+
std.debug.print("\nREGISTERS:\n", .{});
108+
for (cpu.regs, 0..) |reg, i| {
109+
if (i % 8 == 0) std.debug.print("r{d:0>2}-r{d:0>2}: ", .{ i, @min(i + 7, 31) });
110+
std.debug.print("{X:0>2} ", .{reg});
111+
if ((i + 1) % 8 == 0) std.debug.print("\n", .{});
112+
}
113+
114+
// Dump SRAM using absolute addresses based on configured base
115+
const sram_end: u16 = cpu.sram_base + @as(u16, @truncate(cpu.sram_size - 1));
116+
std.debug.print("\nSRAM DUMP (0x{X:0>4}-0x{X:0>4}, {d} bytes):\n", .{ cpu.sram_base, sram_end, cpu.sram_size });
117+
118+
const row_width = 16;
119+
var prev_row: ?[row_width]u8 = null;
120+
var prev_len: usize = 0;
121+
var elided = false;
122+
123+
var i: usize = 0;
124+
while (i < cpu.sram_size) : (i += @min(row_width, cpu.sram_size - i)) {
125+
const row_len: usize = @min(row_width, cpu.sram_size - i);
126+
var cur_row: [row_width]u8 = undefined;
127+
128+
var j: usize = 0;
129+
while (j < row_len) : (j += 1) {
130+
cur_row[j] = cpu.data.read(cpu.sram_base + @as(u16, @intCast(i + j)));
131+
}
132+
133+
// Only elide repeated lines of all zeroes
134+
const is_all_zeros = blk: {
135+
for (cur_row[0..row_len]) |byte| {
136+
if (byte != 0) break :blk false;
137+
}
138+
break :blk true;
139+
};
140+
const same_as_prev = if (prev_row) |prev|
141+
prev_len == row_len and std.mem.eql(u8, prev[0..prev_len], cur_row[0..row_len])
142+
else
143+
false;
144+
if (same_as_prev and is_all_zeros) {
145+
elided = true;
146+
} else {
147+
if (elided) {
148+
std.debug.print("*\n", .{});
149+
elided = false;
150+
}
151+
152+
std.debug.print("0x{X:0>4}: ", .{cpu.sram_base + i});
153+
// hex bytes
154+
j = 0;
155+
while (j < row_len) : (j += 1) {
156+
std.debug.print("{X:0>2} ", .{cur_row[j]});
157+
}
158+
159+
// pad hex area for short rows to align ASCII column
160+
if (row_len < row_width) {
161+
var pad: usize = row_width - row_len;
162+
while (pad > 0) : (pad -= 1) {
163+
std.debug.print(" ", .{});
164+
}
165+
}
166+
167+
// ASCII representation
168+
std.debug.print(" |", .{});
169+
j = 0;
170+
while (j < row_len) : (j += 1) {
171+
const b = cur_row[j];
172+
if (b >= 32 and b <= 126) {
173+
std.debug.print("{c}", .{b});
174+
} else {
175+
std.debug.print(".", .{});
176+
}
177+
}
178+
std.debug.print("|\n", .{});
179+
180+
// store as previous row
181+
if (prev_row == null) {
182+
prev_row = [_]u8{0} ** row_width;
183+
}
184+
@memcpy(prev_row.?[0..row_len], cur_row[0..row_len]);
185+
prev_len = row_len;
186+
}
187+
}
188+
if (elided)
189+
std.debug.print("*\n", .{});
190+
191+
std.debug.print("=== END DUMP ===\n", .{});
192+
}
193+
87194
pub fn run(cpu: *Cpu, mileage: ?u64, breakpoint: ?u24) RunError!RunResult {
88195
var rest_gas = mileage;
89196

@@ -172,12 +279,29 @@ fn shift_program_counter(cpu: *Cpu, by: i12) void {
172279
}
173280

174281
fn fetch_code(cpu: *Cpu) u16 {
175-
const value = cpu.flash.read(cpu.pc);
282+
// Program memory is word-addressed; PC is in words.
283+
const value: u16 = cpu.flash.read(@intCast(cpu.pc));
176284
cpu.pc +%= 1; // increment with wraparound
177285
cpu.pc &= @intFromEnum(cpu.code_model); // then wrap to lower bit size
178286
return value;
179287
}
180288

289+
inline fn data_read(cpu: *Cpu, addr: u24) u8 {
290+
return cpu.data.read8(addr) catch |e| switch (e) {
291+
error.Unmapped => @panic("Read from unmapped memory address"),
292+
error.OutOfRange => @panic("Read out of range"),
293+
error.ReadOnly => @panic("ReadOnly error on read"),
294+
};
295+
}
296+
297+
inline fn data_write(cpu: *Cpu, addr: u24, value: u8) void {
298+
cpu.data.write8(addr, value) catch |e| switch (e) {
299+
error.Unmapped => @panic("Write to unmapped memory address"),
300+
error.OutOfRange => @panic("Write out of range"),
301+
error.ReadOnly => @panic("Write to read-only memory"),
302+
};
303+
}
304+
181305
fn push(cpu: *Cpu, val: u8) void {
182306
// AVR PUSH: Write to [SP] first, then decrement SP
183307
// SP points to the first unused location
@@ -213,6 +337,7 @@ fn push_code_loc(cpu: *Cpu, val: u24) void {
213337
fn pop_code_loc(cpu: *Cpu) u24 {
214338
const mask = @intFromEnum(cpu.code_model);
215339

340+
// With increment-then-read POP, we pop most significant byte first.
216341
var pc: u24 = 0;
217342
if ((mask & 0xFF0000) != 0) {
218343
pc |= (@as(u24, cpu.pop()) << 16);
@@ -242,11 +367,11 @@ fn read_wide_reg(cpu: *Cpu, comptime reg: WideReg, comptime mode: IndexRegReadMo
242367
switch (mode) {
243368
.raw => 0,
244369
.ramp => switch (reg) {
245-
.x => if (cpu.sio.ramp_x) |ramp| cpu.io.read(ramp) else 0,
246-
.y => if (cpu.sio.ramp_y) |ramp| cpu.io.read(ramp) else 0,
247-
.z => if (cpu.sio.ramp_z) |ramp| cpu.io.read(ramp) else 0,
370+
.x => if (cpu.sio.ramp_x) |ramp| (cpu.io.read(@intCast(ramp))) else 0,
371+
.y => if (cpu.sio.ramp_y) |ramp| (cpu.io.read(@intCast(ramp))) else 0,
372+
.z => if (cpu.sio.ramp_z) |ramp| (cpu.io.read(@intCast(ramp))) else 0,
248373
},
249-
.eind => if (cpu.sio.e_ind) |e_ind| cpu.io.read(e_ind) else 0,
374+
.eind => if (cpu.sio.e_ind) |e_ind| (cpu.io.read(@intCast(e_ind))) else 0,
250375
},
251376
cpu.regs[reg.base() + 1],
252377
cpu.regs[reg.base() + 0],
@@ -778,7 +903,7 @@ const instructions = struct {
778903
/// Loads data from the I/O Space (Ports, Timers, Configuration Registers, etc.) into register Rd in the Register File.
779904
inline fn in(cpu: *Cpu, info: isa.opinfo.a6d5) void {
780905
// Rd ← I/O(A)
781-
cpu.regs[info.d.num()] = cpu.io.read(info.a);
906+
cpu.regs[info.d.num()] = cpu.io.read(@intCast(info.a));
782907
}
783908

784909
/// CBI – Clear Bit in I/O Register
@@ -828,7 +953,7 @@ const instructions = struct {
828953
/// instruction operates on the lower 32 I/O Registers – addresses 0-31.
829954
inline fn sbic(cpu: *Cpu, info: isa.opinfo.a5b3) void {
830955
// If I/O(A,b) = 0 then PC ← PC + 2 (or 3) else PC ← PC + 1
831-
const val = cpu.io.read(info.a);
956+
const val = cpu.io.read(@intCast(info.a));
832957
if ((val & info.b.mask()) == 0) {
833958
cpu.instr_effect = .skip_next;
834959
}
@@ -839,7 +964,7 @@ const instructions = struct {
839964
/// instruction operates on the lower 32 I/O Registers – addresses 0-31.
840965
inline fn sbis(cpu: *Cpu, info: isa.opinfo.a5b3) void {
841966
// If I/O(A,b) = 1 then PC ← PC + 2 (or 3) else PC ← PC + 1
842-
const val = cpu.io.read(info.a);
967+
const val = cpu.io.read(@intCast(info.a));
843968
if ((val & info.b.mask()) != 0) {
844969
cpu.instr_effect = .skip_next;
845970
}

0 commit comments

Comments
 (0)