Compare commits

..

22 Commits

Author SHA1 Message Date
ddff443183 docs: добавил README с основной информацией по использованию 2025-04-12 20:00:52 +03:00
99512fa611 chore: немного лучше описал причину грязной структуры 2025-04-12 19:37:25 +03:00
89608dcb22 chore: сменил лицензию на более официальную 2025-04-12 19:35:06 +03:00
0707a981e1 Merge branch 'hotfix/broken-encoding'
fix: исправлена ошибка кодировки при чтении файл
2025-04-12 19:34:40 +03:00
9a3399a975 fix: исправлена ошибка при чтении и записи файлов 2025-04-12 19:33:57 +03:00
7b6b3f73cd sync: тут мелкие изменения, которые я делал в процессе дебага
История разработки превращается в кашу, но что поделать, этот репозиторий поддерживается на скорую руку
2025-04-03 22:15:59 +03:00
6a4a02ffb9 sync: Пытаюсь наладить точки останова 2025-04-03 09:14:24 +03:00
1aade33b48 chore: добавил пермессивную лицензию 2025-04-02 10:15:21 +03:00
94160414aa chore: заменил импорты внутри пакета на относительные 2025-04-02 01:29:22 +03:00
4f1c9286a2 chore: переместил проект в свой пакет, чтобы избежать конфликтов с системными пакетами 2025-04-02 01:13:48 +03:00
afbe0a5dfe fix: починил ошибку в регулярном выражении для регистров 2025-04-02 00:49:37 +03:00
8e719f7f6e chore: починил возможность запускать дебаггер как исполняемый скрипт 2025-04-02 00:47:06 +03:00
4236ad59b4 chore: указал новые зависимости в pyproject.toml 2025-04-02 00:39:56 +03:00
82eb4e96ff feat: исправлено поведение консоли при переполнении сделана обработка исключений 2025-04-02 00:39:24 +03:00
eae2846925 feat: написал нормальное регулярное выражение для парсинга номеров регистров 2025-04-02 00:38:18 +03:00
a9bedc18cf chore: переместил функцию получения текущей строкиисходного кода 2025-04-02 00:37:34 +03:00
90040c9823 feat: добавлена команда сброса виртуальной машины 2025-04-02 00:17:47 +03:00
69234aac2d feat: добавил статусы виртуальной машине 2025-04-02 00:15:21 +03:00
922670be47 feat: рабочая версия дебаггера
скорее всего конкретно в TUI ничего уже меняться не будет
2025-04-01 16:50:31 +03:00
8f63f4d09f feat: заменен фреймворк для написания TUI, написана первая рабочая версия TUI 2025-04-01 16:24:56 +03:00
13f244d118 sync: в процессе разработки TUI 2025-03-31 22:03:49 +03:00
942f3c73fd feat: добавил сокращенные команды в пул и возможность получения текущей строки исходного кода 2025-03-31 22:03:12 +03:00
15 changed files with 461 additions and 148 deletions

10
LICENSE Normal file
View File

@ -0,0 +1,10 @@
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE
FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View File

