From ee6eec440d7c471593bd23e822d7bd562b4eff41 Mon Sep 17 00:00:00 2001 From: dataisland Date: Sat, 30 Oct 2021 23:21:02 +0800 Subject: [PATCH 01/11] RTC has cheated the MCU --- qiling/hw/timer/stm32f4xx_rtc.py | 42 +++++++++++++++++++++++++++++++- qiling/hw/utils/bcd.py | 13 ++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 qiling/hw/utils/bcd.py diff --git a/qiling/hw/timer/stm32f4xx_rtc.py b/qiling/hw/timer/stm32f4xx_rtc.py index 3cb94420b..b38348d4e 100644 --- a/qiling/hw/timer/stm32f4xx_rtc.py +++ b/qiling/hw/timer/stm32f4xx_rtc.py @@ -4,8 +4,15 @@ # import ctypes + from qiling.hw.peripheral import QlPeripheral +from qiling.hw.const.stm32f4xx_rtc import RTC_TR, RTC_ISR +from qiling.hw.utils.bcd import byte2bcd + +class Time: + def __init__(self): + pass class STM32F4xxRtc(QlPeripheral): class Type(ctypes.Structure): @@ -99,5 +106,38 @@ def read(self, offset: int, size: int) -> int: @QlPeripheral.debug_info() def write(self, offset: int, size: int, value: int): + if offset == self.struct.ISR.offset: + for bitmask in [ + RTC_ISR.TAMP1F, + RTC_ISR.TSOVF, + RTC_ISR.TSF, + RTC_ISR.WUTF, + RTC_ISR.ALRBF, + RTC_ISR.ALRAF, + RTC_ISR.RSF + ]: + if value & bitmask == 0: + self.rtc.ISR &= ~bitmask + + self.rtc.ISR = (self.rtc.ISR & ~RTC_ISR.INIT) | (value & RTC_ISR.INIT) + return + data = (value).to_bytes(size, 'little') - ctypes.memmove(ctypes.addressof(self.rtc) + offset, data, size) + ctypes.memmove(ctypes.addressof(self.rtc) + offset, data, size) + + def set_time(self, hour=0, minute=0, second=0, time_format=0): + hour = (byte2bcd(hour) << 16) & (RTC_TR.HT | RTC_TR.HU) + minute = (byte2bcd(minute) << 8) & (RTC_TR.MNT | RTC_TR.MNU) + second = byte2bcd(second) & (RTC_TR.ST | RTC_TR.SU) + time_format = (time_format << 16) & RTC_TR.PM + + self.rtc.TR = hour | minute | second | time_format + + def get_time(self): + pass + + def step(self): + if self.rtc.ISR & RTC_ISR.INIT: + self.rtc.ISR |= RTC_ISR.INITF + + self.rtc.ISR |= RTC_ISR.RSF diff --git a/qiling/hw/utils/bcd.py b/qiling/hw/utils/bcd.py new file mode 100644 index 000000000..da3acbed3 --- /dev/null +++ b/qiling/hw/utils/bcd.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python3 +# +# Cross Platform and Multi Architecture Advanced Binary Emulation Framework +# + + +def byte2bcd(value): + bcdhigh = 0 + while value >= 10: + bcdhigh += 1 + value -= 10 + + return (bcdhigh << 4) | value \ No newline at end of file From 3bdec8eabefd94c71e838f01d76c1ec48d74db18 Mon Sep 17 00:00:00 2001 From: dataisland Date: Sat, 30 Oct 2021 23:48:17 +0800 Subject: [PATCH 02/11] Add BCD util tools --- qiling/hw/utils/bcd.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/qiling/hw/utils/bcd.py b/qiling/hw/utils/bcd.py index da3acbed3..aa8c2bc26 100644 --- a/qiling/hw/utils/bcd.py +++ b/qiling/hw/utils/bcd.py @@ -10,4 +10,7 @@ def byte2bcd(value): bcdhigh += 1 value -= 10 - return (bcdhigh << 4) | value \ No newline at end of file + return (bcdhigh << 4) | value + +def bcd2byte(value): + return ((value & 0xF0) >> 0x4) * 10 + (value & 0xf) \ No newline at end of file From f009b387fe51961e0c2d2fd9e475511225d32a5e Mon Sep 17 00:00:00 2001 From: dataisland Date: Sat, 30 Oct 2021 23:50:02 +0800 Subject: [PATCH 03/11] The RTC is harder than I think --- qiling/hw/timer/stm32f4xx_rtc.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/qiling/hw/timer/stm32f4xx_rtc.py b/qiling/hw/timer/stm32f4xx_rtc.py index b38348d4e..e453dbc94 100644 --- a/qiling/hw/timer/stm32f4xx_rtc.py +++ b/qiling/hw/timer/stm32f4xx_rtc.py @@ -3,17 +3,15 @@ # Cross Platform and Multi Architecture Advanced Binary Emulation Framework # +import time import ctypes +from datetime import datetime from qiling.hw.peripheral import QlPeripheral from qiling.hw.const.stm32f4xx_rtc import RTC_TR, RTC_ISR -from qiling.hw.utils.bcd import byte2bcd +from qiling.hw.utils.bcd import bcd2byte, byte2bcd -class Time: - def __init__(self): - pass - class STM32F4xxRtc(QlPeripheral): class Type(ctypes.Structure): """ the structure is available in : @@ -133,8 +131,17 @@ def set_time(self, hour=0, minute=0, second=0, time_format=0): self.rtc.TR = hour | minute | second | time_format - def get_time(self): - pass + def get_time(self, value): + hour = (value & (RTC_TR.HT | RTC_TR.HU)) >> 16 + minute = (value & (RTC_TR.MNT | RTC_TR.MNU)) >> 8 + second = value & (RTC_TR.ST | RTC_TR.SU) + time_format = (value & RTC_TR.PM) >> 16 + + return hour, minute, second, time_format + + def increase(self): + self.calendar += 1 + self.set_time(self.calendar.tm_hour, self.calendar.tm_min, self.calendar.tm_sec) def step(self): if self.rtc.ISR & RTC_ISR.INIT: From c63335bd55242d1e177e7181896e474f86fd1490 Mon Sep 17 00:00:00 2001 From: dataisland Date: Sat, 30 Oct 2021 23:55:47 +0800 Subject: [PATCH 04/11] Add missing I2c Interrupt --- qiling/hw/i2c/stm32f4xx_i2c.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/qiling/hw/i2c/stm32f4xx_i2c.py b/qiling/hw/i2c/stm32f4xx_i2c.py index 46f7c67ce..049e4abb5 100644 --- a/qiling/hw/i2c/stm32f4xx_i2c.py +++ b/qiling/hw/i2c/stm32f4xx_i2c.py @@ -141,7 +141,9 @@ def generate_stop(self): self.i2c.SR1 |= I2C_SR1.STOPF self.i2c.SR1 &= ~I2C_SR1.ADDR + self.set_slave_mode() + self.send_event_interrupt() def send_address(self): if self.i2c.DR == self.i2c.OAR1 >> 1: From dfcdf627526cd5c0b25af15be02065c7229f7fae Mon Sep 17 00:00:00 2001 From: dataisland Date: Sun, 31 Oct 2021 16:34:21 +0800 Subject: [PATCH 05/11] Add BCD format utils --- qiling/hw/utils/bcd.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 qiling/hw/utils/bcd.py diff --git a/qiling/hw/utils/bcd.py b/qiling/hw/utils/bcd.py new file mode 100644 index 000000000..aa8c2bc26 --- /dev/null +++ b/qiling/hw/utils/bcd.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 +# +# Cross Platform and Multi Architecture Advanced Binary Emulation Framework +# + + +def byte2bcd(value): + bcdhigh = 0 + while value >= 10: + bcdhigh += 1 + value -= 10 + + return (bcdhigh << 4) | value + +def bcd2byte(value): + return ((value & 0xF0) >> 0x4) * 10 + (value & 0xf) \ No newline at end of file From a4522ecca190445a3cdfd3c328f0e06ffcc0d991 Mon Sep 17 00:00:00 2001 From: dataisland Date: Sun, 31 Oct 2021 16:36:06 +0800 Subject: [PATCH 06/11] The RTC pass the initlization stage --- qiling/hw/timer/stm32f4xx_rtc.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/qiling/hw/timer/stm32f4xx_rtc.py b/qiling/hw/timer/stm32f4xx_rtc.py index 3cb94420b..dea16642e 100644 --- a/qiling/hw/timer/stm32f4xx_rtc.py +++ b/qiling/hw/timer/stm32f4xx_rtc.py @@ -4,7 +4,9 @@ # import ctypes + from qiling.hw.peripheral import QlPeripheral +from qiling.hw.const.stm32f4xx_rtc import RTC_TR, RTC_ISR class STM32F4xxRtc(QlPeripheral): @@ -99,5 +101,27 @@ def read(self, offset: int, size: int) -> int: @QlPeripheral.debug_info() def write(self, offset: int, size: int, value: int): + if offset == self.struct.ISR.offset: + for bitmask in [ + RTC_ISR.TAMP1F, + RTC_ISR.TSOVF, + RTC_ISR.TSF, + RTC_ISR.WUTF, + RTC_ISR.ALRBF, + RTC_ISR.ALRAF, + RTC_ISR.RSF + ]: + if value & bitmask == 0: + self.rtc.ISR &= ~bitmask + + self.rtc.ISR = (self.rtc.ISR & ~RTC_ISR.INIT) | (value & RTC_ISR.INIT) + return + data = (value).to_bytes(size, 'little') - ctypes.memmove(ctypes.addressof(self.rtc) + offset, data, size) + ctypes.memmove(ctypes.addressof(self.rtc) + offset, data, size) + + def step(self): + if self.rtc.ISR & RTC_ISR.INIT: + self.rtc.ISR |= RTC_ISR.INITF + + self.rtc.ISR |= RTC_ISR.RSF From efbb1c063c68ed49fb489e17ca8d4670d4921362 Mon Sep 17 00:00:00 2001 From: dataisland Date: Sun, 31 Oct 2021 23:39:06 +0800 Subject: [PATCH 07/11] Thread safe pipe for connectivity periperhal IO --- qiling/hw/char/stm32f4xx_usart.py | 31 +++---------- qiling/hw/connectivity.py | 73 +++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 26 deletions(-) create mode 100644 qiling/hw/connectivity.py diff --git a/qiling/hw/char/stm32f4xx_usart.py b/qiling/hw/char/stm32f4xx_usart.py index f2257dec4..8e3e6daec 100644 --- a/qiling/hw/char/stm32f4xx_usart.py +++ b/qiling/hw/char/stm32f4xx_usart.py @@ -6,9 +6,10 @@ import ctypes from qiling.hw.peripheral import QlPeripheral +from qiling.hw.connectivity import QlConnectivityPeripheral from qiling.hw.const.stm32f4xx_usart import USART_SR, USART_CR1 -class STM32F4xxUsart(QlPeripheral): +class STM32F4xxUsart(QlConnectivityPeripheral): class Type(ctypes.Structure): """ the structure available in : stm32f413xx.h @@ -56,9 +57,6 @@ def __init__(self, ql, label, intn=None): self.intn = intn - self.recv_buf = bytearray() - self.send_buf = bytearray() - @QlPeripheral.debug_info() def read(self, offset: int, size: int) -> int: buf = ctypes.create_string_buffer(size) @@ -92,32 +90,13 @@ def transfer(self): data = self.usart.DR self.usart.SR |= USART_SR.TXE - self.send_buf.append(data) + self.send_to_user(data) if not (self.usart.SR & USART_SR.RXNE): # TXE bit must had been cleared - if self.recv_buf: + if self.can_recv(): self.usart.SR |= USART_SR.RXNE - self.usart.DR = self.recv_buf.pop(0) - - - def send(self, data: bytes): - """ send user data into USART. - - Args: - data (bytes): Input Data - """ - self.recv_buf += data - - def recv(self) -> bytes: - """ receive data from USART. - - Returns: - bytes: USART send buffer data - """ - data = bytes(self.send_buf) - self.send_buf.clear() - return data + self.usart.DR = self.recv_from_user() def step(self): self.transfer() diff --git a/qiling/hw/connectivity.py b/qiling/hw/connectivity.py new file mode 100644 index 000000000..62bc3a401 --- /dev/null +++ b/qiling/hw/connectivity.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python3 +# +# Cross Platform and Multi Architecture Advanced Binary Emulation Framework +# + + +import ctypes +import queue + +from qiling.core import Qiling +from qiling.hw.peripheral import QlPeripheral + + +class QlConnectivityPeripheral(QlPeripheral): + class Type(ctypes.Structure): + """ Define the reigister fields of peripheral. + + Example: + fields_ = [ + ('SR' , ctypes.c_uint32), + ('DR' , ctypes.c_uint32), + ('BRR' , ctypes.c_uint32), + ('CR1' , ctypes.c_uint32), + ('CR2' , ctypes.c_uint32), + ('CR3' , ctypes.c_uint32), + ('GTPR', ctypes.c_uint32), + ] + """ + _fields_ = [] + + def __init__(self, ql: Qiling, label: str): + super().__init__(ql, label) + + self.rtube = queue.Queue() + self.wtube = queue.Queue() + + def send(self, data: bytes): + """ Send data into the peripheral. + + Example: + ql.hw.usart1.send(b'hello') + """ + + for byte in bytearray(data): + self.rtube.put(byte) + + def recv(self, numb:int = 4096) -> bytes: + """ Receive data from peripheral + + Example: + data = ql.hw.i2c1.send() + """ + data = bytearray() + while not self.wtube.empty() and numb != 0: + data.append(self.wtube.get()) + numb -= 1 + + return bytes(data) + + def can_recv(self): + return not self.rtube.empty() + + def recv_from_user(self) -> bytes: + """ Read single byte from user input + """ + + return self.rtube.get() + + def send_to_user(self, data: int): + """ send single byte to user + """ + + self.wtube.put(data) From f6c4d2fb7629a883fd86cdfa7504ef4c8d71bd1d Mon Sep 17 00:00:00 2001 From: dataisland Date: Mon, 1 Nov 2021 00:24:12 +0800 Subject: [PATCH 08/11] Add support for device connect --- qiling/hw/connectivity.py | 40 ++++++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/qiling/hw/connectivity.py b/qiling/hw/connectivity.py index 62bc3a401..986c3bfd3 100644 --- a/qiling/hw/connectivity.py +++ b/qiling/hw/connectivity.py @@ -28,12 +28,18 @@ class Type(ctypes.Structure): """ _fields_ = [] - def __init__(self, ql: Qiling, label: str): + def __init__(self, ql: Qiling, label: str, limit:int = 1): super().__init__(ql, label) self.rtube = queue.Queue() self.wtube = queue.Queue() + self.limit = limit + self.device_list = [] + + def has_input(self): + return not self.rtube.empty() + def send(self, data: bytes): """ Send data into the peripheral. @@ -51,14 +57,20 @@ def recv(self, numb:int = 4096) -> bytes: data = ql.hw.i2c1.send() """ data = bytearray() - while not self.wtube.empty() and numb != 0: + while self.can_recv() and numb != 0: data.append(self.wtube.get()) numb -= 1 - return bytes(data) - + return bytes(data) + def can_recv(self): - return not self.rtube.empty() + return not self.wtube.empty() + + def send_to_user(self, data: int): + """ send single byte to user + """ + + self.wtube.put(data) def recv_from_user(self) -> bytes: """ Read single byte from user input @@ -66,8 +78,18 @@ def recv_from_user(self) -> bytes: return self.rtube.get() - def send_to_user(self, data: int): - """ send single byte to user - """ + def connect(self, device): + if len(self.device_list) < self.limit: + self.device_list.append(device) + + @staticmethod + def device_handler(func): + def wrapper(self): + func(self) + + if self.device_list and self.can_recv(): + data = self.recv(numb=1) + for device in self.device_list: + device.send(data) - self.wtube.put(data) + return wrapper \ No newline at end of file From 203b7e88698a39fac9511a3346195d63acda1aeb Mon Sep 17 00:00:00 2001 From: dataisland Date: Mon, 1 Nov 2021 00:26:26 +0800 Subject: [PATCH 09/11] Add device support for USART, I2C and SPI --- qiling/hw/char/stm32f4xx_usart.py | 3 ++- qiling/hw/i2c/stm32f4xx_i2c.py | 25 +++++++++++-------------- qiling/hw/spi/stm32f4xx_spi.py | 12 ++++++++---- 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/qiling/hw/char/stm32f4xx_usart.py b/qiling/hw/char/stm32f4xx_usart.py index 8e3e6daec..992e37360 100644 --- a/qiling/hw/char/stm32f4xx_usart.py +++ b/qiling/hw/char/stm32f4xx_usart.py @@ -94,10 +94,11 @@ def transfer(self): if not (self.usart.SR & USART_SR.RXNE): # TXE bit must had been cleared - if self.can_recv(): + if self.has_input(): self.usart.SR |= USART_SR.RXNE self.usart.DR = self.recv_from_user() + @QlConnectivityPeripheral.device_handler def step(self): self.transfer() diff --git a/qiling/hw/i2c/stm32f4xx_i2c.py b/qiling/hw/i2c/stm32f4xx_i2c.py index 049e4abb5..b29b27a63 100644 --- a/qiling/hw/i2c/stm32f4xx_i2c.py +++ b/qiling/hw/i2c/stm32f4xx_i2c.py @@ -6,10 +6,11 @@ import ctypes from qiling.hw.peripheral import QlPeripheral +from qiling.hw.connectivity import QlConnectivityPeripheral from qiling.hw.const.stm32f4xx_i2c import I2C_CR1, I2C_CR2, I2C_SR1, I2C_SR2, I2C_DR, I2C_OAR1, I2C_OAR2 -class STM32F4xxI2c(QlPeripheral): +class STM32F4xxI2c(QlConnectivityPeripheral): class Type(ctypes.Structure): """ the structure is available in : stm32f423xx.h @@ -52,9 +53,6 @@ def __init__(self, ql, label, ev_intn=None, er_intn=None): self.ev_intn = ev_intn # event interrupt self.er_intn = er_intn # error interrupt - self.devices = [] - self.current = None - self.reset() def reset(self): @@ -147,9 +145,6 @@ def generate_stop(self): def send_address(self): if self.i2c.DR == self.i2c.OAR1 >> 1: - for dev in self.devices: - if self.i2c.DR == dev.address: - self.current = dev # TODO: send ACK self.i2c.SR1 |= I2C_SR1.ADDR | I2C_SR1.TXE @@ -157,7 +152,8 @@ def send_address(self): def send_data(self): self.i2c.SR1 |= I2C_SR1.BTF | I2C_SR1.TXE - self.current.send(self.i2c.DR) + + self.send_to_user(self.i2c.DR) self.send_event_interrupt() ## I2C Status register 2 (I2C_SR2) @@ -191,16 +187,17 @@ def is_7bit_mode(self): def fetch_device_address(self): # dual addressing mode if self.i2c.OAR2 & I2C_OAR2.ENDUAL: - self.i2c.OAR1 = self.devices[0].address << 1 - self.i2c.OAR2 = I2C_OAR2.ENDUAL | (self.devices[1].address << 1) + self.i2c.OAR1 = self.device_list[0].address << 1 + self.i2c.OAR2 = I2C_OAR2.ENDUAL | (self.device_list[1].address << 1) # single device, 10-bit slave address elif self.i2c.OAR1 & I2C_OAR1.ADDMODE: - self.i2c.OAR1 = I2C_OAR1.ADDMODE | self.devices[0].address + self.i2c.OAR1 = I2C_OAR1.ADDMODE | self.device_list[0].address # single device, 7-bit slave address else: - self.i2c.OAR1 = self.devices[0].address << 1 + self.i2c.OAR1 = self.device_list[0].address << 1 - def connect(self, dev): - self.devices.append(dev) + @QlConnectivityPeripheral.device_handler + def step(self): + pass diff --git a/qiling/hw/spi/stm32f4xx_spi.py b/qiling/hw/spi/stm32f4xx_spi.py index 39dc86a48..ddfc9e033 100644 --- a/qiling/hw/spi/stm32f4xx_spi.py +++ b/qiling/hw/spi/stm32f4xx_spi.py @@ -4,11 +4,13 @@ # import ctypes + from qiling.hw.peripheral import QlPeripheral +from qiling.hw.connectivity import QlConnectivityPeripheral from qiling.hw.const.stm32f4xx_spi import SPI_CR1, SPI_CR2, SPI_SR, SPI_CRCPR, SPI_I2SCFGR, SPI_I2SPR -class STM32F4xxSpi(QlPeripheral): +class STM32F4xxSpi(QlConnectivityPeripheral): class Type(ctypes.Structure): """ the structure available in : stm32f413xx.h @@ -99,10 +101,12 @@ def write(self, offset: int, size: int, value: int): ctypes.memmove(ctypes.addressof(self.spi) + offset, data, size) if self.in_field(self.struct.DR, offset, size): - self.send_data() + self.spi.SR |= SPI_SR.RXNE + self.send_to_user(self.spi.DR) def send_interrupt(self): self.ql.hw.nvic.set_pending(self.intn) - def send_data(self): - self.spi.SR |= SPI_SR.RXNE + @QlConnectivityPeripheral.device_handler + def step(self): + pass \ No newline at end of file From b02ff6542fe27f10e764a4973503b6ed9205db7b Mon Sep 17 00:00:00 2001 From: dataisland Date: Mon, 1 Nov 2021 00:26:45 +0800 Subject: [PATCH 10/11] Adjust the call sequence --- qiling/hw/connectivity.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qiling/hw/connectivity.py b/qiling/hw/connectivity.py index 986c3bfd3..bbbc75412 100644 --- a/qiling/hw/connectivity.py +++ b/qiling/hw/connectivity.py @@ -84,12 +84,12 @@ def connect(self, device): @staticmethod def device_handler(func): - def wrapper(self): - func(self) - + def wrapper(self): if self.device_list and self.can_recv(): data = self.recv(numb=1) for device in self.device_list: device.send(data) + + func(self) return wrapper \ No newline at end of file From 66d2cc3897cb9a5e58339edfab1344a81165e04d Mon Sep 17 00:00:00 2001 From: dataisland Date: Mon, 1 Nov 2021 00:27:00 +0800 Subject: [PATCH 11/11] Fix the LCD example --- examples/mcu/stm32f411_i2c_lcd.py | 51 ++++++++++++++++--------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/examples/mcu/stm32f411_i2c_lcd.py b/examples/mcu/stm32f411_i2c_lcd.py index b26e4411c..b1d58ad85 100644 --- a/examples/mcu/stm32f411_i2c_lcd.py +++ b/examples/mcu/stm32f411_i2c_lcd.py @@ -80,31 +80,32 @@ def render(self): print('LCD quit') pygame.quit() - def send(self, data): - self.buf.append(data) - - if len(self.buf) == 4: - up = self.buf[0] & 0xf0 - lo = self.buf[3] & 0xf0 - cmd = up | (lo >> 4) - - if self.buf[0] & 0x1: - if self.cur_col < 16 and self.cur_row < 2: - self.data[self.cur_row][self.cur_col] = cmd - self.cur_col += 1 - self.pixels = LCD.make_screen(self.data) - - elif cmd == 0x1: - self.data = [[ord(' ') for _ in range(self.cols)] for _ in range(self.rows)] - self.pixels = LCD.make_screen(self.data) - - elif up == 0x80: - self.cur_row, self.cur_col = 0, lo >> 4 - - elif up == 0xc0: - self.cur_row, self.cur_col = 1, lo >> 4 - - self.buf = [] + def send(self, buf): + for data in buf: + self.buf.append(data) + + if len(self.buf) == 4: + up = self.buf[0] & 0xf0 + lo = self.buf[3] & 0xf0 + cmd = up | (lo >> 4) + + if self.buf[0] & 0x1: + if self.cur_col < 16 and self.cur_row < 2: + self.data[self.cur_row][self.cur_col] = cmd + self.cur_col += 1 + self.pixels = LCD.make_screen(self.data) + + elif cmd == 0x1: + self.data = [[ord(' ') for _ in range(self.cols)] for _ in range(self.rows)] + self.pixels = LCD.make_screen(self.data) + + elif up == 0x80: + self.cur_row, self.cur_col = 0, lo >> 4 + + elif up == 0xc0: + self.cur_row, self.cur_col = 1, lo >> 4 + + self.buf = [] def run(self): threading.Thread(target=self.render).start()