summaryrefslogtreecommitdiff
path: root/v5/apf_interpreter.c
diff options
context:
space:
mode:
Diffstat (limited to 'v5/apf_interpreter.c')
-rw-r--r--v5/apf_interpreter.c59
1 files changed, 32 insertions, 27 deletions
diff --git a/v5/apf_interpreter.c b/v5/apf_interpreter.c
index 84afca8..f71db4d 100644
--- a/v5/apf_interpreter.c
+++ b/v5/apf_interpreter.c
@@ -260,6 +260,7 @@ typedef union {
/* NOTE: Only APFv6+ implements R=1 'jbseq' version */
#define EXT_OPCODE 21 /* Immediate value is one of *_EXT_OPCODE */
#define LDDW_OPCODE 22 /* Load 4 bytes from data address (register + signed imm): "lddw R0, [5+R1]" */
+ /* LDDW/STDW in APFv6+ *mode* load/store from counter specified in imm. */
#define STDW_OPCODE 23 /* Store 4 bytes to data address (register + signed imm): "stdw R0, [5+R1]" */
/* Write 1, 2 or 4 byte immediate to the output buffer and auto-increment the output buffer pointer.
@@ -595,7 +596,7 @@ extern void APF_TRACE_HOOK(u32 pc, const u32* regs, const u8* program,
#define ENFORCE_UNSIGNED(c) ((c)==(u32)(c))
u32 apf_version(void) {
- return 20240313;
+ return 20240314;
}
typedef struct {
@@ -975,34 +976,38 @@ static int do_apf_run(apf_context* ctx) {
return PASS_PACKET; /* Bail out */
}
break;
- case LDDW_OPCODE: {
- u32 offs = OTHER_REG + (u32)signed_imm;
- u32 size = 4;
- u32 val = 0;
- /* Negative offsets wrap around the end of the address space. */
- /* This allows us to efficiently access the end of the */
- /* address space with one-byte immediates without using %=. */
- if (offs & 0x80000000) offs += ctx->ram_len; /* unsigned overflow intended */
- ASSERT_IN_DATA_BOUNDS(offs, size);
- while (size--) val = (val << 8) | ctx->program[offs++];
- REG = val;
- break;
- }
- case STDW_OPCODE: {
- u32 offs = OTHER_REG + (u32)signed_imm;
- u32 size = 4;
- u32 val = REG;
- /* Negative offsets wrap around the end of the address space. */
- /* This allows us to efficiently access the end of the */
- /* address space with one-byte immediates without using %=. */
- if (offs & 0x80000000) offs += ctx->ram_len; /* unsigned overflow intended */
- ASSERT_IN_DATA_BOUNDS(offs, size);
- while (size--) {
- ctx->program[offs++] = (val >> 24);
- val <<= 8;
+ case LDDW_OPCODE:
+ case STDW_OPCODE:
+ if (ctx->v6) {
+ if (!imm) return PASS_PACKET;
+ if (imm > 0xFFFF) return PASS_PACKET;
+ if (imm * 4 > ctx->ram_len) return PASS_PACKET;
+ if (opcode == LDDW_OPCODE) {
+ REG = counter[-(s32)imm];
+ } else {
+ counter[-(s32)imm] = REG;
+ }
+ } else {
+ u32 offs = OTHER_REG + (u32)signed_imm;
+ /* Negative offsets wrap around the end of the address space. */
+ /* This allows us to efficiently access the end of the */
+ /* address space with one-byte immediates without using %=. */
+ if (offs & 0x80000000) offs += ctx->ram_len; /* unsigned overflow intended */
+ u32 size = 4;
+ ASSERT_IN_DATA_BOUNDS(offs, size);
+ if (opcode == LDDW_OPCODE) {
+ u32 val = 0;
+ while (size--) val = (val << 8) | ctx->program[offs++];
+ REG = val;
+ } else {
+ u32 val = REG;
+ while (size--) {
+ ctx->program[offs++] = (val >> 24);
+ val <<= 8;
+ }
+ }
}
break;
- }
case WRITE_OPCODE: {
ASSERT_RETURN(ctx->tx_buf);
ASSERT_RETURN(len_field);