style(eeprom): move eeprom code to it's own file
This commit is contained in:
parent
9baadadba2
commit
bfe97c671e
|
@ -2,6 +2,8 @@ const std = @import("std");
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
const log = std.log.scoped(.Backup);
|
const log = std.log.scoped(.Backup);
|
||||||
|
|
||||||
|
const Eeprom = @import("backup/eeprom.zig").Eeprom;
|
||||||
|
|
||||||
const escape = @import("../../util.zig").escape;
|
const escape = @import("../../util.zig").escape;
|
||||||
const span = @import("../../util.zig").span;
|
const span = @import("../../util.zig").span;
|
||||||
|
|
||||||
|
@ -55,7 +57,7 @@ pub const Backup = struct {
|
||||||
.title = title,
|
.title = title,
|
||||||
.save_path = path,
|
.save_path = path,
|
||||||
.flash = Flash.init(),
|
.flash = Flash.init(),
|
||||||
.eeprom = Eeprom.init(allocator),
|
.eeprom = Eeprom.create(allocator),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (backup.save_path) |p| backup.loadSaveFromDisk(allocator, p) catch |e| log.err("Failed to load save: {}", .{e});
|
if (backup.save_path) |p| backup.loadSaveFromDisk(allocator, p) catch |e| log.err("Failed to load save: {}", .{e});
|
||||||
|
@ -300,267 +302,3 @@ const Flash = struct {
|
||||||
return if (self.bank == 1) 0x10000 else @as(usize, 0);
|
return if (self.bank == 1) 0x10000 else @as(usize, 0);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const Eeprom = struct {
|
|
||||||
const Self = @This();
|
|
||||||
|
|
||||||
addr: u14,
|
|
||||||
|
|
||||||
kind: Kind,
|
|
||||||
state: State,
|
|
||||||
writer: Writer,
|
|
||||||
reader: Reader,
|
|
||||||
|
|
||||||
allocator: Allocator,
|
|
||||||
|
|
||||||
const Kind = enum {
|
|
||||||
Unknown,
|
|
||||||
Small, // 512B
|
|
||||||
Large, // 8KB
|
|
||||||
};
|
|
||||||
|
|
||||||
const State = enum {
|
|
||||||
Ready,
|
|
||||||
Read,
|
|
||||||
Write,
|
|
||||||
WriteTransfer,
|
|
||||||
RequestEnd,
|
|
||||||
};
|
|
||||||
|
|
||||||
fn init(allocator: Allocator) Self {
|
|
||||||
return .{
|
|
||||||
.kind = .Unknown,
|
|
||||||
.state = .Ready,
|
|
||||||
.writer = Writer.init(),
|
|
||||||
.reader = Reader.init(),
|
|
||||||
.addr = 0,
|
|
||||||
.allocator = allocator,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read(self: *Self) u1 {
|
|
||||||
return self.reader.read();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn dbgRead(self: *const Self) u1 {
|
|
||||||
return self.reader.dbgRead();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write(self: *Self, word_count: u16, buf: *[]u8, bit: u1) void {
|
|
||||||
if (self.guessKind(word_count)) |found| {
|
|
||||||
log.info("EEPROM Kind: {}", .{found});
|
|
||||||
self.kind = found;
|
|
||||||
|
|
||||||
// buf.len will not equal zero when a save file was found and loaded.
|
|
||||||
// Right now, we assume that the save file is of the correct size which
|
|
||||||
// isn't necessarily true, since we can't trust anything a user can influence
|
|
||||||
// TODO: use ?[]u8 instead of a 0-sized slice?
|
|
||||||
if (buf.len == 0) {
|
|
||||||
const len: usize = switch (found) {
|
|
||||||
.Small => 0x200,
|
|
||||||
.Large => 0x2000,
|
|
||||||
else => unreachable,
|
|
||||||
};
|
|
||||||
|
|
||||||
buf.* = self.allocator.alloc(u8, len) catch |e| {
|
|
||||||
log.err("Failed to resize EEPROM buf to {} bytes", .{len});
|
|
||||||
std.debug.panic("EEPROM entered irrecoverable state {}", .{e});
|
|
||||||
};
|
|
||||||
std.mem.set(u8, buf.*, 0xFF);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self.state == .RequestEnd) {
|
|
||||||
if (bit != 0) log.debug("EEPROM Request did not end in 0u1. TODO: is this ok?", .{});
|
|
||||||
self.state = .Ready;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (self.state) {
|
|
||||||
.Ready => self.writer.requestWrite(bit),
|
|
||||||
.Read, .Write => self.writer.addressWrite(self.kind, bit),
|
|
||||||
.WriteTransfer => self.writer.dataWrite(bit),
|
|
||||||
.RequestEnd => unreachable, // We return early just above this block
|
|
||||||
}
|
|
||||||
|
|
||||||
self.tick(buf.*);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn guessKind(self: *const Self, word_count: u16) ?Kind {
|
|
||||||
if (self.kind != .Unknown or self.state != .Read) return null;
|
|
||||||
|
|
||||||
return switch (word_count) {
|
|
||||||
17 => .Large,
|
|
||||||
9 => .Small,
|
|
||||||
else => blk: {
|
|
||||||
log.err("Unexpected length of DMA3 Transfer upon initial EEPROM read: {}", .{word_count});
|
|
||||||
break :blk null;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn tick(self: *Self, buf: []u8) void {
|
|
||||||
switch (self.state) {
|
|
||||||
.Ready => {
|
|
||||||
if (self.writer.len() == 2) {
|
|
||||||
const req = @intCast(u2, self.writer.finish());
|
|
||||||
switch (req) {
|
|
||||||
0b11 => self.state = .Read,
|
|
||||||
0b10 => self.state = .Write,
|
|
||||||
else => log.err("Unknown EEPROM Request 0b{b:0>2}", .{req}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
.Read => {
|
|
||||||
switch (self.kind) {
|
|
||||||
.Large => {
|
|
||||||
if (self.writer.len() == 14) {
|
|
||||||
const addr = @intCast(u10, self.writer.finish());
|
|
||||||
const value = std.mem.readIntSliceLittle(u64, buf[@as(u13, addr) * 8 ..][0..8]);
|
|
||||||
|
|
||||||
self.reader.configure(value);
|
|
||||||
self.state = .RequestEnd;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
.Small => {
|
|
||||||
if (self.writer.len() == 6) {
|
|
||||||
// FIXME: Duplicated code from above
|
|
||||||
const addr = @intCast(u6, self.writer.finish());
|
|
||||||
const value = std.mem.readIntSliceLittle(u64, buf[@as(u13, addr) * 8 ..][0..8]);
|
|
||||||
|
|
||||||
self.reader.configure(value);
|
|
||||||
self.state = .RequestEnd;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
else => log.err("Unable to calculate EEPROM read address. EEPROM size UNKNOWN", .{}),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
.Write => {
|
|
||||||
switch (self.kind) {
|
|
||||||
.Large => {
|
|
||||||
if (self.writer.len() == 14) {
|
|
||||||
self.addr = @intCast(u10, self.writer.finish());
|
|
||||||
self.state = .WriteTransfer;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
.Small => {
|
|
||||||
if (self.writer.len() == 6) {
|
|
||||||
self.addr = @intCast(u6, self.writer.finish());
|
|
||||||
self.state = .WriteTransfer;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
else => log.err("Unable to calculate EEPROM write address. EEPROM size UNKNOWN", .{}),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
.WriteTransfer => {
|
|
||||||
if (self.writer.len() == 64) {
|
|
||||||
std.mem.writeIntSliceLittle(u64, buf[self.addr * 8 ..][0..8], self.writer.finish());
|
|
||||||
self.state = .RequestEnd;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
.RequestEnd => unreachable, // We return early in write() if state is .RequestEnd
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const Reader = struct {
|
|
||||||
const This = @This();
|
|
||||||
|
|
||||||
data: u64,
|
|
||||||
i: u8,
|
|
||||||
enabled: bool,
|
|
||||||
|
|
||||||
fn init() This {
|
|
||||||
return .{
|
|
||||||
.data = 0,
|
|
||||||
.i = 0,
|
|
||||||
.enabled = false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn configure(self: *This, value: u64) void {
|
|
||||||
self.data = value;
|
|
||||||
self.i = 0;
|
|
||||||
self.enabled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn read(self: *This) u1 {
|
|
||||||
if (!self.enabled) return 1;
|
|
||||||
|
|
||||||
const bit = if (self.i < 4) blk: {
|
|
||||||
break :blk 0;
|
|
||||||
} else blk: {
|
|
||||||
const idx = @intCast(u6, 63 - (self.i - 4));
|
|
||||||
break :blk @truncate(u1, self.data >> idx);
|
|
||||||
};
|
|
||||||
|
|
||||||
self.i = (self.i + 1) % (64 + 4);
|
|
||||||
if (self.i == 0) self.enabled = false;
|
|
||||||
|
|
||||||
return bit;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dbgRead(self: *const This) u1 {
|
|
||||||
if (!self.enabled) return 1;
|
|
||||||
|
|
||||||
const bit = if (self.i < 4) blk: {
|
|
||||||
break :blk 0;
|
|
||||||
} else blk: {
|
|
||||||
const idx = @intCast(u6, 63 - (self.i - 4));
|
|
||||||
break :blk @truncate(u1, self.data >> idx);
|
|
||||||
};
|
|
||||||
|
|
||||||
return bit;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const Writer = struct {
|
|
||||||
const This = @This();
|
|
||||||
|
|
||||||
data: u64,
|
|
||||||
i: u8,
|
|
||||||
|
|
||||||
fn init() This {
|
|
||||||
return .{ .data = 0, .i = 0 };
|
|
||||||
}
|
|
||||||
|
|
||||||
fn requestWrite(self: *This, bit: u1) void {
|
|
||||||
const idx = @intCast(u1, 1 - self.i);
|
|
||||||
self.data = (self.data & ~(@as(u64, 1) << idx)) | (@as(u64, bit) << idx);
|
|
||||||
self.i += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn addressWrite(self: *This, kind: Eeprom.Kind, bit: u1) void {
|
|
||||||
if (kind == .Unknown) return;
|
|
||||||
|
|
||||||
const size: u4 = switch (kind) {
|
|
||||||
.Large => 13,
|
|
||||||
.Small => 5,
|
|
||||||
.Unknown => unreachable,
|
|
||||||
};
|
|
||||||
|
|
||||||
const idx = @intCast(u4, size - self.i);
|
|
||||||
self.data = (self.data & ~(@as(u64, 1) << idx)) | (@as(u64, bit) << idx);
|
|
||||||
self.i += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dataWrite(self: *This, bit: u1) void {
|
|
||||||
const idx = @intCast(u6, 63 - self.i);
|
|
||||||
self.data = (self.data & ~(@as(u64, 1) << idx)) | (@as(u64, bit) << idx);
|
|
||||||
self.i += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn len(self: *const This) u8 {
|
|
||||||
return self.i;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn finish(self: *This) u64 {
|
|
||||||
defer self.reset();
|
|
||||||
return self.data;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn reset(self: *This) void {
|
|
||||||
self.i = 0;
|
|
||||||
self.data = 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
|
@ -0,0 +1,269 @@
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
|
const log = std.log.scoped(.Eeprom);
|
||||||
|
|
||||||
|
pub const Eeprom = struct {
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
addr: u14,
|
||||||
|
|
||||||
|
kind: Kind,
|
||||||
|
state: State,
|
||||||
|
writer: Writer,
|
||||||
|
reader: Reader,
|
||||||
|
|
||||||
|
allocator: Allocator,
|
||||||
|
|
||||||
|
const Kind = enum {
|
||||||
|
Unknown,
|
||||||
|
Small, // 512B
|
||||||
|
Large, // 8KB
|
||||||
|
};
|
||||||
|
|
||||||
|
const State = enum {
|
||||||
|
Ready,
|
||||||
|
Read,
|
||||||
|
Write,
|
||||||
|
WriteTransfer,
|
||||||
|
RequestEnd,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn read(self: *Self) u1 {
|
||||||
|
return self.reader.read();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dbgRead(self: *const Self) u1 {
|
||||||
|
return self.reader.dbgRead();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write(self: *Self, word_count: u16, buf: *[]u8, bit: u1) void {
|
||||||
|
if (self.guessKind(word_count)) |found| {
|
||||||
|
log.info("EEPROM Kind: {}", .{found});
|
||||||
|
self.kind = found;
|
||||||
|
|
||||||
|
// buf.len will not equal zero when a save file was found and loaded.
|
||||||
|
// Right now, we assume that the save file is of the correct size which
|
||||||
|
// isn't necessarily true, since we can't trust anything a user can influence
|
||||||
|
// TODO: use ?[]u8 instead of a 0-sized slice?
|
||||||
|
if (buf.len == 0) {
|
||||||
|
const len: usize = switch (found) {
|
||||||
|
.Small => 0x200,
|
||||||
|
.Large => 0x2000,
|
||||||
|
else => unreachable,
|
||||||
|
};
|
||||||
|
|
||||||
|
buf.* = self.allocator.alloc(u8, len) catch |e| {
|
||||||
|
log.err("Failed to resize EEPROM buf to {} bytes", .{len});
|
||||||
|
std.debug.panic("EEPROM entered irrecoverable state {}", .{e});
|
||||||
|
};
|
||||||
|
std.mem.set(u8, buf.*, 0xFF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self.state == .RequestEnd) {
|
||||||
|
if (bit != 0) log.debug("EEPROM Request did not end in 0u1. TODO: is this ok?", .{});
|
||||||
|
self.state = .Ready;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (self.state) {
|
||||||
|
.Ready => self.writer.requestWrite(bit),
|
||||||
|
.Read, .Write => self.writer.addressWrite(self.kind, bit),
|
||||||
|
.WriteTransfer => self.writer.dataWrite(bit),
|
||||||
|
.RequestEnd => unreachable, // We return early just above this block
|
||||||
|
}
|
||||||
|
|
||||||
|
self.tick(buf.*);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create(allocator: Allocator) Self {
|
||||||
|
return .{
|
||||||
|
.kind = .Unknown,
|
||||||
|
.state = .Ready,
|
||||||
|
.writer = Writer.create(),
|
||||||
|
.reader = Reader.create(),
|
||||||
|
.addr = 0,
|
||||||
|
.allocator = allocator,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn guessKind(self: *const Self, word_count: u16) ?Kind {
|
||||||
|
if (self.kind != .Unknown or self.state != .Read) return null;
|
||||||
|
|
||||||
|
return switch (word_count) {
|
||||||
|
17 => .Large,
|
||||||
|
9 => .Small,
|
||||||
|
else => blk: {
|
||||||
|
log.err("Unexpected length of DMA3 Transfer upon initial EEPROM read: {}", .{word_count});
|
||||||
|
break :blk null;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tick(self: *Self, buf: []u8) void {
|
||||||
|
switch (self.state) {
|
||||||
|
.Ready => {
|
||||||
|
if (self.writer.len() == 2) {
|
||||||
|
const req = @intCast(u2, self.writer.finish());
|
||||||
|
switch (req) {
|
||||||
|
0b11 => self.state = .Read,
|
||||||
|
0b10 => self.state = .Write,
|
||||||
|
else => log.err("Unknown EEPROM Request 0b{b:0>2}", .{req}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.Read => {
|
||||||
|
switch (self.kind) {
|
||||||
|
.Large => {
|
||||||
|
if (self.writer.len() == 14) {
|
||||||
|
const addr = @intCast(u10, self.writer.finish());
|
||||||
|
const value = std.mem.readIntSliceLittle(u64, buf[@as(u13, addr) * 8 ..][0..8]);
|
||||||
|
|
||||||
|
self.reader.configure(value);
|
||||||
|
self.state = .RequestEnd;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.Small => {
|
||||||
|
if (self.writer.len() == 6) {
|
||||||
|
// FIXME: Duplicated code from above
|
||||||
|
const addr = @intCast(u6, self.writer.finish());
|
||||||
|
const value = std.mem.readIntSliceLittle(u64, buf[@as(u13, addr) * 8 ..][0..8]);
|
||||||
|
|
||||||
|
self.reader.configure(value);
|
||||||
|
self.state = .RequestEnd;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
else => log.err("Unable to calculate EEPROM read address. EEPROM size UNKNOWN", .{}),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.Write => {
|
||||||
|
switch (self.kind) {
|
||||||
|
.Large => {
|
||||||
|
if (self.writer.len() == 14) {
|
||||||
|
self.addr = @intCast(u10, self.writer.finish());
|
||||||
|
self.state = .WriteTransfer;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.Small => {
|
||||||
|
if (self.writer.len() == 6) {
|
||||||
|
self.addr = @intCast(u6, self.writer.finish());
|
||||||
|
self.state = .WriteTransfer;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
else => log.err("Unable to calculate EEPROM write address. EEPROM size UNKNOWN", .{}),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.WriteTransfer => {
|
||||||
|
if (self.writer.len() == 64) {
|
||||||
|
std.mem.writeIntSliceLittle(u64, buf[self.addr * 8 ..][0..8], self.writer.finish());
|
||||||
|
self.state = .RequestEnd;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.RequestEnd => unreachable, // We return early in write() if state is .RequestEnd
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const Reader = struct {
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
data: u64,
|
||||||
|
i: u8,
|
||||||
|
enabled: bool,
|
||||||
|
|
||||||
|
fn create() Self {
|
||||||
|
return .{
|
||||||
|
.data = 0,
|
||||||
|
.i = 0,
|
||||||
|
.enabled = false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read(self: *Self) u1 {
|
||||||
|
if (!self.enabled) return 1;
|
||||||
|
|
||||||
|
const bit = if (self.i < 4) blk: {
|
||||||
|
break :blk 0;
|
||||||
|
} else blk: {
|
||||||
|
const idx = @intCast(u6, 63 - (self.i - 4));
|
||||||
|
break :blk @truncate(u1, self.data >> idx);
|
||||||
|
};
|
||||||
|
|
||||||
|
self.i = (self.i + 1) % (64 + 4);
|
||||||
|
if (self.i == 0) self.enabled = false;
|
||||||
|
|
||||||
|
return bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dbgRead(self: *const Self) u1 {
|
||||||
|
if (!self.enabled) return 1;
|
||||||
|
|
||||||
|
const bit = if (self.i < 4) blk: {
|
||||||
|
break :blk 0;
|
||||||
|
} else blk: {
|
||||||
|
const idx = @intCast(u6, 63 - (self.i - 4));
|
||||||
|
break :blk @truncate(u1, self.data >> idx);
|
||||||
|
};
|
||||||
|
|
||||||
|
return bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn configure(self: *Self, value: u64) void {
|
||||||
|
self.data = value;
|
||||||
|
self.i = 0;
|
||||||
|
self.enabled = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const Writer = struct {
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
data: u64,
|
||||||
|
i: u8,
|
||||||
|
|
||||||
|
fn create() Self {
|
||||||
|
return .{ .data = 0, .i = 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
fn requestWrite(self: *Self, bit: u1) void {
|
||||||
|
const idx = @intCast(u1, 1 - self.i);
|
||||||
|
self.data = (self.data & ~(@as(u64, 1) << idx)) | (@as(u64, bit) << idx);
|
||||||
|
self.i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn addressWrite(self: *Self, kind: Eeprom.Kind, bit: u1) void {
|
||||||
|
if (kind == .Unknown) return;
|
||||||
|
|
||||||
|
const size: u4 = switch (kind) {
|
||||||
|
.Large => 13,
|
||||||
|
.Small => 5,
|
||||||
|
.Unknown => unreachable,
|
||||||
|
};
|
||||||
|
|
||||||
|
const idx = @intCast(u4, size - self.i);
|
||||||
|
self.data = (self.data & ~(@as(u64, 1) << idx)) | (@as(u64, bit) << idx);
|
||||||
|
self.i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dataWrite(self: *Self, bit: u1) void {
|
||||||
|
const idx = @intCast(u6, 63 - self.i);
|
||||||
|
self.data = (self.data & ~(@as(u64, 1) << idx)) | (@as(u64, bit) << idx);
|
||||||
|
self.i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn len(self: *const Self) u8 {
|
||||||
|
return self.i;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finish(self: *Self) u64 {
|
||||||
|
defer self.reset();
|
||||||
|
return self.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(self: *Self) void {
|
||||||
|
self.i = 0;
|
||||||
|
self.data = 0;
|
||||||
|
}
|
||||||
|
};
|
Loading…
Reference in New Issue