@ -0,0 +1,54 @@
# DP32-proto
Прототип эмулятора процессора DP32
## Описание
Это проект программной эмуляции и отладки процессора DP32 на языке python. В этом проекте можно найти 2 компонента - отладчик dp32dbg и эмулятор dp32emu. Первый полностью опирается на отладочную информацию в следующем формате. Это очень простой отладчик и очень нестабильный, поскольку писался с упором на скорость, а не на качество.
```json
{
"src": "absolute/path/to/source"
"labels" : {
"label1": 1
"label2": 2
...
},
"instructions" : {
"<offset-in-words>" : {
"lenght": 2,
"srcline": 3
}
...
}
}
```
## Установка
Стяните данный репозиторий и выполните в корне проекта команду `pip install .`
## Использование
После установки у вас появятся 2 программы:
- dp32emu - программный эмулятор процессора dp32. Принимает на вход бинарный файл с командами dp32, выполняет их и в результате своей работы выдает дамп памяти процессора после завершения работы
- dp32dbg - отладчик, опирающийся на вышеуказанный эмулятор. Позволяет выполнять действия пошаговой отладки и предоставляет базовую функциональность точек останова (но с ньюансами)
Посмотреть инструкцию по запуску проще всего передав флаг `-h` каждой из этих программ
## Использование отладчика
help не встроен в отладчик, поскольку до этого его функционал не был документирован. Приведу здесь основной набор команд отладчика
- `step/s` - выполняет одну инструкцию, на которую сейчас указывает регистр pc виртуальной машины
- `breakpoint <lineno>` - устанавливает точку останова на определенной строке исходного кода. С точками останова 2 ньюанса:
1. Они они не могут быть установлены на комментарии или пустые строки. Если на строке больше одной операции, точка будет установлена на самую раннюю
2. Когда виртуальная машина встретит точку останова, временно она не сможет выполнять команду continue, поскольку не предусмотрено ротации точек останова. Если нужно продолжить выполнение программы дальше - выполните одну команду step
- `continue/c` - выполняет программу до ближайшей точки останова
- `reset` - сбрасывает состояние виртуальной машины и ее память, но не точки останова
- `run` - сбрасывает состояние виртуальной машины и запускает выполнение кода
- `cbp` - очищает все точки останова
- `print/p <reg>` - печатает содержимое регистров. Регистры записываются в формате `r1-r255`, позволяет также посмотреть адреса меток (указав название меток в качестве параметра) и системные регистры: pc, cc
- `inspect/i <size> <amount> <place>` - печатает содержимое памяти по определенному адресу в определенном формате (строчная - знаковые, заглавная - беззнаковые): b/B - байт, h/H - полуслова - 16 бит, w/W - слова - 32 бита. `amount` - количество подряд идущих ячеек, содержимое которых нужно прочитать. `place` - место, с которого следует начать печать. Можеть представлять собой либо имя метки, либо полный адрес в десятичной или 16-ричной системе счисления

View File

@ -5,12 +5,12 @@ description = "Add your description here"
readme = "README.md"
requires-python = ">=3.11"
dependencies = [
"rich>=14.0.0",
"py-cui>=0.1.6",
]
[project.scripts]
dp32emu = "main:main"
dp32dbg = "dbg_main:main"
dp32emu = "dp32proto.main:main"
dp32dbg = "dp32proto.dbg_main:main"
[build-system]
requires = ["setuptools"]

View File

@ -1,3 +0,0 @@
# почему так грязно?
Потому что я заколебался воевать с модульностью питона, чтобы сделать это красиво

View File

