diff --git a/.github/workflows/ports_m68kmac.yml b/.github/workflows/ports_m68kmac.yml index 15b76f2d082c4..b0ca331ee7a25 100644 --- a/.github/workflows/ports_m68kmac.yml +++ b/.github/workflows/ports_m68kmac.yml @@ -15,14 +15,11 @@ jobs: build: permissions: write-all runs-on: ubuntu-latest - container: ghcr.io/autc04/retro68 + container: ghcr.io/m68k-micropython/builder:latest steps: - uses: actions/checkout@v4 - name: Build run: | - apt update - apt install -y python3-pip - pip install pyyaml click git config --global --add safe.directory $(pwd) make -C mpy-cross -j$(nproc) make -C ports/m68kmac submodules diff --git a/extmod/moductypes.c b/extmod/moductypes.c index d0c3178a9fe16..b3a20937e2053 100644 --- a/extmod/moductypes.c +++ b/extmod/moductypes.c @@ -43,11 +43,13 @@ #define IS_SCALAR_ARRAY_OF_BYTES(tuple_desc) (GET_TYPE(MP_OBJ_SMALL_INT_VALUE((tuple_desc)->items[1]), VAL_TYPE_BITS) == UINT8) // "struct" in uctypes context means "structural", i.e. aggregate, type. -static const mp_obj_type_t uctypes_struct_type; +const mp_obj_type_t uctypes_struct_type; // Get size of any type descriptor static mp_uint_t uctypes_struct_size(mp_obj_t desc_in, int layout_type, mp_uint_t *max_field_size); +static mp_obj_t uctypes_struct_attr_op(mp_obj_t self_in, qstr attr, mp_obj_t set_val); + #define CTYPES_FLAGS_SIZE_BITS (2) #define CTYPES_OFFSET_SIZE_BITS (8 * sizeof(uint32_t) - 2) @@ -75,6 +77,11 @@ static bool is_struct_type(mp_obj_t obj_in) { return make_new == uctypes_struct_type_make_new; } +static bool is_struct_instance(mp_obj_t obj_in) { + const mp_obj_type_t *type = mp_obj_get_type(obj_in); + return MP_OBJ_TYPE_GET_SLOT_OR_NULL(type, subscr) == uctypes_struct_subscr; +} + mp_obj_t uctypes_struct_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { mp_arg_check_num(n_args, n_kw, 2, 3, false); mp_obj_t desc = args[1]; @@ -149,6 +156,9 @@ mp_obj_t uctypes_struct_type_make_new(const mp_obj_type_t *type_in, size_t n_arg } else if (agg_type != PTR) { syntax_error(); } + if (n_kw) { + mp_raise_TypeError(MP_ERROR_TEXT("struct: no fields")); + } } else { mp_obj_dict_t *d = MP_OBJ_TO_PTR(desc); // only for packed ROM tables.. @@ -161,10 +171,17 @@ mp_obj_t uctypes_struct_type_make_new(const mp_obj_type_t *type_in, size_t n_arg for (size_t i = 0; i < n_args; i++) { mp_store_attr(result, mp_obj_str_get_qstr(d->map.table[i].key), args[i]); } - } - args += n_args; - for (size_t i = 0; i < 2 * n_kw; i += 2) { - mp_store_attr(result, mp_obj_str_get_qstr(args[i]), args[i + 1]); + args += n_args; + for (size_t i = 0; i < 2 * n_kw; i += 2) { + qstr q = mp_obj_str_get_qstr(args[i]); + for (size_t j = 0; j < n_args; j++) { + if (mp_obj_str_get_qstr(d->map.table[j].key) == q) { + mp_raise_msg_varg(&mp_type_TypeError, + MP_ERROR_TEXT("function got multiple values for argument '%q'"), q); + } + } + mp_store_attr(result, q, args[i + 1]); + } } return result; } @@ -173,8 +190,10 @@ mp_obj_t uctypes_struct_type_make_new(const mp_obj_type_t *type_in, size_t n_arg void uctypes_struct_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { (void)kind; mp_obj_uctypes_struct_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_dict_t *d = 0; const char *typen = "unk"; if (mp_obj_is_dict_or_ordereddict(self->desc)) { + d = MP_OBJ_TO_PTR(self->desc); typen = "STRUCT"; } else if (mp_obj_is_type(self->desc, &mp_type_tuple)) { mp_obj_tuple_t *t = MP_OBJ_TO_PTR(self->desc); @@ -192,7 +211,17 @@ void uctypes_struct_print(const mp_print_t *print, mp_obj_t self_in, mp_print_ki typen = "ERROR"; } - mp_printf(print, "<%q %s %p>", (qstr)mp_obj_get_type_qstr(self_in), typen, struct_addr(self)); + mp_printf(print, "<%q %s %p", (qstr)mp_obj_get_type_qstr(self_in), typen, struct_addr(self)); + if (kind == PRINT_REPR && d) { + for (mp_uint_t i = 0; i < d->map.alloc; i++) { + if (mp_map_slot_is_filled(&d->map, i)) { + qstr k = mp_obj_str_get_qstr(d->map.table[i].key); + mp_obj_t attr = uctypes_struct_attr_op(self_in, k, MP_OBJ_NULL); + mp_printf(print, "\n %q=%r", k, attr); + } + } + } + mp_printf(print, ">"); } // Get size of scalar type descriptor @@ -346,13 +375,25 @@ static mp_obj_t uctypes_struct_sizeof(size_t n_args, const mp_obj_t *args) { } static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(uctypes_struct_sizeof_obj, 1, 2, uctypes_struct_sizeof); +mp_obj_t uctypes_get_struct_desc(mp_obj_t arg) { + if (is_struct_instance(arg)) { + mp_obj_uctypes_struct_t *struct_ = MP_OBJ_TO_PTR(arg); + return struct_->desc; + } + if (is_struct_type(arg)) { + mp_obj_ctypes_struct_type_t *struct_type = MP_OBJ_TO_PTR(arg); + return struct_type->desc; + } + return MP_OBJ_NULL; +} static mp_obj_t uctypes_struct_desc(mp_obj_t arg) { - if (!is_struct_type(arg)) { + mp_obj_t result = uctypes_get_struct_desc(arg); + if (result == MP_OBJ_NULL) { mp_raise_TypeError(NULL); } - mp_obj_ctypes_struct_type_t *struct_type = MP_OBJ_TO_PTR(arg); - return struct_type->desc; + return result; } + MP_DEFINE_CONST_FUN_OBJ_1(uctypes_struct_desc_obj, uctypes_struct_desc); static const char type2char[16] = { @@ -757,7 +798,7 @@ static mp_obj_t uctypes_struct_bytes_at(mp_obj_t ptr, mp_obj_t size) { } MP_DEFINE_CONST_FUN_OBJ_2(uctypes_struct_bytes_at_obj, uctypes_struct_bytes_at); -static MP_DEFINE_CONST_OBJ_TYPE( +MP_DEFINE_CONST_OBJ_TYPE( uctypes_struct_type, MP_QSTR_struct, MP_TYPE_FLAG_NONE, diff --git a/extmod/moductypes.h b/extmod/moductypes.h index 1806cb9ffc23a..95bca49b1fccd 100644 --- a/extmod/moductypes.h +++ b/extmod/moductypes.h @@ -30,6 +30,8 @@ #if MICROPY_PY_UCTYPES #include "py/obj.h" +extern const mp_obj_type_t uctypes_struct_type; + typedef struct _mp_obj_ctypes_struct_type_t { // This is a mp_obj_type_t with six slots. mp_obj_empty_type_t base; @@ -45,12 +47,14 @@ mp_obj_t uctypes_struct_unary_op(mp_unary_op_t op, mp_obj_t self_in); mp_int_t uctypes_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags); mp_obj_t uctypes_struct_type_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args); mp_obj_t uctypes_struct_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args); +// may call on an instance or named struct type +mp_obj_t uctypes_get_struct_desc(const mp_obj_t obj); #define MP_DECLARE_CTYPES_STRUCT(type_name) \ - extern mp_obj_ctypes_struct_type_t type_name; + extern const mp_obj_ctypes_struct_type_t type_name; #define MP_DEFINE_CTYPES_STRUCT(type_name, name_, desc_, flags_) \ - mp_obj_ctypes_struct_type_t type_name = { \ + const mp_obj_ctypes_struct_type_t type_name = { \ .base = { \ .base = { &mp_type_type }, \ .flags = MP_TYPE_FLAG_NONE, \ diff --git a/extmod/multiversal.mk b/extmod/multiversal.mk new file mode 100644 index 0000000000000..4244bc9a9f9fe --- /dev/null +++ b/extmod/multiversal.mk @@ -0,0 +1,14 @@ +MKAPIFORMAT ?= +# $(eval $(call multiversal_module, module name, defs files)) +define multiversal_module +$$(BUILD)/mod$(1).c: $(TOP)/tools/mkapi.py $(filter-out -t,$(2)) $$(DEFS) + $$(ECHO) "MKAPI $(1)" + $$(Q)$$(MKDIR) -p $$(BUILD) + $$(Q)$$(PYTHON) $(TOP)/tools/mkapi.py $(MKAPIFORMAT) -o $$@ -m $(1) $$(TDEFS) $(2) +SRC_C += $(BUILD)/mod$(1).c +SRC_QSTR += $(BUILD)/mod$(1).c +mkapi:: $$(BUILD)/mod$(1).c +endef + +SRC_C += extmod/multiverse_support.c +SRC_QSTR += extmod/multiverse_support.c diff --git a/extmod/multiverse_support.c b/extmod/multiverse_support.c new file mode 100644 index 0000000000000..826c04cd6fced --- /dev/null +++ b/extmod/multiverse_support.c @@ -0,0 +1,219 @@ +#include "extmod/multiverse_support.h" + +#if __m68k +#define lm_base ((char *)0) +#else +char lm_base[65536]; +#endif + +MP_DECLARE_CTYPES_STRUCT(Point_obj); + +#define DEFINE_PTR_SCALAR(type_c, tag) \ + const mp_rom_obj_tuple_t type_c##_ptr_tuple = ROM_TUPLE(MP_ROM_INT(UCTYPE_AGG(PTR)), MP_ROM_INT(UCTYPE_TYPE(tag))); \ + MP_DEFINE_CTYPES_STRUCT(type_c##_obj, MP_QSTR_##type_c, MP_ROM_PTR((void *)&type_c##_ptr_tuple), LAYOUT_NATIVE) + +DEFINE_PTR_SCALAR(bool, UINT8); +DEFINE_PTR_SCALAR(char, UINT8); +DEFINE_PTR_SCALAR(uint8_t, UINT8); +DEFINE_PTR_SCALAR(uint16_t, UINT16); +DEFINE_PTR_SCALAR(uint32_t, UINT32); +DEFINE_PTR_SCALAR(uint64_t, UINT64); +DEFINE_PTR_SCALAR(int8_t, INT8); +DEFINE_PTR_SCALAR(int16_t, INT16); +DEFINE_PTR_SCALAR(int32_t, INT32); +DEFINE_PTR_SCALAR(int64_t, INT64); +DEFINE_PTR_SCALAR(float, FLOAT32); +DEFINE_PTR_SCALAR(double, FLOAT64); + +static mp_uint_t get_agg_type(mp_obj_tuple_t *sub) { + mp_int_t offset = MP_OBJ_SMALL_INT_VALUE(sub->items[0]); + mp_uint_t agg_type = GET_TYPE(offset, AGG_TYPE_BITS); + return agg_type; +} + +static mp_uint_t get_val_type(mp_obj_tuple_t *sub) { + mp_int_t offset = MP_OBJ_SMALL_INT_VALUE(sub->items[1]); + mp_uint_t val_type = GET_TYPE(offset, VAL_TYPE_BITS); + return val_type; +} + +static bool arg_compatible(const mp_obj_t arg_descr, const mp_obj_t type_descr) { + // types are compatible if their descriptors are equal + if (mp_obj_equal(arg_descr, type_descr)) { + return true; + } + + // Otherwise, they are compatible if + // * type is a pointer to T, and + // * arg is a pointer to T or array of T + if (!mp_obj_is_type(type_descr, &mp_type_tuple)) { + return false; + } + mp_obj_tuple_t *type_sub = MP_OBJ_TO_PTR(type_descr); + if (get_agg_type(type_descr) != PTR) { + return false; + } + + if (!mp_obj_is_type(arg_descr, &mp_type_tuple)) { + return false; + } + mp_obj_tuple_t *arg_sub = MP_OBJ_TO_PTR(arg_descr); + + if (arg_sub->len == 2) { + // argument is a pointer-to-scalar or array-of-scalar, compare scalar types + return get_val_type(type_sub) == get_val_type(arg_sub); + } else { + // argument is array-of-struct, compare descriptors + return mp_obj_equal(type_sub->items[1], arg_sub->items[2]); + } +} + +static bool deref_compatible(const mp_obj_t arg_descr, const mp_obj_t type_descr) { + if (!mp_obj_is_type(arg_descr, &mp_type_tuple)) { + return false; + } + mp_obj_tuple_t *arg_sub = MP_OBJ_TO_PTR(arg_descr); + if (get_agg_type(arg_sub) != PTR) { + return false; + } + bool r = mp_obj_equal(type_descr, arg_sub->items[1]); + return r; +} + +#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, qstr fieldname) { + if (obj == mp_const_none) { + return NULL; + } + if (struct_type && !mp_obj_is_type(obj, struct_type)) { + mp_obj_t arg_descr = uctypes_get_struct_desc(obj); + mp_obj_t type_descr = uctypes_get_struct_desc(MP_OBJ_FROM_PTR(struct_type)); + + if (deref_compatible(arg_descr, MP_OBJ_FROM_PTR(struct_type))) { + // Consider the case of + // ```py + // w = WindowMgr.NewWindow(...) + // qd.SetPort(w) + // ``` + // which would otherwise say `TypeError: p must be of type GrafPort, not GrafPtr + // + // (currently) mkapi treats the argument to SetPort + // as a GrafPort. But `GrafPtr` is a typedef to `GrafPort*`. It might be + // better to fix this in mkapi, but instead cater here... + + mp_buffer_info_t bufinfo = {0}; + mp_get_buffer_raise(obj, &bufinfo, is_const ? MP_BUFFER_READ : MP_BUFFER_READ | MP_BUFFER_WRITE); + void *result = *(void **)bufinfo.buf; + return result; + } + if (!arg_compatible(arg_descr, type_descr)) { + mp_raise_msg_varg(&mp_type_TypeError, + MP_ERROR_TEXT("%q must be of type %q, not %q"), fieldname, (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; +} + +static const mp_rom_map_elem_t empty_table[] = {}; +static MP_DEFINE_CONST_DICT(empty_dict, empty_table); + +mp_obj_t from_struct_helper(void *buf, const mp_obj_type_t *type) { + if (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); + } else { + mp_obj_t args[] = { mp_obj_new_int((mp_uint_t)buf), MP_OBJ_FROM_PTR(&empty_dict) }; + return uctypes_struct_make_new(&uctypes_struct_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, qstr fieldname) { + 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, fieldname); + } + return result; +} + +void *void_ptr_from_py(mp_obj_t arg) { + if (mp_obj_is_int(arg)) { + return (void *)mp_obj_get_int_truncated(arg); + } + mp_buffer_info_t bufinfo = {0}; + mp_get_buffer_raise(arg, &bufinfo, MP_BUFFER_READ); + return bufinfo.buf; +} + +#if __m68k +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); +} +#endif diff --git a/ports/m68kmac/multiverse_support.h b/extmod/multiverse_support.h similarity index 51% rename from ports/m68kmac/multiverse_support.h rename to extmod/multiverse_support.h index 7b39382590ab7..77165d712d86a 100644 --- a/ports/m68kmac/multiverse_support.h +++ b/extmod/multiverse_support.h @@ -2,15 +2,42 @@ #include "py/obj.h" #include "py/runtime.h" #include "extmod/moductypes.h" -#include + +#if __m68k__ +#include "Multiverse.h" +#else +typedef long LONGINT; +typedef short INTEGER; +typedef unsigned char Byte; +typedef struct { INTEGER h, v; +} Point; +#define pascal /* nothing */ +#endif // 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__}} + {{&mp_type_tuple}, MP_ARRAY_SIZE(((const mp_obj_t[]) {__VA_ARGS__})), {__VA_ARGS__}} + +#define DECLARE_PTR_SCALAR(type_c) \ + MP_DECLARE_CTYPES_STRUCT(type_c##_obj) + +DECLARE_PTR_SCALAR(bool); +DECLARE_PTR_SCALAR(char); +DECLARE_PTR_SCALAR(uint8_t); +DECLARE_PTR_SCALAR(uint16_t); +DECLARE_PTR_SCALAR(uint32_t); +DECLARE_PTR_SCALAR(uint64_t); +DECLARE_PTR_SCALAR(int8_t); +DECLARE_PTR_SCALAR(int16_t); +DECLARE_PTR_SCALAR(int32_t); +DECLARE_PTR_SCALAR(int64_t); + +#define void_obj uint8_t_obj +void *void_ptr_from_py(mp_obj_t obj); void *to_struct_helper(mp_obj_t obj, const mp_obj_type_t *struct_type, bool is_const, qstr fieldname); 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); diff --git a/ports/m68kmac/Makefile b/ports/m68kmac/Makefile index 21555f5799192..ab59f56e859ae 100644 --- a/ports/m68kmac/Makefile +++ b/ports/m68kmac/Makefile @@ -1,3 +1,8 @@ +ifeq ($(shell which m68k-apple-macos-gcc),) +.PHONY: default +default: docker-build +endif + include ../../py/mkenv.mk CROSS_COMPILE=m68k-apple-macos- @@ -44,7 +49,6 @@ SRC_C = \ main.c \ vfs_mac.c \ macutil.c \ - multiverse_support.c \ SRC_C += \ shared/readline/readline.c \ @@ -64,37 +68,28 @@ SRC_S += \ shared/runtime/gchelper_m68k.s \ SRC_QSTR += \ - vfs_mac.c \ shared/readline/readline.c \ shared/runtime/pyexec.c \ + vfs_mac.c \ .PHONY: mkapi DEFS = lib/multiversal/defs/MacTypes.yaml etc/MacTypesExtras.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 +include $(TOP)/extmod/multiversal.mk $(eval $(call multiversal_module,eventmgr,lib/multiversal/defs/EventMgr.yaml -t lib/multiversal/defs/QuickDraw.yaml)) -$(eval $(call multiversal_module,deskmgr,lib/multiversal/defs/DeskMgr.yaml -t lib/multiversal/defs/WindowMgr.yaml -t lib/multiversal/defs/QuickDraw.yaml)) -$(eval $(call multiversal_module,dialogmgr,lib/multiversal/defs/DialogMgr.yaml -t lib/multiversal/defs/WindowMgr.yaml -t lib/multiversal/defs/QuickDraw.yaml -t etc/DialogMgrExtras.yaml)) +$(eval $(call multiversal_module,deskmgr,lib/multiversal/defs/DeskMgr.yaml -t lib/multiversal/defs/WindowMgr.yaml -t lib/multiversal/defs/QuickDraw.yaml -t lib/multiversal/defs/EventMgr.yaml)) +$(eval $(call multiversal_module,dialogmgr,lib/multiversal/defs/DialogMgr.yaml -t lib/multiversal/defs/WindowMgr.yaml -t lib/multiversal/defs/QuickDraw.yaml -t etc/DialogMgrExtras.yaml -t lib/multiversal/defs/EventMgr.yaml)) $(eval $(call multiversal_module,fontmgr,lib/multiversal/defs/FontMgr.yaml -t lib/multiversal/defs/QuickDraw.yaml)) $(eval $(call multiversal_module,mactypes,lib/multiversal/defs/MacTypes.yaml etc/MacTypesExtras.yaml)) $(eval $(call multiversal_module,menumgr,lib/multiversal/defs/MenuMgr.yaml -t lib/multiversal/defs/QuickDraw.yaml)) $(eval $(call multiversal_module,qd,lib/multiversal/defs/QuickDraw.yaml etc/QuickDrawExtras.yaml)) $(eval $(call multiversal_module,textedit,lib/multiversal/defs/TextEdit.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,toolboxevent,lib/multiversal/defs/ToolboxEvent.yaml -t lib/multiversal/defs/QuickDraw.yaml -t lib/multiversal/defs/EventMgr.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,windowmgr,lib/multiversal/defs/WindowMgr.yaml -t lib/multiversal/defs/QuickDraw.yaml)) +$(eval $(call multiversal_module,windowmgr,lib/multiversal/defs/WindowMgr.yaml -t lib/multiversal/defs/QuickDraw.yaml -t lib/multiversal/defs/EventMgr.yaml)) OBJ += $(PY_CORE_O) $(PY_O) OBJ += $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) @@ -118,20 +113,33 @@ $(BUILD)/micropython.bin $(BUILD)/micropython.APPL $(BUILD)/micropython.dsk: $(B "$(RINCLUDES)/Retro68APPL.r" \ -t "APPL" -c "mupy" \ -o $(BUILD)/micropython.bin --cc $(BUILD)/micropython.APPL --cc $(BUILD)/micropython.dsk + hmount $(BUILD)/micropython.dsk + (cd examples; set -x; \ + for i in *.py; do \ + hcopy $$i ":$$i"; \ + hattrib -c mupy -t text ":$$i"; \ + done) .PHONY: docker-build -docker-build: mkapi +docker-build: ./docker-run.sh make -j$(shell nproc) BASE_IMG ?= base.img UMAC=$(HOME)/src/umac/main -w -r ~/src/umac/rom.bin .PHONY: run +run-%: + cp $(BASE_IMG) $(BUILD)/run.dsk + hmount $(BUILD)/run.dsk + hcopy $(BUILD)/micropython.bin ":Desktop Folder" + hcopy examples/$*.py ":Desktop Folder:code.py" + hattrib -c mupy -t text ":Desktop Folder:code.py" + $(UMAC) -d build/run.dsk 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" + hattrib -c mupy -t text ":Desktop Folder:code.py" $(UMAC) -d build/run.dsk include $(TOP)/py/mkrules.mk diff --git a/ports/m68kmac/README.md b/ports/m68kmac/README.md index 1df117c04862a..1bd336315d5c9 100644 --- a/ports/m68kmac/README.md +++ b/ports/m68kmac/README.md @@ -9,8 +9,6 @@ Two methods of building are supported. First, entirely within docker, using the `.github/workflows/port_m68k.yaml`. Second, building on a standard Linux host machine (e.g., debian stable) with docker installed: - $ pip install pyyaml click - $ make submodules $ make docker-build There's a `make run` target to launch the emulator but it is very specific to diff --git a/ports/m68kmac/code.py b/ports/m68kmac/code.py deleted file mode 100644 index 0f83da69e3046..0000000000000 --- a/ports/m68kmac/code.py +++ /dev/null @@ -1,45 +0,0 @@ -# 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") diff --git a/ports/m68kmac/docker-run.sh b/ports/m68kmac/docker-run.sh index 2d6cc086462b6..f890d5dfa453f 100755 --- a/ports/m68kmac/docker-run.sh +++ b/ports/m68kmac/docker-run.sh @@ -7,4 +7,5 @@ if [ $# -eq 0 ]; then it=-it; else it=; fi docker run \ -w /work/ports/m68kmac --rm \ -v "${TOP}":/work \ - ${it} ghcr.io/autc04/retro68 "$@" + ${it} ghcr.io/m68k-micropython/builder:latest "$@" + diff --git a/ports/m68kmac/etc/MacTypesExtras.yaml b/ports/m68kmac/etc/MacTypesExtras.yaml index fe51488c7066f..176bb688c6ba0 100644 --- a/ports/m68kmac/etc/MacTypesExtras.yaml +++ b/ports/m68kmac/etc/MacTypesExtras.yaml @@ -1 +1,13 @@ -[] +- typedef: + name: ProcPtr + type: void* +- pyverbatim: + typedef_content: | + #define WindowRef_obj GrafPtr_obj + #define WindowPtr_obj GrafPtr_obj + #define DialogPtr_obj GrafPtr_obj + #define INTEGER_obj int16_t_obj + #define Fixed_obj int32_t_obj + #define LONGINT_obj int32_t_obj + #define Byte_obj uint8_t_obj + #define SignedByte_obj int8_t_obj diff --git a/ports/m68kmac/etc/needs-glue.txt b/ports/m68kmac/etc/needs-glue.txt index 789a13ee05fb8..5252b999982e7 100644 --- a/ports/m68kmac/etc/needs-glue.txt +++ b/ports/m68kmac/etc/needs-glue.txt @@ -1,4 +1,3 @@ -Using multiversal interfaces ADBOp FSOpen OpenRF @@ -175,3 +174,5 @@ GetDblTime GetCaretTime GetIndString GetIndPattern +R_X2Fix +R_X2Frac diff --git a/ports/m68kmac/examples/region.py b/ports/m68kmac/examples/region.py new file mode 100644 index 0000000000000..4b8ed21791273 --- /dev/null +++ b/ports/m68kmac/examples/region.py @@ -0,0 +1,55 @@ +# m68k-micropython window creation demo + +import eventmgr +import mactypes +import qd +import uctypes +import windowmgr +import random + +scrn = qd.qdGlobals().screenBits + + +def pstr(s): + b = mactypes.Str255() + b[0] = len(s) + for i in range(len(s)): + b[i + 1] = ord(s[i]) + return uctypes.struct(b, mactypes.ConstStringPtr) + + +ev = eventmgr.EventRecord() + +NIL_WINDOW = uctypes.struct(0, qd.GrafPtr) +ABOVE_ALL_WINDOWS = uctypes.struct(-1, qd.GrafPtr) + +title = pstr("Hello World 11") +r = mactypes.Rect() +r[:] = scrn.bounds +r.top += 80 +r.left += 100 +qd.InsetRect(r, 25, 25) + +w = windowmgr.NewWindow(NIL_WINDOW, r, title, True, 0, ABOVE_ALL_WINDOWS, True, 0) +qd.SetPort(w) + +g = qd.qdGlobals() + +barbell = qd.NewRgn() +tempRect = mactypes.Rect() +qd.OpenRgn() +qd.SetRect(tempRect, 20, 20, 30, 50) +qd.FrameOval(tempRect) +qd.SetRect(tempRect, 25, 30, 85, 40) +qd.FrameRect(tempRect) +qd.SetRect(tempRect, 80, 20, 90, 50) +qd.FrameOval(tempRect) +qd.CloseRgn(barbell) + +r[:] = barbell[0].rgnBBox +print(f"region size {barbell[0].rgnSize} bbox {r.top},{r.left}..{r.right},{r.bottom}") +qd.FillRgn(barbell, g.black) + +qd.DisposeRgn(barbell) + +input("hit enter to exit") diff --git a/ports/m68kmac/multiverse_support.c b/ports/m68kmac/multiverse_support.c deleted file mode 100644 index eb01c3a725880..0000000000000 --- a/ports/m68kmac/multiverse_support.c +++ /dev/null @@ -1,101 +0,0 @@ -#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, qstr fieldname) { - 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("%q must be of type %q, not %q"), fieldname, (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, qstr fieldname) { - 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, fieldname); - } - 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); -} diff --git a/ports/unix/makefile b/ports/unix/makefile new file mode 100644 index 0000000000000..b0bb3cadce8e6 --- /dev/null +++ b/ports/unix/makefile @@ -0,0 +1,3 @@ +MAKEFLAGS=-j10 +VARIANT ?= coverage +include Makefile diff --git a/ports/unix/variants/coverage/mkapi_test.yaml b/ports/unix/variants/coverage/mkapi_test.yaml new file mode 100644 index 0000000000000..7bbffb75f6907 --- /dev/null +++ b/ports/unix/variants/coverage/mkapi_test.yaml @@ -0,0 +1,167 @@ +- typedef: + name: Byte + type: uint8_t + +- typedef: + name: INTEGER + type: int16_t + +- struct: + name: Rect + members: + - name: top + type: INTEGER + - name: left + type: INTEGER + - name: bottom + type: INTEGER + - name: right + type: INTEGER + size: 8 + +- typedef: + name: Str15 + type: Byte[16] + +- struct: + name: GrafPort + members: + - name: device + type: INTEGER + - name: portRect + type: Rect + +- typedef: + name: GrafPtr + type: GrafPort* + +- typedef: + name: WindowPtr + type: GrafPtr + + +# #### + +- struct: + name: Region + members: + - name: rgnSize + type: INTEGER + - name: rgnBBox + type: Rect + size: 10 + +# #### + +- struct: + name: Point + members: + - name: h + type: INTEGER + - name: v + type: INTEGER + +- typedef: + name: RegionPtr + type: Region* + +- typedef: + name: RgnHandle + type: RegionPtr* + + +# #### + +- pyverbatim: + typedef_content: | + + typedef struct { + INTEGER top, left, bottom, right; + } Rect; + + typedef struct { + INTEGER rgnSize; Rect rgnBBox; + } Region; + + typedef struct { + INTEGER device; + Rect portRect; + } GrafPort; + + typedef GrafPort *GrafPtr; + + typedef GrafPtr WindowPtr; + + #define WindowPtr_obj GrafPtr_obj + + typedef Region *RegionPtr; + typedef RegionPtr *RgnHandle; + + static void OffsetRgn(RgnHandle rh, INTEGER dh, INTEGER dv) { + Region *region = *rh; + region->rgnBBox.top += dv; + region->rgnBBox.bottom += dv; + region->rgnBBox.left += dh; + region->rgnBBox.right += dh; + } + + + static RgnHandle NewRgn(void) { + static Region rgn = { 0x505, {100, 200, 300, 400} }; + static RegionPtr rp = &rgn; + return &rp; + } + + static WindowPtr NewWindow(void) { + static GrafPort result = {0, {0x100, 0x200, 0x300, 0x400} }; + return &result; + } + + static void PtToAngle(const Rect *r, Point p, INTEGER *angle) { + mp_printf(&mp_plat_print, "PtToAngle(rect@%p, p={%d,%d}, angle@%p\n", r, p.h, p.v, angle); + *angle = 314; + } + + static INTEGER GetWinDevice(WindowPtr w) { + return w->device; + } +- function: + name: NewRgn + return: RgnHandle + trap: 0xA8D8 + executor: C_ + +- function: + name: OffsetRgn + args: + - name: rh + type: RgnHandle + - name: dh + type: INTEGER + - name: dv + type: INTEGER + trap: 0xA8E0 + executor: C_ + +- function: + name: PtToAngle + args: + - name: rp + type: const Rect* + - name: p + type: Point + - name: angle + type: INTEGER* + trap: 0xA8C3 + executor: C_ + +- function: + name: NewWindow + return: WindowPtr + +- function: + name: GetWinDevice + return: INTEGER + args: + - name: w + type: WindowPtr diff --git a/ports/unix/variants/coverage/mpconfigvariant.mk b/ports/unix/variants/coverage/mpconfigvariant.mk index cc37ba1582470..39f7899b27feb 100644 --- a/ports/unix/variants/coverage/mpconfigvariant.mk +++ b/ports/unix/variants/coverage/mpconfigvariant.mk @@ -12,5 +12,13 @@ LDFLAGS += -fprofile-arcs -ftest-coverage FROZEN_MANIFEST ?= $(VARIANT_DIR)/manifest.py USER_C_MODULES = $(TOP)/examples/usercmodule +# Optionally format the generated files with uncrustify if installed +ifneq ($(shell which uncrustify-apple-macos-gcc),) +MKAPIFORMAT = --format +endif +include $(TOP)/extmod/multiversal.mk +$(eval $(call multiversal_module,mkapitest,variants/coverage/mkapi_test.yaml)) +$(info $(call multiversal_module,mkapitest,variants/coverage/mkapi_test.yaml)) + SRC_C += coverage.c SRC_CXX += coveragecpp.cpp diff --git a/py/mpprint.c b/py/mpprint.c index c347714902b26..3a2f4ba04693b 100644 --- a/py/mpprint.c +++ b/py/mpprint.c @@ -495,6 +495,11 @@ int mp_vprintf(const mp_print_t *print, const char *fmt, va_list args) { chrs += mp_print_strn(print, str, len, flags, fill, width); break; } + case 'r': { + mp_obj_t arg = va_arg(args, mp_obj_t); + mp_obj_print_helper(print, arg, width); + break; + } case 's': { const char *str = va_arg(args, const char *); #ifndef NDEBUG diff --git a/tests/extmod/mkapi.py b/tests/extmod/mkapi.py new file mode 100644 index 0000000000000..18e8d2f2de3a1 --- /dev/null +++ b/tests/extmod/mkapi.py @@ -0,0 +1,36 @@ +import mkapitest + +rh = mkapitest.NewRgn() +r = rh[0] +print(type(rh), type(r)) +print(r.rgnBBox.top) +print(r.rgnBBox.left) +print(r.rgnBBox.right) +print(r.rgnBBox.bottom) + +mkapitest.OffsetRgn(rh, 3, 5) +print(r.rgnBBox.top) +print(r.rgnBBox.left) +print(r.rgnBBox.right) +print(r.rgnBBox.bottom) + +w = mkapitest.NewWindow() +print(type(w)) +print(w.portRect.top) +print(w.portRect.left) +print(w.portRect.right) +print(w.portRect.bottom) + +r = mkapitest.Rect(1, 2, 3, 4) +print(r) +print(repr(r)) +r = mkapitest.Rect(top=10, left=20, right=30, bottom=40) +print(repr(r)) +r = mkapitest.Rect(100, 200, right=300, bottom=400) +print(repr(r)) + +try: + r = mkapitest.Rect(1000, 2000, 3000, right=4000, bottom=5000) + print(repr(r)) +except Exception as e: + print(type(e)) diff --git a/tests/extmod/mkapi.py.exp b/tests/extmod/mkapi.py.exp new file mode 100644 index 0000000000000..12b96425e8129 --- /dev/null +++ b/tests/extmod/mkapi.py.exp @@ -0,0 +1,31 @@ + +100 +200 +400 +300 +105 +203 +403 +305 + +256 +512 +1024 +768 + + + + + diff --git a/tests/ports/unix/extra_coverage.py.exp b/tests/ports/unix/extra_coverage.py.exp index ed21ced24258b..c7446ec575a0e 100644 --- a/tests/ports/unix/extra_coverage.py.exp +++ b/tests/ports/unix/extra_coverage.py.exp @@ -57,13 +57,14 @@ cppexample cryptolib deflate errno example_package ffi framebuf gc hashlib heapq io json machine marshal math -os platform random re -select socket struct sys -termios time tls uctypes -vfs websocket +mkapitest os platform random +re select socket struct +sys termios time tls +uctypes vfs websocket me micropython machine marshal math +mkapitest argv atexit byteorder exc_info executable exit getsizeof implementation diff --git a/tests/run-tests.py b/tests/run-tests.py index faf1d2e3b485e..b612c37883122 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -357,6 +357,7 @@ def run_script_on_remote_target(pyb, args, test_file, is_special): "misc/sys_settrace_cov.py", "thread/thread_exc2.py", "ports/esp32/partition_ota.py", + "extmod/mkapi.py", ) ] diff --git a/tools/ci.sh b/tools/ci.sh index ac57e61967286..ab62956a70a3f 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -627,6 +627,7 @@ function ci_unix_coverage_setup { pip3 install setuptools pip3 install pyelftools pip3 install ar + pip3 install pyyaml click ci_gcc_plugin_setup gcc --version python3 --version @@ -680,6 +681,7 @@ function ci_unix_32bit_setup { sudo pip3 install setuptools sudo pip3 install pyelftools sudo pip3 install ar + sudo pip3 install --upgrade pyyaml click gcc --version python2.7 --version python3 --version diff --git a/tools/micropython_checks.cc b/tools/micropython_checks.cc index de794f1b78948..312c144dd17a2 100644 --- a/tools/micropython_checks.cc +++ b/tools/micropython_checks.cc @@ -288,6 +288,7 @@ struct micropython_checks : gimple_opt_pass { require_qstr(argno++); break; + case 'r': case 's': case 'p': case 'P': diff --git a/ports/m68kmac/tools/mkapi.py b/tools/mkapi.py similarity index 74% rename from ports/m68kmac/tools/mkapi.py rename to tools/mkapi.py index cb59aa101eb59..f3fdbf8d49ff1 100644 --- a/ports/m68kmac/tools/mkapi.py +++ b/tools/mkapi.py @@ -9,13 +9,34 @@ from dataclasses import dataclass, Field from typing import Any, get_args, get_origin, Union +script_dir = pathlib.Path(__file__).parent + if sys.version_info >= (3, 10): from types import UnionType else: UnionType = type(Union[int, float]) -with open("etc/needs-glue.txt") as f: - needs_glue = set(f.read().split("\n")) +p = pathlib.Path("etc/needs-glue.txt") +if p.exists(): + needs_glue = set(p.read_text().split("\n")) +else: + needs_glue = set() + + +typecodes = { + 'bool': 'B', + 'char': 'B', + 'uint8_t': 'B', + 'uint16_t': 'H', + 'uint32_t': 'L', + 'uint64_t': 'Q', + 'float': 'f', + 'double': 'd', + 'int8_t': 'b', + 'int16_t': 'h', + 'int32_t': 'l', + 'int64_t': 'q', +} @dataclass(frozen=True) @@ -260,9 +281,10 @@ class descr_maker_struct: 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))" + "const mp_rom_obj_tuple_t", + f"ROM_TUPLE(MP_ROM_INT({offset}), MP_ROM_PTR((void*)&{self.tag}_obj))", ) - return f"MP_ROM_PTR(&{obj})" + return f"MP_ROM_PTR((void*)&{obj})" @dataclass @@ -272,10 +294,10 @@ class descr_maker_arr_scalar: 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}))", + "const mp_rom_obj_tuple_t", + f"ROM_TUPLE(MP_ROM_INT({offset} | UCTYPE_AGG(ARRAY)), MP_ROM_INT(UCTYPE_TYPE({self.tag}) | {self.size}))", ) - return f"MP_ROM_PTR(&{obj})" + return f"MP_ROM_PTR((void*)&{obj})" @dataclass @@ -285,10 +307,10 @@ class descr_maker_arr_struct: 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))", + "const mp_rom_obj_tuple_t", + f"ROM_TUPLE(MP_ROM_INT({offset} | UCTYPE_AGG(ARRAY)), MP_ROM_INT({self.size}), MP_ROM_PTR((void*)(&{self.tag}_obj)))", ) - return f"MP_ROM_PTR(&{obj})" + return f"MP_ROM_PTR((void*)&{obj})" @dataclass @@ -297,10 +319,10 @@ class descr_maker_ptr_scalar: 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}))", + "const mp_rom_obj_tuple_t", + f"ROM_TUPLE(MP_ROM_INT({offset} | UCTYPE_AGG(PTR)), MP_ROM_INT(UCTYPE_TYPE({self.tag})))", ) - return f"MP_ROM_PTR(&{obj})" + return f"MP_ROM_PTR((void*)&{obj})" class descr_maker_ptr_struct: @@ -309,10 +331,10 @@ def __init__(self, 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))", + "const mp_rom_obj_tuple_t", + f"ROM_TUPLE(MP_ROM_INT({offset} | UCTYPE_AGG(PTR)), MP_ROM_PTR((void*)&{self.tag}_obj))", ) - return f"MP_ROM_PTR(&{obj})" + return f"MP_ROM_PTR((void*)&{obj})" @dataclass @@ -352,6 +374,7 @@ def emit_call_arg(self, name_c): @dataclass class PtrConverter: + emitter: object fieldname: str type_c: object type_obj: object @@ -360,10 +383,28 @@ class PtrConverter: def emit_to_c(self, name_py, name_c): 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}, MP_QSTR_{self.fieldname});" + resolved_type = self.emitter.parse_type(self.type_c) + if ( + isinstance(resolved_type, Ptr) + and self.emitter.parse_type(resolved_type.pointee) not in all_scalar_types + ): + type_obj = f"(void*)&{resolved_type.pointee}_obj" + return f"{self.type_c} {name_c} = to_struct_helper({name_py}, {type_obj}, {is_const}, MP_QSTR_{self.fieldname}); // 1\n" + else: + return f"{self.type_c} {name_c} = to_struct_helper({name_py}, {self.type_obj}, {is_const}, MP_QSTR_{self.fieldname}); // 2\n" def emit_to_py(self, name_c): - return f"from_struct_helper({name_c}, {self.type_obj});" + resolved_type = self.emitter.parse_type(self.type_c) + if resolved_type in self.emitter.types: + resolved_type = self.emitter.parse_type(self.emitter.types[resolved_type]) + print("emit_to_py", self.type_c, resolved_type) + if isinstance(resolved_type, Ptr): + assert self.type_obj and self.type_obj != "null" + type_str = resolved_type.pointee.replace("*", "Ptr") + return f"from_struct_helper({name_c}, (void*)&{type_str}_obj) /* {self.type_c} 3 */" + else: + type_str = self.type_obj.replace("*", "Ptr") + return f"from_struct_helper({name_c}, {self.type_obj}) /* {resolved_type} 4 */" def emit_call_arg(self, name_c): if self.deref: @@ -372,12 +413,14 @@ def emit_call_arg(self, name_c): def make_converter(emitter, fieldname, type_c): + print(f"make_converter {type_c=}") if converter := converters.get(type_c): return converter(fieldname) 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.types: + resolved_type = emitter.parse_type(emitter.types[resolved_type]) if resolved_type in emitter.funptrs: return ScalarConverter( resolved_type, f"({type_c})mp_obj_get_int_truncated", "mp_obj_new_int_from_ptr" @@ -386,19 +429,48 @@ def make_converter(emitter, fieldname, type_c): return ScalarConverter( resolved_type, "mp_obj_get_int_truncated", "mp_obj_new_int_from_uint" ) + if type_c == "void*": + return ScalarConverter(resolved_type, "void_ptr_from_py", "mp_obj_new_int_from_uint") if isinstance(resolved_type, Ptr): base_type = resolved_type.pointee + if base_type in all_scalar_types: + return PtrConverter( + emitter, + fieldname, + type_c, + f"(const mp_obj_type_t*)&{base_type}_obj", + is_const=resolved_type.is_const, + ) + if type in emitter.structs or type_c in emitter.types: + return PtrConverter( + emitter, + fieldname, + type_c, + f"(const mp_obj_type_t*)&{type_c}_obj", + is_const=resolved_type.is_const, + ) if base_type in emitter.structs: return PtrConverter( + emitter, + fieldname, + type_c, + f"(const mp_obj_type_t*)&{base_type}_obj", + is_const=resolved_type.is_const, + ) + elif base_type in emitter.typedef_objs: + return PtrConverter( + emitter, fieldname, 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(fieldname, 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})") + emitter.info.append(f"need to handle typedef obj {base_type}") + elif base_type != 'void': + emitter.info.append(f"confused about {base_type} from {resolved_type} from {type_c}") + return PtrConverter(emitter, fieldname, 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: @@ -415,7 +487,7 @@ def __init__(self, modname): self.structs = {} self.funptrs = set(("ProcPtr",)) self.decls_dedent(""" - #include "multiverse_support.h" + #include "extmod/multiverse_support.h" """) self.add_local("__name__", f"MP_ROM_QSTR(MP_QSTR_{self.modname})") @@ -429,6 +501,10 @@ def common_definition(self, c_type: str, c_value: str) -> str: return f"common_{self.definitions[k]}" def is_array(self, typename): + if isinstance(typename, Array): + return True + if isinstance(typename, Ptr): + return False if typename.count(']') + ("*" in typename) > 1: raise ValueError( f"array-of-array or pointer-to-array or array-of-pointers NYI {typename}" @@ -436,18 +512,30 @@ def is_array(self, typename): return typename.endswith("]") def remove_array(self, typename): + if isinstance(typename, Array): + return typename.pointee return typename.partition("[")[0] def remove_ptr(self, typename): + if isinstance(typename, Ptr): + return typename.pointee return typename.removesuffix("*") def remove_const(self, typename): + if hasattr(typename, 'is_const'): + return dataclasses.replace(typename, is_const=False) return typename.removeprefix("const ") def array_size(self, typename): + if isinstance(typename, Array): + return typename.bound return int(typename.partition("[")[2].removesuffix("]")) def is_ptr(self, typename): + if isinstance(typename, Ptr): + return True + if isinstance(typename, Array): + return False return typename.endswith("*") def is_scalar(self, typename): @@ -456,6 +544,8 @@ def is_scalar(self, typename): return typename in all_scalar_types def is_const(self, typename): + if hasattr(typename, 'is_const'): + return typename.is_const return typename.startswith("const ") def decls_dedent(self, text): @@ -465,11 +555,8 @@ def body_dedent(self, text): self.body.append(textwrap.dedent(text.rstrip())) def parse_type(self, typestr): - print("parse_type", typestr) - if typestr == 'RgnHandle': - print(self.types) - while typestr in self.types: - typestr = self.types[typestr] + if scalar_type := self.types.get(typestr, None): + typestr = scalar_type is_const = self.is_const(typestr) base_type = self.remove_const(typestr) if self.is_array(base_type): @@ -477,8 +564,7 @@ def parse_type(self, typestr): 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)) + base_type = self.remove_ptr(base_type) return Ptr(base_type, is_const=is_const) else: if is_const: @@ -492,13 +578,15 @@ def typedefs(self, defs): 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);") + self.decls_dedent( + f"MP_DECLARE_CTYPES_STRUCT({d.name}_obj); // typedef {d.fulltype} is_scalar? {self.is_scalar(d.fulltype)}" + ) if isinstance(d, Struct) and d.members: self.structs[d.name] = d - self.decls_dedent(f"MP_DECLARE_CTYPES_STRUCT({d.name}_obj);") + self.decls_dedent(f"MP_DECLARE_CTYPES_STRUCT({d.name}_obj); // struct") if isinstance(d, Union) and d.members: self.structs[d.name] = d - self.decls_dedent(f"MP_DECLARE_CTYPES_STRUCT({d.name}_obj);") + self.decls_dedent(f"MP_DECLARE_CTYPES_STRUCT({d.name}_obj); // union") if isinstance(d, PyVerbatim) and d.typedef_content: self.decls_dedent(d.typedef_content) if isinstance(d, FunPtr): @@ -506,6 +594,7 @@ def typedefs(self, defs): def emit(self, defs): for d in defs: + print("emit", id(d), d) try: self.emit_node(d) except Exception as e: @@ -516,31 +605,53 @@ def emit_node(self, node): if type(node) in self.unknowns: return self.unknowns.add(type(node)) - self.info.append(f"# Unknown {node!r:.68s}...") + raise RuntimeError(f"# Unknown {node!r:.68s}...") + + @emit_node.register + def emit_dispatcher(self, dispatcher: Dispatcher): + pass # Nothing to emit @emit_node.register def emit_typedef(self, typedef: Typedef): + print("emit_typedef", typedef) + self.body_dedent(f"// typedef {typedef}") name = typedef.name type = typedef.type if type.endswith("*") or type.endswith("]"): - make_descr = self.type_details(typedef.type) + make_descr = self.type_details(typedef.fulltype) + self.body_dedent(f"// {type} {make_descr=}") if make_descr is None: + self.body_dedent(f"// {typedef}: no make_descr") return offset = 0 self.body_dedent(f""" - MP_DEFINE_CTYPES_STRUCT({name}_obj, MP_QSTR_{name}, {make_descr(self, offset)}, LAYOUT_NATIVE); + MP_DEFINE_CTYPES_STRUCT({name}_obj, MP_QSTR_{name}, {make_descr(self, offset)}, LAYOUT_NATIVE); // 1 """) self.typedef_objs.add(name) self.add_local(name) offset = 0 + else: + self.body_dedent(f"// no need for {typedef} !?") 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 isinstance(typename, Ptr): + is_ptr = True + is_array = False + basetypename = typename.pointee + print(f"{typename=} -- {basetypename=} {basetypename in self.types}") + elif isinstance(typename, Array): + is_ptr = False + is_array = True + fulltype = typename + basetypename = typename.pointee + print(f"{typename=} -- {basetypename=} {basetypename in self.types}") + else: + 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 "" @@ -569,7 +680,18 @@ def type_details(self, typename): descr = descr_maker_scalar(type_str) else: if is_ptr: - if basetypename in self.structs: + if typename in self.types: + deref_typename = self.parse_type(self.types[typename]) + if ( + isinstance(deref_typename, Ptr) + and deref_typename.pointee in self.structs + or deref_typename.pointee in self.types + ): + typename = deref_typename.pointee + descr = descr_maker_ptr_struct(typename) + elif basetypename in self.structs: + descr = descr_maker_ptr_struct(basetypename) + elif basetypename in self.types: descr = descr_maker_ptr_struct(basetypename) else: descr = descr_maker_ptr_scalar("UINT8") @@ -607,7 +729,7 @@ def emit_union(self, e: Union): {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); + MP_DEFINE_CTYPES_STRUCT({name}_obj, MP_QSTR_{name}, MP_ROM_PTR((void*)&{name}_descr_dict), LAYOUT_NATIVE); // 2 """) self.add_local(name) @@ -623,7 +745,7 @@ def emit_struct(self, e: Struct): {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); + MP_DEFINE_CTYPES_STRUCT({name}_obj, MP_QSTR_{name}, MP_ROM_PTR((void*)&{name}_descr_dict), LAYOUT_NATIVE); // 3 """) self.add_local(name) @@ -657,7 +779,7 @@ def emit_lowmem(self, lm: LowMem): def add_local(self, name, value=...): if value is ...: - value = f"MP_ROM_PTR(&{name}_obj)" + value = f"MP_ROM_PTR((void*)&{name}_obj)" self.locals.append(f"{{ MP_ROM_QSTR(MP_QSTR_{name}), {value} }},") @emit_node.register @@ -719,7 +841,6 @@ def fun_call_fun(self, fun): 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};" @@ -732,9 +853,9 @@ def fun_convert_return(self, fun): return_type = fun.return_ if return_type: converter = self.make_converter(0, return_type) - return f" return {converter.emit_to_py('retval')};" + return f" return {converter.emit_to_py('retval')};\n" else: - return " return mp_const_none;" + return " return mp_const_none;\n" @emit_node.register def emit_function(self, node: Function): @@ -746,7 +867,7 @@ def emit_function(self, node: Function): 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) {{ + static 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(node)} @@ -795,7 +916,11 @@ def do_print(*args): "-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): +@click.option("--format/-no-format", "do_format") +def main(defs_files, output, modname, typedefs, do_format=False): + if modname is None: + modname = defs_files[0].stem + if output is None: output = pathlib.Path(f"mod{modname}.c") processor = Processor(modname) @@ -810,8 +935,16 @@ def main(defs_files, output, modname, typedefs): processor.typedefs(defs) processor.emit(defs) - with open(output, "w") as f: - processor.make_output(f) + if do_format: + tmpfile = output.with_suffix(".tmp.c") + with open(tmpfile, "w") as f: + processor.make_output(f) + print(f"Formatting {output}: ", [script_dir / "codeformat.py", "-c", tmpfile]) + subprocess.check_call([script_dir / "codeformat.py", "-c", tmpfile]) + tmpfile.rename(output) + else: + with open(output, "w") as f: + processor.make_output(f) if __name__ == '__main__':