Skip to content

PVM

op

Bases: Enum

This enum serves as a readable lookup for the different opcodes defined in gp::

Source code in pyjamaz/pvm/constants.py
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
class Opcode(Enum):
    """
    This enum serves as a readable lookup for the different opcodes defined in gp::
    """
    # GP_A.5.1
    # Instructions without Arguments (none)
    trap: int                                      = 0
    fallthrough: int                               = 1

    # GP_A.5.2
    # Instructions with Arguments of One Immediate (imm)
    ecalli: int                                    = 10

    # GP_A.5.3
    # Instructions with Arguments of One Register and One Extended Width Immediate (reg_ext_imm)
    load_imm_64: int                              = 20

    # GP_A.5.4
    # Instructions with Arguments of two Immediates (imm_imm)
    store_imm_u8: int                              = 30
    store_imm_u16: int                             = 31
    store_imm_u32: int                             = 32
    store_imm_u64: int                             = 33

    # GP_A.5.5
    # Instructions with Arguments of One Offset (offset)
    jump: int                                      = 40

    # GP_A.5.6
    # Instructions with Arguments Of One Register & One Immediate (reg_imm)
    jump_ind: int                                  = 50
    load_imm: int                                  = 51
    load_u8: int                                   = 52
    load_i8: int                                   = 53
    load_u16: int                                  = 54
    load_i16: int                                  = 55
    load_u32: int                                  = 56
    load_i32: int                                  = 57
    load_u64: int                                  = 58
    store_u8: int                                  = 59
    store_u16: int                                 = 60
    store_u32: int                                 = 61
    store_u64: int                                 = 62

    # GP_A.5.7
    # Instructions with Arguments Of One Register & Two Immediates (reg_imm_imm)
    store_imm_ind_u8: int                          = 70
    store_imm_ind_u16: int                         = 71
    store_imm_ind_u32: int                         = 72
    store_imm_ind_u64: int                         = 73

    # GP_A.5.8
    # Instructions with Arguments Of One Register, One Immediate and One Offset (reg_imm_offset)
    load_imm_jump: int                             = 80
    branch_eq_imm: int                             = 81
    branch_ne_imm: int                             = 82
    branch_lt_u_imm: int                           = 83
    branch_le_u_imm: int                           = 84
    branch_ge_u_imm: int                           = 85
    branch_gt_u_imm: int                           = 86
    branch_lt_s_imm: int                           = 87
    branch_le_s_imm: int                           = 88
    branch_ge_s_imm: int                           = 89
    branch_gt_s_imm: int                           = 90

    # GP_A.5.9
    # Instructions with Arguments Of Two Registers (reg_reg)
    move_reg: int                                  = 100
    sbrk: int                                      = 101
    count_set_bits_64: int                         = 102
    count_set_bits_32: int                         = 103
    leading_zero_bits_64: int                      = 104
    leading_zero_bits_32: int                      = 105
    trailing_zero_bits_64: int                     = 106
    trailing_zero_bits_32: int                     = 107
    sign_extend_8: int                             = 108
    sign_extend_16: int                            = 109
    zero_extend_16: int                            = 110
    reverse_bytes: int                             = 111

    # GP_A.5.10
    # Instructions with Arguments Of Two Registers & One Immediate (reg_reg_imm)
    store_ind_u8: int                              = 120
    store_ind_u16: int                             = 121
    store_ind_u32: int                             = 122
    store_ind_u64: int                             = 123
    load_ind_u8: int                               = 124
    load_ind_i8: int                               = 125
    load_ind_u16: int                              = 126
    load_ind_i16: int                              = 127
    load_ind_u32: int                              = 128
    load_ind_i32: int                              = 129
    load_ind_u64: int                              = 130
    add_imm_32: int                                = 131
    and_imm: int                                   = 132
    xor_imm: int                                   = 133
    or_imm: int                                    = 134
    mul_imm_32: int                                = 135
    set_lt_u_imm: int                              = 136
    set_lt_s_imm: int                              = 137
    shlo_l_imm_32: int                             = 138
    shlo_r_imm_32: int                             = 139
    shar_r_imm_32: int                             = 140
    neg_add_imm_32: int                            = 141
    set_gt_u_imm: int                              = 142
    set_gt_s_imm: int                              = 143
    shlo_l_imm_alt_32: int                         = 144
    shlo_r_imm_alt_32: int                         = 145
    shar_r_imm_alt_32: int                         = 146
    cmov_iz_imm: int                               = 147
    cmov_nz_imm: int                               = 148
    add_imm_64: int                                = 149
    mul_imm_64: int                                = 150
    shlo_l_imm_64: int                             = 151
    shlo_r_imm_64: int                             = 152
    shar_r_imm_64: int                             = 153
    neg_add_imm_64: int                            = 154
    shlo_l_imm_alt_64: int                         = 155
    shlo_r_imm_alt_64: int                         = 156
    shar_r_imm_alt_64: int                         = 157
    rot_r_64_imm: int                              = 158
    rot_r_64_imm_alt: int                          = 159
    rot_r_32_imm: int                              = 160
    rot_r_32_imm_alt: int                          = 161


    # GP_A.5.11
    # Instructions with Arguments of Two Registers & One Offset (reg_reg_offset)
    branch_eq: int                                 = 170
    branch_ne: int                                 = 171
    branch_lt_u: int                               = 172
    branch_lt_s: int                               = 173
    branch_ge_u: int                               = 174
    branch_ge_s: int                               = 175

    # GP_A.5.12
    # Instructions with Arguments Of Two Registers And Two Immediates (reg_reg_imm_imm)
    load_imm_jump_ind: int                         = 180

    # GP_A.5.13
    # Instructions with Arguments Of Three Registers (reg_reg_reg)
    add_32: int                                    = 190
    sub_32: int                                    = 191
    mul_32: int                                    = 192
    div_u_32: int                                  = 193
    div_s_32: int                                  = 194
    rem_u_32: int                                  = 195
    rem_s_32: int                                  = 196
    shlo_l_32: int                                 = 197
    shlo_r_32: int                                 = 198
    shar_r_32: int                                 = 199
    add_64: int                                    = 200
    sub_64: int                                    = 201
    mul_64: int                                    = 202
    div_u_64: int                                  = 203
    div_s_64: int                                  = 204
    rem_u_64: int                                  = 205
    rem_s_64: int                                  = 206
    shlo_l_64: int                                 = 207
    shlo_r_64: int                                 = 208
    shar_r_64: int                                 = 209
    _and: int                                      = 210
    xor: int                                       = 211
    _or: int                                       = 212
    mul_upper_s_s: int                             = 213
    mul_upper_u_u: int                             = 214
    mul_upper_s_u: int                             = 215
    set_lt_u: int                                  = 216
    set_lt_s: int                                  = 217
    cmov_iz: int                                   = 218
    cmov_nz: int                                   = 219
    rot_l_64: int                                  = 220
    rot_l_32: int                                  = 221
    rot_r_64: int                                  = 222
    rot_r_32: int                                  = 223
    and_inv: int                                   = 224
    or_inv: int                                    = 225
    xnor: int                                      = 226
    _max: int                                      = 227
    max_u: int                                     = 228
    _min: int                                      = 229
    min_u: int                                     = 230

InstructionType

Bases: Enum

This enum serves as classification for how instructions should be decoded

