diff --git a/doc/code_status.md b/doc/code_status.md new file mode 100644 index 000000000..39f5ce82d --- /dev/null +++ b/doc/code_status.md @@ -0,0 +1,50 @@ +## gcommon.gc +Missing stuff. + +## gstring-h.gc +Empty file. + +## gkernel-h.gc +Likely missing some macros. Missing `handle`, a child type of integer. + +## gkernel.gc +Missing lots of stuff. Will need x86-64 inline assembly and some tweaking for x86. + +## pskernel.gc +Possibly can be entirely left out. Seems to be mostly unused, or only used for PS2 debugging? + +## gstring.gc +Missing lots + +## dgo-h.gc +Done! + +## gstate.gc +Not started, probably needs state support in the compiler + +## types.gc +Needs child types of integer + +## vu-macros.gc +Empty. + +## math.gc +Has a unit test for a lot of functions. +rand-vu-init, rand-vu, rand-vu-nostep, rand-vu-float-range, rand-vu-percent?, rand-vu-int-range, rand-vu-int-count aren't implemented + +rand-uint31-gen might be wrong. + +## vector.gc +Partially done + +## gravity-h.gc +Empty file + +## bounding-box-h.gc +Just types. Done! + +## matrix-h.gc +Types and one function. Done, but the matrix-copy! function isn't that efficient. + +## quaternion-h.gc +Done! \ No newline at end of file diff --git a/goal_src/engine/geometry/bounding-box-h.gc b/goal_src/engine/geometry/bounding-box-h.gc index 4c5d79022..f2304ba51 100644 --- a/goal_src/engine/geometry/bounding-box-h.gc +++ b/goal_src/engine/geometry/bounding-box-h.gc @@ -5,3 +5,43 @@ ;; name in dgo: bounding-box-h ;; dgos: GAME, ENGINE +;; Types related to bounding boxes. + +;; floating point bounding box. +(deftype bounding-box (structure) + ((min vector :inline :offset-assert 0) + (max vector :inline :offset-assert 16) + ) + :method-count-assert 16 + :size-assert #x20 + :flag-assert #x1000000020 + (:methods + (dummy-9 () none 9) + (dummy-10 () none 10) + (dummy-11 () none 11) + (dummy-12 () none 12) + (dummy-13 () none 13) + (dummy-14 () none 14) + (dummy-15 () none 15) + ) + ) + +;; integer (word) bounding box. +(deftype bounding-box4w (structure) + ((min vector4w :inline :offset-assert 0) + (max vector4w :inline :offset-assert 16) + ) + :method-count-assert 9 + :size-assert #x20 + :flag-assert #x900000020 + ) + +;; bounding both that has both a box and box4w. +(deftype bounding-box-both (structure) + ((box bounding-box :inline :offset-assert 0) + (box4w bounding-box4w :inline :offset-assert 32) + ) + :method-count-assert 9 + :size-assert #x40 + :flag-assert #x900000040 + ) \ No newline at end of file diff --git a/goal_src/engine/math/math.gc b/goal_src/engine/math/math.gc index 449497474..ebcc28697 100644 --- a/goal_src/engine/math/math.gc +++ b/goal_src/engine/math/math.gc @@ -148,13 +148,32 @@ ;; I wonder who wrote this code. (set! (-> *random-generator* seed) #x666EDD1E) +(defmacro sext32-64 (x) + `(sarv (shlv ,x 32) 32) + ) + (defun rand-uint31-gen ((gen random-generator)) - "Generate a supposedly random integer" - (let ((result (-> gen seed))) - ;; addiu v1, r0, 16807 - (+! result 16807) - ;; to 32 bit, doing sign extension. - (set! result (shlv result 32)) - (set! result (sarv result 32)) + "Generate a supposedly random integer. + Note, this might not quite be right. + But the highest bit is always zero, like it says + and it looks kinda random to me." + (let* ((sd (-> gen seed)) + ;; addiu v1, r0, 16807 + ;; mult3 v0, v1, a1 + (prod (imul64 16807 sd)) + ;; mfhi v1 + (hi (shrv prod 32)) ;; sign extend this? + (lo (sarv (shlv prod 32) 32)) + ;; daddu v1, v1, v1 + (v1 (+ hi hi)) + ;; srl a1, v0, 31 + (a1 (logand #xffffffff (shrv lo 31))) + ;; or v1, v1, a1 + ;; daddu v0, v0 v1 + (result (+ lo (logior v1 a1))) + ) + (set! result (shrv (logand #xffffffff (shlv result 1)) 1)) + (set! (-> gen seed) result) + result ) ) \ No newline at end of file diff --git a/goal_src/engine/math/matrix-h.gc b/goal_src/engine/math/matrix-h.gc index ca0f65a3c..d60b9fe03 100644 --- a/goal_src/engine/math/matrix-h.gc +++ b/goal_src/engine/math/matrix-h.gc @@ -5,3 +5,45 @@ ;; name in dgo: matrix-h ;; dgos: GAME, ENGINE +;; matrix-h +(deftype matrix (structure) + ((data float 16 :offset-assert 0) + (vector vector 4 :offset 0) + (quad uint128 4 :offset 0) + ) + :method-count-assert 10 + :size-assert #x40 + :flag-assert #xa00000040 + (:methods + (dummy-9 () none 9) + ) + ) + +(deftype matrix3 (structure) + ((data float 12 :offset-assert 0) + (vector vector 3 :offset 0) + (quad uint128 3 :offset 0) + ) + :method-count-assert 9 + :size-assert #x30 + :flag-assert #x900000030 + ) + +;; guess on signs here +(deftype matrix4h (structure) + ((data int16 16 :offset-assert 0) + (vector4h vector4h 4 :offset 0) + (long int64 4 :offset 0) + ) + :method-count-assert 9 + :size-assert #x20 + :flag-assert #x900000020 + ) + +(defun matrix-copy! ((dst matrix) (src matrix)) + "Copy src to dst." + ;; actual implementation is in assembly, unrolled quad copies, loads/stores spaced out. + (dotimes (i 16 dst) + (set! (-> dst data i) (-> src data i)) + ) + ) \ No newline at end of file diff --git a/goal_src/engine/math/quaternion-h.gc b/goal_src/engine/math/quaternion-h.gc index d8dd56d83..d2a8689f3 100644 --- a/goal_src/engine/math/quaternion-h.gc +++ b/goal_src/engine/math/quaternion-h.gc @@ -5,3 +5,18 @@ ;; name in dgo: quaternion-h ;; dgos: GAME, ENGINE +(deftype quaternion (structure) + ((data float 4 :offset-assert 0) + (x float :offset 0) + (y float :offset 4) + (z float :offset 8) + (w float :offset 12) + (vec vector :inline :offset 0) + (quad uint128 :offset 0) + ) + :method-count-assert 9 + :size-assert #x10 + :flag-assert #x900000010 + ) + +(define *unity-quaternion* (new 'static 'quaternion :x 0.0 :y 0.0 :z 0.0 :w 1.0)) \ No newline at end of file diff --git a/goalc/compiler/Compiler.h b/goalc/compiler/Compiler.h index b376389a5..8689c8b37 100644 --- a/goalc/compiler/Compiler.h +++ b/goalc/compiler/Compiler.h @@ -218,9 +218,9 @@ class Compiler { // Math Val* compile_add(const goos::Object& form, const goos::Object& rest, Env* env); - Val* compile_sub(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_mul(const goos::Object& form, const goos::Object& rest, Env* env); + Val* compile_imul64(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_div(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_shlv(const goos::Object& form, const goos::Object& rest, Env* env); Val* compile_sarv(const goos::Object& form, const goos::Object& rest, Env* env); diff --git a/goalc/compiler/IR.cpp b/goalc/compiler/IR.cpp index bb59169ee..1aba46ddb 100644 --- a/goalc/compiler/IR.cpp +++ b/goalc/compiler/IR.cpp @@ -400,6 +400,8 @@ std::string IR_IntegerMath::print() { return fmt::format("subi {}, {}", m_dest->print(), m_arg->print()); case IntegerMathKind::IMUL_32: return fmt::format("imul {}, {}", m_dest->print(), m_arg->print()); + case IntegerMathKind::IMUL_64: + return fmt::format("imul64 {}, {}", m_dest->print(), m_arg->print()); case IntegerMathKind::IDIV_32: return fmt::format("idiv {}, {}", m_dest->print(), m_arg->print()); case IntegerMathKind::IMOD_32: @@ -483,13 +485,15 @@ void IR_IntegerMath::do_codegen(emitter::ObjectGenerator* gen, gen->add_instr(IGen::imul_gpr32_gpr32(dr, get_reg(m_arg, allocs, irec)), irec); gen->add_instr(IGen::movsx_r64_r32(dr, dr), irec); } break; - + case IntegerMathKind::IMUL_64: { + auto dr = get_reg(m_dest, allocs, irec); + gen->add_instr(IGen::imul_gpr64_gpr64(dr, get_reg(m_arg, allocs, irec)), irec); + } break; case IntegerMathKind::IDIV_32: { gen->add_instr(IGen::cdq(), irec); gen->add_instr(IGen::idiv_gpr32(get_reg(m_arg, allocs, irec)), irec); gen->add_instr(IGen::movsx_r64_r32(get_reg(m_dest, allocs, irec), emitter::RAX), irec); } break; - case IntegerMathKind::IMOD_32: { gen->add_instr(IGen::cdq(), irec); gen->add_instr(IGen::idiv_gpr32(get_reg(m_arg, allocs, irec)), irec); diff --git a/goalc/compiler/IR.h b/goalc/compiler/IR.h index 244a7c085..5671e0cf1 100644 --- a/goalc/compiler/IR.h +++ b/goalc/compiler/IR.h @@ -193,6 +193,7 @@ enum class IntegerMathKind { ADD_64, SUB_64, IMUL_32, + IMUL_64, IDIV_32, SHLV_64, SARV_64, diff --git a/goalc/compiler/compilation/Atoms.cpp b/goalc/compiler/compilation/Atoms.cpp index 56b054300..0b6180473 100644 --- a/goalc/compiler/compilation/Atoms.cpp +++ b/goalc/compiler/compilation/Atoms.cpp @@ -101,6 +101,7 @@ static const std::unordered_map< {"+", &Compiler::compile_add}, {"-", &Compiler::compile_sub}, {"*", &Compiler::compile_mul}, + {"imul64", &Compiler::compile_imul64}, {"/", &Compiler::compile_div}, {"shlv", &Compiler::compile_shlv}, {"shrv", &Compiler::compile_shrv}, diff --git a/goalc/compiler/compilation/Math.cpp b/goalc/compiler/compilation/Math.cpp index 674152f17..21824070d 100644 --- a/goalc/compiler/compilation/Math.cpp +++ b/goalc/compiler/compilation/Math.cpp @@ -160,6 +160,7 @@ Val* Compiler::compile_mul(const goos::Object& form, const goos::Object& rest, E auto math_type = get_math_mode(first_type); switch (math_type) { case MATH_INT: { + // todo, signed vs unsigned? auto result = env->make_gpr(first_type); env->emit(std::make_unique(result, first_val->to_gpr(env))); @@ -194,6 +195,41 @@ Val* Compiler::compile_mul(const goos::Object& form, const goos::Object& rest, E return get_none(); } +Val* Compiler::compile_imul64(const goos::Object& form, const goos::Object& rest, Env* env) { + auto args = get_va(form, rest); + if (!args.named.empty() || args.unnamed.empty()) { + throw_compile_error(form, "Invalid * form"); + } + + // look at the first value to determine the math mode + auto first_val = compile_error_guard(args.unnamed.at(0), env); + auto first_type = first_val->type(); + auto math_type = get_math_mode(first_type); + switch (math_type) { + case MATH_INT: { + auto result = env->make_gpr(first_type); + env->emit(std::make_unique(result, first_val->to_gpr(env))); + + for (size_t i = 1; i < args.unnamed.size(); i++) { + env->emit(std::make_unique( + IntegerMathKind::IMUL_64, result, + to_math_type(compile_error_guard(args.unnamed.at(i), env), math_type, env) + ->to_gpr(env))); + } + return result; + } + case MATH_FLOAT: + case MATH_INVALID: + throw_compile_error( + form, "Cannot determine the math mode for object of type " + first_type.print()); + break; + default: + assert(false); + } + assert(false); + return get_none(); +} + Val* Compiler::compile_sub(const goos::Object& form, const goos::Object& rest, Env* env) { auto args = get_va(form, rest); if (!args.named.empty() || args.unnamed.empty()) { diff --git a/goalc/emitter/IGen.h b/goalc/emitter/IGen.h index 219e1efd1..a2c8814c3 100644 --- a/goalc/emitter/IGen.h +++ b/goalc/emitter/IGen.h @@ -1387,6 +1387,19 @@ class IGen { return instr; } + /*! + * Multiply gprs (64-bit, signed). + * DANGER - this treats all operands as 64-bit. This is not like the EE. + */ + static Instruction imul_gpr64_gpr64(Register dst, Register src) { + Instruction instr(0xf); + instr.set_op2(0xaf); + assert(dst.is_gpr()); + assert(src.is_gpr()); + instr.set_modrm_and_rex(dst.hw_id(), src.hw_id(), 3, true); + return instr; + } + /*! * Divide (idiv, 32 bit) * todo UNTESTED diff --git a/test/goalc/source_templates/arithmetic/multiply32.static.gc b/test/goalc/source_templates/arithmetic/multiply32.static.gc new file mode 100644 index 000000000..4a8f3c8ce --- /dev/null +++ b/test/goalc/source_templates/arithmetic/multiply32.static.gc @@ -0,0 +1,2 @@ +(* #x12341234 #x12341234) + diff --git a/test/goalc/source_templates/arithmetic/multiply64.static.gc b/test/goalc/source_templates/arithmetic/multiply64.static.gc new file mode 100644 index 000000000..9a4eb4e01 --- /dev/null +++ b/test/goalc/source_templates/arithmetic/multiply64.static.gc @@ -0,0 +1,2 @@ +(imul64 #x12341234 #x12341234) + diff --git a/test/goalc/test_arithmetic.cpp b/test/goalc/test_arithmetic.cpp index 35a2c6f46..1e7f18f07 100644 --- a/test/goalc/test_arithmetic.cpp +++ b/test/goalc/test_arithmetic.cpp @@ -236,3 +236,8 @@ TEST_F(ArithmeticTests, Subtraction) { runner.run_static_test(env, testCategory, "subtract-2.static.gc", {"4\n"}); runner.run_static_test(env, testCategory, "subtract-let.static.gc", {"3\n"}); } + +TEST_F(ArithmeticTests, Multiplication2) { + runner.run_static_test(env, testCategory, "multiply32.static.gc", {"-1234478448\n"}); + runner.run_static_test(env, testCategory, "multiply64.static.gc", {"93270638141856400\n"}); +} \ No newline at end of file