instruction.cpp (13374B)
1 #include "instruction.hpp" 2 #include "data.hpp" 3 #include "expansion.hpp" 4 #include "../context.hpp" 5 #include "../raw_item.hpp" 6 #include "../../sink.hpp" 7 8 #include <libshit/except.hpp> 9 #include <libshit/container/vector.lua.hpp> 10 11 #include <set> 12 #include <iostream> 13 14 namespace Neptools::Stcm 15 { 16 17 #define IP InstructionItem::Parameter 18 19 static void Param48Validate(uint32_t param, FilePosition file_size) 20 { 21 #define VALIDATE(x) LIBSHIT_VALIDATE_FIELD("Stcm Param48", x) 22 switch (IP::TypeTag(param)) 23 { 24 case IP::Type48::MEM_OFFSET: 25 VALIDATE((IP::Value(param) + 16) < file_size); 26 return; 27 case IP::Type48::IMMEDIATE: 28 return; 29 case IP::Type48::INDIRECT: 30 return; // ?? 31 default: 32 VALIDATE((param >= IP::Type48Special::READ_STACK_MIN && 33 param <= IP::Type48Special::READ_STACK_MAX) || 34 (param >= IP::Type48Special::READ_4AC_MIN && 35 param <= IP::Type48Special::READ_4AC_MAX)); 36 return; 37 } 38 #undef VALIDATE 39 } 40 41 void InstructionItem::Parameter::Validate(FilePosition file_size) const 42 { 43 #define VALIDATE(x) LIBSHIT_VALIDATE_FIELD("Stcm::InstructionItem::Parameter", x) 44 switch (TypeTag(param_0)) 45 { 46 case Type0::MEM_OFFSET: 47 VALIDATE(Value(param_0) < file_size); 48 Param48Validate(param_4, file_size); 49 Param48Validate(param_8, file_size); 50 return; 51 52 //case Type0::UNK: todo 53 case Type0::INDIRECT: 54 VALIDATE(Value(param_0) < 256 && param_4 == 0x40000000); 55 Param48Validate(param_8, file_size); 56 return; 57 58 case Type0::SPECIAL: 59 if ((param_0 >= Type0Special::READ_STACK_MIN && param_0 <= Type0Special::READ_STACK_MAX) || 60 (param_0 >= Type0Special::READ_4AC_MIN && param_0 <= Type0Special::READ_4AC_MAX)) 61 { 62 VALIDATE(param_4 == 0x40000000); 63 VALIDATE(param_8 == 0x40000000); 64 } 65 else if (param_0 == Type0Special::INSTR_PTR0 || 66 param_0 == Type0Special::INSTR_PTR1) 67 { 68 VALIDATE(param_4 < file_size); 69 VALIDATE(param_8 == 0x40000000); 70 } 71 else if (param_0 == Type0Special::COLL_LINK) 72 { 73 VALIDATE(param_4 + 8 < file_size); 74 VALIDATE(param_8 == 0); 75 } 76 else if (param_0 == Type0Special::EXPANSION) 77 { 78 VALIDATE(param_4 + 80 < file_size); 79 VALIDATE(param_8 == 0); 80 } 81 else 82 VALIDATE(!"Unknown special"); 83 return; 84 default: 85 VALIDATE(!"Unknown type0"); 86 } 87 #undef VALIDATE 88 } 89 90 void InstructionItem::Header::Validate(FilePosition file_size) const 91 { 92 #define VALIDATE(x) LIBSHIT_VALIDATE_FIELD("Stcm::InstructionItem::Header", x) 93 VALIDATE(is_call == 0 || is_call == 1); 94 VALIDATE(param_count < 16); 95 VALIDATE(size >= sizeof(Header) + param_count*sizeof(Parameter)); 96 97 if (is_call) 98 VALIDATE(opcode < file_size); 99 else 100 VALIDATE( 101 opcode < USER_OPCODES || 102 (opcode >= SYSTEM_OPCODES_BEGIN && opcode <= SYSTEM_OPCODES_END)); 103 #undef VALIDATE 104 } 105 106 InstructionItem::InstructionItem(Key k, Context& ctx, Source src) 107 : ItemWithChildren{k, ctx} 108 { 109 ADD_SOURCE(Parse_(ctx, src), src); 110 } 111 112 void InstructionItem::Parse_(Context& ctx, Source& src) 113 { 114 auto instr = src.ReadGen<Header>(); 115 instr.Validate(ctx.GetSize()); 116 117 if (instr.is_call) 118 SetTarget(ctx.GetLabelTo(instr.opcode)); 119 else 120 SetOpcode(instr.opcode); 121 122 params.reserve(instr.param_count); 123 for (size_t i = 0; i < instr.param_count; ++i) 124 { 125 auto p = src.ReadGen<Parameter>(); 126 params.emplace_back(ctx, p); 127 } 128 } 129 130 auto InstructionItem::Param::GetVariant(Context& ctx, const Parameter& in) 131 -> Variant 132 { 133 in.Validate(ctx.GetSize()); 134 135 switch (Parameter::TypeTag(in.param_0)) 136 { 137 case Parameter::Type0::MEM_OFFSET: 138 return MemOffset{ 139 ctx.GetLabelTo(Parameter::Value(in.param_0)), 140 Param48{ctx, in.param_4}, 141 Param48{ctx, in.param_8}}; 142 143 case Parameter::Type0::INDIRECT: 144 return Indirect{ 145 Parameter::Value(in.param_0), 146 Param48{ctx, in.param_8}}; 147 148 #define RETVAR(type, val) return Variant{ \ 149 std::in_place_index<static_cast<size_t>(Type::type)>, val} 150 case Parameter::Type0::SPECIAL: 151 if (in.param_0 >= Parameter::Type0Special::READ_STACK_MIN && 152 in.param_0 <= Parameter::Type0Special::READ_STACK_MAX) 153 RETVAR(READ_STACK, in.param_0 - Parameter::Type0Special::READ_STACK_MIN); 154 else if (in.param_0 >= Parameter::Type0Special::READ_4AC_MIN && 155 in.param_0 <= Parameter::Type0Special::READ_4AC_MAX) 156 RETVAR(READ_4AC, in.param_0 - Parameter::Type0Special::READ_4AC_MIN); 157 else if (in.param_0 == Parameter::Type0Special::INSTR_PTR0) 158 RETVAR(INSTR_PTR0, ctx.GetLabelTo(in.param_4)); 159 else if (in.param_0 == Parameter::Type0Special::INSTR_PTR1) 160 RETVAR(INSTR_PTR1, ctx.GetLabelTo(in.param_4)); 161 else if (in.param_0 == Parameter::Type0Special::COLL_LINK) 162 RETVAR(COLL_LINK, ctx.GetLabelTo(in.param_4)); 163 else if (in.param_0 == Parameter::Type0Special::EXPANSION) 164 RETVAR(EXPANSION, ctx.GetLabelTo(in.param_4)); 165 else 166 LIBSHIT_UNREACHABLE("Invalid special parameter type"); 167 } 168 169 LIBSHIT_UNREACHABLE("Invalid parameter type"); 170 } 171 172 void InstructionItem::Param::Dump(Sink& sink) const 173 { 174 Parameter pp; 175 switch (GetType()) 176 { 177 case Type::MEM_OFFSET: 178 { 179 const auto& o = Get<Type::MEM_OFFSET>(); 180 pp.param_0 = Parameter::Tag( 181 Parameter::Type0::MEM_OFFSET, ToFilePos(o.target->GetPtr())); 182 pp.param_4 = o.param_4.Dump(); 183 pp.param_8 = o.param_8.Dump(); 184 break; 185 } 186 187 case Type::INDIRECT: 188 { 189 const auto& i = Get<Type::INDIRECT>(); 190 pp.param_0 = Parameter::Tag(Parameter::Type0::INDIRECT, i.param_0); 191 pp.param_4 = 0x40000000; 192 pp.param_8 = i.param_8.Dump(); 193 break; 194 } 195 196 case Type::READ_STACK: 197 pp.param_0 = Parameter::Type0Special::READ_STACK_MIN + 198 Get<Type::READ_STACK>(); 199 pp.param_4 = 0x40000000; 200 pp.param_8 = 0x40000000; 201 break; 202 203 case Type::READ_4AC: 204 pp.param_0 = Parameter::Type0Special::READ_4AC_MIN + 205 Get<Type::READ_4AC>(); 206 pp.param_4 = 0x40000000; 207 pp.param_8 = 0x40000000; 208 break; 209 210 case Type::INSTR_PTR0: 211 pp.param_0 = Parameter::Type0Special::INSTR_PTR0; 212 pp.param_4 = ToFilePos(Get<Type::INSTR_PTR0>()->GetPtr()); 213 pp.param_8 = 0x40000000; 214 break; 215 216 case Type::INSTR_PTR1: 217 pp.param_0 = Parameter::Type0Special::INSTR_PTR1; 218 pp.param_4 = ToFilePos(Get<Type::INSTR_PTR1>()->GetPtr()); 219 pp.param_8 = 0x40000000; 220 break; 221 222 case Type::COLL_LINK: 223 pp.param_0 = Parameter::Type0Special::COLL_LINK; 224 pp.param_4 = ToFilePos(Get<Type::COLL_LINK>()->GetPtr()); 225 pp.param_8 = 0; 226 break; 227 228 case Type::EXPANSION: 229 pp.param_0 = Parameter::Type0Special::EXPANSION; 230 pp.param_4 = ToFilePos(Get<Type::EXPANSION>()->GetPtr()); 231 pp.param_8 = 0; 232 break; 233 } 234 sink.WriteGen(pp); 235 } 236 237 std::ostream& operator<<(std::ostream& os, const InstructionItem::Param& p) 238 { 239 using T = InstructionItem::Param::Type; 240 switch (p.GetType()) 241 { 242 case T::MEM_OFFSET: 243 { 244 const auto& o = p.Get<T::MEM_OFFSET>(); 245 return os << "{'mem_offset', " << PrintLabel(o.target) << ", " 246 << o.param_4 << ", " << o.param_8 << '}'; 247 } 248 case T::INDIRECT: 249 { 250 const auto& i = p.Get<T::INDIRECT>(); 251 return os << "{'indirect', " << i.param_0 << ", " << i.param_8 << '}'; 252 } 253 case T::READ_STACK: 254 return os << "{'read_stack', " << p.Get<T::READ_STACK>() << "}"; 255 case T::READ_4AC: 256 return os << "{'read_4ac', " << p.Get<T::READ_4AC>() << "}"; 257 case T::INSTR_PTR0: 258 return os << "{'instr_ptr0', " << PrintLabel(p.Get<T::INSTR_PTR0>()) << '}'; 259 case T::INSTR_PTR1: 260 return os << "{'instr_ptr1', " << PrintLabel(p.Get<T::INSTR_PTR1>()) << '}'; 261 case T::COLL_LINK: 262 return os << "{'coll_link', " << PrintLabel(p.Get<T::COLL_LINK>()) << '}'; 263 case T::EXPANSION: 264 return os << "{'expansion', " << PrintLabel(p.Get<T::EXPANSION>()) << '}'; 265 } 266 LIBSHIT_UNREACHABLE("Invalid type"); 267 } 268 269 270 auto InstructionItem::Param48::GetVariant(Context& ctx, uint32_t in) -> Variant 271 { 272 switch (Parameter::TypeTag(in)) 273 { 274 case Parameter::Type48::MEM_OFFSET: 275 RETVAR(MEM_OFFSET, ctx.GetLabelTo(Parameter::Value(in))); 276 277 case Parameter::Type48::IMMEDIATE: 278 RETVAR(IMMEDIATE, Parameter::Value(in)); 279 280 case Parameter::Type48::INDIRECT: 281 RETVAR(INDIRECT, Parameter::Value(in)); 282 283 case Parameter::Type48::SPECIAL: 284 if (in >= Parameter::Type48Special::READ_STACK_MIN && 285 in <= Parameter::Type48Special::READ_STACK_MAX) 286 { 287 RETVAR(READ_STACK, in - Parameter::Type48Special::READ_STACK_MIN); 288 } 289 else if (in >= Parameter::Type48Special::READ_4AC_MIN && 290 in <= Parameter::Type48Special::READ_4AC_MAX) 291 { 292 RETVAR(READ_4AC, in - Parameter::Type48Special::READ_4AC_MIN); 293 } 294 else 295 LIBSHIT_UNREACHABLE("Invalid 48Special param"); 296 } 297 #undef RETVAR 298 299 LIBSHIT_UNREACHABLE("Invalid 48 param"); 300 } 301 302 std::ostream& operator<<(std::ostream& os, const InstructionItem::Param48& p) 303 { 304 using T = InstructionItem::Param48::Type; 305 switch (p.GetType()) 306 { 307 case T::MEM_OFFSET: 308 return os << "{'mem_offset', " << PrintLabel(p.Get<T::MEM_OFFSET>()) << '}'; 309 case T::IMMEDIATE: 310 return os << "{'immediate', " << p.Get<T::IMMEDIATE>() << '}'; 311 case T::INDIRECT: 312 return os << "{'indirect', " << p.Get<T::INDIRECT>() << '}'; 313 case T::READ_STACK: 314 return os << "{'read_stack', " << p.Get<T::READ_STACK>() << '}'; 315 case T::READ_4AC: 316 return os << "{'read_4ac', " << p.Get<T::READ_4AC>() << '}'; 317 } 318 319 LIBSHIT_UNREACHABLE("Invalid 48 param"); 320 } 321 322 static const std::set<uint32_t> no_returns{0, 6}; 323 324 InstructionItem& InstructionItem::CreateAndInsert(ItemPointer ptr) 325 { 326 auto x = RawItem::GetSource(ptr, -1); 327 328 x.src.CheckSize(sizeof(Header)); 329 auto inst = x.src.PreadGen<Header>(0); 330 x.src.CheckSize(inst.size); 331 332 auto& ret = x.ritem.SplitCreate<InstructionItem>(ptr.offset, x.src); 333 334 auto rem_data = inst.size - sizeof(Header) - 335 sizeof(Parameter) * inst.param_count; 336 if (rem_data) 337 ret.MoveNextToChild(rem_data); 338 339 LIBSHIT_ASSERT(ret.GetSize() == inst.size); 340 341 // recursive parse 342 if (ret.IsCall()) 343 MaybeCreate<InstructionItem>(ret.GetTarget()->GetPtr()); 344 if (ret.IsCall() || !no_returns.count(ret.GetOpcode())) 345 MaybeCreate<InstructionItem>({&*++ret.Iterator(), 0}); 346 for (const auto& p : ret.params) 347 { 348 using T = Param::Type; 349 switch (p.GetType()) 350 { 351 case T::MEM_OFFSET: 352 MaybeCreate<DataItem>(p.Get<T::MEM_OFFSET>().target->GetPtr()); 353 break; 354 case T::INSTR_PTR0: 355 MaybeCreate<InstructionItem>(p.Get<T::INSTR_PTR0>()->GetPtr()); 356 break; 357 case T::INSTR_PTR1: 358 MaybeCreate<InstructionItem>(p.Get<T::INSTR_PTR1>()->GetPtr()); 359 break; 360 default:; 361 } 362 } 363 364 return ret; 365 } 366 367 368 FilePosition InstructionItem::GetSize() const noexcept 369 { 370 return sizeof(Header) + params.size() * sizeof(Parameter) + 371 ItemWithChildren::GetSize(); 372 } 373 374 uint32_t InstructionItem::Param48::Dump() const noexcept 375 { 376 switch (GetType()) 377 { 378 case Type::MEM_OFFSET: 379 return Parameter::Tag(Parameter::Type48::MEM_OFFSET, 380 ToFilePos(Get<Type::MEM_OFFSET>()->GetPtr())); 381 case Type::IMMEDIATE: 382 return Parameter::Tag(Parameter::Type48::IMMEDIATE, Get<Type::IMMEDIATE>()); 383 case Type::INDIRECT: 384 return Parameter::Tag(Parameter::Type48::INDIRECT, Get<Type::INDIRECT>()); 385 case Type::READ_STACK: 386 return Parameter::Type48Special::READ_STACK_MIN + Get<Type::READ_STACK>(); 387 case Type::READ_4AC: 388 return Parameter::Type48Special::READ_4AC_MIN + Get<Type::READ_4AC>(); 389 } 390 LIBSHIT_UNREACHABLE("Invalid Param48 Type stored"); 391 } 392 393 void InstructionItem::Fixup() 394 { 395 ItemWithChildren::Fixup_(sizeof(Header) + params.size() * sizeof(Parameter)); 396 } 397 398 void InstructionItem::Dispose() noexcept 399 { 400 params.clear(); 401 ItemWithChildren::Dispose(); 402 } 403 404 void InstructionItem::Dump_(Sink& sink) const 405 { 406 Header hdr; 407 hdr.is_call = IsCall(); 408 409 if (IsCall()) 410 hdr.opcode = ToFilePos(GetTarget()->GetPtr()); 411 else 412 hdr.opcode = GetOpcode(); 413 hdr.param_count = params.size(); 414 hdr.size = GetSize(); 415 sink.WriteGen(hdr); 416 417 for (const auto& p : params) 418 p.Dump(sink); 419 420 ItemWithChildren::Dump_(sink); 421 } 422 423 void InstructionItem::Inspect_(std::ostream &os, unsigned indent) const 424 { 425 Item::Inspect_(os, indent); 426 427 if (IsCall()) 428 os << "call(" << PrintLabel(GetTarget()); 429 else 430 os << "instruction(" << GetOpcode(); 431 if (!params.empty()) 432 { 433 os << ", {\n"; 434 size_t i = 0; 435 for (const auto& p : params) 436 Indent(os, indent+1) << '[' << i++ << "] = " << p << ",\n"; 437 Indent(os, indent) << '}'; 438 } 439 os << ')'; 440 InspectChildren(os, indent); 441 } 442 443 } 444 445 LIBSHIT_STD_VECTOR_LUAGEN( 446 stcm_instruction_param, Neptools::Stcm::InstructionItem::Param); 447 #include "instruction.binding.hpp"