Source code in pyjamaz/pvm/constants.py
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
class InstructionType(Enum):
    """
    This enum serves as classification for how instructions should be decoded
    """
    none: int                                      = 0   #GP_A.5.1
    imm: int                                       = 1   #GP_A.5.2
    reg_ext_imm: int                               = 2   #GP_A.5.3
    imm_imm: int                                   = 3   #GP_A.5.4
    offset: int                                    = 4   #GP_A.5.5
    reg_imm: int                                   = 5   #GP_A.5.6
    reg_imm_imm: int                               = 6   #GP_A.5.7
    reg_imm_offset: int                            = 7   #GP_A.5.8
    reg_reg: int                                   = 8   #GP_A.5.9
    reg_reg_imm: int                               = 9   #GP_A.5.10
    reg_reg_offset: int                            = 10  #GP_A.5.11
    reg_reg_imm_imm: int                           = 11  #GP_A.5.12
    reg_reg_reg: int                               = 12  #GP_A.5.13

PVMProgram dataclass

Bases: Serializable

Source code in pyjamaz/pvm/types.py
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
@dataclass
class PVMProgram(Serializable):
    """

    """
    # c
    code: PVMCode
    # ω
    registers: List[int]
    # µ
    memory: PVMMemory

    name: str = ''

    """
    GP-0.7.2-eq:A.42 | Initializing of memory pages
    """

    @staticmethod
    def init_memory(
            rom_contents: bytes,
            heap_contents: bytes,
            argument_contents: bytes,
            heap_mem_pages: int,
            stack_mem_size: int
    ) -> PVMMemory:

        _rom = MemorySection(
            address=PVM_INIT_ZONE_SIZE,
            size=page_size(len(rom_contents)),
            contents=rom_contents,
            acl=MEM_R
        )

        # If PVM_MIN_HEAP_SIZE is set, we preallocate at least that size to (hopefully) prevent lots of memory allocations...
        heap_mem_size = max(page_size(settings.PVM_MIN_HEAP_SIZE), page_size(len(heap_contents)) + heap_mem_pages * PVM_PAGE_SIZE)
        _heap = MemorySection(
            address=(2 * PVM_INIT_ZONE_SIZE) + PVMMemory.zone_size(len(rom_contents)),
            size=heap_mem_size,
            contents=heap_contents,
            acl=MEM_W
        )

        _stack = MemorySection(
            address=2 ** 32 - (2 * PVM_INIT_ZONE_SIZE) - PVM_INPUT_DATA_SIZE - page_size(stack_mem_size),
            size=page_size(stack_mem_size),
            contents=bytes(page_size(stack_mem_size)),    #TODO: hoeft niet dubbel hier
            acl=MEM_W
        )

        _arguments = MemorySection(
            address=2 ** 32 - PVM_INIT_ZONE_SIZE - PVM_INPUT_DATA_SIZE,
            size=page_size(len(argument_contents)),
            contents=argument_contents,
            acl=MEM_R
        )

        return PVMMemory(rom=_rom, heap=_heap, stack=_stack, arguments=_arguments)

    @staticmethod
    def init_registers(arguments: bytes) -> List[int]:
        """
        GP-0.7.2-eq:A.43
        """
        regs = [0] * 13
        regs[0] = 2**32 - 2**16
        regs[1] = 2**32 - 2*PVM_INIT_ZONE_SIZE - PVM_INPUT_DATA_SIZE
        regs[7] = 2 ** 32 - PVM_INIT_ZONE_SIZE - PVM_INPUT_DATA_SIZE
        regs[8] = len(arguments)
        return regs


    @classmethod
    def from_serialized_bytes(cls, serialized_program: bytes, argument_contents: bytes, name: Optional[str]) -> Optional['PVMProgram']:
        """
        GP-0.7.2-eq:A.37 (function_Y)
        """

        # GP-0.7.2-eq:A.41
        if len(argument_contents) > PVM_INPUT_DATA_SIZE:
            return None

        try:

            jam_bytes = JamBytes(serialized_program)

            if settings.DEBUG:
                override_heap_mem_pages = None
                if name in settings.DEBUG_PROGRAM_OVERRIDE:
                    with open(settings.DEBUG_PROGRAM_OVERRIDE.get(name)['file'], 'rb') as fp:
                        jam_bytes = JamBytes(fp.read())
                        override_heap_mem_pages = settings.DEBUG_PROGRAM_OVERRIDE.get(name)['heap_mem_pages']

                        metadata = Bytes.decode(jam_bytes)

            # GP-0.7.2-eq:A.38 (|o|)
            pvm_rom_size = int.from_bytes(jam_bytes.get_next_bytes(3), byteorder='little')
            # GP-0.7.2-eq:A.38 (|w|)
            pvm_heap_size = int.from_bytes(jam_bytes.get_next_bytes(3), byteorder='little')
            # GP-0.7.2-eq:A.38 (z)
            heap_mem_pages = int.from_bytes(jam_bytes.get_next_bytes(2), byteorder='little')
            # GP-0.7.2-eq:A.38 (s)
            stack_mem_size = int.from_bytes(jam_bytes.get_next_bytes(3), byteorder='little')
            # GP-0.7.2-eq:A.38 (o)
            pvm_rom_contents = jam_bytes.get_next_bytes(pvm_rom_size)
            # GP-0.7.2-eq:A.38 (w)
            pvm_heap_contents = jam_bytes.get_next_bytes(pvm_heap_size)

            pvm_code_size = int.from_bytes(jam_bytes.get_next_bytes(4), byteorder='little')
            pvm_code = jam_bytes.get_next_bytes(pvm_code_size)

            if settings.DEBUG and override_heap_mem_pages:
                heap_mem_pages = override_heap_mem_pages

            # GP-0.7.2-eq:A.42
            if (5 * PVM_INIT_ZONE_SIZE +
                PVMMemory.zone_size(pvm_rom_size) +
                PVMMemory.zone_size(pvm_heap_size + heap_mem_pages * PVM_PAGE_SIZE) +
                PVMMemory.zone_size(stack_mem_size) + PVM_INPUT_DATA_SIZE
            ) <= 2**32:

                instance = cls(
                    code=PVMCode.from_jam_bytes(JamBytes(pvm_code)),
                    registers=cls.init_registers(argument_contents),
                    memory=cls.init_memory(pvm_rom_contents, pvm_heap_contents, argument_contents, heap_mem_pages, stack_mem_size),
                    name=name
                )

                #TODO: TEMP HACK TO DEBUG INJECT CUSTOM PROGRAMS!!!!!!!
                if settings.DEBUG:
                    instance._code = pvm_code
                    instance._ram = pvm_heap_contents
                    instance._rom = pvm_rom_contents

                return instance
            else:
                #TODO
                raise Exception("HUH?")

        except RemainingScaleBytesNotEmptyException as e: # TODO deserialize exception
            pass

        return None


    def to_serialized_bytes(self) -> bytes:
        """
        GP-0.7.2-eq:A.37 (Y)
        """
        #TODO!!!!!!!!!!!!!!
        # data = bytes()
        #
        # # GP?? |o|
        # data += len(self.memory._rom.contents).to_bytes(length=3, byteorder='little')
        # # GP?? |w|
        # data += len(self.memory._heap.contents).to_bytes(length=3, byteorder='little')
        # # GP?? z
        # data += int(1).to_bytes(length=2, byteorder='little')
        # # GP?? s
        # data += len(self.memory._stack.contents).to_bytes(length=3, byteorder='little')
        #
        # # GP?? o
        # data += len(self.memory._rom.contents).to_bytes(length=3, byteorder='little')
        # # GP?? w
        # data += len(self.memory._heap.contents).to_bytes(length=3, byteorder='little')
        #
        # code_bytes = self.code.to_jam_bytes().to_bytes()
        # data += int(len(code_bytes)).to_bytes(length=4, byteorder='little')
        # data += code_bytes
        #
        # return data
        return self.code.to_jam_bytes().to_bytes()


    @classmethod
    def initialize(cls, pvm_code: bytes) -> 'PVMProgram':
        pass

