m68kmac: Wrap many core parts of the OS.
This is enough to create a window & draw to it! 🎉
Signed-off-by: Jeff Epler <jepler@gmail.com>
This commit is contained in:
parent
7ff5b6cf59
commit
1d3bf9f19b
9 changed files with 868 additions and 177 deletions
2
.github/workflows/ports_m68kmac.yml
vendored
2
.github/workflows/ports_m68kmac.yml
vendored
|
|
@ -22,7 +22,7 @@ jobs:
|
|||
run: |
|
||||
apt update
|
||||
apt install -y python3-pip
|
||||
pip install pyyaml
|
||||
pip install pyyaml click
|
||||
git config --global --add safe.directory $(pwd)
|
||||
make -C mpy-cross -j$(nproc)
|
||||
make -C ports/m68kmac submodules
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ UNAME_S := $(shell uname -s)
|
|||
LD = $(CXX)
|
||||
CFLAGS += $(INC) -Wall -Wdouble-promotion -Wfloat-conversion -std=c99 $(COPT)
|
||||
CFLAGS += --param=min-pagesize=0
|
||||
CFLAGS += -save-temps=obj
|
||||
CSUPEROPT = -Os # save some code space
|
||||
|
||||
# Tune for Debugging or Optimization
|
||||
|
|
@ -43,7 +44,7 @@ SRC_C = \
|
|||
main.c \
|
||||
vfs_mac.c \
|
||||
macutil.c \
|
||||
$(BUILD)/modqd.c \
|
||||
multiverse_support.c \
|
||||
|
||||
SRC_C += \
|
||||
shared/readline/readline.c \
|
||||
|
|
@ -66,21 +67,35 @@ SRC_QSTR += \
|
|||
vfs_mac.c \
|
||||
shared/readline/readline.c \
|
||||
shared/runtime/pyexec.c \
|
||||
$(BUILD)/modqd.c \
|
||||
|
||||
.PHONY: mkapi
|
||||
|
||||
DEFS = lib/multiversal/defs/MacTypes.yaml
|
||||
TDEFS = $(patsubst %, -t %, $(DEFS))
|
||||
|
||||
# $(eval $(call multiversal_module, module name, defs files))
|
||||
define multiversal_module
|
||||
$$(BUILD)/mod$(1).c: tools/mkapi.py $(filter-out -t,$(2)) $$(DEFS)
|
||||
$$(ECHO) "MKAPI $(1)"
|
||||
$$(Q)$$(MKDIR) -p $$(BUILD)
|
||||
$$(Q)$$(PYTHON) tools/mkapi.py -o $$@ -m $(1) $$(TDEFS) $(2)
|
||||
SRC_C += $(BUILD)/mod$(1).c
|
||||
SRC_QSTR += $(BUILD)/mod$(1).c
|
||||
mkapi:: $$(BUILD)/mod$(1).c
|
||||
endef
|
||||
|
||||
$(eval $(call multiversal_module,qd,lib/multiversal/defs/QuickDraw.yaml etc/QuickDrawExtras.yaml))
|
||||
$(eval $(call multiversal_module,windowmgr,lib/multiversal/defs/WindowMgr.yaml -t lib/multiversal/defs/QuickDraw.yaml))
|
||||
$(eval $(call multiversal_module,toolboxevent,lib/multiversal/defs/ToolboxEvent.yaml -t lib/multiversal/defs/QuickDraw.yaml))
|
||||
$(eval $(call multiversal_module,toolboxutil,lib/multiversal/defs/ToolboxUtil.yaml -t lib/multiversal/defs/EventMgr.yaml -t lib/multiversal/defs/QuickDraw.yaml))
|
||||
$(eval $(call multiversal_module,mactypes,lib/multiversal/defs/MacTypes.yaml etc/MacTypesExtras.yaml))
|
||||
$(eval $(call multiversal_module,eventmgr,lib/multiversal/defs/EventMgr.yaml -t lib/multiversal/defs/QuickDraw.yaml))
|
||||
|
||||
OBJ += $(PY_CORE_O) $(PY_O)
|
||||
OBJ += $(addprefix $(BUILD)/, $(SRC_C:.c=.o))
|
||||
OBJ += $(addprefix $(BUILD)/, $(SRC_CXX:.cpp=.o))
|
||||
OBJ += $(addprefix $(BUILD)/, $(SRC_S:.s=.o))
|
||||
|
||||
.PHONY: mkapi
|
||||
mkapi: $(BUILD)/modqd.c
|
||||
|
||||
$(BUILD)/modqd.c: tools/mkapi.py
|
||||
$(ECHO) "MKAPI $@"
|
||||
$(Q)$(MKDIR) -p $(BUILD)
|
||||
$(Q)$(PYTHON) tools/mkapi.py lib/multiversal/defs/QuickDraw.yaml qd > $@
|
||||
|
||||
all: $(BUILD)/micropython.bin
|
||||
|
||||
$(BUILD)/micropython.code.bin: $(OBJ)
|
||||
|
|
@ -110,6 +125,8 @@ run:
|
|||
cp $(BASE_IMG) $(BUILD)/run.dsk
|
||||
hmount $(BUILD)/run.dsk
|
||||
hcopy $(BUILD)/micropython.bin ":Desktop Folder"
|
||||
hcopy code.py ":Desktop Folder"
|
||||
hattrib -t mupy -c mupy -t text ":Desktop Folder:code.py"
|
||||
$(UMAC) -d build/run.dsk
|
||||
|
||||
include $(TOP)/py/mkrules.mk
|
||||
|
|
|
|||
45
ports/m68kmac/code.py
Normal file
45
ports/m68kmac/code.py
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
# m68k-micropython window creation demo
|
||||
|
||||
import eventmgr
|
||||
import mactypes
|
||||
import qd
|
||||
import uctypes
|
||||
import windowmgr
|
||||
|
||||
|
||||
def let(dst, src):
|
||||
memoryview(dst)[:] = memoryview(src)[:]
|
||||
|
||||
|
||||
def pstr(s):
|
||||
b = mactypes.Str255()
|
||||
b[0] = len(s)
|
||||
for i in range(len(s)):
|
||||
b[i + 1] = ord(s[i])
|
||||
return b
|
||||
|
||||
|
||||
ev = eventmgr.EventRecord()
|
||||
|
||||
NIL_WINDOW = uctypes.struct(0, qd.GrafPort)
|
||||
ABOVE_ALL_WINDOWS = uctypes.struct(-1, qd.GrafPort)
|
||||
|
||||
title = pstr("Hello World")
|
||||
r = mactypes.Rect()
|
||||
scrn = qd.qdGlobals().screenBits
|
||||
let(r, scrn.bounds)
|
||||
r.top += 80
|
||||
qd.InsetRect(r, 25, 25)
|
||||
|
||||
w = windowmgr.NewWindow(NIL_WINDOW, r, title, True, 0, ABOVE_ALL_WINDOWS, True, 0)
|
||||
let(r, w.portRect)
|
||||
qd.SetPort(w)
|
||||
|
||||
mid_x = (r.left + r.right) // 2
|
||||
mid_y = (r.top + r.bottom) // 2
|
||||
for i in range(r.left, r.right, 2):
|
||||
qd.MoveTo(mid_x, r.bottom)
|
||||
qd.LineTo(i, r.top)
|
||||
|
||||
qd.MoveTo(mid_x, mid_y)
|
||||
input("hit enter to exit")
|
||||
1
ports/m68kmac/etc/MacTypesExtras.yaml
Normal file
1
ports/m68kmac/etc/MacTypesExtras.yaml
Normal file
|
|
@ -0,0 +1 @@
|
|||
[]
|
||||
22
ports/m68kmac/etc/QuickDrawExtras.yaml
Normal file
22
ports/m68kmac/etc/QuickDrawExtras.yaml
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
- pyverbatim:
|
||||
content: |
|
||||
#include "uart_core.h"
|
||||
mp_obj_t __init___fn(void) {
|
||||
mp_hal_stdout_tx_strn(NULL, 0); // this suffices to create the console
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_0(__init___obj, __init___fn);
|
||||
name: __init__
|
||||
- pyverbatim:
|
||||
typedef_content: |
|
||||
// because we fix the constness of DrawText in our multiversal fork but build
|
||||
// against retro68's flawed multiversal ...
|
||||
#define DrawText(a, b, c) DrawText((char *)a, b, c)
|
||||
- pyverbatim:
|
||||
typedef_content: |
|
||||
static QDGlobals *qdGlobals() {
|
||||
return &qd;
|
||||
}
|
||||
- function:
|
||||
name: qdGlobals
|
||||
return: QDGlobals*
|
||||
101
ports/m68kmac/multiverse_support.c
Normal file
101
ports/m68kmac/multiverse_support.c
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
#include "multiverse_support.h"
|
||||
|
||||
MP_DECLARE_CTYPES_STRUCT(Point_obj);
|
||||
|
||||
#define mp_obj_get_type_qstr(o_in) ((qstr)(mp_obj_get_type((o_in))->name))
|
||||
void *to_struct_helper(mp_obj_t obj, const mp_obj_type_t *struct_type, bool is_const) {
|
||||
if (obj == mp_const_none) {
|
||||
return NULL;
|
||||
}
|
||||
if (struct_type && !mp_obj_is_type(obj, struct_type)) {
|
||||
mp_raise_msg_varg(&mp_type_TypeError,
|
||||
MP_ERROR_TEXT("Expected %q, got %q"), (qstr)struct_type->name, mp_obj_get_type_qstr(obj));
|
||||
}
|
||||
mp_buffer_info_t bufinfo = {0};
|
||||
mp_get_buffer_raise(obj, &bufinfo, is_const ? MP_BUFFER_READ : MP_BUFFER_READ | MP_BUFFER_WRITE);
|
||||
return bufinfo.buf;
|
||||
}
|
||||
|
||||
|
||||
mp_obj_t from_struct_helper(void *buf, const mp_obj_type_t *type) {
|
||||
mp_obj_t args[] = { mp_obj_new_int((mp_uint_t)buf), MP_OBJ_FROM_PTR(type) };
|
||||
return uctypes_struct_make_new(type, 2, 0, args);
|
||||
}
|
||||
|
||||
void *to_scalar_helper(mp_obj_t obj, size_t objsize, bool is_const) {
|
||||
if (mp_obj_is_int(obj)) {
|
||||
return (void *)mp_obj_get_int_truncated(obj);
|
||||
}
|
||||
mp_buffer_info_t bufinfo = {0};
|
||||
mp_get_buffer_raise(obj, &bufinfo, is_const ? MP_BUFFER_READ : MP_BUFFER_READ | MP_BUFFER_WRITE);
|
||||
if (objsize > 1 && bufinfo.len != objsize) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("buffer has wrong length"));
|
||||
}
|
||||
return bufinfo.buf;
|
||||
}
|
||||
|
||||
|
||||
mp_obj_t from_scalar_helper(void *buf, size_t objsize, bool is_signed_hint) {
|
||||
return mp_obj_new_int_from_uint(*(unsigned long *)buf);
|
||||
}
|
||||
|
||||
mp_obj_t LMGet_common(long address, size_t objsize, mp_obj_t arg) {
|
||||
if (arg == mp_const_none) {
|
||||
return mp_obj_new_bytearray(objsize, (void *)address);
|
||||
}
|
||||
mp_buffer_info_t bufinfo = {0};
|
||||
mp_get_buffer_raise(arg, &bufinfo, MP_BUFFER_WRITE);
|
||||
if (bufinfo.len != objsize) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("buffer has wrong length"));
|
||||
}
|
||||
memcpy(bufinfo.buf, (void *)address, objsize);
|
||||
return arg;
|
||||
}
|
||||
void LMSet_common(long address, size_t objsize, mp_obj_t arg) {
|
||||
mp_buffer_info_t bufinfo = {0};
|
||||
mp_get_buffer_raise(arg, &bufinfo, MP_BUFFER_READ);
|
||||
if (bufinfo.len != objsize) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("buffer has wrong length"));
|
||||
}
|
||||
memcpy((void *)address, bufinfo.buf, objsize);
|
||||
}
|
||||
|
||||
Point Point_to_c(mp_obj_t obj) {
|
||||
Point result;
|
||||
if (mp_obj_len_maybe(obj) == MP_OBJ_NEW_SMALL_INT(2)) {
|
||||
result.h = mp_obj_get_int(mp_obj_subscr(obj, mp_obj_new_int(0), MP_OBJ_SENTINEL));
|
||||
result.v = mp_obj_get_int(mp_obj_subscr(obj, mp_obj_new_int(1), MP_OBJ_SENTINEL));
|
||||
} else {
|
||||
result = *(Point *)to_struct_helper(obj, (const mp_obj_type_t *)&Point_obj, true);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
pascal LONGINT GetDblTime(void) {
|
||||
return LMGetDoubleTime();
|
||||
}
|
||||
|
||||
pascal LONGINT GetCaretTime(void) {
|
||||
return LMGetCaretTime();
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
INTEGER count;
|
||||
Byte data[0];
|
||||
} patternlist;
|
||||
|
||||
pascal void GetIndPattern(Byte *pat, INTEGER patListId, INTEGER index) {
|
||||
Handle h = (Handle)GetResource('PAT#', patListId);
|
||||
if (!h) {
|
||||
return;
|
||||
}
|
||||
LoadResource(h);
|
||||
if (ResError() != noErr) {
|
||||
return;
|
||||
}
|
||||
patternlist *patterns = (patternlist *)*h;
|
||||
if (index < 0 || index >= patterns->count) {
|
||||
return;
|
||||
}
|
||||
memcpy(pat, patterns->data + 8 * index, 8);
|
||||
}
|
||||
20
ports/m68kmac/multiverse_support.h
Normal file
20
ports/m68kmac/multiverse_support.h
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
#pragma once
|
||||
#include "py/obj.h"
|
||||
#include "py/runtime.h"
|
||||
#include "extmod/moductypes.h"
|
||||
#include <Multiverse.h>
|
||||
|
||||
// Relies on gcc Variadic Macros and Statement Expressions
|
||||
#define NEW_TUPLE(...) \
|
||||
({mp_obj_t _z[] = {__VA_ARGS__}; mp_obj_new_tuple(MP_ARRAY_SIZE(_z), _z); })
|
||||
|
||||
#define ROM_TUPLE(...) \
|
||||
{{&mp_type_tuple}, MP_ARRAY_SIZE(((mp_obj_t[]) {__VA_ARGS__})), {__VA_ARGS__}}
|
||||
|
||||
void *to_struct_helper(mp_obj_t obj, const mp_obj_type_t *struct_type, bool is_const);
|
||||
mp_obj_t from_struct_helper(void *buf, const mp_obj_type_t *type);
|
||||
void *to_scalar_helper(mp_obj_t obj, size_t objsize, bool is_const);
|
||||
mp_obj_t from_scalar_helper(void *buf, size_t objsize, bool is_signed_hint);
|
||||
mp_obj_t LMGet_common(long address, size_t objsize, mp_obj_t arg);
|
||||
void LMSet_common(long address, size_t objsize, mp_obj_t arg);
|
||||
Point Point_to_c(mp_obj_t obj);
|
||||
|
|
@ -1,18 +1,313 @@
|
|||
import pathlib
|
||||
import sys
|
||||
import subprocess
|
||||
import textwrap
|
||||
import yaml
|
||||
import dataclasses
|
||||
import click
|
||||
from functools import singledispatchmethod
|
||||
from dataclasses import dataclass, Field
|
||||
from typing import Any, get_args, get_origin, Union
|
||||
|
||||
with open(sys.argv[1]) as f:
|
||||
defs = yaml.safe_load(f)
|
||||
|
||||
if len(sys.argv) > 2:
|
||||
modname = sys.argv[2]
|
||||
if sys.version_info >= (3, 10):
|
||||
from types import UnionType
|
||||
else:
|
||||
modname = pathlib.Path(sys.argv[1]).stem
|
||||
UnionType = type(Union[int, float])
|
||||
|
||||
signed_integer_types = {'uint8_t', 'uint16_t', 'uint32_t', 'Fixed', 'GrafVerb', 'CharParameter'}
|
||||
unsigned_integer_types = {'int8_t', 'int16_t', 'int32_t'}
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Ptr:
|
||||
pointee: Any
|
||||
is_const: bool = False
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.pointee}*"
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Array:
|
||||
pointee: Any
|
||||
bound: int
|
||||
is_const: bool = False
|
||||
|
||||
|
||||
@dataclass
|
||||
class CommonFields:
|
||||
comment: str | None = None
|
||||
only_for: str | None = None
|
||||
not_for: str | None = None
|
||||
api: str | None = None
|
||||
doc: str | None = None
|
||||
emit: bool = True
|
||||
|
||||
|
||||
def toplevel(cls):
|
||||
for f in dataclasses.fields(CommonFields):
|
||||
setattr(cls, f.name, f)
|
||||
cls.__annotations__[f.name] = CommonFields.__annotations__[f.name]
|
||||
return dataclass(cls)
|
||||
|
||||
|
||||
@dataclass
|
||||
class StructMember:
|
||||
name: str
|
||||
type: str
|
||||
comment: str | None = None
|
||||
|
||||
|
||||
@toplevel
|
||||
class Union:
|
||||
name: str
|
||||
members: list[StructMember] = dataclasses.field(default_factory=list)
|
||||
size: int | None = None
|
||||
|
||||
|
||||
@toplevel
|
||||
class Struct:
|
||||
name: str
|
||||
members: list[StructMember] = dataclasses.field(default_factory=list)
|
||||
size: int | None = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class EnumMember:
|
||||
name: str
|
||||
value: int | str | None = None # a number or a 4 character literal
|
||||
|
||||
|
||||
@toplevel
|
||||
class Enum:
|
||||
values: list[EnumMember]
|
||||
name: str | None = None
|
||||
|
||||
|
||||
@toplevel
|
||||
class Typedef:
|
||||
name: str
|
||||
type: str
|
||||
size: int | None = None # typedef AE_hdlr_t has this .. why?
|
||||
fulltype: any = None
|
||||
|
||||
|
||||
@toplevel
|
||||
class LowMem:
|
||||
name: str
|
||||
type: str
|
||||
address: int
|
||||
|
||||
|
||||
@toplevel
|
||||
class PyVerbatim:
|
||||
content: str | None = None
|
||||
typedef_content: str | None = None
|
||||
name: str | None = None
|
||||
|
||||
|
||||
@toplevel
|
||||
class Verbatim:
|
||||
verbatim: str
|
||||
|
||||
|
||||
@dataclass
|
||||
class Argument:
|
||||
type: str
|
||||
name: str = ""
|
||||
register: str | None = None
|
||||
|
||||
|
||||
@toplevel
|
||||
class Function:
|
||||
name: str
|
||||
trap: int | None = None
|
||||
args: list[Argument] = dataclasses.field(default_factory=list)
|
||||
executor: str | bool | None = None
|
||||
return_: str | None = None
|
||||
inline: str | None = None
|
||||
noinline: str | None = None
|
||||
dispatcher: str | None = None
|
||||
selector: int | None = None
|
||||
returnreg: str | None = None
|
||||
|
||||
|
||||
@toplevel
|
||||
class FunPtr:
|
||||
name: str
|
||||
args: list[Argument] = dataclasses.field(default_factory=list)
|
||||
return_: str | None = None
|
||||
|
||||
|
||||
@toplevel
|
||||
class Dispatcher:
|
||||
name: str
|
||||
trap: int
|
||||
selector_location: str | None = None
|
||||
|
||||
|
||||
yaml_types = {
|
||||
'function': Function,
|
||||
'funptr': FunPtr,
|
||||
'verbatim': Verbatim,
|
||||
'pyverbatim': PyVerbatim,
|
||||
'lowmem': LowMem,
|
||||
'typedef': Typedef,
|
||||
'enum': Enum,
|
||||
'struct': Struct,
|
||||
'union': Union,
|
||||
'dispatcher': Dispatcher,
|
||||
}
|
||||
|
||||
|
||||
def fix_key(k):
|
||||
k = k.replace('-', '_')
|
||||
if k == 'return':
|
||||
k += "_"
|
||||
return k
|
||||
|
||||
|
||||
def identify(y):
|
||||
for k in y.keys():
|
||||
if r := yaml_types.get(k):
|
||||
result = dict(y)
|
||||
if k != 'verbatim':
|
||||
# Verbatim just has a str value, it's not a namespace
|
||||
result.update(result.pop(k, {}))
|
||||
return result, r
|
||||
raise RuntimeError(f"Could not identify field type for {y!r}")
|
||||
|
||||
|
||||
@dataclass
|
||||
class OneOf:
|
||||
types: tuple[type]
|
||||
|
||||
def __call__(self, value):
|
||||
for t in self.types:
|
||||
try:
|
||||
return t(value)
|
||||
except TypeError:
|
||||
continue
|
||||
else:
|
||||
raise TypeError(f"Could not convert {value} to any of {self.types}")
|
||||
|
||||
|
||||
def get_field_type(field: Field[Any] | type) -> Any:
|
||||
if isinstance(field, type):
|
||||
return field
|
||||
field_type = field.type
|
||||
if isinstance(field_type, str):
|
||||
raise RuntimeError("parameters dataclass may not use 'from __future__ import annotations")
|
||||
origin = get_origin(field_type)
|
||||
if origin in (Union, UnionType):
|
||||
return OneOf(tuple(a for a in get_args(field_type) if a is not None))
|
||||
if origin is list:
|
||||
return [get_field_type(get_args(field_type)[0])]
|
||||
return field_type
|
||||
|
||||
|
||||
def yaml_to_type(y, t=None):
|
||||
if t is None:
|
||||
y, t = identify(y)
|
||||
if isinstance(t, OneOf):
|
||||
if isinstance(y, t.types):
|
||||
return y
|
||||
else:
|
||||
if isinstance(y, t):
|
||||
return y
|
||||
try:
|
||||
kwargs = {}
|
||||
fields = {f.name: f for f in dataclasses.fields(t)}
|
||||
for k, v in y.items():
|
||||
k1 = fix_key(k)
|
||||
field = fields[k1]
|
||||
field_type = get_field_type(field)
|
||||
if isinstance(v, list):
|
||||
kwargs[k1] = [yaml_to_type(vi, field_type[0]) for vi in v]
|
||||
else:
|
||||
kwargs[k1] = yaml_to_type(v, field_type)
|
||||
return t(**kwargs)
|
||||
except (KeyError, TypeError, AttributeError) as e:
|
||||
raise TypeError(f"Converting node {y} to {t}") from e
|
||||
|
||||
|
||||
def load_defs(path):
|
||||
with open(path) as f:
|
||||
defs = yaml.safe_load(f)
|
||||
return [yaml_to_type(d) for d in defs]
|
||||
|
||||
|
||||
signed_integer_types = {'uint8_t', 'uint16_t', 'uint32_t'}
|
||||
unsigned_integer_types = {'bool', 'char', 'int8_t', 'int16_t', 'int32_t'}
|
||||
all_integer_types = signed_integer_types | unsigned_integer_types
|
||||
all_float_types = {"float", "double"}
|
||||
all_scalar_types = all_integer_types | all_float_types
|
||||
|
||||
|
||||
@dataclass
|
||||
class descr_maker_scalar:
|
||||
tag: str
|
||||
|
||||
def __call__(self, emitter, offset):
|
||||
return f"MP_ROM_INT(UCTYPE_TYPE({self.tag}) | {offset})"
|
||||
|
||||
|
||||
@dataclass
|
||||
class descr_maker_struct:
|
||||
tag: str
|
||||
|
||||
def __call__(self, emitter, offset):
|
||||
obj = emitter.common_definition(
|
||||
"mp_rom_obj_tuple_t", f"ROM_TUPLE(MP_ROM_INT({offset}), MP_ROM_PTR(&{self.tag}_obj))"
|
||||
)
|
||||
return f"MP_ROM_PTR(&{obj})"
|
||||
|
||||
|
||||
@dataclass
|
||||
class descr_maker_arr_scalar:
|
||||
tag: str
|
||||
size: int
|
||||
|
||||
def __call__(self, emitter, offset):
|
||||
obj = emitter.common_definition(
|
||||
"mp_rom_obj_tuple_t",
|
||||
f"ROM_TUPLE(MP_ROM_INT({offset} | UCTYPE_AGG(ARRAY)), MP_ROM_INT({self.tag} | {self.size}))",
|
||||
)
|
||||
return f"MP_ROM_PTR(&{obj})"
|
||||
|
||||
|
||||
@dataclass
|
||||
class descr_maker_arr_struct:
|
||||
tag: str
|
||||
size: int
|
||||
|
||||
def __call__(self, emitter, offset):
|
||||
obj = emitter.common_definition(
|
||||
"mp_rom_obj_tuple_t",
|
||||
f"ROM_TUPLE(MP_ROM_INT({offset} | UCTYPE_AGG(ARRAY)), MP_ROM_INT({self.size}), MP_ROM_PTR(&{self.tag}_obj))",
|
||||
)
|
||||
return f"MP_ROM_PTR(&{obj})"
|
||||
|
||||
|
||||
@dataclass
|
||||
class descr_maker_ptr_scalar:
|
||||
tag: str
|
||||
|
||||
def __call__(self, emitter, offset):
|
||||
obj = emitter.common_definition(
|
||||
"mp_rom_obj_tuple_t",
|
||||
f"ROM_TUPLE(MP_ROM_INT({offset} | UCTYPE_AGG(PTR)), MP_ROM_INT({self.tag}))",
|
||||
)
|
||||
return f"MP_ROM_PTR(&{obj})"
|
||||
|
||||
|
||||
class descr_maker_ptr_struct:
|
||||
def __init__(self, tag):
|
||||
self.tag = tag
|
||||
|
||||
def __call__(self, emitter, offset):
|
||||
obj = emitter.common_definition(
|
||||
"mp_rom_obj_tuple_t",
|
||||
f"ROM_TUPLE(MP_ROM_INT({offset} | UCTYPE_AGG(PTR)), MP_ROM_PTR(&{self.tag}_obj))",
|
||||
)
|
||||
return f"MP_ROM_PTR(&{obj})"
|
||||
|
||||
|
||||
class PointConverter:
|
||||
|
|
@ -20,7 +315,7 @@ class PointConverter:
|
|||
return f"Point {name_c} = Point_to_c({name_py});\n"
|
||||
|
||||
def emit_to_py(self, name_c):
|
||||
return f"NEW_TUPLE({name_c}.x, {name_c}.y)"
|
||||
raise RuntimeError("not implemented")
|
||||
|
||||
|
||||
converters = {
|
||||
|
|
@ -28,7 +323,7 @@ converters = {
|
|||
}
|
||||
|
||||
|
||||
class SimpleConverter:
|
||||
class ScalarConverter:
|
||||
def __init__(self, type_c, to_c, to_py):
|
||||
self.type_c = type_c
|
||||
self.to_c = to_c
|
||||
|
|
@ -43,30 +338,58 @@ class SimpleConverter:
|
|||
def emit_to_py(self, name_c):
|
||||
return f"{self.to_py}({name_c})"
|
||||
|
||||
def emit_call_arg(self, name_c):
|
||||
return name_c
|
||||
|
||||
|
||||
class PtrConverter:
|
||||
def __init__(self, type_c):
|
||||
def __init__(self, type_c, type_obj, *, deref=False, is_const=False):
|
||||
print(f"PtrConverter({type_c} {type_obj}")
|
||||
self.type_c = type_c
|
||||
self.type_obj = type_obj
|
||||
self.deref = deref
|
||||
self.is_const = is_const
|
||||
|
||||
def emit_to_c(self, name_py, name_c):
|
||||
is_const = +self.type_c.startswith("const ") # to get 0/1, not True/False
|
||||
return f"{self.type_c} {name_c} = to_c_helper({name_py}, sizeof(*{name_c}), {is_const});\n"
|
||||
is_const = +self.is_const # to get 0/1, not True/False
|
||||
return (
|
||||
f"{self.type_c} {name_c} = to_struct_helper({name_py}, {self.type_obj}, {is_const});"
|
||||
)
|
||||
|
||||
def emit_to_py(self, name_c):
|
||||
is_signed_hint = not self.type_c.lower().startswith('u')
|
||||
return f"from_c_helper({name_c}, sizeof(*{name_c}), {+is_signed_hint})"
|
||||
return f"from_struct_helper({name_c}, {self.type_obj});"
|
||||
|
||||
def emit_call_arg(self, name_c):
|
||||
if self.deref:
|
||||
return f"*{name_c}"
|
||||
return name_c
|
||||
|
||||
|
||||
def make_converter(type_c):
|
||||
def make_converter(emitter, type_c):
|
||||
if converter := converters.get(type_c):
|
||||
return converter()
|
||||
if type_c in signed_integer_types:
|
||||
return SimpleConverter(type_c, "mp_obj_get_int", "mp_obj_new_int")
|
||||
if type_c in unsigned_integer_types:
|
||||
return SimpleConverter(type_c, "mp_obj_get_int_truncated", "mp_obj_new_int_from_uint")
|
||||
if type_c.endswith("*") or type_c.endswith("Handle") or type_c.endswith("Ptr"):
|
||||
return PtrConverter(type_c)
|
||||
raise ValueError(f"no converter possible for {type_c}")
|
||||
resolved_type = emitter.parse_type(type_c)
|
||||
print(f"{type_c} -> {resolved_type}")
|
||||
if resolved_type in signed_integer_types:
|
||||
return ScalarConverter(resolved_type, "mp_obj_get_int", "mp_obj_new_int")
|
||||
if resolved_type in emitter.funptrs:
|
||||
return ScalarConverter(
|
||||
resolved_type, f"({type_c})mp_obj_get_int_truncated", "mp_obj_new_int_from_ptr"
|
||||
)
|
||||
if resolved_type in unsigned_integer_types:
|
||||
return ScalarConverter(
|
||||
resolved_type, "mp_obj_get_int_truncated", "mp_obj_new_int_from_uint"
|
||||
)
|
||||
if isinstance(resolved_type, Ptr):
|
||||
base_type = resolved_type.pointee
|
||||
if base_type in emitter.structs:
|
||||
return PtrConverter(
|
||||
type_c, f"(const mp_obj_type_t*)&{base_type}_obj", is_const=resolved_type.is_const
|
||||
)
|
||||
emitter.info.append(f"confused about {base_type} from {resolved_type} from {type_c}")
|
||||
return PtrConverter(type_c, "NULL", is_const=resolved_type.is_const)
|
||||
print(f"note: {emitter.funptrs}")
|
||||
raise ValueError(f"no converter possible for {type_c} ({resolved_type})")
|
||||
|
||||
|
||||
class Processor:
|
||||
|
|
@ -75,132 +398,239 @@ class Processor:
|
|||
self.decls = []
|
||||
self.body = []
|
||||
self.locals = []
|
||||
self.definitions = {}
|
||||
self.info = []
|
||||
self.unknowns = set()
|
||||
self.types = {
|
||||
'Byte': 'uint8_t',
|
||||
'Boolean': 'uint8_t',
|
||||
'SignedByte': 'int8_t',
|
||||
'Sint8': 'int8_t',
|
||||
'Uint8': 'uint8_t',
|
||||
'Sint16': 'int16_t',
|
||||
'Uint16': 'uint16_t',
|
||||
'Sint32': 'int32_t',
|
||||
'Uint32': 'uint32_t',
|
||||
'ULONGINT': 'uint32_t',
|
||||
'INTEGER': 'int16_t',
|
||||
'LONGINT': 'int32_t',
|
||||
}
|
||||
self.body_dedent("""
|
||||
#include "py/obj.h"
|
||||
#include "py/runtime.h"
|
||||
#include <Multiverse.h>
|
||||
self.types = {}
|
||||
self.typedef_objs = set()
|
||||
self.structs = {}
|
||||
self.funptrs = set(("ProcPtr",))
|
||||
self.decls_dedent("""
|
||||
#include "multiverse_support.h"
|
||||
|
||||
// Relies on gcc Variadic Macros and Statement Expressions
|
||||
#define NEW_TUPLE(...) \
|
||||
({mp_obj_t _z[] = {__VA_ARGS__}; mp_obj_new_tuple(MP_ARRAY_SIZE(_z), _z); })
|
||||
|
||||
static void *to_c_helper(mp_obj_t obj, size_t objsize, bool is_const) {
|
||||
if (mp_obj_is_int(obj)) {
|
||||
return (void*)mp_obj_get_int_truncated(obj);
|
||||
}
|
||||
mp_buffer_info_t bufinfo = {0};
|
||||
mp_get_buffer_raise(obj, &bufinfo, is_const ? MP_BUFFER_READ : MP_BUFFER_READ | MP_BUFFER_WRITE);
|
||||
if (objsize > 1 && bufinfo.len != objsize) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("buffer has wrong length"));
|
||||
}
|
||||
return bufinfo.buf;
|
||||
}
|
||||
|
||||
Point Point_to_c(mp_obj_t obj) {
|
||||
Point result;
|
||||
if (mp_obj_len_maybe(obj) == MP_OBJ_NEW_SMALL_INT(2)) {
|
||||
result.h = mp_obj_get_int(mp_obj_subscr(obj, mp_obj_new_int(0), MP_OBJ_SENTINEL));
|
||||
result.v = mp_obj_get_int(mp_obj_subscr(obj, mp_obj_new_int(1), MP_OBJ_SENTINEL));
|
||||
} else {
|
||||
result = *(Point*)to_c_helper(obj, sizeof(Point), true);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
mp_obj_t from_c_helper(void *buf, size_t objsize, bool is_signed_hint) {
|
||||
return mp_obj_new_int_from_uint(*(unsigned long*)buf);
|
||||
}
|
||||
|
||||
mp_obj_t LMGet_common(long address, size_t objsize, mp_obj_t arg) {
|
||||
if (arg == mp_const_none) {
|
||||
return mp_obj_new_bytearray(objsize, (void*)address);
|
||||
}
|
||||
mp_buffer_info_t bufinfo = {0};
|
||||
mp_get_buffer_raise(arg, &bufinfo, MP_BUFFER_WRITE);
|
||||
if (bufinfo.len != objsize) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("buffer has wrong length"));
|
||||
}
|
||||
memcpy(bufinfo.buf, (void*)address, objsize);
|
||||
return arg;
|
||||
}
|
||||
void LMSet_common(long address, size_t objsize, mp_obj_t arg) {
|
||||
mp_buffer_info_t bufinfo = {0};
|
||||
mp_get_buffer_raise(arg, &bufinfo, MP_BUFFER_READ);
|
||||
if (bufinfo.len != objsize) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("buffer has wrong length"));
|
||||
}
|
||||
memcpy((void*)address, bufinfo.buf, objsize);
|
||||
}
|
||||
""")
|
||||
self.add_local("__name__", f"MP_ROM_QSTR(MP_QSTR_{self.modname})")
|
||||
|
||||
def resolve_type(self, typename):
|
||||
is_const = self.is_const(typename)
|
||||
typename = typename.removeprefix("const ")
|
||||
def common_definition(self, c_type: str, c_value: str) -> str:
|
||||
k = (c_type, c_value)
|
||||
if k not in self.definitions:
|
||||
i = len(self.definitions)
|
||||
self.definitions[k] = i
|
||||
self.decls_dedent(f"static {c_type} common_{i} = {c_value};")
|
||||
return f"common_{self.definitions[k]}"
|
||||
|
||||
while typename in self.types:
|
||||
typename = self.types[typename]
|
||||
def is_array(self, typename):
|
||||
if typename.count(']') + ("*" in typename) > 1:
|
||||
raise ValueError(
|
||||
f"array-of-array or pointer-to-array or array-of-pointers NYI {typename}"
|
||||
)
|
||||
return typename.endswith("]")
|
||||
|
||||
if is_const and self.is_ptr(typename):
|
||||
return f"const {typename}"
|
||||
def remove_array(self, typename):
|
||||
return typename.partition("[")[0]
|
||||
|
||||
return typename
|
||||
def remove_ptr(self, typename):
|
||||
return typename.removesuffix("*")
|
||||
|
||||
def remove_const(self, typename):
|
||||
return typename.removeprefix("const ")
|
||||
|
||||
def array_size(self, typename):
|
||||
return int(typename.partition("[")[2].removesuffix("]"))
|
||||
|
||||
def is_ptr(self, typename):
|
||||
return typename.endswith("*") or typename.endswith("Handle") or typename.endswith("Ptr")
|
||||
return typename.endswith("*")
|
||||
|
||||
def is_scalar(self, typename):
|
||||
if hasattr(typename, 'pointee'):
|
||||
typename = typename.pointee
|
||||
return typename in all_scalar_types
|
||||
|
||||
def is_const(self, typename):
|
||||
return typename.startswith("const ")
|
||||
|
||||
def decls_dedent(self, text):
|
||||
self.decls.append(textwrap.dedent(text.rstrip()))
|
||||
|
||||
def body_dedent(self, text):
|
||||
self.body.append(textwrap.dedent(text.rstrip()))
|
||||
|
||||
def process(self, defs):
|
||||
def parse_type(self, typestr):
|
||||
print("parse_type", typestr)
|
||||
if typestr == 'RgnHandle':
|
||||
print(self.types)
|
||||
while typestr in self.types:
|
||||
typestr = self.types[typestr]
|
||||
is_const = self.is_const(typestr)
|
||||
base_type = self.remove_const(typestr)
|
||||
if self.is_array(base_type):
|
||||
bound = self.array_size(base_type)
|
||||
base_type = self.parse_type(self.remove_array(base_type))
|
||||
return Array(base_type, bound, is_const=is_const)
|
||||
elif self.is_ptr(base_type):
|
||||
print(base_type, self.remove_ptr(base_type))
|
||||
base_type = self.parse_type(self.remove_ptr(base_type))
|
||||
return Ptr(base_type, is_const=is_const)
|
||||
else:
|
||||
if is_const:
|
||||
base_type = self.parse_type(self.remove_const(base_type))
|
||||
return dataclasses.replace(base_type, is_const=True)
|
||||
return typestr
|
||||
|
||||
def typedefs(self, defs):
|
||||
for d in defs:
|
||||
self.only_for = d.pop('only-for', None)
|
||||
self.not_for = d.pop('not-for', None)
|
||||
self.api = d.pop('api', None)
|
||||
if isinstance(d, Typedef):
|
||||
d.fulltype = self.parse_type(d.type)
|
||||
print(f"full type of {d.name} is {d.fulltype}")
|
||||
self.types[d.name] = d.type
|
||||
self.decls_dedent(f"MP_DECLARE_CTYPES_STRUCT({d.name}_obj);")
|
||||
if isinstance(d, Struct) and d.members:
|
||||
self.structs[d.name] = d
|
||||
self.decls_dedent(f"MP_DECLARE_CTYPES_STRUCT({d.name}_obj);")
|
||||
if isinstance(d, Union) and d.members:
|
||||
self.structs[d.name] = d
|
||||
self.decls_dedent(f"MP_DECLARE_CTYPES_STRUCT({d.name}_obj);")
|
||||
if isinstance(d, PyVerbatim) and d.typedef_content:
|
||||
self.decls_dedent(d.typedef_content)
|
||||
if isinstance(d, FunPtr):
|
||||
self.funptrs.add(d.name)
|
||||
|
||||
if len(d) != 1:
|
||||
raise ValueError(f"Definition with more than one key: {defs!r}")
|
||||
k, v = d.popitem()
|
||||
meth = getattr(self, 'handle_' + k, lambda v: self.handle_unknown(k, v))
|
||||
meth(v)
|
||||
def emit(self, defs):
|
||||
for d in defs:
|
||||
try:
|
||||
self.emit_node(d)
|
||||
except Exception as e:
|
||||
raise RuntimeError(f"failed to convert {d}") from e
|
||||
|
||||
def handle_unknown(self, k, v):
|
||||
if k in self.unknowns:
|
||||
@singledispatchmethod
|
||||
def emit_node(self, node):
|
||||
if type(node) in self.unknowns:
|
||||
return
|
||||
self.unknowns.add(k)
|
||||
self.info.append(f"# Unknown {k} {v!r:.55s}...")
|
||||
self.unknowns.add(type(node))
|
||||
self.info.append(f"# Unknown {node!r:.68s}...")
|
||||
|
||||
def handle_enum(self, e):
|
||||
for v in e['values']:
|
||||
if 'value' in v:
|
||||
self.locals.append(
|
||||
f"{{ MP_ROM_QSTR(MP_QSTR_{v['name']}), MP_ROM_INT({v['value']}) }},"
|
||||
)
|
||||
@emit_node.register
|
||||
def emit_typedef(self, typedef: Typedef):
|
||||
name = typedef.name
|
||||
type = typedef.type
|
||||
if type.endswith("*") or type.endswith("]"):
|
||||
make_descr = self.type_details(typedef.type)
|
||||
if make_descr is None:
|
||||
return
|
||||
offset = 0
|
||||
self.body_dedent(f"""
|
||||
MP_DEFINE_CTYPES_STRUCT({name}_obj, MP_QSTR_{name}, {make_descr(self, offset)}, LAYOUT_NATIVE);
|
||||
""")
|
||||
self.typedef_objs.add(name)
|
||||
self.add_local(name)
|
||||
offset = 0
|
||||
|
||||
def type_details(self, typename):
|
||||
if typename in self.funptrs:
|
||||
return None
|
||||
fulltype = self.parse_type(typename)
|
||||
is_ptr = isinstance(fulltype, Ptr)
|
||||
is_array = isinstance(fulltype, Array)
|
||||
basetypename = fulltype if isinstance(fulltype, str) else fulltype.pointee
|
||||
|
||||
if basetypename in all_scalar_types:
|
||||
u = "U" if basetypename.startswith("u") else ""
|
||||
if basetypename == "bool":
|
||||
type_str = "UINT8"
|
||||
elif basetypename == "char":
|
||||
type_str = "INT8"
|
||||
elif basetypename == "double":
|
||||
type_str = "FLOAT64"
|
||||
elif basetypename == "float":
|
||||
type_str = "FLOAT32"
|
||||
elif basetypename.endswith(("32", "32_t")):
|
||||
type_str = f"{u}INT32"
|
||||
elif basetypename.endswith(("16", "16_t")):
|
||||
type_str = f"{u}INT16"
|
||||
elif basetypename.endswith(("8", "8_t")):
|
||||
type_str = f"{u}INT8"
|
||||
else:
|
||||
raise RuntimeError(f"teach me about {basetypename}")
|
||||
|
||||
if is_ptr:
|
||||
descr = descr_maker_ptr_scalar(type_str)
|
||||
elif is_array:
|
||||
descr = descr_maker_arr_scalar(type_str, fulltype.bound)
|
||||
else:
|
||||
descr = descr_maker_scalar(type_str)
|
||||
else:
|
||||
if is_ptr:
|
||||
if basetypename in self.structs:
|
||||
descr = descr_maker_ptr_struct(basetypename)
|
||||
else:
|
||||
descr = descr_maker_ptr_scalar("UINT8")
|
||||
elif is_array:
|
||||
descr = descr_maker_arr_struct(basetypename, fulltype.bound)
|
||||
else:
|
||||
descr = descr_maker_struct(basetypename)
|
||||
|
||||
return descr
|
||||
|
||||
def struct_make_table(self, struct):
|
||||
rows = []
|
||||
for member in struct.members:
|
||||
make_descr = self.type_details(member.type)
|
||||
if make_descr is None:
|
||||
continue
|
||||
offset = f"offsetof({struct.name}, {member.name})"
|
||||
rows.append(f"{{ MP_ROM_QSTR(MP_QSTR_{member.name}), {make_descr(self, offset)} }},")
|
||||
return "\n".join(rows)
|
||||
|
||||
def union_make_table(self, union):
|
||||
rows = []
|
||||
for member in union.members:
|
||||
make_descr = self.type_details(member.type)
|
||||
if make_descr is None:
|
||||
continue
|
||||
rows.append(f"{{ MP_ROM_QSTR(MP_QSTR_{member.name}), {make_descr(self, 0)} }},")
|
||||
return "\n".join(rows)
|
||||
|
||||
@emit_node.register
|
||||
def emit_union(self, e: Union):
|
||||
name = e.name
|
||||
self.body_dedent(f"""
|
||||
static const mp_rom_map_elem_t {name}_descr_table[] = {{
|
||||
{self.union_make_table(e)}
|
||||
}};
|
||||
static MP_DEFINE_CONST_DICT({name}_descr_dict, {name}_descr_table);
|
||||
MP_DEFINE_CTYPES_STRUCT({name}_obj, MP_QSTR_{name}, MP_ROM_PTR((void*)&{name}_descr_dict), LAYOUT_NATIVE);
|
||||
""")
|
||||
self.add_local(name)
|
||||
|
||||
@emit_node.register
|
||||
def emit_struct(self, e: Struct):
|
||||
name = e.name
|
||||
ee = self.structs.get(e.name)
|
||||
if ee and (e is not ee):
|
||||
print(f"omitting {e} in favor of {ee}")
|
||||
return # better defn available
|
||||
self.body_dedent(f"""
|
||||
static const mp_rom_map_elem_t {name}_descr_table[] = {{
|
||||
{self.struct_make_table(e)}
|
||||
}};
|
||||
static MP_DEFINE_CONST_DICT({name}_descr_dict, {name}_descr_table);
|
||||
MP_DEFINE_CTYPES_STRUCT({name}_obj, MP_QSTR_{name}, MP_ROM_PTR((void*)&{name}_descr_dict), LAYOUT_NATIVE);
|
||||
""")
|
||||
self.add_local(name)
|
||||
|
||||
@emit_node.register
|
||||
def emit_enum(self, e: Enum):
|
||||
for v in e.values:
|
||||
if v.value is not None and v.name is not None:
|
||||
self.locals.append(f"{{ MP_ROM_QSTR(MP_QSTR_{v.name}), MP_ROM_INT({v.value}) }},")
|
||||
# else:
|
||||
# self.info.append(f"enumerant without value: {v['name']}")
|
||||
|
||||
def handle_lowmem(self, lm):
|
||||
name = lm['name']
|
||||
address = lm['address']
|
||||
typename = lm['type']
|
||||
@emit_node.register
|
||||
def emit_lowmem(self, lm: LowMem):
|
||||
name = lm.name
|
||||
address = lm.address
|
||||
typename = lm.type
|
||||
self.body_dedent(f"""
|
||||
static mp_obj_t LMGet{name}_fn(size_t n_args, const mp_obj_t *args) {{
|
||||
return LMGet_common({address}, sizeof({typename}), n_args == 0 ? mp_const_none : args[0]);
|
||||
|
|
@ -213,19 +643,25 @@ class Processor:
|
|||
}}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(LMSet{name}_obj, LMSet{name}_fn);
|
||||
""")
|
||||
self.locals.append(
|
||||
f"{{ MP_ROM_QSTR(MP_QSTR_LMGet{name}), MP_ROM_PTR(&LMGet{name}_obj) }},"
|
||||
)
|
||||
self.locals.append(
|
||||
f"{{ MP_ROM_QSTR(MP_QSTR_LMSet{name}), MP_ROM_PTR(&LMSet{name}_obj) }},"
|
||||
)
|
||||
self.add_local(f"LMGet{name}")
|
||||
self.add_local(f"LMSet{name}")
|
||||
|
||||
def handle_typedef(self, v):
|
||||
pass # ignore typedefs for now ??
|
||||
def add_local(self, name, value=...):
|
||||
if value is ...:
|
||||
value = f"MP_ROM_PTR(&{name}_obj)"
|
||||
self.locals.append(f"{{ MP_ROM_QSTR(MP_QSTR_{name}), {value} }},")
|
||||
|
||||
def handle_verbatim(self, v):
|
||||
@emit_node.register
|
||||
def emit_verbatim(self, v: Verbatim):
|
||||
pass # Ignore verbatim blocks
|
||||
|
||||
@emit_node.register
|
||||
def emit_pyverbatim(self, v: PyVerbatim):
|
||||
if v.content:
|
||||
self.body.append(v.content)
|
||||
if v.name is not None:
|
||||
self.add_local(v.name)
|
||||
|
||||
# {'args': [{'name': 'src_bitmap', 'type': 'BitMap*'},
|
||||
# {'name': 'dst_bitmap', 'type': 'BitMap*'},
|
||||
# {'name': 'src_rect', 'type': 'const Rect*'},
|
||||
|
|
@ -238,13 +674,14 @@ class Processor:
|
|||
|
||||
def fun_declare_args_enum(self, args):
|
||||
if args:
|
||||
args = ", ".join(f"ARG_{arg['name']}" for arg in args)
|
||||
argnames = [arg.name or f"arg{i}" for i, arg in enumerate(args)]
|
||||
args = ", ".join(f"ARG_{argname}" for argname in argnames)
|
||||
return f"enum {{ {args} }};"
|
||||
return ""
|
||||
|
||||
@staticmethod
|
||||
def fun_declare_allowed_arg(arg):
|
||||
name = arg['name']
|
||||
name = arg.name
|
||||
return f"{{ MP_QSTR_{name}, MP_ARG_OBJ | MP_ARG_REQUIRED, {{0}}, }},"
|
||||
|
||||
def fun_parse_args(self, args):
|
||||
|
|
@ -259,65 +696,73 @@ class Processor:
|
|||
return "\n".join(f" {line}" for line in body)
|
||||
|
||||
def make_converter(self, typename):
|
||||
if not typename.endswith('*'):
|
||||
typename = self.resolve_type(typename)
|
||||
return make_converter(typename)
|
||||
return make_converter(self, typename)
|
||||
|
||||
def fun_convert_arg(self, idx, arg):
|
||||
return self.make_converter(arg['type']).emit_to_c(f"args[{idx}].u_obj", arg['name'])
|
||||
return self.make_converter(arg.type).emit_to_c(
|
||||
f"args[{idx}].u_obj", arg.name or f"arg{idx}"
|
||||
)
|
||||
|
||||
def fun_convert_args(self, args):
|
||||
return "".join(" " + self.fun_convert_arg(i, a) for i, a in enumerate(args))
|
||||
|
||||
def fun_call_fun(self, fun):
|
||||
return_type = fun.get('return', None)
|
||||
args = fun.get('args', [])
|
||||
fun_args = ", ".join(arg['name'] for arg in args)
|
||||
funcall = f"{fun['name']}({fun_args});"
|
||||
return_type = fun.return_
|
||||
args = fun.args
|
||||
argnames = [arg.name or f"arg{i}" for i, arg in enumerate(args)]
|
||||
print(argnames)
|
||||
fun_args = ", ".join(argnames)
|
||||
if fun.inline:
|
||||
funcall = f"{fun.inline};"
|
||||
funcall = f"{fun.name}({fun_args});"
|
||||
if return_type:
|
||||
funcall = f"{return_type} retval = {funcall}"
|
||||
return " " + funcall
|
||||
|
||||
def fun_convert_return(self, fun):
|
||||
return_type = fun.get('return', None)
|
||||
return_type = fun.return_
|
||||
if return_type:
|
||||
converter = self.make_converter(return_type)
|
||||
return f" return {converter.emit_to_py('retval')};// TODO"
|
||||
return f" return {converter.emit_to_py('retval')};"
|
||||
else:
|
||||
return " return mp_const_none;"
|
||||
|
||||
def handle_function(self, fun):
|
||||
name = fun['name']
|
||||
args = fun.get('args', [])
|
||||
if self.api == 'carbon':
|
||||
@emit_node.register
|
||||
def emit_function(self, node: Function):
|
||||
name = node.name
|
||||
args = node.args
|
||||
if node.api == 'carbon':
|
||||
return
|
||||
self.body_dedent(f"""
|
||||
mp_obj_t {name}_fn(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {{
|
||||
{self.fun_parse_args(args)}
|
||||
{self.fun_convert_args(args)}
|
||||
{self.fun_call_fun(fun)}
|
||||
{self.fun_convert_return(fun)}
|
||||
{self.fun_call_fun(node)}
|
||||
{self.fun_convert_return(node)}
|
||||
}}
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW({name}_obj, {len(args)}, {name}_fn);
|
||||
""")
|
||||
self.locals.append(f"{{ MP_ROM_QSTR(MP_QSTR_{name}), MP_ROM_PTR(&{name}_obj) }},")
|
||||
self.add_local(name)
|
||||
|
||||
def handle_funptr(self, fun):
|
||||
@emit_node.register
|
||||
def emit_funptr(self, node: FunPtr):
|
||||
pass # Ignore function pointers for now
|
||||
|
||||
def emit(self):
|
||||
def make_output(self, target):
|
||||
def do_print(*args):
|
||||
print(*args, file=target)
|
||||
|
||||
for row in self.decls:
|
||||
print(row)
|
||||
do_print(row)
|
||||
for row in self.body:
|
||||
print(row)
|
||||
print()
|
||||
print("static const mp_rom_map_elem_t module_globals_table[] = {")
|
||||
print(f" {{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_{self.modname}) }},")
|
||||
do_print(row)
|
||||
do_print()
|
||||
do_print("static const mp_rom_map_elem_t module_globals_table[] = {")
|
||||
for row in self.locals:
|
||||
print(f" {row}")
|
||||
print("};")
|
||||
print("static MP_DEFINE_CONST_DICT(module_globals, module_globals_table);")
|
||||
print(
|
||||
do_print(f" {row}")
|
||||
do_print("};")
|
||||
do_print("static MP_DEFINE_CONST_DICT(module_globals, module_globals_table);")
|
||||
do_print(
|
||||
textwrap.dedent(f"""
|
||||
const mp_obj_module_t {self.modname}_module = {{
|
||||
.base = {{ &mp_type_module }},
|
||||
|
|
@ -331,6 +776,31 @@ class Processor:
|
|||
print(row, file=sys.stderr)
|
||||
|
||||
|
||||
p = Processor(modname)
|
||||
p.process(defs)
|
||||
p.emit()
|
||||
@click.command
|
||||
@click.argument("defs_files", type=click.Path(path_type=pathlib.Path, exists=True), nargs=-1)
|
||||
@click.option("-o", "--output", type=click.Path(path_type=pathlib.Path), default=None)
|
||||
@click.option(
|
||||
"-t", "--typedefs", multiple=True, type=click.Path(path_type=pathlib.Path, exists=True)
|
||||
)
|
||||
@click.option("-m", "--modname", type=str)
|
||||
def main(defs_files, output, modname, typedefs):
|
||||
if output is None:
|
||||
output = pathlib.Path(f"mod{modname}.c")
|
||||
processor = Processor(modname)
|
||||
|
||||
for t in typedefs:
|
||||
defs = load_defs(t)
|
||||
processor.typedefs(defs)
|
||||
|
||||
defs = []
|
||||
for f in defs_files:
|
||||
defs.extend(load_defs(f))
|
||||
processor.typedefs(defs)
|
||||
processor.emit(defs)
|
||||
|
||||
with open(output, "w") as f:
|
||||
processor.make_output(f)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -55,6 +55,10 @@
|
|||
#include "genhdr/mpversion.h"
|
||||
#include "input.h"
|
||||
|
||||
#if defined(MICROPY_UNIX_COVERAGE)
|
||||
#include "extmod/moductypes.h"
|
||||
#endif
|
||||
|
||||
// Command line options, with their defaults
|
||||
static bool compile_only = false;
|
||||
static uint emit_opt = MP_EMIT_OPT_NONE;
|
||||
|
|
@ -609,10 +613,21 @@ MP_NOINLINE int main_(int argc, char **argv) {
|
|||
|
||||
#if defined(MICROPY_UNIX_COVERAGE)
|
||||
{
|
||||
static const mp_rom_map_elem_t rect_descr_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR_top), MP_ROM_INT(UCTYPE_TYPE(INT16) | 0) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_left), MP_ROM_INT(UCTYPE_TYPE(INT16) | 2) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_bottom), MP_ROM_INT(UCTYPE_TYPE(INT16) | 4) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_right), MP_ROM_INT(UCTYPE_TYPE(INT16) | 6) },
|
||||
};
|
||||
static MP_DEFINE_CONST_DICT(rect_descr_dict, rect_descr_table);
|
||||
static MP_DEFINE_CTYPES_STRUCT(rect_type_obj, MP_QSTR_Rect, MP_ROM_PTR((void *)&rect_descr_dict), LAYOUT_NATIVE);
|
||||
|
||||
MP_DECLARE_CONST_FUN_OBJ_0(extra_coverage_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_0(extra_cpp_coverage_obj);
|
||||
|
||||
mp_store_global(MP_QSTR_extra_coverage, MP_OBJ_FROM_PTR(&extra_coverage_obj));
|
||||
mp_store_global(MP_QSTR_extra_cpp_coverage, MP_OBJ_FROM_PTR(&extra_cpp_coverage_obj));
|
||||
mp_store_global(MP_QSTR_Rect, MP_OBJ_FROM_PTR(&rect_type_obj));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue