Implement RTC #1
| @@ -318,7 +318,6 @@ const Gpio = struct { | ||||
| const Clock = struct { | ||||
|     const This = @This(); | ||||
|  | ||||
|     cmd: Command, | ||||
|     writer: Writer, | ||||
|     reader: Reader, | ||||
|     state: State, | ||||
| @@ -343,7 +342,7 @@ const Clock = struct { | ||||
|  | ||||
|     const State = union(enum) { | ||||
|         Idle, | ||||
|         CommandInput, | ||||
|         Command, | ||||
|         Write: Register, | ||||
|         Read: Register, | ||||
|     }; | ||||
| @@ -499,72 +498,6 @@ const Clock = struct { | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     const Command = struct { | ||||
|         buf: u8, | ||||
|         i: u4, | ||||
|  | ||||
|         fn write(self: *Command, value: u1) void { | ||||
|             const idx = @intCast(u3, self.i); | ||||
|             self.buf = (self.buf & ~(@as(u8, 1) << idx)) | @as(u8, value) << idx; | ||||
|             self.i += 1; | ||||
|         } | ||||
|  | ||||
|         fn reset(self: *Command) void { | ||||
|             self.buf = 0; | ||||
|             self.i = 0; | ||||
|         } | ||||
|  | ||||
|         fn isFinished(self: *const Command) bool { | ||||
|             return self.i >= 8; | ||||
|         } | ||||
|  | ||||
|         fn getCommand(self: *const Command) u8 { | ||||
|             // If High Nybble is 0x6, no need to switch the endianness | ||||
|             if (self.buf >> 4 & 0xF == 0x6) return self.buf; | ||||
|  | ||||
|             // Turns out reversing the order of bits isn't trivial at all | ||||
|             // https://stackoverflow.com/questions/2602823/in-c-c-whats-the-simplest-way-to-reverse-the-order-of-bits-in-a-byte | ||||
|             var ret = self.buf; | ||||
|             ret = (ret & 0xF0) >> 4 | (ret & 0x0F) << 4; | ||||
|             ret = (ret & 0xCC) >> 2 | (ret & 0x33) << 2; | ||||
|             ret = (ret & 0xAA) >> 1 | (ret & 0x55) << 1; | ||||
|  | ||||
|             return ret; | ||||
|         } | ||||
|  | ||||
|         fn handleCommand(self: *const Command, rtc: *Clock) State { | ||||
|             const command = self.getCommand(); | ||||
|             log.debug("RTC: Handling Command 0x{X:0>2} [0b{b:0>8}]", .{ command, command }); | ||||
|  | ||||
|             const is_write = command & 1 == 0; | ||||
|             const rtc_register = @truncate(u3, command >> 1 & 0x7); | ||||
|  | ||||
|             if (is_write) { | ||||
|                 return switch (rtc_register) { | ||||
|                     0 => blk: { | ||||
|                         rtc.reset(); | ||||
|                         break :blk .Idle; | ||||
|                     }, | ||||
|                     1 => .{ .Write = .Control }, | ||||
|                     2 => .{ .Write = .DateTime }, | ||||
|                     3 => .{ .Write = .Time }, | ||||
|                     6 => blk: { | ||||
|                         rtc.irq(); | ||||
|                         break :blk .Idle; | ||||
|                     }, | ||||
|                     4, 5, 7 => .Idle, | ||||
|                 }; | ||||
|             } else { | ||||
|                 return switch (rtc_register) { | ||||
|                     1 => .{ .Read = .Control }, | ||||
|                     2 => .{ .Read = .DateTime }, | ||||
|                     3 => .{ .Read = .Time }, | ||||
|                     0, 4, 5, 6, 7 => .Idle, // Do Nothing | ||||
|                 }; | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     const Data = extern union { | ||||
|         sck: Bit(u8, 0), | ||||
|         sio: Bit(u8, 1), | ||||
| @@ -590,7 +523,6 @@ const Clock = struct { | ||||
|  | ||||
|     fn init(ptr: *This, cpu: *Arm7tdmi, gpio: *const Gpio) void { | ||||
|         ptr.* = .{ | ||||
|             .cmd = .{ .buf = 0, .i = 0 }, | ||||
|             .writer = .{ .buf = 0, .i = 0, .count = 0 }, | ||||
|             .reader = .{ .i = 0, .count = 0 }, | ||||
|             .state = .Idle, | ||||
| @@ -616,22 +548,23 @@ const Clock = struct { | ||||
|                 if (cache.sck.read()) { | ||||
|                     if (!cache.cs.read() and value.cs.read()) { | ||||
|                         log.debug("RTC: Entering Command Mode", .{}); | ||||
|                         self.state = .CommandInput; | ||||
|                         self.cmd.reset(); | ||||
|                         self.state = .Command; | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 break :blk @truncate(u4, value.raw); | ||||
|             }, | ||||
|             .CommandInput => blk: { | ||||
|             .Command => blk: { | ||||
|                 if (!value.cs.read()) log.err("RTC: Expected CS to be set during {}, however CS was cleared", .{self.state}); | ||||
|  | ||||
|                 // If SCK rises, sample SIO | ||||
|                 if (!cache.sck.read() and value.sck.read()) { | ||||
|                     self.cmd.write(@boolToInt(value.sio.read())); | ||||
|                     self.writer.push(@boolToInt(value.sio.read())); | ||||
|  | ||||
|                     if (self.writer.finished()) { | ||||
|                         self.state = self.processCommand(self.writer.buf); | ||||
|                         self.writer.reset(); | ||||
|  | ||||
|                     if (self.cmd.isFinished()) { | ||||
|                         self.state = self.cmd.handleCommand(self); | ||||
|                         log.debug("RTC: Switching to {}", .{self.state}); | ||||
|                     } | ||||
|                 } | ||||
| @@ -707,4 +640,48 @@ const Clock = struct { | ||||
|         self.cpu.bus.io.irq.game_pak.set(); | ||||
|         self.cpu.handleInterrupt(); | ||||
|     } | ||||
|  | ||||
|     fn processCommand(self: *This, raw_command: u8) State { | ||||
|         const command = blk: { | ||||
|             // If High Nybble is 0x6, no need to switch the endianness | ||||
|             if (raw_command >> 4 & 0xF == 0x6) break :blk raw_command; | ||||
|  | ||||
|             // Turns out reversing the order of bits isn't trivial at all | ||||
|             // https://stackoverflow.com/questions/2602823/in-c-c-whats-the-simplest-way-to-reverse-the-order-of-bits-in-a-byte | ||||
|             var ret = raw_command; | ||||
|             ret = (ret & 0xF0) >> 4 | (ret & 0x0F) << 4; | ||||
|             ret = (ret & 0xCC) >> 2 | (ret & 0x33) << 2; | ||||
|             ret = (ret & 0xAA) >> 1 | (ret & 0x55) << 1; | ||||
|  | ||||
|             break :blk ret; | ||||
|         }; | ||||
|         log.debug("RTC: Handling Command 0x{X:0>2} [0b{b:0>8}]", .{ command, command }); | ||||
|  | ||||
|         const is_write = command & 1 == 0; | ||||
|         const rtc_register = @truncate(u3, command >> 1 & 0x7); | ||||
|  | ||||
|         if (is_write) { | ||||
|             return switch (rtc_register) { | ||||
|                 0 => blk: { | ||||
|                     self.reset(); | ||||
|                     break :blk .Idle; | ||||
|                 }, | ||||
|                 1 => .{ .Write = .Control }, | ||||
|                 2 => .{ .Write = .DateTime }, | ||||
|                 3 => .{ .Write = .Time }, | ||||
|                 6 => blk: { | ||||
|                     self.irq(); | ||||
|                     break :blk .Idle; | ||||
|                 }, | ||||
|                 4, 5, 7 => .Idle, | ||||
|             }; | ||||
|         } else { | ||||
|             return switch (rtc_register) { | ||||
|                 1 => .{ .Read = .Control }, | ||||
|                 2 => .{ .Read = .DateTime }, | ||||
|                 3 => .{ .Read = .Time }, | ||||
|                 0, 4, 5, 6, 7 => .Idle, // Do Nothing | ||||
|             }; | ||||
|         } | ||||
|     } | ||||
| }; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user