@ -1,73 +0,0 @@
from dpdebugger import Debugger
from dataclasses import dataclass
from rich import print as rprint
from rich.layout import Layout
from rich.live import Live
from rich.text import Text
from rich.console import Console
@dataclass
class SourceLines:
active: int
begining_number: int
lines: list[str]
@dataclass
class RichedSourceLines:
active: int
begining_number: int
lines: list[Text]
def _get_source_lines(
srcline: int,
lines: list[str],
max_height: int
) -> SourceLines:
"""
Собирает выборку из строк исходного кода так, чтобы она
подходила по количесту строчек
"""
# номер строки, с которого начнется отрисовка (НЕ ИНДЕКС)
start = max(srcline - max_height//2, 1)
# сколько строк, находится над "серединной"
upper_length = srcline - start
# номер строки, на которой закругляемся
end = min(
srcline + (max_height - upper_length - 1),
len(lines)
)
return SourceLines(
active=srcline,
begining_number=start,
lines=lines[start-1:end].copy()
)
def _source_lines_to_rich(srclines: SourceLines) -> RichedSourceLines:
riched_lines = RichedSourceLines(
active=srclines.active,
begining_number=srclines.begining_number,
lines=list(map(Text, srclines.lines))
)
active_index = srclines.active - srclines.begining_number
active_line = riched_lines.lines[active_index]
active_line.stylize('bold red')
return riched_lines
def main(debugger: Debugger, srcfile: str):
layout = Layout()
layout.split_column(
Layout(name="source"),
Layout(name="terminal")
)
rprint(layout)
if __name__ == "__main__":
console = Console()
with open("test.dasm", 'r') as f:
lines = f.read().split('\n')
srclines = _get_source_lines(20, lines, 7)
riched = _source_lines_to_rich(srclines)
print(f"focus: {srclines.active}; start: {srclines.begining_number}")
print("_"*20)
console.print(*riched.lines, sep="\n")

4
src/dp32proto/README.md Normal file
View File

@ -0,0 +1,4 @@
# Почему такая некрасивая структура
Этот репозиторий нужен был для того, чтобы отладить программу для процессора DP32. Структура проекта сделана такой для того, чтобы сократить время, необходимое на структуризацию и разрешение зависимостей.

98
src/dp32proto/dbg.json Normal file
View File

@ -0,0 +1,98 @@
{
"labels": {
"_start": 0,
"_start_return": 9,
"f_fibo": 11,
"f_fibo_loop": 14,
"return": 27,
"result": 28,
"end": 29
},
"instructions": {
"0": {
"length": 1,
"srcline": 6
},
"1": {
"length": 1,
"srcline": 7
},
"2": {
"length": 2,
"srcline": 8
},
"4": {
"length": 2,
"srcline": 9
},
"6": {
"length": 2,
"srcline": 10
},
"8": {
"length": 1,
"srcline": 11
},
"9": {
"length": 2,
"srcline": 13
},
"11": {
"length": 1,
"srcline": 16
},
"12": {
"length": 1,
"srcline": 17
},
"13": {
"length": 1,
"srcline": 18
},
"14": {
"length": 1,
"srcline": 20
},
"15": {
"length": 1,
"srcline": 21
},
"16": {
"length": 1,
"srcline": 22
},
"17": {
"length": 1,
"srcline": 23
},
"18": {
"length": 1,
"srcline": 24
},
"19": {
"length": 2,
"srcline": 25
},
"21": {
"length": 2,
"srcline": 27
},
"23": {
"length": 2,
"srcline": 28
},
"25": {
"length": 2,
"srcline": 29
},
"27": {
"length": 1,
"srcline": 31
},
"28": {
"length": 1,
"srcline": 32
}
},
"src": "C:\\Users\\etdia\\code\\dp32-proto\\src\\test.dasm"
}

View File

@ -4,10 +4,10 @@
"""
from typing import cast
from dpdebugger import Debugger, DbgDict, DbgInstrDesc
from .dpdebugger import Debugger, DbgDict, DbgInstrDesc
from argparse import ArgumentParser
import json
import dbg_tui
from . import dbg_tui
def parse_dbg(dbg_json_dict: dict) -> DbgDict:
"""
@ -25,7 +25,7 @@ def parse_dbg(dbg_json_dict: dict) -> DbgDict:
dbg_copy = cast(DbgDict, dbg_copy)
return dbg_copy
if __name__ == "__main__":
def main():
parser = ArgumentParser(
prog="dp32dbg",
description="Bad debugger for DP32 processor"
@ -51,7 +51,10 @@ if __name__ == "__main__":
dbg_dict: DbgDict = parse_dbg(json.load(f))
dbg = Debugger(mem, dbg_dict)
with open (dbg_dict["src"], 'r') as f:
with open (dbg_dict["src"], 'r', encoding="utf8") as f:
src = f.read()
dbg_tui.main(dbg, src)
dbg_tui.run_tui(dbg, src)
if __name__ == "__main__":
main()

164
src/dp32proto/dbg_tui.py Normal file
View File

@ -0,0 +1,164 @@
from .dpdebugger import Debugger
from .vm import VMException, VMExceptionType, VMStatus
from dataclasses import dataclass
from typing import cast
import math
import py_cui
import py_cui.keys
from collections import deque
@dataclass
class SourceLines:
active: int
begining_number: int
lines: list[str]
def _get_source_lines(
srcline: int,
lines: list[str],
max_height: int
) -> SourceLines:
"""
Собирает выборку из строк исходного кода так, чтобы она
подходила по количесту строчек
"""
if srcline > len(lines):
raise IndexError(f"Cant take line number {srcline} from passed lines, "
"index is out of range")
start_bound = srcline - math.floor(max_height//2)
end_bound = srcline + math.ceil(max_height//2)
lines_len = len(lines)
if start_bound < 1:
end_bound += abs(start_bound) + 1
if end_bound > lines_len:
start_bound -= end_bound - lines_len
start_bound = max(start_bound, 1)
end_bound = min(end_bound, lines_len)
return SourceLines(
active=srcline,
begining_number=start_bound,
lines=list(lines[start_bound-1:end_bound])
)
class DebuggerUI:
ACTIVE_LINE_POINTER = "~> "
def __init__(self, master: py_cui.PyCUI, debugger: Debugger, src: str):
self.master = master
self.dbg = debugger
self.src = src
self.srclines = src.split('\n')[:-1]
self.source_panel = self.master.add_text_block(
"Source code", 0, 0
)
self.prompt_panel = self.master.add_text_box("Prompt",
1, 0,
row_span=1
)
self.prompt_panel.add_key_command(py_cui.keys.KEY_ENTER, self._process_prompt)
self.history_index = 0
self.prompt_panel.add_key_command(py_cui.keys.KEY_UP_ARROW, self._history_up)
self.prompt_panel.add_key_command(py_cui.keys.KEY_DOWN_ARROW, self._history_down)
self.terminal_panel = self.master.add_text_block("Debugger Console",
2, 0,
initial_text="to exit type in \"exit\""
)
self.master.move_focus(self.prompt_panel)
self.master.add_key_command(
py_cui.keys.KEY_ENTER,
lambda: self.master.move_focus(self.prompt_panel)
)
self.commands_history: deque[str] = deque(maxlen=10)
self._display_source_lines()
def _process_prompt(self):
user_input = self.prompt_panel.get()
self.prompt_panel.clear()
if user_input == "exit":
exit()
if user_input == "clear":
self.terminal_panel.clear()
return
if user_input.strip() == "":
if len(self.commands_history) < 1:
return
user_input = self.commands_history[0]
self.history_index = 0
self.commands_history.appendleft("")
try:
dbg_output = self.dbg.do_command(user_input.split())
except VMException as vme:
self._terminal_append(
"VM Exception: " + vme.message
)
return
# self._terminal_append(dbg_output)
self._terminal_append(dbg_output)
self._display_source_lines()
self.commands_history[0] = user_input
def _terminal_append(self, text: str):
self.terminal_panel.write(text)
viewport_height = self.terminal_panel.get_viewport_height()
contents = self.terminal_panel.get().split('\n')[-viewport_height:]
self.terminal_panel.set_text("\n".join(contents))
def _display_source_lines(self):
if self.dbg._vm.status == VMStatus.FINISHED:
return
try:
active_line = self.dbg.get_current_source_line_number()
except KeyError:
self._terminal_append(
"Cant find source line your are on. "
"May be you reached end of program. "
)
self._terminal_append(
"Please type \"reset\" to reset vm "
"it's initial state"
)
return
lines = _get_source_lines(
srcline=active_line,
lines=self.srclines,
max_height=self.source_panel.get_viewport_height()
)
# emplace line lumbers
linenos = [lines.begining_number + i
for i in range(len(lines.lines))]
lines_with_linenos: list[str] =[]
# длина самого большого числа для вычисления выравнивания
align_with = len(str(linenos[-1]))
for lineno, line in zip(linenos, lines.lines):
if lineno == lines.active:
padding = len(line) - len(stripped := line.lstrip(" "))
line = self.ACTIVE_LINE_POINTER.rjust(padding) + stripped
lines_with_linenos.append(
f"{str(lineno).rjust(align_with)}. {line}"
)
self.source_panel.set_text("\n".join(lines_with_linenos))
def _history_up(self):
if len(self.commands_history) == 0:
return
self.history_index += 1
self.history_index %= len(self.commands_history)
self.prompt_panel.set_text(self.commands_history[self.history_index])
def _history_down(self):
if len(self.commands_history) == 0:
return
self.history_index -= 1
self.history_index %= len(self.commands_history)
self.prompt_panel.set_text(self.commands_history[self.history_index])
def run_tui(debugger: Debugger, srcfile: str):
root = py_cui.PyCUI(3, 1)
root.set_title("Stupid DP32 debugger")
root.set_status_bar_text("")
DebuggerUI(root, debugger, srcfile)
root.start()

View File

@ -3,7 +3,7 @@
хоть как-то помочь
"""
from vm import VM, VMException, Breakpoint, Condition, WORD_SIZE
from .vm import VM, VMException, Breakpoint, Condition, WORD_SIZE
from dataclasses import dataclass
from typing import TypedDict, Callable, cast
@ -55,21 +55,31 @@ class Debugger:
}
def __init__(self, mem: bytearray, dbg_dict: DbgDict):
with open(dbg_dict["src"], 'r') as f:
with open(dbg_dict["src"], 'r', encoding="utf8") as f:
self._source_lines = f.readlines()
self._vm = VM(mem)
self._dbg_dict = dbg_dict
self._breakpoints = set()
self.__init_callbacks__()
self.last_breakpoint: Breakpoint | None = None
def __init_callbacks__(self):
self._callbacks_table = {
"step": self._step,
"s": self._step,
"continue": self._continue,
"c": self._continue,
"breakpoint": self._breakpoint,
"b": self._breakpoint,
"clearbreakpoints": self._breakpointsclear,
"cbp": self._breakpointsclear,
"print": self._print,
"p": self._print,
"run": self._run,
"inspect": self._inspect
"r": self._run,
"inspect": self._inspect,
"i": self._inspect,
"reset": self._reset
}
def do_command(self, command: list[str]) -> str:
@ -78,6 +88,13 @@ class Debugger:
return "Unknown command"
return self._callbacks_table[callback_name](command[1:])
def get_current_source_line_number(self) -> int:
return self._dbg_dict["instructions"][self._vm.pc.value]['srcline']
def _reset(self, args: list[str]) -> str:
self._vm.reset()
return "VM reseted"
def _step(self, args: list[str]) -> str:
try:
self._vm.step()
@ -104,15 +121,21 @@ class Debugger:
addr = -1
# находим за линейное время. Плохая практика, но так как
# сходные коды вряд ли будут длиннее 1000 строк - приемлемо
for addr, desc in self._dbg_dict.items():
for addr, desc in self._dbg_dict["instructions"].items():
desc = cast(DbgInstrDesc, desc)
if not desc["srcline"] == desired_addr:
continue
self._breakpoints.add(int(addr))
self._vm.breakpoints.add(int(addr))
return f"Breakpoint succesfully set on line {desired_addr}"
return f"Couldn't place breakpoint on src line {desired_addr}"
def _breakpointsclear(self, args: list[str]):
self._breakpoints.clear()
self._vm.breakpoints.clear()
return "Cleared all breakpoints"
def _print(self, args: list[str]) -> str:
to_print = args[0]
if to_print == "pc":
@ -125,7 +148,10 @@ class Debugger:
f"z: {flags.z}\n"
)
elif re.fullmatch(r'r\d+', to_print):
elif re.fullmatch(
# r0-r255
r'r([0-9]|[1-9][0-9]|1[0-9][0-9]|2[1-4][1-9]|25[1-5])',
to_print):
index = int(to_print[1:])
return f"{to_print}: {self._vm.registers[index].value}"
@ -137,8 +163,9 @@ class Debugger:
def _run(self, args: list[str]) -> str:
try:
self._vm.run()
except Breakpoint:
return ""
except Breakpoint as b:
return (f'breakpoint on line '
f'{self._dbg_dict["instructions"][b.address]["srcline"]}')
return "Program finished"
def _inspect(self, args: list[str]) -> str:
@ -156,6 +183,7 @@ class Debugger:
return ("You passed wrong offset parameter. It should be either "
"name of some label or explicit decimal or hexdecimal "
"number, in the second case it should start with 0x")
offset = self._vm._to_raw_bytes_offset(offset)
try:
sym = self.UNPACK_SYMBOL_TABLE[size_arg]
@ -165,8 +193,20 @@ class Debugger:
"options as size: b/B - byte, h/H - 2 bytes, "
"w/W - 4 bytes. Big - unsigned, small - signed")
contents = struct.unpack(
sym*amount,
">"+sym*amount,
self._vm.mem[offset:offset+size*amount]
)
return f"{mem_location}:" + " ".join(map(str, contents))
def _recover_from_breakpoint(self, cur_bp: Breakpoint):
"""
Когда виртуальная машина сталкивается с точкой останова, она на
ней зависает навечно. Для продолжения работы надо текущую точку
останова убрать, сделать шаг, а потом добавить. Предыдущая точка
остановка как раз хранится в дебаггере
"""
if not self.last_breakpoint:
return
if cur_bp.address != self.last_breakpoint.address:
return
self._vm.breakpoints.remove(cur_bp.address)

View File

@ -1,5 +1,5 @@
from argparse import ArgumentParser
from vm import VM
from .vm import VM
def main():
parser = ArgumentParser(

View File

@ -2,7 +2,7 @@ from dataclasses import dataclass
from typing import ClassVar, Callable
from ctypes import c_uint32, c_int32
import struct
from optable import OPCODES, OpcodeDescription, OpL, OpA, OpF, OpD
from .optable import OPCODES, OpcodeDescription, OpL, OpA, OpF, OpD
from enum import IntFlag, Enum, auto
WORD_SIZE: int = 4
@ -25,6 +25,11 @@ class VMCC(IntFlag):
NEGATIVE = 1 << 1
ZERO = 1 << 0
class VMStatus(Enum):
RUNNING = auto()
INITED = auto()
FINISHED = auto()
@dataclass
class Breakpoint(Exception):
address: int
@ -48,15 +53,13 @@ class VM:
registers: list[c_int32]
breakpoints: set[int]
_vm_flags: VMFlags
status: VMStatus
def __init__(self, mem: bytearray):
self.mem: bytearray = mem
self.cc: VMCC = VMCC(0)
self.pc: c_uint32 = c_uint32(0)
self.registers: list[c_int32] = [c_int32(0) for _ in range(256)]
self.breakpoints: set[int] = set()
self._vm_flags: VMFlags = VMFlags(0)
self._initial_mem = mem.copy()
self.__init_callbacks__()
self.reset()
self.breakpoints: set[int] = set()
def __init_callbacks__(self):
VM.instr_callbacks = {
@ -119,16 +122,26 @@ class VM:
self._branch_indexed_callback
}
def reset(self):
self.mem: bytearray = self._initial_mem.copy()
self.cc: VMCC = VMCC(0)
self.pc: c_uint32 = c_uint32(0)
self.registers: list[c_int32] = [c_int32(0) for _ in range(256)]
self._vm_flags: VMFlags = VMFlags(0)
self.status = VMStatus.INITED
def step(self) -> None:
"""
Make one step (only step into)
"""
if self._to_raw_bytes_offset(self.pc) > len(self.mem) - WORD_SIZE:
self.status = VMStatus.FINISHED
raise VMException(
VMExceptionType.END_OF_MEM,
self.pc.value,
"couldn't perform step because end of memory occured"
)
self.status = VMStatus.RUNNING
opcode, *_ = instr = self._fetch_instr()
opdesc = self._get_opcode_desc(opcode)
args: tuple[int, ...] = self._parse_instr_fields(bytes(instr))
@ -148,14 +161,16 @@ class VM:
if self.pc.value in self.breakpoints:
raise Breakpoint(self.pc.value)
self.step()
self.status = VMStatus.FINISHED
def run(self) -> None:
"""
Run from very beginning
"""
self.pc = c_uint32(0)
self.reset()
while (self._to_raw_bytes_offset(self.pc.value) < len(self.mem)):
self.continue_()
self.status = VMStatus.FINISHED
def _fetch_instr(self) -> bytearray:
"""
@ -247,6 +262,25 @@ class VM:
cond, disp
)
@staticmethod
def _perform_ariphmetic_operation(
lhs: int,
rhs: int,
op: Callable[[int, int], int]
) -> tuple[int, VMCC]:
cc = VMCC(0)
result = op(lhs, rhs)
if result < 0:
cc |= VMCC.NEGATIVE
elif result == 0:
cc |= VMCC.ZERO
# самая дорогая проверка на переполнение)
try:
struct.pack('i', result)
except struct.error:
cc |= VMCC.OVERFLOW
return result, cc
def _math_callback_gen(
self,
operation: Callable[[int, int], int]
@ -259,10 +293,16 @@ class VM:
def callback(self, r3: int, r1: int, r2: int):
lhs = self.registers[r1].value
rhs = self.registers[r2].value
self.registers[r3] = c_int32(operation(lhs, rhs))
result, cc = self._perform_ariphmetic_operation(
lhs,
rhs,
operation)
self.registers[r3] = c_int32(result)
self.cc = cc
return callback
def _math_quick_callback_gen(
self,
operation: Callable[[int, int], int]
@ -278,24 +318,21 @@ class VM:
def callback(self, r3: int, r1: int, i8: int) -> None:
self.cc = VMCC(0)
lhs = self.registers[r1].value
result = operation(lhs, i8)
if result < 0:
self.cc |= VMCC.NEGATIVE
elif result == 0:
self.cc |= VMCC.ZERO
# самая дорогая проверка на переполнение)
try:
struct.pack('i', result)
except struct.error:
self.cc |= VMCC.OVERFLOW
result, flags = self._perform_ariphmetic_operation(
lhs,
i8,
operation)
self.registers[r3] = c_int32(result)
self.cc = flags
return callback
def _load_callback(self, r3: int, r1: int, disp: int) -> None:
addr = self._to_raw_bytes_offset(self.registers[r1].value + disp)
raw_bytes = self.mem[addr:addr+WORD_SIZE]
self.registers[r3] = c_int32(
struct.unpack(
">i", self.mem[addr:addr+WORD_SIZE])[0]
">i", raw_bytes
)[0]
)
def _store_callback(self, r3: int, r1: int, disp: int) -> None:

49
uv.lock generated
View File

@ -7,51 +7,30 @@ name = "dp32-proto"
version = "0.1.1"
source = { editable = "." }
dependencies = [
{ name = "rich" },
{ name = "py-cui" },
]
[package.metadata]
requires-dist = [{ name = "rich", specifier = ">=14.0.0" }]
requires-dist = [{ name = "py-cui", specifier = ">=0.1.6" }]
[[package]]
name = "markdown-it-py"
version = "3.0.0"
name = "py-cui"
version = "0.1.6"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "mdurl" },
]
sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 },
{ name = "windows-curses", marker = "sys_platform == 'win32'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/da/d8/bb92087dc36dc94a60993955133d0f6473237a6ce1a6145ad95dce3b2a21/py_cui-0.1.6.tar.gz", hash = "sha256:91186f33f26216cd82a676e6b02e85110a7b562d8ab9792359cfdde4ee6b9abc", size = 63009 }
[[package]]
name = "mdurl"
version = "0.1.2"
name = "windows-curses"
version = "2.4.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 },
]
[[package]]
name = "pygments"
version = "2.19.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293 },
]
[[package]]
name = "rich"
version = "14.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "markdown-it-py" },
{ name = "pygments" },
]
sdist = { url = "https://files.pythonhosted.org/packages/a1/53/830aa4c3066a8ab0ae9a9955976fb770fe9c6102117c8ec4ab3ea62d89e8/rich-14.0.0.tar.gz", hash = "sha256:82f1bc23a6a21ebca4ae0c45af9bdbc492ed20231dcb63f297d6d1021a9d5725", size = 224078 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/0d/9b/63f4c7ebc259242c89b3acafdb37b41d1185c07ff0011164674e9076b491/rich-14.0.0-py3-none-any.whl", hash = "sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0", size = 243229 },
{ url = "https://files.pythonhosted.org/packages/74/b3/46a2508fff83b5affb5a311797c584c494b4b0c4c8796a1afa2c1b1e03ac/windows_curses-2.4.1-cp311-cp311-win32.whl", hash = "sha256:4fa1a176bfcf098d0c9bb7bc03dce6e83a4257fc0c66ad721f5745ebf0c00746", size = 71129 },
{ url = "https://files.pythonhosted.org/packages/50/29/fd7c0c80177d8df55f841504a5f332b95b0002c80f82055913b6caac94e6/windows_curses-2.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:fd7d7a9cf6c1758f46ed76b8c67f608bc5fcd5f0ca91f1580fd2d84cf41c7f4f", size = 81431 },
{ url = "https://files.pythonhosted.org/packages/f4/99/60f34e51514c82631aa5c6d21eab88ce1701562de9844608411e39462a46/windows_curses-2.4.1-cp312-cp312-win32.whl", hash = "sha256:bdbe7d58747408aef8a9128b2654acf6fbd11c821b91224b9a046faba8c6b6ca", size = 71489 },
{ url = "https://files.pythonhosted.org/packages/62/8f/d908bcab1954375b156a9300fa86ceccb110f39457cd6fd59c72777626ab/windows_curses-2.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:5c9c2635faf171a229caca80e1dd760ab00db078e2a285ba2f667bbfcc31777c", size = 81777 },
{ url = "https://files.pythonhosted.org/packages/25/a0/e8d074f013117633f6b502ca123ecfc377fe0bd36818fe65e8935c91ca9c/windows_curses-2.4.1-cp313-cp313-win32.whl", hash = "sha256:05d1ca01e5199a435ccb6c8c2978df4a169cdff1ec99ab15f11ded9de8e5be26", size = 71390 },
{ url = "https://files.pythonhosted.org/packages/2b/4b/2838a829b074a68c570d54ae0ae8539979657d3e619a4dc5a4b03eb69745/windows_curses-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:8cf653f8928af19c103ae11cfed38124f418dcdd92643c4cd17239c0cec2f9da", size = 81636 },
]