microzig
microzig copied to clipboard
Add support for mappers
Some mcus, e.g. the atxmega, map peripherals to different parts of the address space. For example, the RAM starts at 0x2000, and EEPROM starts at 0x1000.
This can be supported by adding a 'base' field to the type contructor to adjust incoming r/w addresses. A mapper will be needed to send the requests to the correct peripheral. For example, the way I hack it in:
if (address >= cpu.sram.base) {
const val = cpu.sram.read(address);
cpu.regs[d.num()] = val;
} else if (address >= cpu.eeprom.base) {
cpu.regs[d.num()] = cpu.eeprom.read(@truncate(address));
} else {
const val = cpu.io.read(@truncate(address));
cpu.regs[d.num()] = val;
}
And the way I support a memory base:
pub fn Static(comptime size: comptime_int, comptime base: usize) type {
return struct {
const Self = @This();
data: [size]u8 align(2) = .{0} ** size,
base: usize = base,
pub fn memory(self: *Self) RAM {
return RAM{
.ctx = self,
.vtable = &vtable,
.size = size,
.base = base,
};
}
pub const vtable = VTable{
.readFn = memRead,
.writeFn = memWrite,
};
fn memRead(ctx: ?*anyopaque, addr: Address) u8 {
const mem: *Self = @ptrCast(@alignCast(ctx.?));
return mem.data[addr - mem.base];
}
fn memWrite(ctx: ?*anyopaque, addr: Address, value: u8) void {
const mem: *Self = @ptrCast(@alignCast(ctx.?));
mem.data[addr - mem.base] = value;
}
};
}