name: str = '' class-attribute instance-attribute

GP-0.7.2-eq:A.42 | Initializing of memory pages

init_registers(arguments: bytes) -> List[int] staticmethod

GP-0.7.2-eq:A.43

Source code in pyjamaz/pvm/types.py
129
130
131
132
133
134
135
136
137
138
139
@staticmethod
def init_registers(arguments: bytes) -> List[int]:
    """
    GP-0.7.2-eq:A.43
    """
    regs = [0] * 13
    regs[0] = 2**32 - 2**16
    regs[1] = 2**32 - 2*PVM_INIT_ZONE_SIZE - PVM_INPUT_DATA_SIZE
    regs[7] = 2 ** 32 - PVM_INIT_ZONE_SIZE - PVM_INPUT_DATA_SIZE
    regs[8] = len(arguments)
    return regs

from_serialized_bytes(serialized_program: bytes, argument_contents: bytes, name: Optional[str]) -> Optional[PVMProgram] classmethod

GP-0.7.2-eq:A.37 (function_Y)

Source code in pyjamaz/pvm/types.py
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
@classmethod
def from_serialized_bytes(cls, serialized_program: bytes, argument_contents: bytes, name: Optional[str]) -> Optional['PVMProgram']:
    """
    GP-0.7.2-eq:A.37 (function_Y)
    """

    # GP-0.7.2-eq:A.41
    if len(argument_contents) > PVM_INPUT_DATA_SIZE:
        return None

    try:

        jam_bytes = JamBytes(serialized_program)

        if settings.DEBUG:
            override_heap_mem_pages = None
            if name in settings.DEBUG_PROGRAM_OVERRIDE:
                with open(settings.DEBUG_PROGRAM_OVERRIDE.get(name)['file'], 'rb') as fp:
                    jam_bytes = JamBytes(fp.read())
                    override_heap_mem_pages = settings.DEBUG_PROGRAM_OVERRIDE.get(name)['heap_mem_pages']

                    metadata = Bytes.decode(jam_bytes)

        # GP-0.7.2-eq:A.38 (|o|)
        pvm_rom_size = int.from_bytes(jam_bytes.get_next_bytes(3), byteorder='little')
        # GP-0.7.2-eq:A.38 (|w|)
        pvm_heap_size = int.from_bytes(jam_bytes.get_next_bytes(3), byteorder='little')
        # GP-0.7.2-eq:A.38 (z)
        heap_mem_pages = int.from_bytes(jam_bytes.get_next_bytes(2), byteorder='little')
        # GP-0.7.2-eq:A.38 (s)
        stack_mem_size = int.from_bytes(jam_bytes.get_next_bytes(3), byteorder='little')
        # GP-0.7.2-eq:A.38 (o)
        pvm_rom_contents = jam_bytes.get_next_bytes(pvm_rom_size)
        # GP-0.7.2-eq:A.38 (w)
        pvm_heap_contents = jam_bytes.get_next_bytes(pvm_heap_size)

        pvm_code_size = int.from_bytes(jam_bytes.get_next_bytes(4), byteorder='little')
        pvm_code = jam_bytes.get_next_bytes(pvm_code_size)

        if settings.DEBUG and override_heap_mem_pages:
            heap_mem_pages = override_heap_mem_pages

        # GP-0.7.2-eq:A.42
        if (5 * PVM_INIT_ZONE_SIZE +
            PVMMemory.zone_size(pvm_rom_size) +
            PVMMemory.zone_size(pvm_heap_size + heap_mem_pages * PVM_PAGE_SIZE) +
            PVMMemory.zone_size(stack_mem_size) + PVM_INPUT_DATA_SIZE
        ) <= 2**32:

            instance = cls(
                code=PVMCode.from_jam_bytes(JamBytes(pvm_code)),
                registers=cls.init_registers(argument_contents),
                memory=cls.init_memory(pvm_rom_contents, pvm_heap_contents, argument_contents, heap_mem_pages, stack_mem_size),
                name=name
            )

            #TODO: TEMP HACK TO DEBUG INJECT CUSTOM PROGRAMS!!!!!!!
            if settings.DEBUG:
                instance._code = pvm_code
                instance._ram = pvm_heap_contents
                instance._rom = pvm_rom_contents

            return instance
        else:
            #TODO
            raise Exception("HUH?")

    except RemainingScaleBytesNotEmptyException as e: # TODO deserialize exception
        pass

    return None

to_serialized_bytes() -> bytes

GP-0.7.2-eq:A.37 (Y)

Source code in pyjamaz/pvm/types.py
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
def to_serialized_bytes(self) -> bytes:
    """
    GP-0.7.2-eq:A.37 (Y)
    """
    #TODO!!!!!!!!!!!!!!
    # data = bytes()
    #
    # # GP?? |o|
    # data += len(self.memory._rom.contents).to_bytes(length=3, byteorder='little')
    # # GP?? |w|
    # data += len(self.memory._heap.contents).to_bytes(length=3, byteorder='little')
    # # GP?? z
    # data += int(1).to_bytes(length=2, byteorder='little')
    # # GP?? s
    # data += len(self.memory._stack.contents).to_bytes(length=3, byteorder='little')
    #
    # # GP?? o
    # data += len(self.memory._rom.contents).to_bytes(length=3, byteorder='little')
    # # GP?? w
    # data += len(self.memory._heap.contents).to_bytes(length=3, byteorder='little')
    #
    # code_bytes = self.code.to_jam_bytes().to_bytes()
    # data += int(len(code_bytes)).to_bytes(length=4, byteorder='little')
    # data += code_bytes
    #
    # return data
    return self.code.to_jam_bytes().to_bytes()

PVMMemory dataclass

