From 389592fc129de4f8e91e2a7b0586639b8848985e Mon Sep 17 00:00:00 2001 From: Scott Duensing Date: Tue, 30 Jan 2024 21:12:23 -0600 Subject: [PATCH] Many, many, untested opcodes added. --- include/messages.h | 2 +- include/object.h | 4 ++ include/oc_call.h | 3 + include/oc_compare.h | 6 ++ include/oc_input.h | 3 + include/oc_math.h | 7 ++ include/oc_memory.h | 3 + include/oc_misc.h | 5 ++ include/oc_object.h | 11 ++++ include/oc_output.h | 6 ++ include/oc_save.h | 5 ++ include/portme.h | 20 ++++-- src/object.c | 61 +++++++++++++++++ src/oc_call.c | 15 +++++ src/oc_compare.c | 52 +++++++++++++++ src/oc_input.c | 8 +++ src/oc_math.c | 40 +++++++++++- src/oc_memory.c | 16 +++++ src/oc_misc.c | 18 ++++++ src/oc_object.c | 151 ++++++++++++++++++++++++++++++++++++++++++- src/oc_output.c | 70 +++++++++++++++++++- src/oc_save.c | 98 ++++++++++++++++++++++++++++ src/opcodes.c | 92 +++++++++++++------------- src/portme.c | 43 ++++++++++-- src/zscii.c | 2 +- 25 files changed, 676 insertions(+), 65 deletions(-) diff --git a/include/messages.h b/include/messages.h index 5791745..0da85ed 100644 --- a/include/messages.h +++ b/include/messages.h @@ -41,7 +41,7 @@ #define MSG_INT_V12_SHIFT "Add V1/V2 shifting." #define MSG_INT_V12_SHIFT_LOCK "Add V1/V2 shift locking." #define MSG_MEM_BUFFER "Unable to allocate memory buffer." -#define MSG_OP_VAR_MISSING_PROPERTY "Missing object property." +#define MSG_OP_OBJ_MISSING_PROPERTY "Missing object property." #define MSG_OP_VAR_TOO_MANY_LOCALS "Too many local variables!" #define MSG_OP_VAR_STACK_OVERFLOW "Stack overflow!" #else // DEBUGGING diff --git a/include/object.h b/include/object.h index ca51fc9..ee732d3 100644 --- a/include/object.h +++ b/include/object.h @@ -29,7 +29,11 @@ uint32_t objectPointerGet(uint16_t objectId); +uint32_t objectPointerParentGet(uint32_t objectPointer); +uint16_t objectPropertyDefaultGet(uint32_t propertyId); uint8_t objectPropertyGet(uint16_t objectId, uint32_t propertyId, uint8_t *size); +uint16_t objectRelationGet(uint16_t objectId, uint8_t relationship); +void objectUnparent(uint16_t objectId); #endif // OBJECT_H diff --git a/include/oc_call.h b/include/oc_call.h index 9cc9aaa..92dfd3e 100644 --- a/include/oc_call.h +++ b/include/oc_call.h @@ -30,6 +30,9 @@ void opcodes_call(void); void opcodes_ret(void); +void opcodes_rfalse(void); +void opcodes_rtrue(void); +void opcodes_ret_popped(void); #endif // OC_CALL_H diff --git a/include/oc_compare.h b/include/oc_compare.h index e0bcb31..5e9f0f5 100644 --- a/include/oc_compare.h +++ b/include/oc_compare.h @@ -28,9 +28,15 @@ #include "common.h" +void opcodes_dec_chk(void); +void opcodes_inc_chk(void); void opcodes_je(void); +void opcodes_jg(void); +void opcodes_jin(void); +void opcodes_jl(void); void opcodes_jump(void); void opcodes_jz(void); +void opcodes_test(void); #endif // OC_COMPARE_H diff --git a/include/oc_input.h b/include/oc_input.h index 286f02c..92c0759 100644 --- a/include/oc_input.h +++ b/include/oc_input.h @@ -28,4 +28,7 @@ #include "common.h" +void opcodes_read(void); + + #endif // OC_INPUT_H diff --git a/include/oc_math.h b/include/oc_math.h index cbe910c..4373934 100644 --- a/include/oc_math.h +++ b/include/oc_math.h @@ -29,6 +29,13 @@ void opcodes_add(void); +void opcodes_and(void); +void opcodes_dec(void); +void opcodes_div(void); +void opcodes_inc(void); +void opcodes_mod(void); +void opcodes_mul(void); +void opcodes_or(void); void opcodes_sub(void); diff --git a/include/oc_memory.h b/include/oc_memory.h index 0b33c3e..78699ff 100644 --- a/include/oc_memory.h +++ b/include/oc_memory.h @@ -28,8 +28,11 @@ #include "common.h" +void opcodes_load(void); +void opcodes_loadb(void); void opcodes_loadw(void); void opcodes_store(void); +void opcodes_storeb(void); void opcodes_storew(void); diff --git a/include/oc_misc.h b/include/oc_misc.h index 73230d4..34259d4 100644 --- a/include/oc_misc.h +++ b/include/oc_misc.h @@ -28,4 +28,9 @@ #include "common.h" +void opcodes_nop(void); +void opcodes_quit(void); +void opcodes_random(void); + + #endif // OC_MISC_H diff --git a/include/oc_object.h b/include/oc_object.h index 6766aa8..06c0c3d 100644 --- a/include/oc_object.h +++ b/include/oc_object.h @@ -28,7 +28,18 @@ #include "common.h" +void opcodes_clear_attr(void); +void opcodes_get_child(void); +void opcodes_get_next_prop(void); +void opcodes_get_parent(void); +void opcodes_get_prop(void); +void opcodes_get_prop_addr(void); +void opcodes_get_prop_len(void); +void opcodes_get_sibling(void); +void opcodes_insert_obj(void); void opcodes_put_prop(void); +void opcodes_remove_obj(void); +void opcodes_set_attr(void); void opcodes_test_attr(void); diff --git a/include/oc_output.h b/include/oc_output.h index ba53d85..e14af21 100644 --- a/include/oc_output.h +++ b/include/oc_output.h @@ -30,6 +30,12 @@ void opcodes_new_line(void); void opcodes_print(void); +void opcodes_print_addr(void); +void opcodes_print_char(void); +void opcodes_print_num(void); +void opcodes_print_obj(void); +void opcodes_print_paddr(void); +void opcodes_print_ret(void); #endif // OC_OUTPUT_H diff --git a/include/oc_save.h b/include/oc_save.h index 4cbfe64..435c0b7 100644 --- a/include/oc_save.h +++ b/include/oc_save.h @@ -28,4 +28,9 @@ #include "common.h" +void opcodes_save(void); +void opcodes_restart(void); +void opcodes_restore(void); + + #endif // OC_SAVE_H diff --git a/include/portme.h b/include/portme.h index c82eee5..7e8fde5 100644 --- a/include/portme.h +++ b/include/portme.h @@ -28,13 +28,19 @@ #include "common.h" -uint8_t portByteGet(uint16_t address); -void portByteSet(uint16_t address, uint8_t value); -void portDie(char *fmt, ...); -void portPrintChars(char *chars, uint16_t len); -void portStoryLoad(void); -uint16_t portWordGet(uint16_t address); -void portWordSet(uint16_t address, uint16_t value); +uint8_t portByteGet(uint16_t address); +void portByteSet(uint16_t address, uint8_t value); +void portCharsPrint(char *chars, uint16_t len); +void portDie(char *fmt, ...); +void portFileClose(uint32_t *handle); +byte portFileByteRead(uint32_t *handle); +bool portFileByteWrite(uint32_t *handle, byte value); +bool portFileReadOpen(uint32_t *handle, char *name); +bool portFileWriteOpen(uint32_t *handle, char *name); +uint16_t portRandomGet(int16_t range); +void portStoryLoad(void); +uint16_t portWordGet(uint16_t address); +void portWordSet(uint16_t address, uint16_t value); #endif // PORTME_H diff --git a/src/object.c b/src/object.c index 0540b1d..7d08001 100644 --- a/src/object.c +++ b/src/object.c @@ -42,6 +42,35 @@ uint32_t objectPointerGet(uint16_t objectId) { } +uint32_t objectPointerParentGet(uint32_t objectPointer) { + uint32_t result = 0; + uint32_t parent; + + if (storyVersion() <= 3) { + parent = ZPEEK(objectPointer + 4); + result = parent ? objectPointerGet(parent) : 0; + } else { + portDie(MSG_UNIMPLEMENTED); + } + + return result; +} + + +uint16_t objectPropertyDefaultGet(uint32_t propertyId) { + uint32_t values; + + if (((storyVersion() <= 3) && (propertyId > 31)) || ((storyVersion() >= 4) && (propertyId > 63))) { + //***TODO*** Should we die here? This seems to work. + return 0; + } + + values = storyObjectTableAddress() + ((propertyId - 1) * 2); + + return ZPEEKW(values); +} + + uint8_t objectPropertyGet(uint16_t objectId, uint32_t propertyId, uint8_t *size) { uint32_t ptr = objectPointerGet(objectId); uint16_t addr; @@ -78,3 +107,35 @@ uint8_t objectPropertyGet(uint16_t objectId, uint32_t propertyId, uint8_t *size) } +uint16_t objectRelationGet(uint16_t objectId, uint8_t relationship) { + uint32_t objPtr = objectPointerGet(objectId); + uint16_t result = 0; + + if (storyVersion() <= 3) { + result = ZPEEKW(objPtr + relationship); + } else { + portDie(MSG_UNIMPLEMENTED); + } + + return result; +} + + +void objectUnparent(uint16_t objectId) { + uint32_t objPtr = objectPointerGet(objectId); + uint32_t parentPtr = objectPointerParentGet(objPtr); + uint32_t ptr; + + if (parentPtr != 0) { + if (storyVersion() <= 3) { + ptr = parentPtr + 6; // 4 to skip attrs, 2 to skip to child. + while (ZPEEK(ptr) != objectId) { // If not direct child, look through sibling list.. + ptr = objectPointerGet(ZPEEK(ptr)) + 5; + } + ZPOKE(ptr, ZPEEK(objPtr + 5)); // obj sibling takes obj's place. + } else { + portDie(MSG_UNIMPLEMENTED); + } + } +} + diff --git a/src/oc_call.c b/src/oc_call.c index 5ad3697..dc0e868 100644 --- a/src/oc_call.c +++ b/src/oc_call.c @@ -93,3 +93,18 @@ void opcodes_call(void) { void opcodes_ret(void) { interpreterDoReturn(__state.operands[0]); } + +void opcodes_rfalse(void) { + interpreterDoReturn(0); +} + + +void opcodes_rtrue(void) { + interpreterDoReturn(1); +} + + +void opcodes_ret_popped(void) { + // Top of stack. + interpreterDoReturn(variableLoad(0)); +} diff --git a/src/oc_compare.c b/src/oc_compare.c index 4e354b0..1bf5227 100644 --- a/src/oc_compare.c +++ b/src/oc_compare.c @@ -24,6 +24,12 @@ #include "oc_compare.h" #include "state.h" #include "interpreter.h" +#include "variable.h" +#include "object.h" +#include "story.h" +#include "portme.h" +#include "messages.h" +#include "memory.h" void opcodes_je(void) { @@ -41,6 +47,48 @@ void opcodes_je(void) { } +void opcodes_dec_chk(void) { + int16_t val = variableLoad(__state.operands[0]); + val--; + variableStore(__state.operands[0], val); + interpreterDoBranch(((int16_t)val < (int16_t)__state.operands[1]) ? 1 : 0); +} + + +void opcodes_inc_chk(void) { + int16_t val = variableLoad(__state.operands[0]); + val++; + variableStore(__state.operands[0], val); + interpreterDoBranch(((int16_t)val > (int16_t)__state.operands[1]) ? 1 : 0); +} + + +void opcodes_jg(void) { + interpreterDoBranch(((int16_t)__state.operands[0] > (int16_t)__state.operands[1]) ? 1 : 0); +} + + +void opcodes_jin(void) { + uint16_t objId = __state.operands[0]; + uint16_t parentId = __state.operands[1]; + uint32_t objPtr = objectPointerGet(objId); + + // Zork 1 will trigger this on "go X" where "x" isn't a direction. + if (objPtr == 0) return; + + if (storyVersion() <= 3) { + interpreterDoBranch((ZPEEKW(objPtr + 4) == parentId) ? 1 : 0); + } else { + portDie(MSG_UNIMPLEMENTED); + } +} + + +void opcodes_jl(void) { + interpreterDoBranch(((int16_t)__state.operands[0] < (int16_t)__state.operands[1]) ? 1 : 0); +} + + void opcodes_jump(void) { __state.pc = __state.pc + ((int16_t)__state.operands[0]) - 2; } @@ -51,3 +99,7 @@ void opcodes_jz(void) { } +void opcodes_test(void) { + interpreterDoBranch(((__state.operands[0] & __state.operands[1]) == __state.operands[0]) ? 1 : 0); +} + diff --git a/src/oc_input.c b/src/oc_input.c index ee94eef..14d479e 100644 --- a/src/oc_input.c +++ b/src/oc_input.c @@ -22,3 +22,11 @@ #include "oc_input.h" +#include "portme.h" +#include "messages.h" + + +void opcodes_read(void) { + //***TODO*** + portDie(MSG_UNIMPLEMENTED); +} diff --git a/src/oc_math.c b/src/oc_math.c index f416460..9a92422 100644 --- a/src/oc_math.c +++ b/src/oc_math.c @@ -28,11 +28,45 @@ void opcodes_add(void) { - variableStore(ZPEEK(__state.pc++), __state.operands[0] + __state.operands[1]); + variableStore(ZPEEK(__state.pc++), (int16_t)__state.operands[0] + (int16_t)__state.operands[1]); +} + + +void opcodes_and(void) { + variableStore(ZPEEK(__state.pc++), __state.operands[0] & __state.operands[1]); +} + + +void opcodes_dec(void) { + variableStore(__state.operands[0], variableLoad(__state.operands[0]) - 1); +} + + +void opcodes_div(void) { + variableStore(ZPEEK(__state.pc++), (int16_t)__state.operands[0] / (int16_t)__state.operands[1]); +} + + +void opcodes_inc(void) { + variableStore(__state.operands[0], variableLoad(__state.operands[0]) + 1); +} + + +void opcodes_mod(void) { + variableStore(ZPEEK(__state.pc++), (int16_t)__state.operands[0] % (int16_t)__state.operands[1]); +} + + +void opcodes_mul(void) { + variableStore(ZPEEK(__state.pc++), (int16_t)__state.operands[0] * (int16_t)__state.operands[1]); +} + + +void opcodes_or(void) { + variableStore(ZPEEK(__state.pc++), __state.operands[0] | __state.operands[1]); } void opcodes_sub(void) { - variableStore(ZPEEK(__state.pc++), __state.operands[0] - __state.operands[1]); + variableStore(ZPEEK(__state.pc++), (int16_t)__state.operands[0] - (int16_t)__state.operands[1]); } - diff --git a/src/oc_memory.c b/src/oc_memory.c index 4a04ca7..3dca653 100644 --- a/src/oc_memory.c +++ b/src/oc_memory.c @@ -27,6 +27,17 @@ #include "memory.h" +void opcodes_load(void) { + variableStore(ZPEEK(__state.pc++), ZPEEK((__state.operands[0] & 0xFF))); +} + + +void opcodes_loadb(void) { + uint16_t offset = __state.operands[0] + __state.operands[1]; + variableStore(ZPEEK(__state.pc++), ZPEEK(offset)); +} + + void opcodes_loadw(void) { uint16_t offset = __state.operands[0] + (__state.operands[1] * 2); variableStore(ZPEEK(__state.pc++), ZPEEKW(offset)); @@ -38,6 +49,11 @@ void opcodes_store(void) { } +void opcodes_storeb(void) { + ZPOKE(__state.operands[0] + __state.operands[1], __state.operands[2]); +} + + void opcodes_storew(void) { ZPOKEW(__state.operands[0] + (__state.operands[1] * 2), __state.operands[2]); } diff --git a/src/oc_misc.c b/src/oc_misc.c index 31e4900..3460a3b 100644 --- a/src/oc_misc.c +++ b/src/oc_misc.c @@ -22,3 +22,21 @@ #include "oc_misc.h" +#include "state.h" +#include "variable.h" +#include "portme.h" + + +void opcodes_nop(void) { + // Well this one is easy. +} + + +void opcodes_quit(void) { + __state.quit = true; +} + + +void opcodes_random(void) { + variableStore((__state.pc++), portRandomGet((int16_t)__state.operands[0])); +} diff --git a/src/oc_object.c b/src/oc_object.c index bee7134..89ae27f 100644 --- a/src/oc_object.c +++ b/src/oc_object.c @@ -29,13 +29,133 @@ #include "portme.h" #include "messages.h" #include "object.h" +#include "variable.h" + + +void opcodes_clear_attr(void) { + uint32_t ptr = objectPointerGet(__state.operands[0]); + uint16_t attrId = __state.operands[1]; + + // Zork 1 will trigger this on "go X" where "x" isn't a direction, so ignore it. + if (ptr == 0) return; + + if (storyVersion() <= 3) { + ptr += (attrId / 8); + ZPOKE(ptr, ZPEEK(ptr) & ~(0x80 >> (attrId & 7))); + } else { + portDie(MSG_UNIMPLEMENTED); + } +} + + +void opcodes_get_child(void) { + uint16_t result = objectRelationGet(__state.operands[0], 6); + variableStore(ZPEEK(__state.pc++), result); + interpreterDoBranch(result != 0 ? 1 : 0); +} + + +void opcodes_get_next_prop(void) { + uint16_t objId = __state.operands[0]; + bool firstProp = (__state.operands[1] == 0); + uint16_t result = 0; + uint8_t size = 0; + uint32_t ptr = objectPropertyGet(objId, firstProp ? 0xFFFFFFFF : __state.operands[1], &size); + + if (ptr == 0) { + portDie(MSG_OP_OBJ_MISSING_PROPERTY); + } + + if (storyVersion() <= 3) { + // 5 bits for the prop id. + result = ZPEEK(ptr + (firstProp ? -1 : (int8_t)size)) & 0x1f; + } else { + portDie(MSG_UNIMPLEMENTED); + } + + variableStore(ZPEEK(__state.pc++), result); +} + + +void opcodes_get_parent(void) { + uint16_t result = objectRelationGet(__state.operands[0], 4); + variableStore(ZPEEK(__state.pc++), result); + interpreterDoBranch(result != 0 ? 1 : 0); +} + + +void opcodes_get_prop(void) { + uint16_t objId = __state.operands[0]; + uint16_t propId = __state.operands[1]; + uint16_t result = 0; + uint8_t size = 0; + uint32_t ptr = objectPropertyGet(objId, propId, &size); + + if (ptr == 0) { + result = objectPropertyDefaultGet(propId); + } else { + result = ZPEEK(ptr); + } + + variableStore(ZPEEK(__state.pc++), result); +} + + +void opcodes_get_prop_addr(void) { + uint16_t objId = __state.operands[0]; + uint16_t propId = __state.operands[1]; + + variableStore(ZPEEK(__state.pc++), objectPropertyGet(objId, propId, NULL)); +} + + +void opcodes_get_prop_len(void) { + uint16_t result = 0; + uint8_t info; + + // This must return 0 if OP0 is zero, to avoid a bug in older Infocom games. + if (__state.operands[0] != 0) { + if (storyVersion() <= 3) { + info = ZPEEK(__state.operands[0] - 1); + result = ((info >> 5) & 0x7) + 1; // 3 bits for property size. + } else { + portDie(MSG_UNIMPLEMENTED); + } + } + + variableStore(ZPEEK(__state.pc++), result); +} + + +void opcodes_get_sibling(void) { + uint16_t result = objectRelationGet(__state.operands[0], 5); + variableStore(ZPEEK(__state.pc++), result); + interpreterDoBranch(result != 0 ? 1 : 0); +} + + +void opcodes_insert_obj(void) { + uint32_t objPtr = objectPointerGet(__state.operands[0]); + uint32_t dstPtr = objectPointerGet(__state.operands[1]); + + if (storyVersion() <= 3) { + // Take object out of tree. + objectUnparent(__state.operands[0]); + // Reinsert in right spot. + ZPOKE(objPtr + 4, __state.operands[1]); + ZPOKE(objPtr + 5, ZPEEK(dstPtr + 6)); + ZPOKE(dstPtr + 6, __state.operands[0]); + } else { + portDie(MSG_UNIMPLEMENTED); + } +} void opcodes_put_prop(void) { uint8_t size = 0; uint8_t ptr = objectPropertyGet(__state.operands[0], __state.operands[1], &size); - if (ptr == 0) portDie(MSG_OP_VAR_MISSING_PROPERTY); + if (ptr == 0) portDie(MSG_OP_OBJ_MISSING_PROPERTY); if (size == 1) { ZPOKE(ptr, (__state.operands[2] & 0xff)); @@ -45,6 +165,35 @@ void opcodes_put_prop(void) { } +void opcodes_remove_obj(void) { + uint16_t objId = __state.operands[0]; + uint32_t objPtr = objectPointerGet(objId); + + if (storyVersion() <= 3) { + // Remove object from tree. + objectUnparent(objId); + // Clear out object's relationships. + ZPOKE(objPtr + 4, 0); // Parent. + ZPOKE(objPtr + 5, 0); // Sibling. + } else { + portDie(MSG_UNIMPLEMENTED); + } +} + + +void opcodes_set_attr(void) { + uint32_t ptr = objectPointerGet(__state.operands[0]); + uint16_t attrId = __state.operands[1]; + + if (storyVersion() <= 3) { + ptr += (attrId / 8); + ZPOKE(ptr, ZPEEK(ptr) | 0x80 >> (attrId & 7)); + } else { + portDie(MSG_UNIMPLEMENTED); + } +} + + void opcodes_test_attr(void) { uint32_t ptr = objectPointerGet(__state.operands[0]); uint16_t attrId = __state.operands[1]; diff --git a/src/oc_output.c b/src/oc_output.c index aa4a866..3907ce6 100644 --- a/src/oc_output.c +++ b/src/oc_output.c @@ -25,13 +25,81 @@ #include "state.h" #include "portme.h" #include "zscii.h" +#include "memory.h" +#include "story.h" +#include "object.h" +#include "messages.h" +#include "interpreter.h" void opcodes_new_line(void) { - portPrintChars("\n", 1); + portCharsPrint("\n", 1); } void opcodes_print(void) { __state.pc += zsciiPrint(__state.pc, 0); } + + +void opcodes_print_addr(void) { + zsciiPrint(__state.operands[0], 0); +} + + +void opcodes_print_char(void) { + static char c[2] = { 0, 0 }; + + c[0] = zsciiDecodeChar(__state.operands[0]); + if (c[0]) portCharsPrint(c, 1); +} + + +static void opcodes_print_num_helper(int16_t val) { + static char c[2] = { 0, 0 }; + + if (val < 0) { + portCharsPrint("-", 1); + val = -val; + } + + if (val > 9) { + opcodes_print_num_helper(val / 10); + } + + c[0] = '0' + (val % 10); + portCharsPrint(c, 1); +} + + +void opcodes_print_num(void) { + opcodes_print_num_helper(__state.operands[0]); +} + + +void opcodes_print_obj(void) { + uint32_t ptr = objectPointerGet(__state.operands[0]); + uint32_t addr; + + if (storyVersion() <= 3) { + // Skip to properties field. + ptr += 7; + // Dereference to get to property table. + addr = ZPEEKW(ptr); + zsciiPrint(addr + 1, 0); + } else { + portDie(MSG_UNIMPLEMENTED); + } +} + + +void opcodes_print_paddr(void) { + zsciiPrint(memoryUnpackAddress(__state.operands[0], MEMORY_PRINT), 0); +} + + +void opcodes_print_ret(void) { + __state.pc += zsciiPrint(__state.pc, 0); + portCharsPrint("\n", 1); + interpreterDoReturn(1); +} diff --git a/src/oc_save.c b/src/oc_save.c index c2ed1d4..79046e2 100644 --- a/src/oc_save.c +++ b/src/oc_save.c @@ -22,3 +22,101 @@ #include "oc_save.h" +#include "interpreter.h" +#include "state.h" +#include "portme.h" +#include "story.h" +#include "memory.h" + + +void opcodes_save(void) { + uint32_t f; + bool ok; + uint16_t i; + uint8_t *b; + + ok = portFileWriteOpen(&f, "save.dat"); + + // Write out PC. + b = (uint8_t *)&__state.pc; + ok &= portFileByteWrite(&f, b[0]); + ok &= portFileByteWrite(&f, b[1]); + ok &= portFileByteWrite(&f, b[2]); + ok &= portFileByteWrite(&f, b[3]); + + // Write out SP. + b = (uint8_t *)&__state.sp; + ok &= portFileByteWrite(&f, b[0]); + ok &= portFileByteWrite(&f, b[1]); + + // Write out BP. + b = (uint8_t *)&__state.sp; + ok &= portFileByteWrite(&f, b[0]); + ok &= portFileByteWrite(&f, b[1]); + + // Write out dynamic memory. + for (i=0; i