Skip to content

Commit 9949d83

Browse files
Grazfathermattnite
authored andcommitted
Separate interfaces for IO and data bus
1 parent 7fa0dbb commit 9949d83

File tree

5 files changed

+77
-72
lines changed

5 files changed

+77
-72
lines changed

sim/aviron/src/lib/Cpu.zig

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -261,9 +261,9 @@ fn write_wide_reg(cpu: *Cpu, reg: WideReg, value: u24, comptime mode: IndexRegWr
261261
cpu.regs[reg.base() + 1] = parts[1];
262262
if (mode == .ramp) {
263263
switch (reg) {
264-
.x => if (cpu.sio.ramp_x) |ramp| cpu.io.write(ramp, parts[2]),
265-
.y => if (cpu.sio.ramp_y) |ramp| cpu.io.write(ramp, parts[2]),
266-
.z => if (cpu.sio.ramp_z) |ramp| cpu.io.write(ramp, parts[2]),
264+
.x => if (cpu.sio.ramp_x) |ramp| cpu.io.write(ramp, 0xFF, parts[2]),
265+
.y => if (cpu.sio.ramp_y) |ramp| cpu.io.write(ramp, 0xFF, parts[2]),
266+
.z => if (cpu.sio.ramp_z) |ramp| cpu.io.write(ramp, 0xFF, parts[2]),
267267
}
268268
}
269269
}
@@ -771,7 +771,7 @@ const instructions = struct {
771771
/// Stores data from register Rr in the Register File to I/O Space (Ports, Timers, Configuration Registers, etc.).
772772
inline fn out(cpu: *Cpu, info: isa.opinfo.a6r5) void {
773773
// I/O(A) ← Rr
774-
cpu.io.write(info.a, cpu.regs[info.r.num()]);
774+
cpu.io.write(info.a, 0xFF, cpu.regs[info.r.num()]);
775775
}
776776

777777
/// IN - Load an I/O Location to Register
@@ -786,14 +786,14 @@ const instructions = struct {
786786
/// addresses 0-31.
787787
inline fn cbi(cpu: *Cpu, info: isa.opinfo.a5b3) void {
788788
// I/O(A,b) ← 0
789-
cpu.io.write_masked(info.a, info.b.mask(), 0x00);
789+
cpu.io.write(info.a, info.b.mask(), 0x00);
790790
}
791791

792792
/// SBI – Set Bit in I/O Register
793793
/// Sets a specified bit in an I/O Register. This instruction operates on the lower 32 I/O Registers – addresses 0-31.
794794
inline fn sbi(cpu: *Cpu, info: isa.opinfo.a5b3) void {
795795
// I/O(A,b) ← 1
796-
cpu.io.write_masked(info.a, info.b.mask(), 0xFF);
796+
cpu.io.write(info.a, info.b.mask(), 0xFF);
797797
}
798798

799799
// Branching:
@@ -1662,8 +1662,8 @@ fn get_sp(cpu: *Cpu) u16 {
16621662
fn set_sp(cpu: *Cpu, value: u16) void {
16631663
const lo: u8 = @truncate(value >> 0);
16641664
const hi: u8 = @truncate(value >> 8);
1665-
cpu.io.write(cpu.sio.sp_l, lo);
1666-
cpu.io.write(cpu.sio.sp_h, hi);
1665+
cpu.io.write(cpu.sio.sp_l, 0xFF, lo);
1666+
cpu.io.write(cpu.sio.sp_h, 0xFF, hi);
16671667
}
16681668

16691669
fn compose24(hi: u8, mid: u8, lo: u8) u24 {

sim/aviron/src/lib/aviron.zig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ pub const Cpu = @import("Cpu.zig");
88
pub const Flash = bus.Flash;
99
pub const IO = bus.IO;
1010
pub const Bus = bus.Bus;
11+
pub const IOBus = bus.IOBus;
1112

1213
pub const FixedSizedMemory = bus.FixedSizedMemory;
1314

sim/aviron/src/lib/bus.zig

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -122,11 +122,42 @@ pub const BusConfig = struct {
122122
address_type: type,
123123
};
124124

125+
/// IO Bus interface with masked writes as the primary write method
126+
pub fn IOBusInterface(comptime address_type: type) type {
127+
return struct {
128+
const Self = @This();
129+
pub const Address = address_type;
130+
131+
ctx: *anyopaque,
132+
vtable: *const VTable,
133+
134+
pub const VTable = struct {
135+
read: *const fn (ctx: *anyopaque, addr: Address) u8,
136+
write: *const fn (ctx: *anyopaque, addr: Address, mask: u8, v: u8) void,
137+
check_exit: ?*const fn (ctx: *anyopaque) ?u8 = null,
138+
};
139+
140+
pub fn read(self: *const Self, addr: Address) u8 {
141+
return self.vtable.read(self.ctx, addr);
142+
}
143+
144+
/// Write with mask - this is the primary write interface for IO
145+
/// `mask` determines which bits of `value` are written.
146+
pub fn write(self: *const Self, addr: Address, mask: u8, v: u8) void {
147+
self.vtable.write(self.ctx, addr, mask, v);
148+
}
149+
150+
pub fn check_exit(self: *const Self) ?u8 {
151+
if (self.vtable.check_exit) |f| return f(self.ctx) else return null;
152+
}
153+
};
154+
}
155+
125156
/// Standard data bus using 24-bit addressing (for AVR data space)
126157
pub const DataBus = Bus(.{ .address_type = u24 });
127158

128159
/// Standard IO bus using 12-bit addressing (for AVR IO space)
129-
pub const IOBus = Bus(.{ .address_type = IO.Address });
160+
pub const IOBus = IOBusInterface(IO.Address);
130161

131162
/// Unified byte-addressable bus interface used by MemoryMapping for RAM and IO
132163
pub fn Bus(comptime config: BusConfig) type {
@@ -140,7 +171,6 @@ pub fn Bus(comptime config: BusConfig) type {
140171
pub const VTable = struct {
141172
read: *const fn (ctx: *anyopaque, addr: Address) u8,
142173
write: *const fn (ctx: *anyopaque, addr: Address, v: u8) void,
143-
write_masked: *const fn (ctx: *anyopaque, addr: Address, mask: u8, v: u8) void,
144174
check_exit: ?*const fn (ctx: *anyopaque) ?u8 = null,
145175
};
146176

@@ -152,10 +182,6 @@ pub fn Bus(comptime config: BusConfig) type {
152182
self.vtable.write(self.ctx, addr, v);
153183
}
154184

155-
pub fn write_masked(self: *const Self, addr: Address, mask: u8, v: u8) void {
156-
self.vtable.write_masked(self.ctx, addr, mask, v);
157-
}
158-
159185
pub fn check_exit(self: *const Self) ?u8 {
160186
if (self.vtable.check_exit) |f| return f(self.ctx) else return null;
161187
}
@@ -182,7 +208,6 @@ pub fn FixedSizedMemory(comptime size: comptime_int, comptime bus_config: ?BusCo
182208
pub const bus_vtable = BusType.VTable{
183209
.read = read,
184210
.write = write,
185-
.write_masked = write_masked,
186211
.check_exit = null,
187212
};
188213

@@ -197,13 +222,6 @@ pub fn FixedSizedMemory(comptime size: comptime_int, comptime bus_config: ?BusCo
197222
std.debug.assert(addr < size);
198223
mem.data[addr] = value;
199224
}
200-
201-
fn write_masked(ctx: *anyopaque, addr: AddressType, mask: u8, value: u8) void {
202-
const mem: *Self = @ptrCast(@alignCast(ctx));
203-
std.debug.assert(addr < size);
204-
const old = mem.data[addr];
205-
mem.data[addr] = (old & ~mask) | (value & mask);
206-
}
207225
};
208226
}
209227

@@ -273,14 +291,6 @@ pub fn MemoryMapping(comptime BusType: type) type {
273291
return;
274292
}
275293

276-
pub fn write_masked(self: *const Self, addr: BusType.Address, mask: u8, v: u8) AccessError!void {
277-
const seg = self.find(addr) orelse return error.Unmapped;
278-
const idx = addr - seg.at;
279-
if (idx >= seg.size) return error.OutOfRange;
280-
seg.backend.write_masked(idx, mask, v);
281-
return;
282-
}
283-
284294
fn find(self: *const Self, addr: BusType.Address) ?*const Segment {
285295
// Linear scan is fine initially; segments are sorted.
286296
for (self.segments) |*s| {
@@ -301,7 +311,6 @@ pub fn MemoryMapping(comptime BusType: type) type {
301311
const bus_vtable = BusType.VTable{
302312
.read = bus_read,
303313
.write = bus_write,
304-
.write_masked = bus_write_masked,
305314
.check_exit = bus_check_exit,
306315
};
307316

@@ -323,15 +332,6 @@ pub fn MemoryMapping(comptime BusType: type) type {
323332
};
324333
}
325334

326-
fn bus_write_masked(ctx: *anyopaque, addr: BusType.Address, mask: u8, v: u8) void {
327-
const self: *const Self = @ptrCast(@alignCast(ctx));
328-
self.write_masked(addr, mask, v) catch |e| switch (e) {
329-
error.Unmapped => @panic("Masked write to unmapped memory address"),
330-
error.OutOfRange => @panic("Masked write out of range"),
331-
error.ReadOnly => @panic("Masked write to read-only memory"),
332-
};
333-
}
334-
335335
fn bus_check_exit(ctx: *anyopaque) ?u8 {
336336
const self: *const Self = @ptrCast(@alignCast(ctx));
337337
// Check all segments for exit condition

sim/aviron/src/main.zig

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -220,8 +220,9 @@ const IO = struct {
220220
// Exit status tracking
221221
exit_code: ?u8 = null,
222222

223-
const IOBusType = @import("aviron").Bus(.{ .address_type = @import("aviron").IO.Address });
224-
const DataBusType = @import("aviron").Bus(.{ .address_type = u24 });
223+
const aviron_module = @import("aviron");
224+
const IOBusType = aviron_module.IOBus;
225+
const DataBusType = aviron_module.Bus(.{ .address_type = u24 });
225226

226227
pub fn bus(self: *IO) DataBusType {
227228
return .{ .ctx = self, .vtable = &data_bus_vtable };
@@ -234,14 +235,12 @@ const IO = struct {
234235
const data_bus_vtable = DataBusType.VTable{
235236
.read = dev_read_data,
236237
.write = dev_write_data,
237-
.write_masked = dev_write_masked_data,
238238
.check_exit = dev_check_exit,
239239
};
240240

241241
const io_bus_vtable = IOBusType.VTable{
242242
.read = dev_read,
243-
.write = dev_write,
244-
.write_masked = dev_write_masked,
243+
.write = dev_write_masked,
245244
.check_exit = dev_check_exit,
246245
};
247246

@@ -318,17 +317,9 @@ const IO = struct {
318317
};
319318
}
320319

321-
/// `mask` determines which bits of `value` are written. To write everything, use `0xFF` for `mask`.
322320
fn dev_write_data(ctx: *anyopaque, addr: DataBusType.Address, value: u8) void {
323-
dev_write_masked_data(ctx, addr, 0xFF, value);
324-
}
325-
326-
fn dev_write_masked_data(ctx: *anyopaque, addr: DataBusType.Address, mask: u8, value: u8) void {
327-
dev_write_masked(ctx, @intCast(addr), mask, value);
328-
}
329-
330-
fn dev_write(ctx: *anyopaque, addr: IOBusType.Address, value: u8) void {
331-
dev_write_masked(ctx, addr, 0xFF, value);
321+
// Data bus writes full bytes (mask = 0xFF)
322+
dev_write_masked(ctx, @intCast(addr), 0xFF, value);
332323
}
333324

334325
fn dev_write_masked(ctx: *anyopaque, addr: IOBusType.Address, mask: u8, value: u8) void {

sim/aviron/src/testrunner.zig

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ const SystemState = struct {
1717

1818
// Emulate Atmega382p device size:
1919
flash_storage: aviron.Flash.Static(32768) = .{},
20-
sram: aviron.FixedSizedMemory(2048) = .{},
21-
eeprom: aviron.FixedSizedMemory(1024) = .{},
20+
sram: aviron.FixedSizedMemory(2048, .{ .address_type = u24 }) = .{},
21+
eeprom: aviron.FixedSizedMemory(1024, null) = .{},
2222
io: IO,
2323

2424
config: testconfig.TestSuiteConfig,
@@ -167,7 +167,7 @@ pub fn main() !u8 {
167167

168168
.flash = test_system.flash_storage.memory(),
169169
.data = spaces.data.bus(),
170-
.io = spaces.io.bus(),
170+
.io = test_system.io.io_bus(),
171171

172172
.code_model = .code16,
173173

@@ -278,17 +278,26 @@ const IO = struct {
278278
// Exit status tracking
279279
exit_code: ?u8 = null,
280280

281-
pub fn bus(self: *IO) aviron.Bus {
282-
return aviron.Bus{
283-
.ctx = self,
284-
.vtable = &bus_vtable,
285-
};
281+
const DataBusType = aviron.Bus(.{ .address_type = u24 });
282+
const IOBusType = aviron.IOBus;
283+
284+
pub fn bus(self: *IO) DataBusType {
285+
return .{ .ctx = self, .vtable = &data_bus_vtable };
286286
}
287287

288-
const bus_vtable = aviron.Bus.VTable{
288+
pub fn io_bus(self: *IO) IOBusType {
289+
return .{ .ctx = self, .vtable = &io_bus_vtable };
290+
}
291+
292+
const data_bus_vtable = DataBusType.VTable{
293+
.read = dev_read_data,
294+
.write = dev_write_data,
295+
.check_exit = dev_check_exit,
296+
};
297+
298+
const io_bus_vtable = IOBusType.VTable{
289299
.read = dev_read,
290-
.write = dev_write,
291-
.write_masked = dev_write_masked,
300+
.write = dev_write_masked,
292301
.check_exit = dev_check_exit,
293302
};
294303

@@ -322,9 +331,13 @@ const IO = struct {
322331
_,
323332
};
324333

325-
fn dev_read(ctx: *anyopaque, addr: aviron.Bus.Address) u8 {
334+
fn dev_read_data(ctx: *anyopaque, addr: DataBusType.Address) u8 {
335+
return dev_read(ctx, @intCast(addr));
336+
}
337+
338+
fn dev_read(ctx: *anyopaque, addr: aviron.IO.Address) u8 {
326339
const io: *IO = @ptrCast(@alignCast(ctx));
327-
const reg: Register = @enumFromInt(@as(aviron.IO.Address, @intCast(addr)));
340+
const reg: Register = @enumFromInt(addr);
328341
return switch (reg) {
329342
.exit => 0,
330343
.stdio => if (io.stdin.len > 0) blk: {
@@ -360,14 +373,14 @@ const IO = struct {
360373
};
361374
}
362375

363-
fn dev_write(ctx: *anyopaque, addr: aviron.Bus.Address, value: u8) void {
364-
dev_write_masked(ctx, addr, 0xFF, value);
376+
fn dev_write_data(ctx: *anyopaque, addr: DataBusType.Address, value: u8) void {
377+
dev_write_masked(ctx, @intCast(addr), 0xFF, value);
365378
}
366379

367380
/// `mask` determines which bits of `value` are written. To write everything, use `0xFF` for `mask`.
368-
fn dev_write_masked(ctx: *anyopaque, addr: aviron.Bus.Address, mask: u8, value: u8) void {
381+
fn dev_write_masked(ctx: *anyopaque, addr: aviron.IO.Address, mask: u8, value: u8) void {
369382
const io: *IO = @ptrCast(@alignCast(ctx));
370-
const reg: Register = @enumFromInt(@as(aviron.IO.Address, @intCast(addr)));
383+
const reg: Register = @enumFromInt(addr);
371384
switch (reg) {
372385
.exit => {
373386
io.exit_code = value & mask;

0 commit comments

Comments
 (0)