Source code in pyjamaz/pvm/memory.py
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
@dataclass
class PVMMemory:
    sections: List[MemorySection]
    section_offsets: List[int]
    _rom: MemorySection
    _heap: MemorySection
    _stack: MemorySection
    _args: MemorySection
    _mem_addr: int
    _section: MemorySection
    _section_addr: int

    SIZE:int = 2**32

    @classmethod
    def allocate(cls, rom_pages, heap_pages, stack_pages, arg_pages):
        _rom = MemorySection(
            address=PVM_INIT_ZONE_SIZE,
            size=rom_pages * PVM_PAGE_SIZE,
            contents=bytes(rom_pages * PVM_PAGE_SIZE),
            acl=MEM_R
        )
        _heap = MemorySection(
            address=(2 * PVM_INIT_ZONE_SIZE) + PVMMemory.zone_size(_rom.size),
            size=heap_pages * PVM_PAGE_SIZE,
            contents=bytes(heap_pages * PVM_PAGE_SIZE),
            acl=MEM_W
        )
        _stack = MemorySection(
            address=2 ** 32 - (2 * PVM_INIT_ZONE_SIZE) - PVM_INPUT_DATA_SIZE - stack_pages * PVM_PAGE_SIZE,
            size=stack_pages * PVM_PAGE_SIZE,
            contents=bytes(stack_pages * PVM_PAGE_SIZE),
            acl=MEM_W,
        )
        _arguments = MemorySection(
            address=2 ** 32 - PVM_INIT_ZONE_SIZE - PVM_INPUT_DATA_SIZE,
            size=arg_pages * PVM_PAGE_SIZE,
            contents=bytes(arg_pages * PVM_PAGE_SIZE),
            acl=MEM_R
        )

        return PVMMemory(rom=_rom, heap=_heap, stack=_stack, arguments=_arguments)


    def __init__(
        self,
        rom: MemorySection,
        heap: MemorySection,
        stack: MemorySection,
        arguments: MemorySection
    ):
        self._rom = rom
        self._heap = heap
        self._stack = stack
        self._args = arguments

        self._mem_addr = None
        self._section = None
        self._section_addr = None

        self.update_offsets()


    def update_offsets(self) -> Optional[MemorySection]:
        self.section_offsets = [p.address for p in (self._rom, self._heap, self._stack, self._args) if p]


    def find_section(self, addr: int) -> Optional[MemorySection]:
        if not self.section_offsets:
            msg = "Memory not initialized"
            logging.error(msg)
            raise PVMMemoryError(msg)

        #GP-0.7.2-eq:A.7
        if addr < 2**16:
            msg = "Invalid memory access"
            DEBUG and logging.debug(msg)
            raise PVMMemoryError(msg)

        if self._heap and addr >= self._heap.address and addr <= self._heap.address + self._heap.size:
            return self._heap
        elif self._stack and addr >= self._stack.address and addr <= self._stack.address + self._stack.size:
            return self._stack
        elif self._rom and addr >= self._rom.address and addr <= self._rom.address + self._rom.size:
            return self._rom
        elif self._args and addr >= self._args.address and addr <= self._args.address + self._args.size:
            return self._args
        else:
            return None


    def write_int(self, addr: int, value: int, length: int):
        # GP: ⌊addr⌋_{2^32} - addresses must wrap around 32-bit address space
        addr = addr % self.SIZE
        # Always store the requested memory address so we can refer it after a PVMMemoryError fx
        self._mem_addr = addr

        # Check for address + length overflow beyond 32-bit address space
        if addr + length > self.SIZE:
            raise PVMMemoryError(f"Memory access overflow: {addr} + {length} > 2^32")

        if not (self._section and self._section.address <= addr < self._section.address + self._section.size):
            section = self.find_section(addr)
        else:
            section = self._section

        if not section:
            raise PVMMemoryError("MemorySection not found")

        section_addr = (addr - section.address)  #% section.size #TODO: not sure if % necesarry?
        if section.acl is not None and not section.acl_check(section_addr, length, MEM_W):
            raise PVMMemoryError(f"Memory address {addr} ACL write check failed")

        self._section = section
        self._section_addr = section_addr

        # Set the mem page according to the found page for this range
        section.write_int(section_addr, value, length)


    def read_int(self, addr: int, length: int):
        # GP: ⌊addr⌋_{2^32} - addresses must wrap around 32-bit address space
        addr = addr % self.SIZE
        # Always store the requested memory address so we can refer it after a PVMMemoryError fx
        self._mem_addr = addr

        # Check for address + length overflow beyond 32-bit address space
        if addr + length > self.SIZE:
            raise PVMMemoryError(f"Memory access overflow: {addr} + {length} > 2^32")

        if not (self._section and self._section.address <= addr < self._section.address + self._section.size):
            section = self.find_section(addr)
        else:
            section = self._section

        if not section:
            raise PVMMemoryError("MemorySection not found")

        section_addr = (addr - section.address) #% section.size  #TODO: not sure if % necesarry?
        if section.acl is not None and not section.acl_check(section_addr, length, MEM_R):
            raise PVMMemoryError(f"Memory address {addr} ACL read check failed")

        self._section = section
        self._section_addr = section_addr

        # Set the mem page according to the found page for this range
        return section.read_int(section_addr, length)


    def is_accessible(self, address: int, length: int, mode: int) -> bool:
        # GP: ⌊addr⌋_{2^32} - addresses must wrap around 32-bit address space
        address = address % self.SIZE

        if length == 0:
            return True

        # Check for address + length overflow beyond 32-bit address space
        if address + length > self.SIZE:
            return False

        try:
            section = self.find_section(address)
        except (PanicError, PVMMemoryError):
            section = None

        if not section:
            return False

        if mode not in (MEM_R, MEM_W, MEM_RW):
            raise PVMError(f"Invalid PVMMemory mode: {mode}")

        local_addr = address - section.address
        if section.acl and not section.acl_check(local_addr, length, mode):
            return False

        bytes_required = local_addr + length

        if bytes_required > section.size:
            return False

        return True


    def read_bytes(self, address: int, length: int, padding:int = None) -> bytes:
        """
        """
        # GP: ⌊addr⌋_{2^32} - addresses must wrap around 32-bit address space
        address = address % self.SIZE
        # Always store the requested memory address so we can refer it after a PVMMemoryError fx
        self._mem_addr = address

        if length == 0:
            return bytes()

        # Check for address + length overflow beyond 32-bit address space
        if address + length > self.SIZE:
            raise PVMMemoryError(f"Memory access overflow: {address} + {length} > 2^32")

        section = self.find_section(address)
        if not section:
            raise PVMMemoryError(f"MemorySection not found {address}")

        section_addr = (address - section.address)  #% section.size  #TODO: not sure if % necesarry?
        if section.acl is not None and not section.acl_check(section_addr, length, MEM_R):
            raise PVMMemoryError(
                f"Memory address {address} ACL read check failed (offset={section_addr}, len={length}, "
                f"section_start={section.address}, paged_tail={section.paged_tail}, section_size={section.size})"
            )

        section_bytes = (section.size - section_addr)
        if section_bytes < length:
            raise PVMMemoryError(
                f"Heap overflow {length} > {section_bytes} (offset={section_addr}, section_size={section.size}, "
                f"section_start={section.address}, paged_tail={section.paged_tail})"
            )

        mem_bytes = bytes(section.contents[section_addr:section_addr+length])
        if padding and len(mem_bytes) < padding:
            mem_bytes = mem_bytes.ljust(padding, b'\0')

        return mem_bytes


    def write_bytes(self, address: int, content: bytes) -> None:
        """
        """
        # GP: ⌊addr⌋_{2^32} - addresses must wrap around 32-bit address space
        address = address % self.SIZE
        # Always store the requested memory address so we can refer it after a PVMMemoryError fx
        self._mem_addr = address

        bytes_remaining = len(content)
        # TODO: or raise PVMMemoryError?
        if bytes_remaining == 0:
            return

        # Check for address + length overflow beyond 32-bit address space
        if address + bytes_remaining > self.SIZE:
            raise PVMMemoryError(f"Memory access overflow: {address} + {bytes_remaining} > 2^32")

        section = self.find_section(address)
        if not section:
            raise PVMMemoryError(f"MemorySection not found {address}")

        section_addr = (address - section.address) #% section.size  #TODO: not sure if % necesarry?
        if section.acl and not section.acl_check(section_addr, len(content), MEM_W):
            raise PVMMemoryError(
                f"Memory address {address} ACL check failed (offset={section_addr}, len={len(content)}, "
                f"section_start={section.address}, paged_tail={section.paged_tail}, section_size={section.size})"
            )

        section_bytes = (section.size - section_addr)

        if section_bytes < len(content):
            raise PVMMemoryError(
                f"Heap overflow {len(content)} > {section_bytes} (offset={section_addr}, section_size={section.size}, "
                f"section_start={section.address}, paged_tail={section.paged_tail})"
            )

        section.set_content(content, section_addr, section_addr+len(content))


    def zero(self, page_idx: int, nr_pages: int, acl: int):
        mem_addr = page_idx * PVM_PAGE_SIZE
        # TODO we assume acl should be set this way, cannot test right now
        if not self.section_offsets and mem_addr == PVM_INIT_ZONE_SIZE:
            if not self._rom:
                self._rom = MemorySection(
                    address=PVM_INIT_ZONE_SIZE,
                    size=nr_pages * PVM_PAGE_SIZE,
                    contents=bytes(nr_pages * PVM_PAGE_SIZE),
                    acl=acl
                )
            section = self._rom
        elif mem_addr == (2 * PVM_INIT_ZONE_SIZE) + PVMMemory.zone_size(len(self._rom.contents)):
            if not self._heap:
                self._heap = MemorySection(
                    address=(2 * PVM_INIT_ZONE_SIZE) + PVMMemory.zone_size(len(self._rom.contents)),
                    size=nr_pages * PVM_PAGE_SIZE,
                    contents=bytes(nr_pages * PVM_PAGE_SIZE),
                    acl=acl
                )
            section = self._heap
        elif self._stack is None and mem_addr >= (2 ** 32 - (2 * PVM_INIT_ZONE_SIZE) - PVM_INPUT_DATA_SIZE - (nr_pages * PVM_PAGE_SIZE)):
            if not self._stack:
                self._stack = MemorySection(
                    address=2 ** 32 - (2 * PVM_INIT_ZONE_SIZE) - PVM_INPUT_DATA_SIZE - (nr_pages * PVM_PAGE_SIZE),
                    size=nr_pages * PVM_PAGE_SIZE,
                    contents=bytes(nr_pages * PVM_PAGE_SIZE),
                    acl=acl
                )
            section = self._stack
        else:
            raise PVMMemoryError(f"Invalid void operation: MemorySection not found {mem_addr}")

        self.update_offsets()
        addr = page_idx * PVM_PAGE_SIZE - section.address
        section.acl_set_pages(addr // PVM_PAGE_SIZE, nr_pages, acl)
        section.contents[addr:addr + nr_pages * PVM_PAGE_SIZE] = 0 #TODO: pvm specific const?


    def void(self, page_idx: int, nr_pages: int, acl: int):
        mem_addr = page_idx * PVM_PAGE_SIZE

        section = self.find_section(mem_addr)
        if not section:
            raise PVMMemoryError(f"MemorySection not found {mem_addr}")

        page_nr = (mem_addr - section.address) // PVM_PAGE_SIZE
        section.acl_set_pages(page_nr, nr_pages, acl)
        offset = mem_addr - section.address
        section.contents[offset:offset + nr_pages * PVM_PAGE_SIZE] = 0 #TODO: pvm specific const?


    @staticmethod
    def zone_size(items: int) -> int:
        """
        GP-0.7.2-eq:A.40 (Z)
        """
        return PVM_INIT_ZONE_SIZE * ceil(items / PVM_INIT_ZONE_SIZE)

read_bytes(address: int, length: int, padding: int = None) -> bytes

Source code in pyjamaz/pvm/memory.py
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
def read_bytes(self, address: int, length: int, padding:int = None) -> bytes:
    """
    """
    # GP: ⌊addr⌋_{2^32} - addresses must wrap around 32-bit address space
    address = address % self.SIZE
    # Always store the requested memory address so we can refer it after a PVMMemoryError fx
    self._mem_addr = address

    if length == 0:
        return bytes()

    # Check for address + length overflow beyond 32-bit address space
    if address + length > self.SIZE:
        raise PVMMemoryError(f"Memory access overflow: {address} + {length} > 2^32")

    section = self.find_section(address)
    if not section:
        raise PVMMemoryError(f"MemorySection not found {address}")

    section_addr = (address - section.address)  #% section.size  #TODO: not sure if % necesarry?
    if section.acl is not None and not section.acl_check(section_addr, length, MEM_R):
        raise PVMMemoryError(
            f"Memory address {address} ACL read check failed (offset={section_addr}, len={length}, "
            f"section_start={section.address}, paged_tail={section.paged_tail}, section_size={section.size})"
        )

    section_bytes = (section.size - section_addr)
    if section_bytes < length:
        raise PVMMemoryError(
            f"Heap overflow {length} > {section_bytes} (offset={section_addr}, section_size={section.size}, "
            f"section_start={section.address}, paged_tail={section.paged_tail})"
        )

    mem_bytes = bytes(section.contents[section_addr:section_addr+length])
    if padding and len(mem_bytes) < padding:
        mem_bytes = mem_bytes.ljust(padding, b'\0')

    return mem_bytes

write_bytes(address: int, content: bytes) -> None

Source code in pyjamaz/pvm/memory.py
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
def write_bytes(self, address: int, content: bytes) -> None:
    """
    """
    # GP: ⌊addr⌋_{2^32} - addresses must wrap around 32-bit address space
    address = address % self.SIZE
    # Always store the requested memory address so we can refer it after a PVMMemoryError fx
    self._mem_addr = address

    bytes_remaining = len(content)
    # TODO: or raise PVMMemoryError?
    if bytes_remaining == 0:
        return

    # Check for address + length overflow beyond 32-bit address space
    if address + bytes_remaining > self.SIZE:
        raise PVMMemoryError(f"Memory access overflow: {address} + {bytes_remaining} > 2^32")

    section = self.find_section(address)
    if not section:
        raise PVMMemoryError(f"MemorySection not found {address}")

    section_addr = (address - section.address) #% section.size  #TODO: not sure if % necesarry?
    if section.acl and not section.acl_check(section_addr, len(content), MEM_W):
        raise PVMMemoryError(
            f"Memory address {address} ACL check failed (offset={section_addr}, len={len(content)}, "
            f"section_start={section.address}, paged_tail={section.paged_tail}, section_size={section.size})"
        )

    section_bytes = (section.size - section_addr)

    if section_bytes < len(content):
        raise PVMMemoryError(
            f"Heap overflow {len(content)} > {section_bytes} (offset={section_addr}, section_size={section.size}, "
            f"section_start={section.address}, paged_tail={section.paged_tail})"
        )

    section.set_content(content, section_addr, section_addr+len(content))

zone_size(items: int) -> int staticmethod

GP-0.7.2-eq:A.40 (Z)

Source code in pyjamaz/pvm/memory.py
327
328
329
330
331
332
@staticmethod
def zone_size(items: int) -> int:
    """
    GP-0.7.2-eq:A.40 (Z)
    """
    return PVM_INIT_ZONE_SIZE * ceil(items / PVM_INIT_ZONE_SIZE)

reverse_bytes(x)

Reverse the byte order of a 64-bit integer (endianness swap).

Converts between big-endian and little-endian representations. Example: 0x0123456789ABCDEF -> 0xEFCDAB8967452301

Note: Optimized using Python's built-in bytes operations. Provides ~4x speedup over bitwise operations.

Source code in pyjamaz/pvm/interpreters/cpython/defs.py
81
82
83
84
85
86
87
88
89
90
91
92
def reverse_bytes(x):
    """
    Reverse the byte order of a 64-bit integer (endianness swap).

    Converts between big-endian and little-endian representations.
    Example: 0x0123456789ABCDEF -> 0xEFCDAB8967452301

    Note:
        Optimized using Python's built-in bytes operations.
        Provides ~4x speedup over bitwise operations.
    """
    return struct.unpack('<Q', struct.pack('>Q', x))[0]

pvm_smod(a: int, b: int) -> int

Signed modulo operation optimized using conditional branching to avoid function call overhead.

Returns a % b with sign of a preserved. Special case: if b == 0, returns a.

Note: Optimized using conditional branching instead of abs() and sign functions for ~18% performance improvement.

Source code in pyjamaz/pvm/interpreters/cpython/defs.py
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
def pvm_smod(a: int, b: int) -> int:
    """
    Signed modulo operation optimized using conditional branching
    to avoid function call overhead.

    Returns a % b with sign of a preserved.
    Special case: if b == 0, returns a.

    Note:
        Optimized using conditional branching instead of abs() and sign functions
        for ~18% performance improvement.
    """
    if b == 0:
        return a

    # Use conditional branching to avoid abs() function calls
    if a >= 0:
        if b >= 0:
            return a % b
        else:
            return a % (-b)
    else:
        if b >= 0:
            return -((-a) % b)
        else:
            return -((-a) % (-b))

pvm_rtz_div(a: int, b: int) -> int

Truncated division (rounds toward zero).

Returns the quotient of a/b rounded toward zero. Examples: 7/3=2, -7/3=-2, 7/-3=-2, -7/-3=2

Note: Optimized using conditional branching to avoid abs() and divmod() overhead. Provides ~1.4x speedup while maintaining exact correctness for all integer values. This approach avoids floating point precision issues with very large integers.

Source code in pyjamaz/pvm/interpreters/cpython/defs.py
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
def pvm_rtz_div(a: int, b: int) -> int:
    """
    Truncated division (rounds toward zero).

    Returns the quotient of a/b rounded toward zero.
    Examples: 7/3=2, -7/3=-2, 7/-3=-2, -7/-3=2

    Note:
        Optimized using conditional branching to avoid abs() and divmod() overhead.
        Provides ~1.4x speedup while maintaining exact correctness for all integer values.
        This approach avoids floating point precision issues with very large integers.
    """
    if a >= 0:
        if b > 0:
            return a // b
        else:
            return -(a // (-b))
    else:
        if b > 0:
            return -((-a) // b)
        else:
            return (-a) // (-b)

pvm_X(x: int, n: int) -> int

Sign extend a number to two's complement form for value X and number of bytes n

Optimized version using bit operations.

Source code in pyjamaz/pvm/interpreters/cpython/defs.py
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
def pvm_X(x: int, n: int) -> int:
    """
    Sign extend a number to two's complement form for value X and number of bytes n

    Optimized version using bit operations.
    """
    # Optimized sign extension for each n
    if n == 1:
        masked = x & 0xFF
        if masked & 0x80:  # Check sign bit
            return masked | 0xFFFFFFFFFFFFFF00
        return masked
    elif n == 2:
        masked = x & 0xFFFF
        if masked & 0x8000:  # Check sign bit
            return masked | 0xFFFFFFFFFFFF0000
        return masked
    elif n == 3:
        masked = x & 0xFFFFFF
        # Check if sign bit (bit 23) is set
        if masked & 0x800000:
            # Negative - sign extend to 64 bits
            return masked | 0xFFFFFFFFFF000000
        else:
            # Positive
            return masked
    elif n == 4:
        masked = x & 0xFFFFFFFF
        if masked & 0x80000000:  # Check sign bit
            return masked | 0xFFFFFFFF00000000
        return masked
    elif n == 5:
        masked = x & 0xFFFFFFFFFF
        # Check if sign bit (bit 39) is set
        if masked & 0x8000000000:
            # Negative - sign extend to 64 bits
            return masked | 0xFFFFFF0000000000
        else:
            # Positive
            return masked
    elif n == 6:
        masked = x & 0xFFFFFFFFFFFF
        # Check if sign bit (bit 47) is set
        if masked & 0x800000000000:
            # Negative - sign extend to 64 bits
            return masked | 0xFFFF000000000000
        else:
            # Positive
            return masked
    elif n == 7:
        masked = x & 0xFFFFFFFFFFFFFF
        # Check if sign bit (bit 55) is set
        if masked & 0x80000000000000:
            # Negative - sign extend to 64 bits
            return masked | 0xFF00000000000000
        else:
            # Positive
            return masked
    elif n == 8:
        return x & 0xFFFFFFFFFFFFFFFF
    else:
        return x

pvm_Z(a: int, n: int) -> int

Interpret the low n bytes of a as a signed two's-complement integer.

Source code in pyjamaz/pvm/interpreters/cpython/defs.py
231
232
233
234
235
236
237
238
239
240
241
def pvm_Z(a: int, n: int) -> int:
    """
    Interpret the low n bytes of `a` as a signed two's-complement integer.
    """
    if n <= 0:
        return 0
    bits = n * 8
    mask = (1 << bits) - 1
    sign = 1 << (bits - 1)
    u = a & mask
    return (u ^ sign) - sign

page_size(bytes: int) -> int

GP-0.7.2-eq:A.40 (P)

Source code in pyjamaz/pvm/memory_section_abstract.py
15
16
17
18
19
def page_size(bytes: int) -> int:
    """
    GP-0.7.2-eq:A.40 (P)
    """
    return PVM_PAGE_SIZE * ceil(bytes / PVM_PAGE_SIZE)

umul64wide_jit(a: U64, b: U64) -> (U64, U64)

Unsigned 64x64 -> (hi, lo) as uint64s.

Source code in pyjamaz/pvm/interpreters/numba/defs.py
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
@njit(types.UniTuple(uint64, 2)(uint64, uint64), cache=NUMBA_CACHE)
def umul64wide_jit(a: U64, b: U64) -> (U64, U64):
    """Unsigned 64x64 -> (hi, lo) as uint64s."""
    mask32 = U64(0xFFFFFFFF)
    a_lo = a & mask32
    a_hi = a >> U64(32)
    b_lo = b & mask32
    b_hi = b >> U64(32)

    ll = a_lo * b_lo  # 64-bit
    lh = a_lo * b_hi
    hl = a_hi * b_lo
    hh = a_hi * b_hi

    carry = (ll >> U64(32)) + (lh & mask32) + (hl & mask32)
    lo = (ll & mask32) | ((carry & mask32) << U64(32))
    hi = hh + (lh >> U64(32)) + (hl >> U64(32)) + (carry >> U64(32))
    return U64(hi), U64(lo)

imul64wide_jit(a: I64, b: I64) -> (U64, U64)

Signed 64x64 -> (hi, lo) representing 128-bit two's-complement product.

Source code in pyjamaz/pvm/interpreters/numba/defs.py
56
57
58
59
60
61
62
63
64
65
66
67
@njit(types.UniTuple(uint64, 2)(int64, int64), cache=NUMBA_CACHE)
def imul64wide_jit(a: I64, b: I64) -> (U64, U64):
    """Signed 64x64 -> (hi, lo) representing 128-bit two's-complement product."""
    ua = U64(a)  # reinterpret
    ub = U64(b)
    hi, lo = umul64wide_jit(ua, ub)
    # Adjust high word for two's-complement signs (see Hacker's Delight)
    if a < 0:
        hi = U64(hi - ub)
    if b < 0:
        hi = U64(hi - ua)
    return U64(hi), U64(lo)

smul_u64wide_jit(a: I64, b: U64) -> (U64, U64)

Signed * Unsigned -> (hi, lo), two's-complement.

Source code in pyjamaz/pvm/interpreters/numba/defs.py
70
71
72
73
74
75
76
77
@njit(types.UniTuple(uint64, 2)(int64, uint64), cache=NUMBA_CACHE)
def smul_u64wide_jit(a: I64, b: U64) -> (U64, U64):
    """Signed * Unsigned -> (hi, lo), two's-complement."""
    ua = U64(a)
    hi, lo = umul64wide_jit(ua, b)
    if a < 0:
        hi = U64(hi - b)
    return U64(hi), U64(lo)

rori64_jit(x: U64, shift_amount: U64) -> U64

Rotate right for 64-bit integers.

Source code in pyjamaz/pvm/interpreters/numba/defs.py
80
81
82
83
@njit(uint64(uint64, uint64), cache=NUMBA_CACHE)
def rori64_jit(x: U64, shift_amount: U64) -> U64:
    """Rotate right for 64-bit integers."""
    return U64(((x >> shift_amount) | (x << (64 - shift_amount))) & 0xFFFFFFFFFFFFFFFF)

roli64_jit(x: U64, shift_amount: U64) -> U64

Rotate left for 64-bit integers.

Source code in pyjamaz/pvm/interpreters/numba/defs.py
86
87
88
89
@njit(uint64(uint64, uint64), cache=NUMBA_CACHE)
def roli64_jit(x: U64, shift_amount: U64) -> U64:
    """Rotate left for 64-bit integers."""
    return U64(((x << shift_amount) | (x >> (64 - shift_amount))) & 0xFFFFFFFFFFFFFFFF)

rori32_jit(x: U32, shift_amount: U32) -> U32

Rotate right for 32-bit integers.

Source code in pyjamaz/pvm/interpreters/numba/defs.py
92
93
94
95
@njit(uint32(uint32, uint32), cache=NUMBA_CACHE)
def rori32_jit(x: U32, shift_amount: U32) -> U32:
    """Rotate right for 32-bit integers."""
    return U32(((x >> shift_amount) | (x << (32 - shift_amount))) & 0xFFFFFFFF)

roli32_jit(x: U32, shift_amount: U32) -> U32

Rotate left for 32-bit integers.

Source code in pyjamaz/pvm/interpreters/numba/defs.py
 98
 99
100
101
@njit(uint32(uint32, uint32), cache=NUMBA_CACHE)
def roli32_jit(x: U32, shift_amount: U32) -> U32:
    """Rotate left for 32-bit integers."""
    return U32(((x << shift_amount) | (x >> (32 - shift_amount))) & 0xFFFFFFFF)

pvm_smod_jit(a: I64, b: I64) -> I64

Signed modulo operation (truncated modulo). Returns a % b with sign of a preserved. Special case: if b == 0, returns a.

Source code in pyjamaz/pvm/interpreters/numba/defs.py
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
@njit(int64(int64, int64), cache=NUMBA_CACHE)
def pvm_smod_jit(a: I64, b: I64) -> I64:
    """
    Signed modulo operation (truncated modulo).
    Returns a % b with sign of a preserved.
    Special case: if b == 0, returns a.
    """
    if b == 0:
        return a

    # Python's % gives remainder with sign of divisor (b)
    # For truncated modulo, we need sign of dividend (a)
    r = a % b

    # If remainder is non zero and has different sign from a, adjust
    # For truncated modulo, remainder should have same sign as dividend (a)
    # Pythons % gives sign of divisor (b), so we need to adjust
    if r != I64(0):
        if a < 0 and r > 0:
            # r has wrong sign (positive), subtract |b| to make it negative
            return r - b  # b is positive here, so r - b is more negative
        elif a > 0 and r < 0:
            # Since b is negative here, -b is positive, so r - b adds |b|
            return r - b

    return r

pvm_rtz_div_jit(a: I64, b: I64) -> I64

Truncated division (rounds toward zero). Uses floor division with adjustment to avoid overflow when negating INT64_MIN.

Source code in pyjamaz/pvm/interpreters/numba/defs.py
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
@njit(int64(int64, int64), cache=NUMBA_CACHE)
def pvm_rtz_div_jit(a: I64, b: I64) -> I64:
    """
    Truncated division (rounds toward zero).
    Uses floor division with adjustment to avoid overflow when negating INT64_MIN.
    """
    if a >= 0:
        if b > 0:
            # Both positive: floor division = truncated division
            return a // b
        else:
            # a >= 0, b < 0: result is negative or zero
            # Floor division rounds toward -infinity, truncated rounds toward zero
            # Need to add 1 if there's a remainder (to make result less negative)
            q = a // b
            return q + I64(1) if a % b != I64(0) else q
    else:
        if b > 0:
            # a < 0, b > 0: result is negative
            # Floor division rounds toward -infinity, truncated rounds toward zero
            # Need to add 1 if there's a remainder (to make result less negative)
            q = a // b
            return q + I64(1) if a % b != I64(0) else q
        else:
            # Both negative: result is positive
            # Floor division works correctly for positive results
            return a // b

pvm_Z_jit(a: U64, n: U64) -> I64

Unsigned->signed conversion for n bytes (1..8). Returns I64 with proper two's-complement sign extension without Python big-ints.

Source code in pyjamaz/pvm/interpreters/numba/defs.py
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
@njit(int64(uint64, uint64), cache=NUMBA_CACHE)
def pvm_Z_jit(a: U64, n: U64) -> I64:
    """
    Unsigned->signed conversion for n bytes (1..8).
    Returns I64 with proper two's-complement sign extension without Python big-ints.
    """
    #TODO: remove casts
    au = U64(a)
    nb = U64(n)
    width = nb << U64(3)  # bits = n * 8

    # Clamp n to [1,8]; if n>=8, interpret full 64-bit as signed
    if width >= U64(64):
        return I64(au)
    if width == U64(0):
        return I64(0)

    mask = (U64(1) << width) - U64(1)
    val = au & mask
    signbit = U64(1) << (width - U64(1))

    if (val & signbit) != U64(0):
        # Negative: extend the sign bit up to 64 bits
        extend_mask = U64(0xFFFFFFFFFFFFFFFF) ^ mask
        return I64(val | extend_mask)
    else:
        # Positive
        return I64(val)

count_leading_zeroes_jit(value: U64, max_bits: U8) -> U64

Count-leading-zeroes with explicit 64-bit masking and shifts. Matches Python implementation for max_bits in {32,64}.

Source code in pyjamaz/pvm/interpreters/numba/defs.py
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
@njit(uint64(uint64, uint8), cache=NUMBA_CACHE)
def count_leading_zeroes_jit(value: U64, max_bits:U8) -> U64:
    """
    Count-leading-zeroes with explicit 64-bit masking and shifts.
    Matches Python implementation for max_bits in {32,64}.
    """
    mb = U64(max_bits)
    # Build mask and starting test bit using 64-bit arithmetic
    if mb >= U64(64):
        mask = U64(0xFFFFFFFFFFFFFFFF)
        test_bit = U64(1) << U64(63)
        maxb = 64
    else:
        mask = (U64(1) << mb) - U64(1)
        test_bit = U64(1) << (mb - U64(1))
        maxb = int(mb)

    val = U64(value) & mask
    if val == U64(0):
        return maxb

    count = 0
    while (val & test_bit) == U64(0) and count < maxb:
        count += 1
        test_bit = test_bit >> U64(1)

    return count

pvm_Z_inv_jit(a: I64, n: U8) -> U64

Signed to unsigned.

Source code in pyjamaz/pvm/interpreters/numba/defs.py
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
@njit(uint64(int64, uint8), cache=NUMBA_CACHE)
def pvm_Z_inv_jit(a: I64, n: U8) -> U64:
    """
    Signed to unsigned.
    """
    if n == 1:
        if a >= 0:
            return U64(a & 0xFF)
        return U64((a + (1 << 8)) & 0xFF)
    elif n == 2:
        if a >= 0:
            return U64(a & 0xFFFF)
        return U64((a + (1 << 16)) & 0xFFFF)
    elif n == 4:
        if a >= 0:
            return U64(a & 0xFFFFFFFF)
        return U64((a + I64(1 << 32)) & 0xFFFFFFFF)
    elif n == 8:
        return U64(a)
    else:
        shift = n << 3
        mask = (1 << shift) - 1
        if a >= 0:
            return U64(a & mask)
        return U64((a + (1 << shift)) & mask)

mem_write_jit(addr: U64, value: U64, bytes_to_write: U8, section_starts, section_ends, section_arrays, section_access) -> (I32, U64)

Returns (status:I32, fault_addr:U64) where status==0 on success, -1 on page fault, -2 on panic. fault_addr is set to the first failing byte address (page aligned) on page fault. GP-0.7.2-eq:A.7 - Addresses below 2^16 are invalid and cause panic.

Source code in pyjamaz/pvm/interpreters/numba/defs.py
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
@njit(types.Tuple((int32, uint64))(
    uint64,        # addr
    uint64,        # value
    uint8,         # bytes_to_write
    uint64[::1],   # section_starts
    uint64[::1],   # section_ends
    u8_array_list, # section_arrays
    int32[::1],    # section_access
), cache=NUMBA_CACHE)
def mem_write_jit(addr: U64, value: U64, bytes_to_write: U8,
                  section_starts, section_ends, section_arrays,
                  section_access) -> (I32, U64):
    """
    Returns (status:I32, fault_addr:U64) where status==0 on success, -1 on page fault, -2 on panic.
    fault_addr is set to the first failing byte address (page aligned) on page fault.
    GP-0.7.2-eq:A.7 - Addresses below 2^16 are invalid and cause panic.
    """
    PAGE_MASK = U64(0xFFFFFFFFFFFFF000)  # Mask for page alignment (4096 = 0x1000)

    # GP-??: addresses must wrap around 32bit address space
    addr = addr & U32_MASK

    # Check for invalid address (below 2^16)
    if addr < U64(65536):
        return I32(-2), U64(0)  # Panic - invalid address

    idx = I32(-1)
    for i in range(len(section_starts)):
        if section_starts[i] <= addr <= section_ends[i]:
            idx = I32(i)
            break
    if idx < 0:
        return I32(-1), addr & PAGE_MASK

    access = section_access[idx]
    if access >= 0 and access < MEM_W:
        return I32(-1), addr & PAGE_MASK

    start = U64(section_starts[idx])
    off = addr - start

    a = section_arrays[idx]  # uint8[::1]
    section_len = U64(len(a))
    if off + U64(bytes_to_write) > section_len:
        # First failing byte is at start + section_len
        fault_addr = start + section_len
        return I32(-1), fault_addr & PAGE_MASK

    # Mask value for <8 byte writes
    if bytes_to_write < U8(8):
        shift = U64(bytes_to_write) * U64(8)
        mask = (U64(1) << shift) - U64(1)
        value = value & mask

    base = int(off)

    if bytes_to_write == U8(1):
        a[base] = U8(value & U64(0xFF))
    elif bytes_to_write == U8(2):
        a[base] = U8(value & U64(0xFF))
        a[base + 1] = U8((value >> U64(8)) & U64(0xFF))
    elif bytes_to_write == U8(4):
        a[base] = U8(value & U64(0xFF))
        a[base + 1] = U8((value >> U64(8)) & U64(0xFF))
        a[base + 2] = U8((value >> U64(16)) & U64(0xFF))
        a[base + 3] = U8((value >> U64(24)) & U64(0xFF))
    elif bytes_to_write == U8(8):
        a[base] = U8(value & U64(0xFF))
        a[base + 1] = U8((value >> U64(8)) & U64(0xFF))
        a[base + 2] = U8((value >> U64(16)) & U64(0xFF))
        a[base + 3] = U8((value >> U64(24)) & U64(0xFF))
        a[base + 4] = U8((value >> U64(32)) & U64(0xFF))
        a[base + 5] = U8((value >> U64(40)) & U64(0xFF))
        a[base + 6] = U8((value >> U64(48)) & U64(0xFF))
        a[base + 7] = U8((value >> U64(56)) & U64(0xFF))
    else:
        return I32(-1), addr & PAGE_MASK

    return I32(0), U64(0)

mem_read_jit(addr: U64, bytes_to_read: U8, section_starts, section_ends, section_arrays, section_access) -> (I32, U64)

Returns (status:I32, value_or_fault:U64) where status==0 on success, -1 on page-fault, -2 on panic. On success, second element is the read value. On page fault, second element is the page aligned fault address. GP-0.7.2-eq:A.7 - Addresses below 2^16 are invalid and cause panic.

Source code in pyjamaz/pvm/interpreters/numba/defs.py
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
@njit(types.Tuple((int32, uint64))(
    uint64,        # addr
    uint8,         # bytes_to_read
    uint64[::1],   # section_starts
    uint64[::1],   # section_ends
    u8_array_list, # section_arrays
    int32[::1],    # section_access
), cache=NUMBA_CACHE)
def mem_read_jit(addr: U64, bytes_to_read: U8,
                 section_starts, section_ends, section_arrays,
                 section_access) -> (I32, U64):
    """
    Returns (status:I32, value_or_fault:U64) where status==0 on success, -1 on page-fault, -2 on panic.
    On success, second element is the read value.
    On page fault, second element is the page aligned fault address.
    GP-0.7.2-eq:A.7 - Addresses below 2^16 are invalid and cause panic.
    """
    PAGE_MASK = U64(0xFFFFFFFFFFFFF000)  # Mask for page alignment (4096 = 0x1000)

    # Check for invalid address (below 2^16)
    if addr < U64(65536):
        return I32(-2), U64(0)  # Panic - invalid address

    # GP-??: addresses must wrap around 32bit address space
    addr = addr & U32_MASK

    idx = I32(-1)
    for i in range(len(section_starts)):
        if section_starts[i] <= addr <= section_ends[i]:
            idx = I32(i)
            break
    if idx < 0:
        return I32(-1), addr & PAGE_MASK

    access = section_access[idx]
    if access >= 0 and access < MEM_R:
        return I32(-1), addr & PAGE_MASK

    start = U64(section_starts[idx])
    off = addr - start

    a = section_arrays[idx]  # uint8[::1] array
    section_len = U64(len(a))
    if off + U64(bytes_to_read) > section_len:
        # First failing byte is at start + section_len
        fault_addr = start + section_len
        return I32(-1), fault_addr & PAGE_MASK
    base = int(off)

    if bytes_to_read == U8(1):
        return I32(0), U64(a[base])
    elif bytes_to_read == U8(2):
        return I32(0), (U64(a[base]) | (U64(a[base + 1]) << U64(8)))
    elif bytes_to_read == U8(4):
        return I32(0), (U64(a[base]) |
                        (U64(a[base + 1]) << U64(8)) |
                        (U64(a[base + 2]) << U64(16)) |
                        (U64(a[base + 3]) << U64(24)))
    elif bytes_to_read == U8(8):
        return I32(0), (U64(a[base]) |
                        (U64(a[base + 1]) << U64(8)) |
                        (U64(a[base + 2]) << U64(16)) |
                        (U64(a[base + 3]) << U64(24)) |
                        (U64(a[base + 4]) << U64(32)) |
                        (U64(a[base + 5]) << U64(40)) |
                        (U64(a[base + 6]) << U64(48)) |
                        (U64(a[base + 7]) << U64(56)))
    else:
        return I32(-1), addr & PAGE_MASK