diff --git a/src/interpreter.cc b/src/interpreter.cc index 8729fd5..23793ac 100644 --- a/src/interpreter.cc +++ b/src/interpreter.cc @@ -3268,4 +3268,29 @@ bool ProgramValue::isEmpty() return true; } +// Matches Sfall implementation. +bool ProgramValue::isInt() +{ + return opcode == VALUE_TYPE_INT; +} + +// Matches Sfall implementation. +bool ProgramValue::isFloat() +{ + return opcode == VALUE_TYPE_FLOAT; +} + +// Matches Sfall implementation. +float ProgramValue::asFloat() +{ + switch (opcode) { + case VALUE_TYPE_INT: + return static_cast(integerValue); + case VALUE_TYPE_FLOAT: + return floatValue; + default: + return 0.0; + } +} + } // namespace fallout diff --git a/src/interpreter.h b/src/interpreter.h index 91d27bb..f84347f 100644 --- a/src/interpreter.h +++ b/src/interpreter.h @@ -149,6 +149,9 @@ typedef struct ProgramValue { }; bool isEmpty(); + bool isInt(); + bool isFloat(); + float asFloat(); } ProgramValue; typedef std::vector ProgramStack; diff --git a/src/sfall_opcodes.cc b/src/sfall_opcodes.cc index bef609d..dd1580c 100644 --- a/src/sfall_opcodes.cc +++ b/src/sfall_opcodes.cc @@ -175,6 +175,25 @@ static void op_set_bodypart_hit_modifier(Program* program) combat_set_hit_location_penalty(hit_location, penalty); } +// sqrt +static void op_sqrt(Program* program) +{ + ProgramValue programValue = programStackPopValue(program); + programStackPushFloat(program, sqrtf(programValue.asFloat())); +} + +// abs +static void op_abs(Program* program) +{ + ProgramValue programValue = programStackPopValue(program); + + if (programValue.isInt()) { + programStackPushInteger(program, abs(programValue.integerValue)); + } else { + programStackPushFloat(program, abs(programValue.asFloat())); + } +} + // get_proto_data static void op_get_proto_data(Program* program) { @@ -395,6 +414,13 @@ static void opParseInt(Program* program) programStackPushInteger(program, static_cast(strtol(string, nullptr, 0))); } +// atof +static void op_atof(Program* program) +{ + const char* string = programStackPopString(program); + programStackPushFloat(program, static_cast(atof(string))); +} + // strlen static void opGetStringLength(Program* program) { @@ -402,6 +428,22 @@ static void opGetStringLength(Program* program) programStackPushInteger(program, static_cast(strlen(string))); } +// pow (^) +static void op_power(Program* program) +{ + ProgramValue expValue = programStackPopValue(program); + ProgramValue baseValue = programStackPopValue(program); + + // CE: Implementation is slightly different, check. + float result = powf(baseValue.asFloat(), expValue.asFloat()); + + if (baseValue.isInt() && expValue.isInt()) { + programStackPushInteger(program, static_cast(result)); + } else { + programStackPushFloat(program, result); + } +} + // message_str_game static void opGetMessage(Program* program) { @@ -430,6 +472,28 @@ static void opArtExists(Program* program) programStackPushInteger(program, artExists(fid)); } +// div (/) +static void op_div(Program* program) +{ + ProgramValue divisorValue = programStackPopValue(program); + ProgramValue dividendValue = programStackPopValue(program); + + if (divisorValue.integerValue == 0) { + debugPrint("Division by zero"); + + // TODO: Looks like execution is not halted in Sfall's div, check. + programStackPushInteger(program, 0); + return; + } + + if (dividendValue.isFloat() || divisorValue.isFloat()) { + programStackPushFloat(program, dividendValue.asFloat() / divisorValue.asFloat()); + } else { + // Unsigned divison. + programStackPushInteger(program, static_cast(dividendValue.integerValue) / static_cast(divisorValue.integerValue)); + } +} + void sfallOpcodesInit() { interpreterRegisterOpcode(0x8156, opReadByte); @@ -448,6 +512,8 @@ void sfallOpcodesInit() interpreterRegisterOpcode(0x81B6, op_set_car_current_town); interpreterRegisterOpcode(0x81DF, op_get_bodypart_hit_modifier); interpreterRegisterOpcode(0x81E0, op_set_bodypart_hit_modifier); + interpreterRegisterOpcode(0x81EC, op_sqrt); + interpreterRegisterOpcode(0x81ED, op_abs); interpreterRegisterOpcode(0x8204, op_get_proto_data); interpreterRegisterOpcode(0x8205, op_set_proto_data); interpreterRegisterOpcode(0x820D, opListBegin); @@ -465,10 +531,13 @@ void sfallOpcodesInit() interpreterRegisterOpcode(0x8220, opGetScreenWidth); interpreterRegisterOpcode(0x8221, opGetScreenHeight); interpreterRegisterOpcode(0x8237, opParseInt); + interpreterRegisterOpcode(0x8238, op_atof); interpreterRegisterOpcode(0x824F, opGetStringLength); + interpreterRegisterOpcode(0x8263, op_power); interpreterRegisterOpcode(0x826B, opGetMessage); interpreterRegisterOpcode(0x8267, opRound); interpreterRegisterOpcode(0x8274, opArtExists); + interpreterRegisterOpcode(0x827F, op_div); } void sfallOpcodesExit()