from __future__ import annotations from dataclasses import dataclass, field LCD_E_CLOCK_STATUS = 0xF200 LCD_E_CLOCK_DATA = 0xF201 LCD_LINE_WIDTH = 16 LCD_LINE_STARTS = (0x00, 0x40, 0x10, 0x50) LCD_DDRAM_SIZE = 0x80 @dataclass class LCD: ddram: bytearray = field(default_factory=lambda: bytearray([0x20] * LCD_DDRAM_SIZE)) cursor: int = 0 data_latch: int = 0 command_latch: int = 0 def read_status(self) -> int: return self.cursor & 0x7F def read_data(self) -> int: return self.data_latch & 0xFF def write_command(self, value: int) -> None: value &= 0xFF self.command_latch = value if value & 0x80: self.cursor = value & 0x7F elif value == 0x01: self.ddram[:] = bytes([0x20]) * LCD_DDRAM_SIZE self.cursor = 0 elif value == 0x02: self.cursor = 0 def write_data(self, value: int) -> None: value &= 0xFF self.data_latch = value if 0 <= self.cursor < LCD_DDRAM_SIZE: self.ddram[self.cursor] = value self.cursor = (self.cursor + 1) & 0x7F def line_text(self, line: int, width: int = LCD_LINE_WIDTH) -> str: if not 0 <= line < len(LCD_LINE_STARTS): raise ValueError(f"LCD line out of range: {line}") start = LCD_LINE_STARTS[line] return "".join(_display_char(self.ddram[(start + offset) & 0x7F]) for offset in range(width)) def display_lines(self, lines: int = 4, width: int = LCD_LINE_WIDTH) -> list[str]: return [self.line_text(line, width) for line in range(lines)] def display_text(self, lines: int = 4, width: int = LCD_LINE_WIDTH) -> str: return " | ".join(self.display_lines(lines, width)) def _display_char(value: int) -> str: return chr(value) if 0x20 <= value <= 0x7E else "." __all__ = [ "LCD", "LCD_DDRAM_SIZE", "LCD_E_CLOCK_DATA", "LCD_E_CLOCK_STATUS", "LCD_LINE_STARTS", "LCD_LINE_WIDTH", ]