diff --git a/decompiler/IR2/FormExpressionAnalysis.cpp b/decompiler/IR2/FormExpressionAnalysis.cpp index 8403bfa80c..310e16f057 100644 --- a/decompiler/IR2/FormExpressionAnalysis.cpp +++ b/decompiler/IR2/FormExpressionAnalysis.cpp @@ -97,6 +97,18 @@ Form* try_cast_simplify(Form* in, return in; } + if (env.version == GameVersion::Jak2) { + if (new_type == TypeSpec("float")) { + auto ic = get_goal_integer_constant(in, env); + if (ic) { + ASSERT(*ic <= UINT32_MAX); + float f; + memcpy(&f, &ic.value(), sizeof(float)); + return pool.form(f); + } + } + } + if (new_type == TypeSpec("meters")) { auto fc = get_goal_float_constant(in); diff --git a/decompiler/config/jak2/all-types.gc b/decompiler/config/jak2/all-types.gc index 19698c8147..b8225091fc 100644 --- a/decompiler/config/jak2/all-types.gc +++ b/decompiler/config/jak2/all-types.gc @@ -14,6 +14,7 @@ (define-extern integer type) (define-extern float type) (define-extern boolean type) ;; not actually added as a runtime type in jak2, but valid? supports it. +(define-extern uint32 type) (define-extern int64 type) (define-extern uint64 type) (define-extern process-tree type) @@ -162,7 +163,7 @@ (define-extern *trace-list* pair) (define-extern print-tree-bitmask (function int int symbol)) (define-extern breakpoint-range-set! (function uint uint uint int)) -(define-extern valid? (function object type basic basic object symbol)) +(define-extern valid? (function object type symbol symbol object symbol)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; gstring-h ;; @@ -231,7 +232,7 @@ (self process-tree :offset-assert 32) ) (:methods - (new (symbol type basic) _type_ 0) + (new (symbol type string) _type_ 0) (activate (_type_ process-tree basic pointer) process-tree 9) (deactivate (_type_) none 10) (init-from-entity! (_type_ entity-actor) none 11) ;; todo check @@ -254,7 +255,7 @@ (relocating-min int32 :offset-assert 32) (relocating-max int32 :offset-assert 36) (relocating-offset int32 :offset-assert 40) - (relocating-level object :offset-assert 44) ;; guessing here + (relocating-level level :offset-assert 44) ;; guessing here (low-memory-message symbol :offset-assert 48) ;; guessed by decompiler (login-object basic :offset-assert 52) ) @@ -271,37 +272,37 @@ (deftype clock (basic) - ((index int32 :offset-assert 4) - (mask uint32 :offset-assert 8) - (clock-ratio float :offset-assert 12) - (accum float :offset-assert 16) - (integral-accum float :offset-assert 20) - (frame-counter uint64 :offset-assert 24) - (old-frame-counter uint64 :offset-assert 32) - (integral-frame-counter uint64 :offset-assert 40) - (old-integral-frame-counter uint64 :offset-assert 48) - (sparticle-data vector :inline :offset-assert 64) - (seconds-per-frame float :offset-assert 80) - (frames-per-second float :offset-assert 84) - (time-adjust-ratio float :offset-assert 88) + ((index int32 :offset-assert 4) ;; which clock we are, in *display* + (mask process-mask :offset-assert 8) ;; mask for ticking + (clock-ratio float :offset-assert 12) ;; how fast to run. 1.0 = realtime. + (accum float :offset-assert 16) ;; fractional time for frame-counter (time-frame units) + (integral-accum float :offset-assert 20) ;; fractional time for integral (time-frame untis) + (frame-counter time-frame :offset-assert 24) ;; how much time has gone by since reset (time-frame units) + (old-frame-counter time-frame :offset-assert 32) ;; the frame-counter on the last engine iteration + (integral-frame-counter uint64 :offset-assert 40) ;; how many vsyncs have gone by since reset + (old-integral-frame-counter uint64 :offset-assert 48) ;; the integral-frame-counter on the last engine iteration + (sparticle-data vector :inline :offset-assert 64) ;; sparticle timescale info + (seconds-per-frame float :offset-assert 80) ;; how many seconds (not time-frames) should go by in 1 vsync + (frames-per-second float :offset-assert 84) ;; inverse of above + (time-adjust-ratio float :offset-assert 88) ;; 1, if the game runs at 60fps NTSC with clock-ratio = 1. ) :method-count-assert 15 :size-assert #x5c :flag-assert #xf0000005c (:methods (new (symbol type int) _type_ 0) - (dummy-9 (_type_ float) none 9) - (dummy-10 () none 10) - (dummy-11 () none 11) - (dummy-12 () none 12) - (dummy-13 () none 13) - (dummy-14 () none 14) + (update-rates! (_type_ float) none 9) + (advance-by! (_type_ float) none 10) + (tick! (_type_) none 11) + (save! (_type_ (pointer uint64)) int 12) + (load! (_type_ (pointer uint64)) int 13) + (reset! (_type_) none 14) ) ) (deftype thread (basic) - ((name basic :offset-assert 4) + ((name symbol :offset-assert 4) (process process :offset-assert 8) ;; guessed by decompiler (previous thread :offset-assert 12) ;; guessed by decompiler (suspend-hook (function cpu-thread none) :offset-assert 16) ;; guessed by decompiler @@ -349,7 +350,7 @@ :flag-assert #x1000000024 ;; Failed to read fields. (:methods - (new (symbol type int int basic) _type_ 0) + (new (symbol type int int string) _type_ 0) (dummy-13 () none 13) (get-process (_type_ type int) process 14) (return-process (_type_ process) none 15) @@ -388,7 +389,7 @@ :flag-assert #x1c00000068 ;; Failed to read fields. (:methods - (new (symbol type basic int int) _type_ 0) + (new (symbol type string int int) _type_ 0) (init (_type_ symbol int) none 16) (compact (dead-pool-heap int) none 17) (shrink-heap (dead-pool-heap process) dead-pool-heap 18) @@ -404,8 +405,6 @@ ) ) - - (deftype stack-frame (basic) ((name symbol :offset 4) (next stack-frame :offset 8) ;; which way does this point? @@ -500,7 +499,7 @@ (deftype process (process-tree) ((pool dead-pool) - (status basic :offset-assert 40) + (status symbol :offset-assert 40) (pid int32) (main-thread cpu-thread :offset-assert 48) (top-thread cpu-thread :offset-assert 52) @@ -518,28 +517,10 @@ (heap-cur pointer :offset-assert 104) (stack-frame-top stack-frame :offset-assert 108) (connection-list connectable :inline :offset-assert 112) - - ; (pool dead-pool :offset-assert 36) - ; (status basic :offset-assert 40) - ; (pid int32 :offset-assert 44) - ; (main-thread cpu-thread :offset-assert 48) - ; (top-thread thread :offset-assert 52) - ; (entity entity-actor :offset-assert 56) - ; (state state :offset-assert 60) - ; (trans-hook function :offset-assert 64) - ; (post-hook function :offset-assert 68) - ; (event-hook (function process int symbol event-message-block object) :offset-assert 72) - ; (allocated-length int32 :offset-assert 76) - ; (next-state state :offset-assert 80) - ; (heap-base pointer :offset-assert 84) - ; (heap-top pointer :offset-assert 88) - ; (heap-cur pointer :offset-assert 92) - ; (stack-frame-top stack-frame :offset-assert 96) - ; (connection-list connectable :inline :offset-assert 112) ;; can be a connection (stack uint8 :dynamic :offset-assert 128) ) (:methods - (new (symbol type basic int) _type_ 0) + (new (symbol type string int) _type_ 0) ) (:states dead-state @@ -592,7 +573,7 @@ (define-extern *global-search-name* basic) (define-extern *global-search-count* int) (define-extern process-by-name (function string process-tree process)) -(define-extern process-not-name (function object process-tree process)) +(define-extern process-not-name (function string process-tree process)) (define-extern process-count (function process-tree int)) (define-extern kill-by-name (function string process-tree symbol)) (define-extern kill-by-type (function type process-tree symbol)) @@ -617,7 +598,7 @@ (define-extern entity-deactivate-handler (function process entity-actor none)) (define-extern *listener-process* process) (define-extern *null-process* process) -(define-extern *vis-boot* basic) +(define-extern *vis-boot* symbol) (define-extern *kernel-clock* clock) (define-extern *16k-dead-pool* dead-pool) (define-extern *8k-dead-pool* dead-pool) @@ -780,6 +761,16 @@ ;; math ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(deftype rgba (uint32) + ((r uint8 :offset 0) + (g uint8 :offset 8) + (b uint8 :offset 16) + (a uint8 :offset 24) + ) + :flag-assert #x900000004 + :no-runtime-type + ) + #| (deftype random-generator (basic) ((seed uint32 :offset-assert 4) @@ -2275,42 +2266,66 @@ ;; timer-h ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -#| +(defenum timer-clock-selection + :type uint8 + (busclk 0) + (busclk/16 1) + (busclk/256 2) + (hblank 3) + ) + +(deftype timer-mode (uint32) + ((clks timer-clock-selection :offset 0 :size 2) + (gate uint8 :offset 2 :size 1) ;; gate function enable + (gats uint8 :offset 3 :size 1) ;; gate selection: 0 = hblank, 1 = vblank + ;; gate mode: + ;; 0: count while gate signal is low + ;; 1: start when gate signal rises + ;; 2: start when gate signal falls + ;; 3: start when gate signal rises/falls + (gatm uint8 :offset 4 :size 2) + (zret uint8 :offset 6 :size 1) ;; zero return: clear counter when equal to reference value + (cue uint8 :offset 7 :size 1) ;; count-up enable + (cmpe uint8 :offset 8 :size 1) ;; compare-interrupt enable + (ovfe uint8 :offset 9 :size 1) ;; overflow-interrupt enable + (equf uint8 :offset 10 :size 1) ;; equal-flag + (ovff uint8 :offset 11 :size 1) ;; overflow-flag + ) + :method-count-assert 9 + :size-assert #x4 + :flag-assert #x900000004 + ) + (deftype timer-bank (structure) ((count uint32 :offset-assert 0) - (mode timer-mode :offset-assert 16) ;; guessed by decompiler - (comp uint32 :offset-assert 32) + (mode timer-mode :offset 16) + (comp uint32 :offset 32) ) :method-count-assert 9 :size-assert #x24 :flag-assert #x900000024 ) -|# -#| (deftype timer-hold-bank (timer-bank) - ((hold uint32 :offset-assert 48) + ((hold uint32 :offset 48) ) :method-count-assert 9 :size-assert #x34 :flag-assert #x900000034 ) -|# -#| (deftype stopwatch (basic) - ((prev-time-elapsed uint64 :offset-assert 8) ;; time-frame - (start-time uint64 :offset-assert 16) ;; time-frame - (begin-level int32 :offset-assert 24) + ((prev-time-elapsed time-frame :offset-assert 8) + (start-time time-frame :offset-assert 16) + (begin-level int32 :offset-assert 24) ) :method-count-assert 9 :size-assert #x1c :flag-assert #x90000001c ) -|# -;; (define-extern *ticks-per-frame* object) ;; int -;; (define-extern timer-init object) ;; (function timer-bank timer-mode int) +(define-extern *ticks-per-frame* int) +(define-extern timer-init (function timer-bank timer-mode int)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; vif-h ;; @@ -2447,41 +2462,107 @@ ) |# -#| +(defenum dma-tag-id + :bitfield #f + :type uint8 + (refe 0) ;; addr=ADDR, ends after this transfer + (cnt 1) ;; addr=after tag, next-tag=after data + (next 2) ;; addr=after tag, next-tag=ADDR + (ref 3) ;; addr=ADDR, next-tag=after tag + (refs 4) ;; ref, but stall controled + (call 5) ;; + (ret 6) ;; + (end 7) ;; next, but ends. + ) + (deftype dma-tag (uint64) - () + ((qwc uint16 :offset 0) ;; quadword count + (pce uint8 :offset 26 :size 2) ;; priority (source mode) + (id dma-tag-id :offset 28 :size 3) ;; ID (what the tag means) + (irq uint8 :offset 31 :size 1) ;; interrupt at the end? + (addr uint32 :offset 32 :size 31) ;; address (31 bits) + (spr uint8 :offset 63 :size 1) ;; spr or not flag. + ) :method-count-assert 9 :size-assert #x8 :flag-assert #x900000008 - ;; Failed to read some fields. ) -|# -#| (deftype dma-bucket (structure) - ((tag uint64 :offset-assert 0) ;; dma-tag - (last (pointer dma-tag) :offset-assert 8) ;; guessed by decompiler + ((tag dma-tag :offset-assert 0) + (last (pointer dma-tag) :offset-assert 8) (dummy uint32 :offset-assert 12) - (next uint32 :offset-assert 4) - (clear uint64 :offset-assert 8) - (vif0 uint32 :offset-assert 8) - (vif1 uint32 :offset-assert 12) + (next uint32 :offset 4) + (clear uint64 :offset 8) + (vif0 uint32 :offset 8) + (vif1 uint32 :offset 12) ) :method-count-assert 9 :size-assert #x10 :flag-assert #x900000010 ) -|# -#| +;; all these have mask (only applies to unpacks) and interrupt not set. +(defenum vif-cmd + :bitfield #f + :type uint8 + (nop 0) ;; no-op, can still have irq set. + (stcycl 1) ;; set write recycle register + (offset 2) ;; set offset register + (base 3) ;; set base register + (itop 4) ;; set data pointer register (itops) + (stmod 5) ;; set mode register + (mskpath3 6) ;; set path 3 mask + (mark 7) ;; set mark register + (pc-port 8) ;; special tag for PC Port data. + (flushe 16) ;; wait for end of microprogram + (flush 17) ;; wait for end of microprogram and transfer (path1/path2) + (flusha 19) ;; wait for end of microprogram and transfer (path1/path2/path3) + (mscal 20) ;; activate microprogram (call) + (mscalf 21) ;; flushe and activate (call) + (mscnt 23) ;; activate microprogram (continue) + (stmask 32) ;; set MASK register. + (strow 48) ;; set filling data + (stcol 49) ;; set filling data + (mpg 74) ;; transfer microprogram + (direct 80) ;; straight to GIF. + (directhl 81) + (unpack-s-32 96) + (unpack-s-16 97) + (unpack-s-8 98) + ;; 99 is invalid + (unpack-v2-32 100) + (unpack-v2-16 101) + (unpack-v2-8 102) + ;; 103 is invalid + (unpack-v3-32 104) + (unpack-v3-16 105) + (unpack-v3-8 106) + ;; 107 is invalid + (unpack-v4-32 108) + (unpack-v4-16 109) + (unpack-v4-8 110) + (unpack-v4-5 111) + (cmd-mask 239) ;; not sure what this is + ) + +(defenum vif-cmd-32 + :bitfield #f + :type uint32 + :copy-entries vif-cmd + ) + (deftype vif-tag (uint32) - () + ((imm uint16 :offset 0 :size 16) + (num uint8 :offset 16 :size 8) + (cmd vif-cmd :offset 24 :size 7) + (irq uint8 :offset 31 :size 1) + (msk uint8 :offset 28 :size 1) + ) :method-count-assert 9 :size-assert #x4 :flag-assert #x900000004 - ;; Failed to read some fields. ) -|# ;; (define-extern dma-sync-fast object) ;; (function dma-bank none) ;; (define-extern dma-send-no-scratch object) ;; (function dma-bank uint32 uint32 none) @@ -2555,7 +2636,6 @@ ;; profile-h ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -#| (deftype profile-segment (structure) ((name basic :offset-assert 0) (start-time int16 :offset-assert 4) @@ -2564,50 +2644,49 @@ (vu-count uint8 :offset-assert 9) (depth uint16 :offset-assert 10) (color uint32 :offset-assert 12) - (code-time uint16 :offset-assert 4) - (vu-time uint16 :offset-assert 6) + (code-time uint16 :offset 4) + (vu-time uint16 :offset 6) ) + :allow-misaligned :method-count-assert 9 :size-assert #x10 :flag-assert #x900000010 ) -|# -#| + (deftype profile-collapse (structure) ((count int32 :offset-assert 0) - (data UNKNOWN 48 :offset-assert 4) + (data profile-segment 48 :inline :offset-assert 4) ) :method-count-assert 9 :size-assert #x304 :flag-assert #x900000304 ) -|# -#| + (deftype profile-segment-array (basic) ((count int16 :offset-assert 4) (depth int8 :offset-assert 6) (max-depth int8 :offset-assert 7) (base-time int16 :offset-assert 8) - (segment UNKNOWN 9 :offset-assert 12) - (data UNKNOWN 512 :offset-assert 48) + (segment basic 9 :offset-assert 12) ;; todo + (data profile-segment 512 :inline :offset-assert 48) ) :method-count-assert 13 :size-assert #x2030 :flag-assert #xd00002030 (:methods - (dummy-9 () none 9) + (get-total-time (_type_) int 9) (dummy-10 () none 10) (dummy-11 () none 11) (dummy-12 () none 12) ) ) -|# -#| + + (deftype profile-array (structure) - ((data UNKNOWN 2 :offset-assert 0) + ((data profile-segment-array 2 :offset-assert 0) ;; guess ) :method-count-assert 12 :size-assert #x8 @@ -2618,45 +2697,45 @@ (dummy-11 () none 11) ) ) -|# -;; (define-extern *profile-gap-color* object) -;; (define-extern *profile-all-color* object) -;; (define-extern *profile-particles-color* object) -;; (define-extern *profile-target-color* object) -;; (define-extern *profile-target-post-color* object) -;; (define-extern *profile-joints-color* object) -;; (define-extern *profile-debug-color* object) -;; (define-extern *profile-draw-hook-color* object) -;; (define-extern *profile-sky-color* object) -;; (define-extern *profile-ocean-color* object) -;; (define-extern *profile-background-color* object) -;; (define-extern *profile-bsp-color* object) -;; (define-extern *profile-foreground-color* object) -;; (define-extern *profile-tfrag-color* object) -;; (define-extern *profile-instance-tie-color* object) -;; (define-extern *profile-instance-shrubbery-color* object) -;; (define-extern *profile-generic-tie-color* object) -;; (define-extern *profile-bones-color* object) -;; (define-extern *profile-generic-merc-color* object) -;; (define-extern *profile-shadow-color* object) -;; (define-extern *profile-update-actors-color* object) -;; (define-extern *profile-menu-hook-color* object) -;; (define-extern *profile-texture-color* object) -;; (define-extern *profile-effects-color* object) -;; (define-extern *profile-sprite-color* object) -;; (define-extern *profile-merc-color* object) -;; (define-extern *profile-actors-color* object) -;; (define-extern *profile-collide-color* object) -;; (define-extern *profile-nav-color* object) -;; (define-extern *profile-camera-color* object) -;; (define-extern *profile-blit-color* object) -;; (define-extern *profile-hud-color* object) -;; (define-extern *profile-emerc-color* object) -;; (define-extern *profile-array* object) -;; (define-extern *profile-collapse* object) -;; (define-extern *profile-interrupt-segment* object) -;; (define-extern *profile-interrupt-start* object) + +(define-extern *profile-gap-color* rgba) +(define-extern *profile-all-color* rgba) +(define-extern *profile-particles-color* rgba) +(define-extern *profile-target-color* rgba) +(define-extern *profile-target-post-color* rgba) +(define-extern *profile-joints-color* rgba) +(define-extern *profile-debug-color* rgba) +(define-extern *profile-draw-hook-color* rgba) +(define-extern *profile-sky-color* rgba) +(define-extern *profile-ocean-color* rgba) +(define-extern *profile-background-color* rgba) +(define-extern *profile-bsp-color* rgba) +(define-extern *profile-foreground-color* rgba) +(define-extern *profile-tfrag-color* rgba) +(define-extern *profile-instance-tie-color* rgba) +(define-extern *profile-instance-shrubbery-color* rgba) +(define-extern *profile-generic-tie-color* rgba) +(define-extern *profile-bones-color* rgba) +(define-extern *profile-generic-merc-color* rgba) +(define-extern *profile-shadow-color* rgba) +(define-extern *profile-update-actors-color* rgba) +(define-extern *profile-menu-hook-color* rgba) +(define-extern *profile-texture-color* rgba) +(define-extern *profile-effects-color* rgba) +(define-extern *profile-sprite-color* rgba) +(define-extern *profile-merc-color* rgba) +(define-extern *profile-actors-color* rgba) +(define-extern *profile-collide-color* rgba) +(define-extern *profile-nav-color* rgba) +(define-extern *profile-camera-color* rgba) +(define-extern *profile-blit-color* rgba) +(define-extern *profile-hud-color* rgba) +(define-extern *profile-emerc-color* rgba) +(define-extern *profile-array* profile-array) +(define-extern *profile-collapse* profile-collapse) +(define-extern *profile-interrupt-segment* profile-segment-array) +(define-extern *profile-interrupt-start* symbol) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; dma ;; @@ -2686,18 +2765,17 @@ ;; dma-buffer ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -#| + (deftype dma-packet (structure) - ((dma uint64 :offset-assert 0) ;; dma-tag - (vif0 vif-tag :offset-assert 8) ;; guessed by decompiler - (vif1 vif-tag :offset-assert 12) ;; guessed by decompiler - (quad uint128 :offset-assert 0) + ((dma dma-tag :offset-assert 0) + (vif0 vif-tag :offset-assert 8) + (vif1 vif-tag :offset-assert 12) + (quad uint128 :offset 0) ) :method-count-assert 9 :size-assert #x10 :flag-assert #x900000010 ) -|# #| (deftype dma-packet-array (inline-array-class) @@ -2709,41 +2787,37 @@ ) |# -#| (deftype dma-gif (structure) - ((gif UNKNOWN 2 :offset-assert 0) - (quad uint128 :offset-assert 0) + ((gif uint64 2 :offset-assert 0) + (quad uint128 :offset 0) ) :method-count-assert 9 :size-assert #x10 :flag-assert #x900000010 ) -|# -#| (deftype dma-gif-packet (structure) ((dma-vif dma-packet :inline :offset-assert 0) - (gif uint64 2 :offset-assert 16) ;; guessed by decompiler - (quad uint128 2 :offset-assert 0) ;; guessed by decompiler + (gif uint64 2 :offset-assert 16) + (quad uint128 2 :offset 0) ) :method-count-assert 9 :size-assert #x20 :flag-assert #x900000020 ) -|# -#| + (deftype dma-buffer (basic) ((allocated-length int32 :offset-assert 4) - (base pointer :offset-assert 8) ;; guessed by decompiler - (end pointer :offset-assert 12) ;; guessed by decompiler - (data uint64 1 :offset-assert 16) ;; guessed by decompiler + (base pointer :offset-assert 8) + (end pointer :offset-assert 12) + (data uint64 1 :offset-assert 16) ) :method-count-assert 9 :size-assert #x18 :flag-assert #x900000018 ) -|# + ;; (define-extern dma-buffer-inplace-new object) ;; (function dma-buffer int dma-buffer) ;; (define-extern dma-buffer-length object) ;; (function dma-buffer int) @@ -3140,14 +3214,13 @@ ;; display-h ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -#| (deftype display-frame (basic) ((buffer dma-buffer 11 :offset-assert 4) ;; guessed by decompiler - (calc-buf dma-buffer :offset-assert 8) ;; guessed by decompiler - (vu1-buf dma-buffer :offset-assert 8) ;; guessed by decompiler - (debug-buf dma-buffer :offset-assert 36) ;; guessed by decompiler - (global-buf dma-buffer :offset-assert 40) ;; guessed by decompiler - (bucket-group dma-bucket :offset-assert 44) ;; (inline-array dma-bucket) + (calc-buf dma-buffer :offset 8) + (vu1-buf dma-buffer :offset 8) + (debug-buf dma-buffer :offset 36) + (global-buf dma-buffer :offset 40) + (bucket-group dma-bucket :offset 44) ;; (inline-array dma-bucket) (profile-array profile-array :inline :offset-assert 48) (start-time uint64 :offset-assert 56) (run-time uint64 :offset-assert 64) ;; int64 @@ -3156,32 +3229,30 @@ :size-assert #x48 :flag-assert #x900000048 ) -|# -#| (deftype display (basic) ((on-screen int32 :offset-assert 4) (last-screen int32 :offset-assert 8) - (frames virtual-frame 2 :offset-assert 12) ;; guessed by decompiler + (frames display-frame 2 :offset-assert 12) (bgcolor uint64 :offset-assert 24) (pmode uint64 :offset-assert 32) - (clock UNKNOWN 13 :offset-assert 40) - (session-clock basic :offset-assert 40) - (game-clock basic :offset-assert 44) - (base-clock basic :offset-assert 48) - (real-clock basic :offset-assert 52) - (frame-clock basic :offset-assert 56) - (real-frame-clock basic :offset-assert 60) - (target-clock basic :offset-assert 64) - (entity-clock basic :offset-assert 68) - (part-clock basic :offset-assert 72) - (bg-clock basic :offset-assert 76) - (camera-clock basic :offset-assert 80) - (user0-clock basic :offset-assert 84) - (total-game-clock basic :offset-assert 88) + (clock clock 13 :offset-assert 40) + (session-clock clock :offset 40) + (game-clock clock :offset 44) + (base-clock clock :offset 48) + (real-clock clock :offset 52) + (frame-clock clock :offset 56) + (real-frame-clock clock :offset 60) + (target-clock clock :offset 64) + (entity-clock clock :offset 68) + (part-clock clock :offset 72) + (bg-clock clock :offset 76) + (camera-clock clock :offset 80) + (user0-clock clock :offset 84) + (total-game-clock clock :offset 88) (time-factor float :offset-assert 92) (dog-ratio float :offset-assert 96) - (vblank-start-time UNKNOWN 2 :offset-assert 104) + (vblank-start-time uint64 2 :offset-assert 104) (total-run-time uint64 :offset-assert 120) (run-half-speed basic :offset-assert 128) (dog-count float :offset-assert 132) @@ -3197,7 +3268,6 @@ (dummy-9 () none 9) ;; (set-time-ratios (_type_ float) float 9) ) ) -|# ;; (define-extern *pre-draw-hook* object) ;; (function object none) ;; (define-extern *post-draw-hook* object) ;; (function dma-buffer none) @@ -3264,17 +3334,17 @@ ;; timer ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; (define-extern timer-count object) ;; (function timer-bank uint) -;; (define-extern disable-irq object) ;; (function none) -;; (define-extern enable-irq object) ;; (function none) -;; (define-extern stopwatch-init object) ;; (function stopwatch int) -;; (define-extern stopwatch-reset object) ;; (function stopwatch int) -;; (define-extern stopwatch-start object) ;; (function stopwatch int) -;; (define-extern stopwatch-stop object) ;; (function stopwatch none) -;; (define-extern stopwatch-begin object) ;; (function stopwatch int) -;; (define-extern stopwatch-end object) ;; (function stopwatch none) -;; (define-extern stopwatch-elapsed-ticks object) ;; (function stopwatch time-frame) -;; (define-extern stopwatch-elapsed-seconds object) ;; (function stopwatch float) +(define-extern timer-count (function timer-bank uint)) +(define-extern disable-irq (function none)) +(define-extern enable-irq (function none)) +(define-extern stopwatch-init (function stopwatch int)) +(define-extern stopwatch-reset (function stopwatch int)) +(define-extern stopwatch-start (function stopwatch int)) +(define-extern stopwatch-stop (function stopwatch none)) +(define-extern stopwatch-begin (function stopwatch int)) +(define-extern stopwatch-end (function stopwatch none)) +(define-extern stopwatch-elapsed-ticks (function stopwatch time-frame)) +(define-extern stopwatch-elapsed-seconds (function stopwatch float)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; vector ;; @@ -4823,7 +4893,7 @@ ;; profile ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -#| + (deftype profile-work (structure) ((sprite-tmpl dma-gif-packet :inline :offset-assert 0) (line-tmpl dma-gif-packet :inline :offset-assert 32) @@ -4833,14 +4903,13 @@ :size-assert #x44 :flag-assert #x900000044 ) -|# -;; (define-extern *profile-work* object) -;; (define-extern *profile-x* object) ;; int -;; (define-extern *profile-y* object) ;; int -;; (define-extern *profile-w* object) ;; int -;; (define-extern *profile-h* object) ;; int -;; (define-extern *profile-ticks* object) ;; symbol +(define-extern *profile-work* profile-work) +(define-extern *profile-x* int) +(define-extern *profile-y* int) +(define-extern *profile-w* int) +(define-extern *profile-h* int) +(define-extern *profile-ticks* symbol) ;; (define-extern profile-texture-test object) ;; (define-extern profile-tfrag-test object) ;; (define-extern profile-tie-test object) @@ -4868,7 +4937,7 @@ ;; (define-extern set-display-gs-state-offset object) ;; (function dma-buffer int int int int int int int dma-buffer) ;; (define-extern reset-display-gs-state object) ;; (function display dma-buffer int display) ;; (define-extern *vu0-dma-list* object) ;; dma-buffer -;; (define-extern *display* object) ;; display +(define-extern *display* display) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; connect ;; diff --git a/decompiler/config/jak2/label_types.jsonc b/decompiler/config/jak2/label_types.jsonc index 9e26dfeeb6..f14f1120e6 100644 --- a/decompiler/config/jak2/label_types.jsonc +++ b/decompiler/config/jak2/label_types.jsonc @@ -1 +1,5 @@ -{} \ No newline at end of file +{ + "profile": [ + ["L14", "profile-work"] + ] +} \ No newline at end of file diff --git a/goal_src/jak2/dgos/kernel.gd b/goal_src/jak2/dgos/kernel.gd new file mode 100644 index 0000000000..4df9a0e142 --- /dev/null +++ b/goal_src/jak2/dgos/kernel.gd @@ -0,0 +1,10 @@ +("KERNEL.CGO" + ("gcommon.o" "gcommon") + ("gstring-h.o" "gstring-h") + ("gkernel-h.o" "gkernel-h") + ("gkernel.o" "gkernel") + ("pskernel.o" "pskernel") + ("gstring.o" "gstring") + ("dgo-h.o" "dgo-h") + ("gstate.o" "gstate") + ) \ No newline at end of file diff --git a/goal_src/jak2/engine/ps2/timer-h.gc b/goal_src/jak2/engine/ps2/timer-h.gc index f64e64ba98..1cc09f6af6 100644 --- a/goal_src/jak2/engine/ps2/timer-h.gc +++ b/goal_src/jak2/engine/ps2/timer-h.gc @@ -5,3 +5,89 @@ ;; name in dgo: timer-h ;; dgos: ENGINE, GAME +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; PC Port Timer +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(defmacro get-cpu-clock () + "Read the 300 MHz clock." + ;; __read-ee-timer is a 300 MHz timer from the C Kernel. + ;; it's a real timer. + `(the uint (logand #xffffffff (__read-ee-timer))) + ) + +(defmacro get-bus-clock/256 () + "Read the 150 MHz / 256 clock." + ;; 300 MHz / (2^9) + `(the uint (logand #xffffffff (shr (__read-ee-timer) 9))) + ) + +(#when PC_PORT + ;; the bus clock can be reset, which just stores the current count here. + (define *timer-reset-value* (the uint 0)) + ) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Timer HW +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(deftype timer-mode (uint32) + ((clks timer-clock-selection :offset 0 :size 2) + (gate uint8 :offset 2 :size 1) + (gats uint8 :offset 3 :size 1) + (gatm uint8 :offset 4 :size 2) + (zret uint8 :offset 6 :size 1) + (cue uint8 :offset 7 :size 1) + (cmpe uint8 :offset 8 :size 1) + (ovfe uint8 :offset 9 :size 1) + (equf uint8 :offset 10 :size 1) + (ovff uint8 :offset 11 :size 1) + ) + :method-count-assert 9 + :size-assert #x4 + :flag-assert #x900000004 + ) + +(deftype timer-bank (structure) + ((count uint32 :offset-assert 0) + (mode timer-mode :offset 16) + (comp uint32 :offset 32) + ) + :method-count-assert 9 + :size-assert #x24 + :flag-assert #x900000024 + ) + +(deftype timer-hold-bank (timer-bank) + ((hold uint32 :offset 48) + ) + :method-count-assert 9 + :size-assert #x34 + :flag-assert #x900000034 + ) + +(deftype stopwatch (basic) + ((prev-time-elapsed time-frame :offset-assert 8) + (start-time time-frame :offset-assert 16) + (begin-level int32 :offset-assert 24) + ) + :method-count-assert 9 + :size-assert #x1c + :flag-assert #x90000001c + ) + +(define *ticks-per-frame* 9765) + +(defun timer-init ((arg0 timer-bank) (arg1 timer-mode)) + (set! (-> arg0 mode) arg1) + (set! (-> arg0 count) (the-as uint 0)) + 0 + ) + +(#unless PC_PORT + (timer-init + (the-as timer-bank #x10000800) + (new 'static 'timer-mode :clks (timer-clock-selection busclk/256) :cue #x1) + ) + ) + + diff --git a/goal_src/jak2/game.gp b/goal_src/jak2/game.gp index 911d56dc39..8e14c734e7 100644 --- a/goal_src/jak2/game.gp +++ b/goal_src/jak2/game.gp @@ -173,4 +173,9 @@ ,@(reverse *all-mus*) ,@(reverse *all-vag*) ,@(reverse *all-cgos*)) - ) \ No newline at end of file + ) + +;; used for the type consistency test. +(group-list "all-code" + `(,@(reverse *all-gc*)) + ) diff --git a/goal_src/jak2/kernel-defs.gc b/goal_src/jak2/kernel-defs.gc index 7c531d76b6..3dea01d128 100644 --- a/goal_src/jak2/kernel-defs.gc +++ b/goal_src/jak2/kernel-defs.gc @@ -5,6 +5,26 @@ ;;;; kscheme - InitHeapAndSymbol ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(defenum kmalloc-flags + :bitfield #t + (align-16 4) + (align-64 6) + (align-256 8) + (memset 12) + (top 13) + ) + +(defenum link-flag + :bitfield #t + :type int32 + (output-load-msg 0) + (output-load-true-msg 1) + (execute-login 2) + (print-login 3) + (force-debug 4) + (fast-link 5) + ) + ;; fixed symbols (define-extern #f symbol) (define-extern #t symbol) @@ -59,32 +79,19 @@ ;; InitHeapAndSymbol (define-extern _format (function _varargs_ object)) (define-extern method-set! (function type int object none)) ;; may actually return function. +(define-extern kmemopen (function kheap string none)) +(define-extern kmemclose (function none)) (define-extern *enable-method-set* int) (define-extern *listener-function* (function object)) (define-extern *debug-segment* symbol) +(define-extern dgo-load (function string kheap link-flag int none)) +(define-extern malloc (function symbol int pointer)) + -(defenum kmalloc-flags - :bitfield #t - (align-16 4) - (align-64 6) - (align-256 8) - (memset 12) - (top 13) - ) -(defenum link-flag - :bitfield #t - :type int32 - (output-load-msg 0) - (output-load-true-msg 1) - (execute-login 2) - (print-login 3) - (force-debug 4) - (fast-link 5) - ) ;; PC stuff diff --git a/goal_src/jak2/kernel/dgo-h.gc b/goal_src/jak2/kernel/dgo-h.gc index 16372eff4d..8dd2cbed43 100644 --- a/goal_src/jak2/kernel/dgo-h.gc +++ b/goal_src/jak2/kernel/dgo-h.gc @@ -5,3 +5,23 @@ ;; name in dgo: dgo-h ;; dgos: KERNEL +(deftype dgo-entry (structure) + ((offset uint32 :offset-assert 0) + (length uint32 :offset-assert 4) + ) + :method-count-assert 9 + :size-assert #x8 + :flag-assert #x900000008 + ) + + +(deftype dgo-file (basic) + ((num-go-files uint32 :offset-assert 4) + (total-length uint32 :offset-assert 8) + (rsvd uint32 :offset-assert 12) + (data uint8 :dynamic :offset-assert 16) + ) + :method-count-assert 9 + :size-assert #x10 + :flag-assert #x900000010 + ) diff --git a/goal_src/jak2/kernel/gcommon.gc b/goal_src/jak2/kernel/gcommon.gc index 0d8ea9a33b..27ba1a7678 100644 --- a/goal_src/jak2/kernel/gcommon.gc +++ b/goal_src/jak2/kernel/gcommon.gc @@ -24,6 +24,23 @@ ;; GOAL code to the frame profiler in C++. (defglobalconstant PC_PROFILER_ENABLE #t) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; GOAL language constants +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; distance from a symbol pointer to a (pointer string) +;; this relies on the memory layout of the symbol table +;; this must match SYM_TO_STRING_OFFSET in goal_constants.h +(defconstant SYM_TO_STRING_OFFSET #xff37) + +(defmacro symbol->string (sym) + "Convert a symbol to a goal string." + `(-> (the-as (pointer string) (+ SYM_TO_STRING_OFFSET (the-as int ,sym)))) + ) + +;; pointers larger than this are invalid by valid? +(defconstant END_OF_MEMORY #x8000000) + (defun identity ((arg0 object)) arg0 ) @@ -170,6 +187,19 @@ obj ) +(deftype vector (structure) + ((data float 4 :offset-assert 0) + (x float :offset 0) + (y float :offset 4) + (z float :offset 8) + (w float :offset 12) + (quad uint128 :offset 0) + ) + :method-count-assert 9 + :size-assert #x10 + :flag-assert #x900000010 + ) + (defmacro print128 (value &key (stream #t)) "Print a 128-bit value" `(let ((temp (new 'stack-no-clear 'array 'uint64 2))) @@ -911,6 +941,208 @@ (break!) ) +(defmacro start-of-symbol-table () + `(rlet ((st :reg r14 :reset-here #t :type uint)) + (the uint (- st 32768)) + ) + ) + +(defmacro end-of-symbol-table () + `(rlet ((st :reg r14 :reset-here #t :type uint)) + (the uint (+ st 32768)) + ) + ) + +(define-extern boolean type) ;; not really... but they use it here as if it was one. +(define-extern valid? (function object type symbol symbol object symbol)) + +(defun valid? ((arg0 object) (arg1 type) (arg2 symbol) (arg3 symbol) (arg4 object)) + (let ((v1-1 + (and (>= (the-as uint arg0) (start-of-symbol-table)) (< (the-as uint arg0) END_OF_MEMORY)) + ) + ) + (cond + ((not arg1) + (cond + ((logtest? (the-as int arg0) 3) + (if arg2 + (format arg4 "ERROR: object #x~X ~S is not a valid object (misaligned)~%" arg0 arg2) + ) + #f + ) + ((not v1-1) + (if arg2 + (format arg4 "ERROR: object #x~X ~S is not a valid object (bad address)~%" arg0 arg2) + ) + #f + ) + (else + #t + ) + ) + ) + ((and arg3 (not arg0)) + #t + ) + ((= arg1 structure) + (cond + ((logtest? (the-as int arg0) 15) + (if arg2 + (format arg4 "ERROR: object #x~X ~S is not a valid object of type '~A' (misaligned)~%" arg0 arg2 arg1) + ) + #f + ) + ((or (not v1-1) (< (the-as uint arg0) (end-of-symbol-table))) + (if arg2 + (format arg4 "ERROR: object #x~X ~S is not a valid object of type '~A' (bad address)~%" arg0 arg2 arg1) + ) + #f + ) + (else + #t + ) + ) + ) + ((= arg1 pair) + (cond + ((not (pair? arg0)) + (if arg2 + (format arg4 "ERROR: object #x~X ~S is not a valid object of type '~A' (misaligned)~%" arg0 arg2 arg1) + ) + #f + ) + ((not v1-1) + (if arg2 + (format arg4 "ERROR: object #x~X ~S is not a valid object of type '~A' (bad address)~%" arg0 arg2 arg1) + ) + #f + ) + (else + #t + ) + ) + ) + ((= arg1 binteger) + (cond + ((zero? (logand (the-as int arg0) 7)) + #t + ) + (else + (if arg2 + (format arg4 "ERROR: object #x~X ~S is not a valid object of type '~A' (misaligned)~%" arg0 arg2 arg1) + ) + #f + ) + ) + ) + ((or (= arg1 symbol) (= arg1 boolean)) + (cond + ((zero? (logand (the-as int arg0) 1)) + (if arg2 + (format arg4 "ERROR: object #x~X ~S is not a valid object of type '~A' (misaligned)~%" arg0 arg2 arg1) + ) + #f + ) + ((or (not v1-1) (< (the-as int arg0) (start-of-symbol-table))(>= (the-as int arg0) (end-of-symbol-table))) + (if arg2 + (format arg4 "ERROR: object #x~X ~S is not a valid object of type '~A' (bad address)~%" arg0 arg2 arg1) + ) + #f + ) + (else + #t + ) + ) + ) + ((!= (logand (the-as int arg0) 7) 4) + (if arg2 + (format arg4 "ERROR: object #x~X ~S is not a valid object of type '~A' (misaligned)~%" arg0 arg2 arg1) + ) + #f + ) + ((not v1-1) + (if arg2 + (format arg4 "ERROR: object #x~X ~S is not a valid object of type '~A' (bad address)~%" arg0 arg2 arg1) + ) + #f + ) + ((and (= arg1 type) (!= (rtype-of arg0) type)) + (if arg2 + (format + arg4 + "ERROR: object #x~X ~S is not a valid object of type '~A' (invalid type #x~X)~%" + arg0 + arg2 + arg1 + (rtype-of arg0) + ) + ) + #f + ) + ((and (!= arg1 type) (not (valid? (rtype-of arg0) type #f #t 0))) + (if arg2 + (format + arg4 + "ERROR: object #x~X ~S is not a valid object of type '~A' (invalid type #x~X)~%" + arg0 + arg2 + arg1 + (rtype-of arg0) + ) + ) + #f + ) + ((not (type? arg0 arg1)) + (if arg2 + (format + arg4 + "ERROR: object #x~X ~S is not a valid object of type '~A' (is type '~A' instead)~%" + arg0 + arg2 + arg1 + (rtype-of arg0) + ) + ) + #f + ) + ((= arg1 symbol) + (cond + ((>= (the-as uint arg0) (end-of-symbol-table)) + (if arg2 + (format + arg4 + "ERROR: object #x~X ~S is not a valid object of type '~A' (not in symbol table)~%" + arg0 + arg2 + arg1 + ) + ) + #f + ) + (else + #t + ) + ) + ) + ((< (the-as uint arg0) (end-of-symbol-table)) + (if arg2 + (format + arg4 + "ERROR: object #x~X ~S is not a valid object of type '~A' (inside symbol table)~%" + arg0 + arg2 + arg1 + ) + ) + #f + ) + (else + #t + ) + ) + ) + ) + ;;;;;;;;;;;;;;;;;;;; ;; Profiler Macros ;;;;;;;;;;;;;;;;;;;; diff --git a/goal_src/jak2/kernel/gkernel-h.gc b/goal_src/jak2/kernel/gkernel-h.gc index 01392d94c5..9d2d488d35 100644 --- a/goal_src/jak2/kernel/gkernel-h.gc +++ b/goal_src/jak2/kernel/gkernel-h.gc @@ -6,4 +6,687 @@ ;; dgos: KERNEL (defconstant *kernel-major-version* 2) -(defconstant *kernel-minor-version* 0) \ No newline at end of file +(defconstant *kernel-minor-version* 0) + +(defconstant DPROCESS_STACK_SIZE (#if PC_PORT #x8000 #x3800)) +(defconstant PROCESS_STACK_SIZE (#if PC_PORT #x6000 #x1c00)) + +(defconstant *tab-size* (the binteger 8)) +(defconstant *gtype-basic-offset* 4) + +;; if set, will attempt to detect memory corruption and stack overflow bugs +;; to some extent. +(defglobalconstant KERNEL_DEBUG #t) + +(defconstant *scratch-memory-top* (the pointer #x70004000)) + + +;; Each process has a bitmask. +;; The kernel can be configured to skip processes with certain mask bits set. +(defenum process-mask + :type uint32 + :bitfield #t + (execute 0) + (freeze 1) + (pause 2) + (menu 3) + (progress 4) + (actor-pause 5) + (sleep 6) + (sleep-code 7) + (process-tree 8) + (heap-shrunk 9) + (going 10) + (kernel-run 11) + (no-kill 12) + (movie 13) + (dark-effect 14) + (target 15) + (sidekick 16) + (crate 17) + (bit18 18) ;; unused? + (enemy 19) + (camera 20) + (platform 21) + (ambient 22) + (entity 23) + (projectile 24) + (bot 25) + (collectable 26) + (death 27) + (no-track 28) + (guard 29) + (vehicle 30) + (civilian 31) + ) + +;; forward declarations +(declare-type process-tree basic) +(declare-type process process-tree) +(declare-type entity basic) +(declare-type entity-actor entity) +(declare-type dead-pool basic) +(declare-type level basic) +(declare-type state basic) +(declare-type event-message-block structure) +(declare-type stack-frame basic) +(declare-type cpu-thread basic) + + +;; The state of the kernel, containing the masks to allow/deny certain processes, +;; the currently running process, and the currently relocating process. +(deftype kernel-context (basic) + ((prevent-from-run process-mask :offset-assert 4) + (require-for-run process-mask :offset-assert 8) + (allow-to-run process-mask :offset-assert 12) + (next-pid int32 :offset-assert 16) + (fast-stack-top pointer :offset-assert 20) + (current-process process :offset-assert 24) + (relocating-process basic :offset-assert 28) + (relocating-min int32 :offset-assert 32) + (relocating-max int32 :offset-assert 36) + (relocating-offset int32 :offset-assert 40) + (relocating-level level :offset-assert 44) + (low-memory-message symbol :offset-assert 48) + (login-object basic :offset-assert 52) + ) + :method-count-assert 9 + :size-assert #x38 + :flag-assert #x900000038 + ) + +;; The usual "time" type. +(deftype time-frame (int64) + () + :method-count-assert 9 + :size-assert #x8 + :flag-assert #x900000008 + ) + +;; times are stored in 300ths of a second. +;; this divides evenly into frames at both 50 and 60 fps. +;; typically these are stored as integers as more precision is not useful. +;; an unsigned 32-bit integer can store about 150 days +(defglobalconstant TICKS_PER_SECOND 300) ;; 5 t/frame @ 60fps, 6 t/frame @ 50fps + +;; this was usec in GOAL +(defmacro seconds (x) + "Convert number to seconds unit. + Returns uint." + (cond + ((integer? x) + (* TICKS_PER_SECOND x) + ) + ((float? x) + (* 1 (* 1.0 x TICKS_PER_SECOND)) + ) + (#t + `(the uint (* TICKS_PER_SECOND ,x)) + ) + ) + ) + +;; Each clock counts in 3 different ways: +;; +;; 1). A "frame counter", which, confusingly, doesn't count frames. +;; It counts elapsed time, in 1/300ths of a second. +;; This counts in real-time, even if the game is lagging. +;; +;; 2). A "integral-frame-counter", which counts the number of vsyncs. +;; This doens't count the number of frames the game actually manages to draw, +;; just the number of vsyncs. It counts at different rates in NTSC/PAL. +;; NOTE: changing clock-ratio will make this count faster/slower. This only counts real +;; vsyncs if clock-ratio is 1.0. +;; +;; 3). The "time ratio", which adjusts based on the actual achieved framerate. +;; Unlock the others, this isn't a incrementing counter, but instead ratios: +;; time-adjust-ratio, frames-per-second, seconds-per-frame. +;; +;; +;; The clock won't tick if its process-mask is prevent-from-run in the kernel. +;; A clock can change the rate it runs at with clock-ratio. +;; Note: both integral-frame-counter and seconds-per-frame/frames-per-second are affected by +;; clock-ratio, which is somewhat weird. +;; Changing clock-ratio will make integral-frame-counter not count actual vsyncs + +(deftype clock (basic) + ((index int32 :offset-assert 4) ;; which clock we are, in *display* + (mask process-mask :offset-assert 8) ;; mask for ticking + (clock-ratio float :offset-assert 12) ;; how fast to run. 1.0 = realtime. + (accum float :offset-assert 16) ;; fractional time for frame-counter (time-frame units) + (integral-accum float :offset-assert 20) ;; fractional time for integral (time-frame untis) + (frame-counter time-frame :offset-assert 24) ;; how much time has gone by since reset (time-frame units) + (old-frame-counter time-frame :offset-assert 32) ;; the frame-counter on the last engine iteration + (integral-frame-counter uint64 :offset-assert 40) ;; how many vsyncs have gone by since reset + (old-integral-frame-counter uint64 :offset-assert 48) ;; the integral-frame-counter on the last engine iteration + (sparticle-data vector :inline :offset-assert 64) ;; sparticle timescale info + (seconds-per-frame float :offset-assert 80) ;; how many seconds (not time-frames) should go by in 1 vsync + (frames-per-second float :offset-assert 84) ;; inverse of above + (time-adjust-ratio float :offset-assert 88) ;; 1, if the game runs at 60fps NTSC with clock-ratio = 1. + ) + :method-count-assert 15 + :size-assert #x5c + :flag-assert #xf0000005c + (:methods + (new (symbol type int) _type_ 0) + (update-rates! (_type_ float) none 9) + (advance-by! (_type_ float) none 10) + (tick! (_type_) none 11) + (save! (_type_ (pointer uint64)) int 12) + (load! (_type_ (pointer uint64)) int 13) + (reset! (_type_) none 14) + ) + ) + +(defmethod new clock ((allocation symbol) (type-to-make type) (arg0 int)) + (let ((gp-0 (object-new allocation type-to-make (the-as int (-> type-to-make size))))) + (set! (-> gp-0 index) arg0) + (set! (-> gp-0 frame-counter) (seconds 1000)) + (set! (-> gp-0 integral-frame-counter) (the-as uint 300000)) + (set! (-> gp-0 old-frame-counter) (+ (-> gp-0 frame-counter) -1)) + (set! (-> gp-0 old-integral-frame-counter) (+ (-> gp-0 integral-frame-counter) -1)) + (update-rates! gp-0 1.0) + gp-0 + ) + ) + +;; The basic node used to organize processes into a tree. +;; The process types themselves are children of the process-tree type +;; Typically, each instance of a game object is a process. +(deftype process-tree (basic) + ((name string :offset-assert 4) + (mask process-mask :offset-assert 8) + (clock clock :offset-assert 12) + (parent (pointer process-tree) :offset-assert 16) + (brother (pointer process-tree) :offset-assert 20) + (child (pointer process-tree) :offset-assert 24) + (ppointer (pointer process) :offset-assert 28) + (self process-tree :offset-assert 32) + ) + (:methods + (new (symbol type string) _type_ 0) + (activate (_type_ process-tree basic pointer) process-tree 9) + (deactivate (_type_) none 10) + (init-from-entity! (_type_ entity-actor) none 11) ;; todo check + (run-logic? (_type_) symbol 12) + (dummy-13 () none 13) + ) + :size-assert #x24 + :method-count-assert 14 + :no-runtime-type + ) + +;; Each process has a single "main" thread that is suspended and resumed. +;; The "thread" object is what holds the needed state to start, suspend, and resume execution. +;; Additionally, the kernel creates various temporary threads to run single functions. +;; These "temporary" threads are never suspended. + +;; unlike modern implementations, the "thread" objects store small "backup" stacks (often only 100's of bytes). +;; when a thread is suspended, it copies the stack from the execution stack to the backup stack. +;; this seems silly, but it has an advantage to reduce memory - typically threads suspend without a very deep call +;; stack, so the backup stack can be much, much smaller than a single large, shared execution stack. +(deftype thread (basic) + ((name symbol :offset-assert 4) + (process process :offset-assert 8) + (previous thread :offset-assert 12) + (suspend-hook (function cpu-thread none) :offset-assert 16) ;; called by user to suspend + (resume-hook (function cpu-thread none) :offset-assert 20) ;; called by kernel to resume + (pc pointer :offset-assert 24) ;; pc (x86 rip) to resume to + (sp pointer :offset-assert 28) ;; stack pointer of thread + (stack-top pointer :offset-assert 32) ;; stack to execute on + (stack-size int32 :offset-assert 36) ;; size of _suspend_ stack + ) + :method-count-assert 12 + :size-assert #x28 + :flag-assert #xc00000028 + (:methods + (stack-size-set! (_type_ int) none 9) + (thread-suspend (_type_) none 10) + (thread-resume (_type_) none 11) + ) + ) + +;; additional information to context switch +(deftype cpu-thread (thread) + ((rreg uint64 7 :offset-assert 40) ;; GPRs + (freg float 8 :offset-assert 96) ;; FPRs + (stack uint8 :dynamic :offset-assert 128) ;; backup stack (dynamically sized) + ) + :method-count-assert 12 + :size-assert #x80 + :flag-assert #xc00000080 + (:methods + (new (symbol type process symbol int pointer) _type_ 0) + ) + ) + +;; Base type for all actual processes. +;; this can be used directly, or child types can be made. +(deftype process (process-tree) + ((pool dead-pool ) ;; where to return us when we die + (status symbol :offset-assert 40) ;; used by kernel to track init/death + (pid int32 ) ;; globally unique ID, never reused for another + (main-thread cpu-thread :offset-assert 48) ;; suspendable main thread + (top-thread cpu-thread :offset-assert 52) ;; currently running thread + (entity entity :offset-assert 56) ;; if we were spawned from an entity, that entity + (level level :offset-assert 60) ;; if we're associated with a level, that level + (state state :offset-assert 64) ;; current state, if we're in one + (next-state state :offset-assert 68) ;; set if we have a pending (go) + (trans-hook function :offset-assert 72) ;; function to run before resuming + (post-hook function :offset-assert 76) ;; function to run after suspending + + ;; function to run if we receive an event + (event-hook (function process int symbol event-message-block object) :offset-assert 80) + + ;; process heap size + (allocated-length int32 :offset-assert 84) + + ;; ?? + (pad0 uint32 2) + + ;; process heap + (heap-base pointer :offset-assert 96) + (heap-top pointer :offset-assert 100) + (heap-cur pointer :offset-assert 104) + + ;; linked list of stack frames that have been created. + ;; note that these aren't created on every function call, only + ;; if the user explicitly creates a catch block or similar + (stack-frame-top stack-frame :offset-assert 108) + + ;; list of engines this process is connected to + (connection-list connectable :inline :offset-assert 112) + + ;; the process memory: contains child fields, then the process heap. + (stack uint8 :dynamic :offset-assert 128) + ) + (:methods + (new (symbol type string int) _type_ 0) + ) + (:states + dead-state + empty-state) + :size-assert #x80 + :method-count-assert 14 + :no-runtime-type ;; already defined by kscheme. Don't do it again. + ) + +;; dead-pool simplest way to store dead processes - it's just a tree of processes that +;; are inactive. +(deftype dead-pool (process-tree) + () + :method-count-assert 16 + :size-assert #x24 + :flag-assert #x1000000024 + (:methods + (new (symbol type int int string) _type_ 0) + (get-process (_type_ type int) process 14) + (return-process (_type_ process) none 15) + ) + ) + +;; todo inspect + +;; dead-pool-heap is a special thing - it pretends to be a dead-pool, but secretly +;; creates and destroys processes on demand, as they are requested/returned. +;; to do this, it has a single large heap and memory allocator. +;; to prevent fragmentation of this heap, it has a relocate/compaction system +;; that moves processes in memory. + + +;; A dead-pool-heap-rec is a record for a process used by the handle system. +;; The kernel will make sure that: +;; - the dead-pool-heap-rec for a process will continue to point to that process until the process +;; is killed. +;; - the dead-pool-heap-rec itself is never moved in memory, and it always points to some process, or #f. +;; (it is always safe to do (-> rec process pid) and see if it still points to your process) +(deftype dead-pool-heap-rec (structure) + ((process process :offset-assert 0) + (prev dead-pool-heap-rec :offset-assert 4) + (next dead-pool-heap-rec :offset-assert 8) + ) + :pack-me + :method-count-assert 9 + :size-assert #xc + :flag-assert #x90000000c + ) + +;; the actual pool implementation +(deftype dead-pool-heap (dead-pool) + ((allocated-length int32 :offset-assert 36) + (compact-time uint32 :offset-assert 40) + (compact-count-targ uint32 :offset-assert 44) + (compact-count uint32 :offset-assert 48) + (fill-percent float :offset-assert 52) + (first-gap dead-pool-heap-rec :offset-assert 56) + (first-shrink dead-pool-heap-rec :offset-assert 60) + (heap kheap :inline :offset-assert 64) + (alive-list dead-pool-heap-rec :inline :offset-assert 80) + (last dead-pool-heap-rec :offset 84) + (dead-list dead-pool-heap-rec :inline :offset-assert 92) + (process-list dead-pool-heap-rec :inline :dynamic :offset-assert 104) + ) + :method-count-assert 28 + :size-assert #x68 + :flag-assert #x1c00000068 + (:methods + (new (symbol type string int int) _type_ 0) + (init (_type_ symbol int) none 16) + (compact (dead-pool-heap int) none 17) + (shrink-heap (dead-pool-heap process) dead-pool-heap 18) + (churn (dead-pool-heap int) none 19) + (memory-used (_type_) int 20) + (memory-total (_type_) int 21) + (memory-free (dead-pool-heap) int 22) + (compact-time (dead-pool-heap) uint 23) + (gap-size (dead-pool-heap dead-pool-heap-rec) int 24) + (gap-location (dead-pool-heap dead-pool-heap-rec) pointer 25) + (find-gap (dead-pool-heap dead-pool-heap-rec) dead-pool-heap-rec 26) + (find-gap-by-size (dead-pool-heap int) dead-pool-heap-rec 27) + ) + ) + +;; todo inspect + +;; parent type for all kinds of stack-frames. +;; at least for jak 1, these are only used internally by the kernel +;; "next" brings you "up" the stack (toward the caller) +(deftype stack-frame (basic) + ((name symbol :offset 4) + (next stack-frame :offset 8) + ) + :size-assert #xc + :method-count-assert 9 + :flag-assert #x90000000c + ) + +;; a "catch" frame is a frame that can be "thrown" to. +;; the "throw" is a nonlocal control flow back to the state befor the "catch" block. +(deftype catch-frame (stack-frame) + ((sp int32 :offset-assert 12) + (ra int32 :offset-assert 16) + (freg float 6 :offset-assert 20) + (rreg uint128 8 :offset-assert 48) + ) + :method-count-assert 9 + :size-assert #xb0 + :flag-assert #x9000000b0 + (:methods + (new (symbol type symbol function (pointer uint64)) object 0) + ) + ) + +;; a "protect" frame is a way to indicate there's a "exit" function that should +;; run if there's a "throw" or "abandon". +(deftype protect-frame (stack-frame) + ((exit (function none) :offset-assert 12) + ) + :method-count-assert 9 + :size-assert #x10 + :flag-assert #x900000010 + (:methods + (new (symbol type (function none)) protect-frame 0) + ) + ) + +;; a handle is a safe way to refer to a process. It solves two problems: +;; - it allows you to find a process that moves in memory +;; - it allows you to tell if the original process has died. otherwise you may get confused +;; because there could be another process located at the exact same address. +(deftype handle (uint64) + ((process (pointer process) :offset 0 :size 32) ;; additional level of indirection to support moving processes + (pid int32 :offset 32 :size 32) ;; unique pid to check if it's the same process or not. + (u64 uint64 :offset 0 :size 64) + ) + :method-count-assert 9 + :size-assert #x8 + :flag-assert #x900000008 + ) + +(defmethod inspect handle ((obj handle)) + (when (not obj) + (return obj) + ) + (format #t "[~8x] ~A~%" obj 'handle) + (format #t "~1Tprocess: #x~X~%" (-> obj process)) + (format #t "~1Tpid: ~D~%" (-> obj pid)) + obj + ) + +(defmacro handle->process (handle) + "Convert a handle to a process. If the process no longer exists, returns #f." + `(let ((the-handle (the-as handle ,handle))) + (if (-> the-handle process) ;; if we don't point to a process, kernel sets this to #f + (let ((proc (-> (-> the-handle process)))) + (if (= (-> the-handle pid) (-> proc pid)) ;; make sure it's the same process + proc + ) + ) + ) + ) + ) + +(defmacro ppointer->process (ppointer) + "convert a (pointer process) to a process." + ;; this uses the self field, which seems to always just get set to the object. + ;; confirmed in Jak 1 that using self here is useless, not sure... + `(let ((the-pp ,ppointer)) + (the process-tree (if the-pp (-> the-pp 0 self))) + ) + ) + +(defmacro process->ppointer (proc) + "safely get a (pointer process) from a process, returning #f if invalid." + `(let ((the-proc ,proc)) + (if the-proc (-> the-proc ppointer)) + ) + ) + +(defmacro ppointer->handle (pproc) + "convert a ppointer to a handle. assumes the ppointer is valid." + `(let ((the-process (the-as (pointer process) ,pproc))) + (new 'static 'handle :process the-process :pid (-> the-process 0 pid)) + ) + ) + +(defmacro process->handle (proc) + "convert a process to a handle. if proc is #f, returns a #f handle." + `(ppointer->handle (process->ppointer ,proc)) + ) + +(defmethod print handle ((obj handle)) + (if (nonzero? obj) + (format #t "#" (handle->process obj) (-> obj pid)) + (format #t "#") + ) + obj + ) + +;; A "state" defines functions that a process should run when it is in that state. +;; the "code" function is executed by the main thread and can suspend/resume. +;; the "trans" function is executed before code is resumed +;; the "post" function is executed after code is suspended +;; the "enter" function is executed when the process first transitions to the state +;; the "exit" function is executed when the process exits the state (or dies) +;; the "event" function is executed when the process receives an event. +;; See gstate.gc for a lot more details on how this all works. +;; This type is just a container to hold those functions. +(deftype state (protect-frame) + ((code function :offset-assert 16) + (trans (function none) :offset-assert 20) + (post function :offset-assert 24) + (enter function :offset-assert 28) + (event (function process int symbol event-message-block object) :offset-assert 32) + ) + :method-count-assert 9 + :size-assert #x24 + :flag-assert #x900000024 + (:methods + (new (symbol + type + symbol + function + (function none) + function + (function none) + (function process int symbol event-message-block object)) + _type_ 0) + ) + ) + +;; data contained in an "event" sent from one process to another +;; in jak2, the events may be queued and sent at a later time, so the block +;; contains handles, to see if the to/from processes are still alive. +(deftype event-message-block (structure) + ((to-handle handle :offset-assert 0) ;; who to send to + (to (pointer process) :offset 0) + (form-handle handle :offset-assert 8) ;; who is doing the sending + (from (pointer process) :offset 8) + (param uint64 6 :offset-assert 16) ;; the data being sent + (message symbol :offset-assert 64) ;; the message name + (num-params int32 :offset-assert 68) + ) + :method-count-assert 9 + :size-assert #x48 + :flag-assert #x900000048 + ) + +;; a queue of messages. +(deftype event-message-block-array (inline-array-class) + ((data event-message-block :inline :dynamic :offset-assert 16) + ) + :method-count-assert 10 + :size-assert #x10 + :flag-assert #xa00000010 + (:methods + (send-all! (_type_) none 9) + ) + ) +(set! (-> event-message-block-array heap-base) (the-as uint 80)) + +;; the type returned by the C Kernel, contains the result of a SQL Query. +(deftype sql-result (basic) + ((len int32 :offset-assert 4) + (allocated-length uint32 :offset-assert 8) + (error symbol :offset-assert 12) + (data symbol :dynamic :offset-assert 16) + ) + :method-count-assert 9 + :size-assert #x10 + :flag-assert #x900000010 + (:methods + (new (symbol type uint) _type_ 0) + ) + ) + +(defmethod new sql-result ((allocation symbol) (type-to-make type) (arg0 uint)) + "Allocate a new sql-result with enough room for arg0 entries in data." + (let ((v0-0 (object-new allocation type-to-make (the-as int (+ (-> type-to-make size) (* arg0 4)))))) + (set! (-> v0-0 allocated-length) arg0) + (set! (-> v0-0 error) 'error) + v0-0 + ) + ) + +(defmethod print sql-result ((obj sql-result)) + "Print a sql-result as an array of symbols." + (format #t "#(~A" (-> obj error)) + (dotimes (s5-0 (-> obj len)) + (format #t " ~A" (-> obj data s5-0)) + ) + (format #t ")") + obj + ) + +;; the result that the C Kernel will send us. +(define *sql-result* (the-as sql-result #f)) + +(defmacro defbehavior (name process-type bindings &rest body) + "define a new behavior. This is simply a function where self is bound to the process register, + which is assumed to have type process-type." + (if (and + (> (length body) 1) ;; more than one thing in function + (string? (first body)) ;; first thing is a string + ) + ;; then it's a docstring and we ignore it. + `(define ,name (lambda :name ,name :behavior ,process-type ,bindings ,@(cdr body))) + ;; otherwise don't ignore it. + `(define ,name (lambda :name ,name :behavior ,process-type ,bindings ,@body)) + ) + ) + +(defmacro process-stack-used (proc) + ;; get how much stack the top thread of a process has used. + `(- (the int (-> ,proc top-thread stack-top)) + (the int (-> ,proc top-thread sp)) + ) + ) + +(defmacro process-stack-size (proc) + ;; get how much stack the top thread of a process has + `(-> ,proc top-thread stack-size) + ) + +(defmacro process-heap-used (proc) + ;; get how much heap a process has used. + `(- (-> ,proc allocated-length) + (- (the int (-> ,proc heap-top)) + (the int (-> ,proc heap-cur)) + ) + ) + ) + +(defmacro process-heap-size (proc) + ;; get how much heap a process has + `(the int (-> ,proc allocated-length)) + ) + +(defmacro break () + `(/ 0 0) + ) + +(defmacro with-pp (&rest body) + "execute the body with pp bound to the current process register." + `(rlet ((pp :reg r13 :reset-here #t :type process)) + ,@body) + ) + +(defmacro process-mask? (mask enum-value) + `(!= 0 (logand ,mask (process-mask ,enum-value))) + ) + +(defmacro process-mask-set! (mask &rest enum-value) + ;; sets the given bits in the process mask (with or) + `(set! ,mask (logior ,mask (process-mask ,@enum-value))) + ) + +(defmacro process-mask-clear! (mask &rest enum-value) + ;; sets the given bits in the process mask (with or) + `(set! ,mask (logand ,mask (lognot (process-mask ,@enum-value)))) + ) + +(defmacro suspend () + "suspend the current process, to be resumed on the next frame." + `(rlet ((pp :reg r13 :reset-here #t)) + ;; debug check for stack overflow here, where we can easily print the process name. + (#when (or KERNEL_DEBUG) + (rlet ((sp :reg rsp :reset-here #t :type int) + (off :reg r15 :type uint)) + (let* ((sp-goal (- sp off)) + (stack-top-goal (-> (the process pp) top-thread stack-top)) + (stack-used (&- stack-top-goal sp-goal)) + (stack-size (-> (the process pp) top-thread stack-size)) + ) + (when (> stack-used stack-size) + (format 0 "ERROR: suspend called without enough stack in proc:~%~A~%Stack: ~D/~D~%" pp stack-used stack-size) + ) + ) + ) + ) + ;; set to the current thread + (set! pp (-> (the process pp) top-thread)) + ;; call the suspend hook (put nothing as the argument) + ((-> (the cpu-thread pp) suspend-hook) (the cpu-thread 0)) + ;; the kernel will set pp (possibly to a new value, if we've been relocated) on resume. + ) + ) \ No newline at end of file diff --git a/goal_src/jak2/kernel/gkernel.gc b/goal_src/jak2/kernel/gkernel.gc index 3aa2896f53..90a55493d5 100644 --- a/goal_src/jak2/kernel/gkernel.gc +++ b/goal_src/jak2/kernel/gkernel.gc @@ -5,10 +5,1577 @@ ;; name in dgo: gkernel ;; dgos: KERNEL -;; HACK kernel +;; Version constants (define *kernel-version* (the binteger (logior (ash *kernel-major-version* 16) *kernel-minor-version*))) +(define *irx-version* (the-as binteger #x200000)) + +;; Boot options +(define *kernel-boot-mode* 'listener) +(define *kernel-boot-level* #f) (define *use-old-listener-print* #f) + +;; Stats +(define *deci-count* 0) +(define *last-loado-length* 0) +(define *last-loado-global-usage* 0) +(define *last-loado-debug-usage* 0) + +;; forward declared stuff +(define-extern *kernel-clock* clock) +(define-extern *debug-dead-pool* dead-pool-heap) +(define-extern *null-process* process) +(define-extern *vis-boot* symbol) +(define-extern *listener-process* process) +(define-extern *active-pool* process-tree) +(define-extern *default-level* level) + + +(define-extern change-parent (function process-tree process-tree process-tree)) +(define-extern search-process-tree (function process-tree (function process-tree object) process-tree)) +(define-extern iterate-process-tree (function process-tree (function object object) kernel-context object)) +(define-extern execute-process-tree (function process-tree (function object object) kernel-context object)) +(define-extern inspect-process-tree (function process-tree int int symbol process-tree)) +(define-extern process-disconnect (function process int)) + +(defmethod relocate object ((obj object) (arg0 int)) + "Most general relocate method." + obj + ) + +;;;;;;;;;;;;;;;;;; +;; Package +;;;;;;;;;;;;;;;;;; + +(define *kernel-packages* '()) + +(defun load-package ((arg0 string) (arg1 kheap)) + "Load a package by name to the given heap." + (when (not (nmember arg0 *kernel-packages*)) + (kmemopen global arg0) + (dgo-load arg0 arg1 (link-flag output-load-msg output-load-true-msg execute-login print-login) #x200000) + (set! *kernel-packages* (cons arg0 *kernel-packages*)) + (kmemclose) + *kernel-packages* + ) + ) + +(defun unload-package ((arg0 string)) + "Mark a package as unloaded." + (let ((v1-0 (nmember arg0 *kernel-packages*))) + (if v1-0 + (set! *kernel-packages* (delete! (car v1-0) *kernel-packages*)) + ) + ) + *kernel-packages* + ) + +;;;;;;;;;;;;;;;;;; +;; Kernel Globals +;;;;;;;;;;;;;;;;;; + +;; the global kernel-context +(define *kernel-context* + (new 'static 'kernel-context + :prevent-from-run (process-mask execute sleep) + :next-pid 3 + :current-process #f + :relocating-process #f + :low-memory-message #t + ) + ) + +;; the main execution stack that's not on the scratchpad +(define *dram-stack* (the-as (pointer uint8) (malloc 'global DPROCESS_STACK_SIZE))) + +;; the top of the stack. +(defconstant *kernel-dram-stack* (&+ *dram-stack* DPROCESS_STACK_SIZE)) + +;; the top of the scratchpad stack +(set! (-> *kernel-context* fast-stack-top) (the-as pointer #x70004000)) + +(define *null-kernel-context* (new 'static 'kernel-context)) + +;;;;;;;;;;;;;;;;;;;;;;; +;; PC Port Scratchpad +;;;;;;;;;;;;;;;;;;;;;;; + +(#cond + (PC_PORT + ;; we'll create a fake scratchpad: + ;; make sure the scratchpad is 64kb aligned, and make it 32 kB so we can big stacks on it. + ;; some (partially buggy) code in generic tie relies on 64 kB alignment. + (let* ((mem (new 'global 'array 'uint8 (* 128 1024))) + ) + (define *fake-scratchpad-data* (the pointer (align-n mem (* 64 1024)))) + ) + + ;; use the same memory for the scratchpad stacks. + ;; defining it as a separate thing so we can split them for debugging stack corruption easily. + (define *fake-scratchpad-stack* *fake-scratchpad-data*) + + (defmacro scratchpad-start () + "Get the start of the scratchpad. At least 64kB aligned." + '*fake-scratchpad-data* + ) + ) + (else + (defmacro scratchpad-start () + #x70000000 + ) + ) + ) + +(defmacro scratchpad-end () + "Get the end of the scratchpad memory" + `(&+ (scratchpad-start) (* 16 1024)) + ) + +(defmacro in-scratchpad? (x) + "Is the given address in the scratchpad?" + `(and + (>= (the-as int ,x) (scratchpad-start)) + (< (the-as int ,x) (scratchpad-end)) + ) + ) + + +;;;;;;;;;;;;; +;; Thread +;;;;;;;;;;;;; + +(defmethod delete thread ((obj thread)) + "Restore the previous thread as the top-thread." + ;; make sure we aren't actually trying to delete the main thread. + (when (= obj (-> obj process main-thread)) + (break!) + ) + (set! (-> obj process top-thread) (the-as cpu-thread (-> obj previous))) + (none) + ) + +(defmethod print thread ((obj thread)) + (format #t "#<~A ~S of ~S pc: #x~X @ #x~X>" (-> obj type) (-> obj name) (-> obj process name) (-> obj pc) obj) + obj + ) + +(defmethod stack-size-set! thread ((obj thread) (arg0 int)) + "Modify the backup stack size of a thread. Must be called from the main thread, before any + allocations have been done on the process heap." + (let ((a2-0 (-> obj process))) + (cond + ((!= obj (-> a2-0 main-thread)) + (format 0 "ERROR: illegal attempt change stack size of ~A when the main-thread is not the top-thread.~%" a2-0) + ) + ((= (-> obj stack-size) arg0) + ) + ((= (-> a2-0 heap-cur) (+ (+ (-> obj stack-size) -4 (-> obj type size)) (the-as int obj))) + (set! (-> a2-0 heap-cur) (the-as pointer (+ (+ arg0 -4 (-> obj type size)) (the-as int obj)))) + (set! (-> obj stack-size) arg0) + ) + (else + (format 0 "ERROR: illegal attempt change stack size of ~A after more heap allocation has occured.~%" a2-0) + ) + ) + ) + (none) + ) + +(defmethod new cpu-thread ((allocation symbol) (type-to-make type) (parent-process process) (name symbol) (arg2 int) (stack-top pointer)) + "Create a new CPU thread. If there is no main thread, it will allocate the main thread on the process. + If there is already a main thread, it will allocate a temporary thread on the given stack. + Sets the thread as the top-thread of the process + This is a special new method which ignores the allocation symbol. + The stack-top is for the execution stack. + The stack-size is for the backup stack (applicable for main thread only)" + (let ((v0-0 (cond + ((-> parent-process top-thread) + ;; this is just a temporary thread, throw the thread on the bottom of the stack + (the cpu-thread (&+ stack-top (- PROCESS_STACK_SIZE *gtype-basic-offset*))) + ) + (else + ;; this is the main thread, allocate it from the process heap. + (let ((v1-2 (logand -16 (&+ (-> parent-process heap-cur) 15)))) + (set! (-> parent-process heap-cur) (&+ (&+ v1-2 (-> type-to-make size)) arg2)) + (the cpu-thread (&+ v1-2 4)) + ) + ) + ) + ) + ) + (set! (-> v0-0 type) type-to-make) + (set! (-> v0-0 name) name) + (set! (-> v0-0 process) parent-process) + (set! (-> v0-0 sp) stack-top) + (set! (-> v0-0 stack-top) stack-top) + (set! (-> v0-0 previous) (-> parent-process top-thread)) + (set! (-> parent-process top-thread) v0-0) + (set! (-> v0-0 suspend-hook) (method-of-object v0-0 thread-suspend)) + (set! (-> v0-0 resume-hook) (method-of-object v0-0 thread-resume)) + (set! (-> v0-0 stack-size) arg2) + v0-0 + ) + ) + +(defmethod asize-of cpu-thread ((obj cpu-thread)) + "Get the size in memory of a cpu-thread." + (the-as int (+ (-> obj type size) (-> obj stack-size))) + ) + +;;;;;;;;;;;;;;; +;; Process +;;;;;;;;;;;;;;; + +(defbehavior remove-exit process () + "Remove a single stack frame. + This can be used to skip an exit of a state, but it's a bit of a hack." + (if (-> self stack-frame-top) + (set! (-> self stack-frame-top) (-> self stack-frame-top next)) + ) + 0 + (none) + ) + +(defun-debug stream<-process-mask ((arg0 object) (arg1 process-mask)) + "Print a process mask." + (bit-enum->string process-mask arg1 arg0) + arg1 + ) + +(define *master-mode* 'game) +(define *pause-lock* #f) + +(defmethod print process-tree ((obj process-tree)) + "Print a process tree." + (format #t "#<~A ~S @ #x~X>" (-> obj type) (-> obj name) obj) + obj + ) + +(defmethod new process-tree ((allocation symbol) (type-to-make type) (arg0 string)) + "Allocate a new process-tree with the given name." + (let ((v0-0 (object-new allocation type-to-make (the-as int (-> type-to-make size))))) + (set! (-> v0-0 name) arg0) + (set! (-> v0-0 mask) (process-mask process-tree)) + (set! (-> v0-0 clock) *kernel-clock*) + (set! (-> v0-0 parent) (the-as (pointer process-tree) #f)) + (set! (-> v0-0 brother) (the-as (pointer process-tree) #f)) + (set! (-> v0-0 child) (the-as (pointer process-tree) #f)) + (set! (-> v0-0 self) v0-0) + (set! (-> v0-0 ppointer) (the-as (pointer process) (&-> v0-0 self))) + v0-0 + ) + ) + +(defmethod inspect process-tree ((obj process-tree)) + "Inspect a process-tree" + (format #t "[~8x] ~A~%" obj (-> obj type)) + (format #t "~Tname: ~S~%" (-> obj name)) + (format #t "~1Tmask: #x~X : (process-mask " (-> obj mask)) + (stream<-process-mask #t (-> obj mask)) + (format #t ")~%") + (format #t "~Tclock: ~A~%" (-> obj clock)) + (format #t "~Tparent: ~A~%" (ppointer->process (-> obj parent))) + (format #t "~Tbrother: ~A~%" (ppointer->process (-> obj brother))) + (format #t "~Tchild: ~A~%" (ppointer->process (-> obj child))) + obj + ) + +(defmethod new process ((allocation symbol) (type-to-make type) (arg0 string) (arg1 int)) + "Allocate or initialize a process." + ;; check if we got a symbol (for a heap) or just a plain address. + (let ((v0-0 (if (logtest? (the-as int allocation) 1) + (object-new allocation type-to-make (the-as int (+ (-> process size) arg1))) ;; allocate on heap + (the process (+ (the-as int allocation) 4)) ;; just use it as an address + ) + ) + ) + (set! (-> v0-0 name) arg0) + (set! (-> v0-0 clock) *kernel-clock*) + (set! (-> v0-0 status) 'dead) + (set! (-> v0-0 pid) 0) + (set! (-> v0-0 pool) #f) + (set! (-> v0-0 allocated-length) arg1) + (set! (-> v0-0 top-thread) #f) + (set! (-> v0-0 main-thread) #f) + (let ((v1-6 (-> v0-0 stack))) + (set! (-> v0-0 heap-cur) v1-6) + (set! (-> v0-0 heap-base) v1-6) + ) + (set! (-> v0-0 heap-top) + (&-> v0-0 stack (-> v0-0 allocated-length)) + ) + (set! (-> v0-0 stack-frame-top) (the-as stack-frame (-> v0-0 heap-top))) + (set! (-> v0-0 stack-frame-top) #f) + (set! (-> v0-0 state) #f) + (set! (-> v0-0 next-state) #f) + (set! (-> v0-0 entity) #f) + (set! (-> v0-0 level) #f) + (set! (-> v0-0 trans-hook) #f) + (set! (-> v0-0 post-hook) #f) + (set! (-> v0-0 event-hook) #f) + (set! (-> v0-0 parent) (the-as (pointer process-tree) #f)) + (set! (-> v0-0 brother) (the-as (pointer process-tree) #f)) + (set! (-> v0-0 child) (the-as (pointer process-tree) #f)) + (set! (-> v0-0 self) v0-0) + (set! (-> v0-0 ppointer) (the-as (pointer process) (&-> v0-0 self))) + v0-0 + ) + ) + +(defun inspect-process-heap ((obj process)) + "Inspect each object on the process heap." + (let ((ptr (&+ (-> obj heap-base) *gtype-basic-offset*))) ; point to first basic + ;; loop over objects + (while (< (the int ptr) (the int (-> obj heap-cur))) + ;; inspect the object + (inspect (the basic ptr)) + ;; seek to the next object on the heap. + (&+! ptr (the int (align16 (asize-of (the basic ptr))))) + ) + ) + #f + ) + +(defmethod inspect process ((obj process)) + "Inspect process and all objects on the heap.. Autogenerated proces inspects will eventually call this one." + (format #t "[~8x] ~A~%" obj (-> obj type)) + (format #t "~Tname: ~S~%" (-> obj name)) + (format #t "~1Tmask: #x~X : (process-mask " (-> obj mask)) + (stream<-process-mask #t (-> obj mask)) + (format #t ")~%") + (format #t "~Tclock: ~A~%" (-> obj clock)) + (format #t "~Tstatus: ~A~%" (-> obj status)) + (format #t "~Tmain-thread: ~A~%" (-> obj main-thread)) + (format #t "~Ttop-thread: ~A~%" (-> obj top-thread)) + (format #t "~Tentity: ~A~%" (-> obj entity)) + (format #t "~Tlevel: ~A~%" (-> obj level)) + (format #t "~Tstate: ~A~%" (-> obj state)) + (format #t "~Tnext-state: ~A~%" (-> obj next-state)) + (format #t "~Ttrans-hook: ~A~%" (-> obj trans-hook)) + (format #t "~Tpost-hook: ~A~%" (-> obj post-hook)) + (format #t "~Tevent-hook: ~A~%" (-> obj event-hook)) + (format #t "~Tparent: ~A~%" (ppointer->process (-> obj parent))) + (format #t "~Tbrother: ~A~%" (ppointer->process (-> obj brother))) + (format #t "~Tchild: ~A~%" (ppointer->process (-> obj child))) + (format #t "~Tconnection-list: ~`connectable`P~%" (-> obj connection-list)) + (format #t "~Tstack-frame-top: ~A~%" (-> obj stack-frame-top)) + (format #t "~Theap-base: #x~X~%" (-> obj heap-base)) + (format #t "~Theap-top: #x~X~%" (-> obj heap-top)) + (format #t "~Theap-cur: #x~X~%" (-> obj heap-cur)) + (let ((s5-0 *print-column*)) + (set! *print-column* (+ *print-column* *tab-size*)) + (format #t "----~%") + (inspect-process-heap obj) + (format #t "----~%") + (set! *print-column* s5-0) + ) + (format #t "~Tallocated-length: ~D~%" (-> obj allocated-length)) + (format #t "~Tstack[~D] @ #x~X~%" (-> obj allocated-length) (-> obj stack)) + obj + ) + +(defmethod asize-of process ((obj process)) + "Get the size in memory of a process." + (the-as int (+ (-> process size) (-> obj allocated-length))) + ) + +(defmethod print process ((obj process)) + "Print a process." + + ;; new: for jak 2, they don't print garbage stack/heap sizes when the process isn't + ;; activated yet. + (cond + ((and (-> obj top-thread) (!= (-> obj status) 'dead)) + (format #t "#<~A ~S ~A :state ~S " + (-> obj type) + (-> obj name) + (-> obj status) + (if (-> obj state) (-> obj state name)) + ) + (format #t ":stack ~D/~D :heap ~D/~D @ #x~X>" + (&- (-> obj top-thread stack-top) (the-as uint (-> obj top-thread sp))) + (-> obj main-thread stack-size) + (- (-> obj allocated-length) (&- (-> obj heap-top) (the-as uint (-> obj heap-cur)))) + (-> obj allocated-length) + obj + ) + ) + (else + (format #t "#<~A ~S ~A :state ~S @ #x~X" + (-> obj type) + (-> obj name) + (-> obj status) + (if (-> obj state) + (-> obj state name) + ) + obj + ) + ) + ) + obj + ) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Context Suspend And Resume - Kernel +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; the following functions are used for going from the kernel to threads and back. +;; saved registers: rbx, rbp, r10, r11, r12 + +;; DANGER - THE KERNEL DOES NOT SAVE ITS FLOATING POINT CONTEXT!!!! + +;; we use this to store a GOAL pointer to the kernel's stack pointer when executing user code. +;; to get back to the kernel, we use this global symbol. +(define-extern *kernel-sp* pointer) + +(defun return-from-thread () + "Context switch to the saved kernel context now. + This is intended to be jumped to with the ret instruction (return trampoline) + at the end of a normal function, so this should preserve rax. + To make sure this happens, all ops should be asm ops and we should have no + GOAL expressions." + (declare (asm-func none) + ;(print-asm) + ) + (rlet ((sp :reg rsp :type uint) + (off :reg r15 :type uint) + (s0 :reg rbx :type uint) + (s1 :reg rbp :type uint) + (s2 :reg r10 :type uint) + (s3 :reg r11 :type uint) + (s4 :reg r12 :type uint) + ) + ;; get the kernel stack pointer as a GOAL pointer (won't use a temp reg) + (.load-sym :sext #f sp *kernel-sp*) + ;; convert it back to a real pointer + (.add sp off) + + ;; restore saved registers... + ;; without coloring system because this is "cheating" and modifying saved registers without backing up. + (.pop :color #f s4) + (.pop :color #f s3) + (.pop :color #f s2) + (.pop :color #f s1) + (.pop :color #f s0) + ;; return to the kernel function that called the user code + ;; rax should still contain the return value. + (.ret) + ) + ) + +(defun return-from-thread-dead () + "Like return from thread, but we clean up our process with deactivate first. + The return register is not preserved here, instead we return the value of deactivate" + (declare (asm-func none) + ;(print-asm) + ) + (rlet ((pp :reg r13 :type process) + (sp :reg rsp :type uint) + (off :reg r15 :type uint) + (s0 :reg rbx :type uint) + (s1 :reg rbp :type uint) + (s2 :reg r10 :type uint) + (s3 :reg r11 :type uint) + (s4 :reg r12 :type uint) + ) + + ;; first call the deactivate method. + (deactivate pp) + ;; get the kernel stack pointer as a GOAL pointer + (.load-sym :sext #f sp *kernel-sp*) + ;; convert it back to a real pointer + (.add sp off) + + ;; restore saved registers... + ;; without coloring system because this is "cheating". + (.pop :color #f s4) + (.pop :color #f s3) + (.pop :color #f s2) + (.pop :color #f s1) + (.pop :color #f s0) + ;; return to the kernel function that called the user code + (.ret) + ) + ) + +(defun reset-and-call ((obj thread) (func function)) + "Make the given thread the top thread, reset the stack, and call the function. + Sets up a return trampoline so when the function returns it will return to the + kernel context. Will NOT deactivate on return, so this is intended for temporary threads. + NOTE: this should only be done from the kernel, running on the + kernel's stack." + (declare (asm-func object) + ) + + (rlet ((pp :reg r13 :type process) + (sp :reg rsp :type uint) + (off :reg r15 :type uint) + (s0 :reg rbx :type uint) + (s1 :reg rbp :type uint) + (s2 :reg r10 :type uint) + (s3 :reg r11 :type uint) + (s4 :reg r12 :type uint) + (temp :reg rax :type uint) + ) + + ;; set up the process pointer + (set! pp (-> obj process)) + ;; mark the process as running and set its top thread + (set! (-> pp status) 'running) + (set! (-> pp top-thread) (the cpu-thread obj)) + + ;; save the current kernel regs + (.push :color #f s0) + (.push :color #f s1) + (.push :color #f s2) + (.push :color #f s3) + (.push :color #f s4) + + ;; make rsp a GOAL pointer + (.sub sp off) + ;; and store it + (set! *kernel-sp* (the pointer sp)) ;; todo, asm form here? + + ;; setup the rsp for the new thread + (set! sp (the uint (-> obj stack-top))) + (.add sp off) + + ;; push the return trampoline to the stack for the user code to return to + (set! temp (the uint return-from-thread)) + (.add temp off) + (.push temp) ;; stack now 16 + 8 aligned + ;; and call the function! + (.add func off) + (.jr func) + ) + ) + +(defmethod thread-suspend cpu-thread ((unused cpu-thread)) + "Suspend the thread and return to the kernel." + + (declare (asm-func none)) + + ;; we begin this function with the thread object in pp. + ;; not sure why we do this, maybe at one point suspending didn't clobber + ;; temp registers? + (rlet ((obj :reg r13 :type cpu-thread) + (temp :reg rax :type uint) + (off :reg r15 :type uint) + (sp :reg rsp :type uint) + (s0 :reg rbx :type uint) + (s1 :reg rbp :type uint) + (s2 :reg r10 :type uint) + (s3 :reg r11 :type uint) + (s4 :reg r12 :type uint) + + (xmm8 :reg xmm8 :class fpr) + (xmm9 :reg xmm9 :class fpr) + (xmm10 :reg xmm10 :class fpr) + (xmm11 :reg xmm11 :class fpr) + (xmm12 :reg xmm12 :class fpr) + (xmm13 :reg xmm13 :class fpr) + (xmm14 :reg xmm14 :class fpr) + (xmm15 :reg xmm15 :class fpr) + ) + + ;; get the return address pushed by "call" in the suspend. + (.pop temp) + ;; convert to a GOAL address + (.sub temp off) + ;; store return address in thread + (set! (-> obj pc) (the pointer temp)) + + ;; convert our stack pointer to a GOAL address + (.sub sp off) + ;; store in thread. + (set! (-> obj sp) (the pointer sp)) + + ;; back up registers + (.mov :color #f temp s0) + (set! (-> obj rreg 0) temp) + (.mov :color #f temp s1) + (set! (-> obj rreg 1) temp) + (.mov :color #f temp s2) + (set! (-> obj rreg 2) temp) + (.mov :color #f temp s3) + (set! (-> obj rreg 3) temp) + (.mov :color #f temp s4) + (set! (-> obj rreg 4) temp) + + ;; back up fprs + (.mov :color #f temp xmm8) + (set! (-> obj freg 0) (the-as float temp)) + (.mov :color #f temp xmm9) + (set! (-> obj freg 1) (the-as float temp)) + (.mov :color #f temp xmm10) + (set! (-> obj freg 2) (the-as float temp)) + (.mov :color #f temp xmm11) + (set! (-> obj freg 3) (the-as float temp)) + (.mov :color #f temp xmm12) + (set! (-> obj freg 4) (the-as float temp)) + (.mov :color #f temp xmm13) + (set! (-> obj freg 5) (the-as float temp)) + (.mov :color #f temp xmm14) + (set! (-> obj freg 6) (the-as float temp)) + (.mov :color #f temp xmm15) + (set! (-> obj freg 7) (the-as float temp)) + + + + ;; get our process + (let ((proc (-> obj process))) + (when (> (process-stack-used proc) (-> obj stack-size)) + (break) ;; too much stack has been used and we can't suspend! + ;; if you hit this, try with DEBUG_PRINT_SUSPEND_FAIL set to #t (see gkernel-h.gc) + ;; it will print more info before reaching here. + ) + + ;; mark the process as suspended and copy the stack + (set! (-> proc status) 'suspended) + (let ((cur (the (pointer uint64) (-> obj stack-top))) + (save (&+ (the (pointer uint64) (-> obj stack)) (-> obj stack-size))) + ) + (while (> (the int cur) (the int sp)) + (set! cur (the (pointer uint64) (&- cur 8))) + (set! save (the (pointer uint64) (&- save 8))) + (set! (-> save) (-> cur)) + ) + ) + ) + + ;; actually setting pp to 0 + (set! obj (the cpu-thread 0)) + + ;; get the kernel stack pointer as a GOAL pointer + (.load-sym :sext #f sp *kernel-sp*) + ;; convert it back to a real pointer + (.add sp off) + + ;; restore saved registers... + ;; without coloring system because this is "cheating". + (.pop :color #f s4) + (.pop :color #f s3) + (.pop :color #f s2) + (.pop :color #f s1) + (.pop :color #f s0) + ;; return to the kernel function that called the user code + (.ret) + ) + (none) + ) + +(defmethod thread-resume cpu-thread ((thread-to-resume cpu-thread)) + "Resume a suspended thread. Call this from the kernel only. + This is also used to start a thread initialized with set-to-run. + As a result of MIPS/x86 differences, there is a hack for this." + (declare (asm-func none) + ;;(print-asm) + ) + + (rlet ((obj :reg r13 :type cpu-thread) + (temp :reg rax :type uint) + (off :reg r15 :type uint) + (sp :reg rsp :type uint) + (s0 :reg rbx :type uint) + (s1 :reg rbp :type uint) + (s2 :reg r10 :type uint) + (s3 :reg r11 :type uint) + (s4 :reg r12 :type uint) + (a4 :reg r8 :type uint) + (a5 :reg r9 :type uint) + + (temp-float :reg xmm0 :class fpr) + (xmm8 :reg xmm8 :class fpr) + (xmm9 :reg xmm9 :class fpr) + (xmm10 :reg xmm10 :class fpr) + (xmm11 :reg xmm11 :class fpr) + (xmm12 :reg xmm12 :class fpr) + (xmm13 :reg xmm13 :class fpr) + (xmm14 :reg xmm14 :class fpr) + (xmm15 :reg xmm15 :class fpr) + ) + + ;; save the current kernel regs + (.push :color #f s0) + (.push :color #f s1) + (.push :color #f s2) + (.push :color #f s3) + (.push :color #f s4) + + ;; make rsp a GOAL pointer + (.sub sp off) + ;; and store it + (set! *kernel-sp* (the pointer sp)) ;; todo, asm form here? + + ;; temp, stash thread in process-pointer + (set! obj thread-to-resume) + + ;; set stack pointer for the thread. leave it as a GOAL pointer for now.. + (set! sp (the uint (-> obj sp))) + + ;; restore the stack (sp is a GOAL pointer) + (let ((cur (the (pointer uint64) (-> obj stack-top))) + (restore (&+ (the (pointer uint64) (-> obj stack)) (-> obj stack-size))) + ) + (while (> (the int cur) (the int sp)) + (set! cur (the (pointer uint64) (&- cur 8))) + (set! restore (the (pointer uint64) (&- restore 8))) + (set! (-> cur) (-> restore)) + ) + ) + + ;; offset sp after we're done using it as a GOAL pointer. + (.add sp off) + + ;; setup process + (set! (-> (-> obj process) top-thread) obj) + (set! (-> (-> obj process) status) 'running) + + ;; restore reg + (set! temp (-> obj rreg 0)) + (.mov :color #f s0 temp) + (set! temp (-> obj rreg 1)) + (.mov :color #f s1 temp) + (set! temp (-> obj rreg 2)) + (.mov :color #f s2 temp) + (set! temp (-> obj rreg 3)) + (.mov :color #f s3 temp) + (set! temp (-> obj rreg 4)) + (.mov :color #f s4 temp) + (set! temp-float (-> obj freg 0)) + (.mov :color #f xmm8 temp-float) + (set! temp-float (-> obj freg 1)) + (.mov :color #f xmm9 temp-float) + (set! temp-float (-> obj freg 2)) + (.mov :color #f xmm10 temp-float) + (set! temp-float (-> obj freg 3)) + (.mov :color #f xmm11 temp-float) + (set! temp-float (-> obj freg 4)) + (.mov :color #f xmm12 temp-float) + (set! temp-float (-> obj freg 5)) + (.mov :color #f xmm13 temp-float) + (set! temp-float (-> obj freg 6)) + (.mov :color #f xmm14 temp-float) + (set! temp-float (-> obj freg 7)) + (.mov :color #f xmm15 temp-float) + + ;; hack for set-to-run-bootstrap. The set-to-run-bootstrap in MIPS + ;; expects to receive 7 values from the cpu thread's rregs. + ;; usually rreg holds saved registers, but on the first resume after + ;; a set-to-run, they hold arguments, and set-to-run-bootstrap copies them. + + ;; We only have 5 saved regs, so we need to cheat and directly pass + ;; two values in other registers + ;; so we load the a4/a5 argument registers with rreg 5 and rreg 6 + ;; In the case where we are doing a normal resume, the + ;; compiler should assume that these registers are overwritten anyway. + (set! temp (-> obj rreg 5)) + (.mov a4 temp) + (set! temp (-> obj rreg 6)) + (.mov a5 temp) + + ;; get the resume address + (set! temp (the uint (-> obj pc))) + (.add temp off) + + ;; setup the process + (set! obj (the cpu-thread (-> obj process))) + ;; resume! + (.jr temp) + (.add a4 a4) + (.add a5 a5) + ) + (none) + ) + +(defmethod new dead-pool ((allocation symbol) (type-to-make type) (arg0 int) (arg1 int) (arg2 string)) + "Allocate a tree of dead processes." + (let ((s3-0 (object-new allocation type-to-make (the-as int (-> type-to-make size))))) + (set! (-> s3-0 name) arg2) + (set! (-> s3-0 mask) (process-mask process-tree)) + (set! (-> s3-0 parent) (the-as (pointer process-tree) #f)) + (set! (-> s3-0 brother) (the-as (pointer process-tree) #f)) + (set! (-> s3-0 child) (the-as (pointer process-tree) #f)) + (set! (-> s3-0 self) s3-0) + (set! (-> s3-0 ppointer) (the-as (pointer process) (&-> s3-0 self))) + (dotimes (s2-1 arg0) + (let ((s1-0 (-> s3-0 child)) + (v1-5 ((method-of-type process new) allocation process "dead" arg1)) + ) + (set! (-> s3-0 child) (process->ppointer v1-5)) + (set! (-> v1-5 parent) (process->ppointer (the-as process s3-0))) + (set! (-> v1-5 pool) s3-0) + (set! (-> v1-5 brother) s1-0) + ) + ) + s3-0 + ) + ) + +(defmethod get-process dead-pool ((obj dead-pool) (arg0 type) (arg1 int)) + "Try to get a process from this dead pool. If it fails, try the debug dead pool and complain." + + ;; grab the first child + (let ((s4-0 (the-as object (-> obj child)))) + (when (and (not (the-as (pointer process-tree) s4-0)) *debug-segment* (!= obj *debug-dead-pool*)) + ;; didn't work, but we have the debug dead pool to try + ;; NOTE: this is a type bug here, s4-0 should be (pointer process), but this uses process. + (set! s4-0 (get-process *debug-dead-pool* arg0 arg1)) + + (if (the-as process s4-0) + ;; that worked. complain. + (format 0 "WARNING: ~A ~A had to be allocated from the debug pool, because ~A was empty.~%" + arg0 + #f ;; (ppointer->process (the-as process s4-0)) bugged in original game + (-> obj name) + ) + ) + ;; this didn't work right in the original game, just crash here. + (break) + ) + + (cond + (s4-0 + ;; got a process somehow, set the type and return. + (set! (-> (the-as (pointer process) s4-0) 0 type) arg0) + (-> (the-as (pointer process) s4-0) 0) + ) + (else + ;; didn't work, complain and return #f. + (format 0 "WARNING: ~A ~A could not be allocated, because ~A was empty.~%" + arg0 + (ppointer->process (the-as (pointer process) s4-0)) + (-> obj name) + ) + (the-as process #f) + ) + ) + ) + ) + +(defmethod return-process dead-pool ((obj dead-pool) (arg0 process)) + "Return a process to the dead pool." + (change-parent arg0 obj) + (none) + ) + +(defmethod new dead-pool-heap ((allocation symbol) (type-to-make type) (arg0 string) (arg1 int) (arg2 int)) + "Allocate a new dead-pool-heap" + (let ((s2-0 (object-new allocation type-to-make (the-as int (+ (-> type-to-make size) (* 12 arg1)))))) + (set! (-> s2-0 name) arg0) + (set! (-> s2-0 mask) (process-mask process-tree)) + (set! (-> s2-0 allocated-length) arg1) + (set! (-> s2-0 parent) (the-as (pointer process-tree) #f)) + (set! (-> s2-0 brother) (the-as (pointer process-tree) #f)) + (set! (-> s2-0 child) (the-as (pointer process-tree) #f)) + (set! (-> s2-0 self) s2-0) + (set! (-> s2-0 ppointer) (the-as (pointer process) (&-> s2-0 self))) + (init s2-0 allocation arg2) + s2-0 + ) + ) + +(defmethod init dead-pool-heap ((obj dead-pool-heap) (arg0 symbol) (arg1 int)) + "Initialize the heap." + + ;; setup the records in a linked list, all referring to *null-process*. + (countdown (v1-0 (-> obj allocated-length)) + (let ((a0-4 (-> obj process-list v1-0))) + (set! (-> a0-4 process) *null-process*) + (set! (-> a0-4 next) (-> obj process-list (+ v1-0 1))) + ) + ) + + ;; set the dead list to that list + (set! (-> obj dead-list next) (the-as dead-pool-heap-rec (-> obj process-list))) + + ;; clear alive list + (set! (-> obj alive-list process) #f) + + ;; terminate dead list. + (set! (-> obj process-list (+ (-> obj allocated-length) -1) next) #f) + (set! (-> obj alive-list prev) (-> obj alive-list)) + (set! (-> obj alive-list next) #f) + (set! (-> obj alive-list process) #f) + (set! (-> obj first-gap) (-> obj alive-list)) + (set! (-> obj first-shrink) #f) + + (cond + ((zero? arg1) + ;; explicit support for a 0 size heap. + (set! (-> obj heap base) (the-as pointer 0)) + (set! (-> obj heap current) (the-as pointer 0)) + (set! (-> obj heap top) (the-as pointer 0)) + (set! (-> obj heap top-base) (the-as pointer 0)) + 0 + ) + (else + ;; otherwise allocate a heap. + (set! (-> obj heap base) (malloc arg0 arg1)) + (set! (-> obj heap current) (-> obj heap base)) + (set! (-> obj heap top) (&+ (-> obj heap base) arg1)) + (set! (-> obj heap top-base) (-> obj heap top)) + ) + ) + (none) + ) + +(defmethod gap-location dead-pool-heap ((obj dead-pool-heap) (arg0 dead-pool-heap-rec)) + "Get the location of the first possible gap after the given record." + (the-as pointer + (if (-> arg0 process) + ;; if we have a process, after that process + (+ (+ (-> arg0 process allocated-length) -4 (-> process size)) (the-as int (-> arg0 process))) + ;; no process, just the start of the dead pool's big heap. + (-> obj heap base) + ) + ) + ) + +(defmethod gap-size dead-pool-heap ((obj dead-pool-heap) (arg0 dead-pool-heap-rec)) + "Get the size of the gap after the given record (possibly 0)" + (cond + ((-> arg0 process) + ;; record has a proc + (let ((v1-3 (&+ (&+ (the-as pointer (-> arg0 process)) (-> process size)) (-> arg0 process allocated-length)))) + (if (-> arg0 next) + ;; and there's a next process, just get the gap in between those + (&- (the-as pointer (-> arg0 next process)) (the-as uint v1-3)) + ;; no next process, the gap is just the distance to the end of the dead pool's heap. + (&- (-> obj heap top) (the-as uint (&+ v1-3 4))) + ) + ) + ) + ((-> arg0 next) + ;; record has no proc, go from start of dead pool heap to the next process. + (&- (the-as pointer (-> arg0 next process)) (the-as uint (&+ (-> obj heap base) 4))) + ) + (else + ;; no processes at all, the gap is the entire heap. + (&- (-> obj heap top) (the-as uint (-> obj heap base))) + ) + ) + ) + +(defmethod find-gap dead-pool-heap ((obj dead-pool-heap) (arg0 dead-pool-heap-rec)) + "Iterate through records, starting at the given one, and find the first one with a gap after it." + (while (and (-> arg0 next) (zero? (gap-size obj arg0))) + (set! arg0 (-> arg0 next)) + ) + arg0 + ) + +(defmethod inspect dead-pool-heap ((obj dead-pool-heap)) + "Inspect a dead-pool heap, printing proccesses and gaps." + (format #t "[~8x] ~A~%" obj (-> obj type)) + (format #t "~Tname: ~A~%" (-> obj name)) + (format #t "~1Tmask: #x~X : (process-mask " (-> obj mask)) + (stream<-process-mask #t (-> obj mask)) + (format #t ")~%") + (format #t "~Tparent: #x~X~%" (-> obj parent)) + (format #t "~Tbrother: #x~X~%" (-> obj brother)) + (format #t "~Tchild: #x~X~%" (-> obj child)) + (format #t "~Tppointer: #x~X~%" (-> obj ppointer)) + (format #t "~Tself: ~A~%" (-> obj self)) + (format #t "~Tallocated-length: ~D~%" (-> obj allocated-length)) + (format #t "~Theap: #~%" (-> obj heap)) + (format #t "~Tfirst-gap: #~%" (-> obj first-gap)) + (format #t "~Tfirst-shrink: #~%" (-> obj first-shrink)) + (format #t "~Talive-list: #~%" (-> obj alive-list)) + (format #t "~Tlast: #~%" (-> obj alive-list prev)) + (format #t "~Tdead-list: #~%" (-> obj dead-list)) + (let* ((s5-0 (&- (-> obj heap top) (the-as uint (-> obj heap base)))) + (v1-3 (if (-> obj alive-list prev) + (gap-size obj (-> obj alive-list prev)) + s5-0 + ) + ) + ) + (format #t "~Tprocess-list[0] @ #x~X ~D/~D bytes used~%" (-> obj process-list) (- s5-0 v1-3) s5-0) + ) + (let ((s5-1 (-> obj alive-list)) + (s4-0 0) + ) + (while s5-1 + (if (-> s5-1 process) + (format #t "~T [~3D] # ~A~%" s4-0 s5-1 (-> s5-1 process)) + ) + (let ((s3-0 (gap-size obj s5-1))) + (if (nonzero? s3-0) + (format #t "~T gap: ~D bytes @ #x~X~%" s3-0 (gap-location obj s5-1)) + ) + ) + (set! s5-1 (-> s5-1 next)) + (+! s4-0 1) + ) + ) + obj + ) + +(defmethod asize-of dead-pool-heap ((obj dead-pool-heap)) + "Get the size in memory of a dead-pool-heap." + (the-as int (+ (-> obj type size) (* 12 (-> obj allocated-length)))) + ) + +(defmethod memory-used dead-pool-heap ((obj dead-pool-heap)) + "Get the amount of used memory. Gaps in between processes are considered used." + (if (-> obj alive-list prev) + (- (memory-total obj) (gap-size obj (-> obj alive-list prev))) + 0 + ) + ) + +(defmethod memory-total dead-pool-heap ((obj dead-pool-heap)) + "Get the total size of the heap." + (&- (-> obj heap top) (the-as uint (-> obj heap base))) + ) + +(defmethod memory-free dead-pool-heap ((obj dead-pool-heap)) + "Get the amount of free memory. Does not include gaps in between processes." + (let ((v1-0 (-> obj heap top))) + (if (-> obj alive-list prev) + (gap-size obj (-> obj alive-list prev)) + (&- v1-0 (the-as uint (-> obj heap base))) + ) + ) + ) + +(defmethod compact-time dead-pool-heap ((obj dead-pool-heap)) + "Not working, likely was supposed to return how long the compaction took." + ;; never set. + (-> obj compact-time) + ) + +(defmethod find-gap-by-size dead-pool-heap ((obj dead-pool-heap) (arg0 int)) + "Find the first gap which is at least the given size." + (let ((gp-0 (-> obj first-gap))) + (while (and gp-0 (< (gap-size obj gp-0) arg0)) + (set! gp-0 (-> gp-0 next)) + ) + gp-0 + ) + ) + +(defmethod get-process dead-pool-heap ((obj dead-pool-heap) (arg0 type) (arg1 int)) + "Get a process!" + (let ((s4-0 (-> obj dead-list next)) + (s3-0 (the-as process #f)) + ) + ;; find a gap! + (let ((s1-0 (find-gap-by-size obj (the-as int (+ (-> process size) arg1))))) + (cond + ((and s4-0 s1-0 (nonzero? (-> obj heap base))) ;; have record, gap, and heap, we are good! + ;; get record + (set! (-> obj dead-list next) (-> s4-0 next)) + (let ((v1-6 (-> s1-0 next))) + (set! (-> s1-0 next) s4-0) + (set! (-> s4-0 next) v1-6) + (if v1-6 + (set! (-> v1-6 prev) s4-0) + ) + ) + (set! (-> s4-0 prev) s1-0) + (if (= s1-0 (-> obj alive-list prev)) + (set! (-> obj alive-list prev) s4-0) + ) + + ;; construct process in-place + (let ((a0-5 (gap-location obj s1-0))) + (set! s3-0 ((method-of-type process new) (the-as symbol a0-5) process "process" arg1)) + ) + + ;; link process to record + (set! (-> s4-0 process) s3-0) + (set! (-> s3-0 ppointer) (&-> s4-0 process)) + + ;; update gap/shrinks + (if (= (-> obj first-gap) s1-0) + (set! (-> obj first-gap) (find-gap obj s4-0)) + ) + (if (or (not (-> obj first-shrink)) (< (the-as int s3-0) (the-as int (-> obj first-shrink process)))) + (set! (-> obj first-shrink) s4-0) + ) + + ;; setup process + (set! (-> s3-0 parent) (-> obj ppointer)) + (set! (-> s3-0 pool) obj) + (set! (-> obj child) (&-> s4-0 process)) + ) + (else + (when (and *debug-segment* (!= obj *debug-dead-pool*)) + (set! s3-0 (get-process *debug-dead-pool* arg0 arg1)) + (if (and s3-0 *vis-boot*) + (format + 0 + "WARNING: ~A ~A had to be allocated from the debug pool, because ~A was empty.~%" + arg0 + s3-0 + (-> obj name) + ) + ) + ) + ) + ) + ) + (if s3-0 + (set! (-> s3-0 type) arg0) + (format 0 "WARNING: ~A ~A could not be allocated, because ~A was empty.~%" arg0 s3-0 (-> obj name)) + ) + s3-0 + ) + ) + +(defmethod return-process dead-pool-heap ((obj dead-pool-heap) (arg0 process)) + "Return a process to the dead pool heap." + (if (!= obj (-> arg0 pool)) + (format 0 "ERROR: process ~A does not belong to dead-pool-heap ~A.~%" arg0 obj) + ) + (change-parent arg0 obj) + (set! (-> obj child) (the-as (pointer process-tree) #f)) + (let ((s5-1 (-> arg0 ppointer))) + (if (or (= (-> obj first-gap) s5-1) (< (the-as int (gap-location obj (the-as dead-pool-heap-rec s5-1))) + (the-as int (gap-location obj (-> obj first-gap))) + ) + ) + (set! (-> obj first-gap) (the-as dead-pool-heap-rec (-> s5-1 1))) + ) + (when (= (-> obj first-shrink) s5-1) + (set! (-> obj first-shrink) (the-as dead-pool-heap-rec (-> s5-1 1))) + (if (not (-> obj first-shrink process)) + (set! (-> obj first-shrink) #f) + ) + ) + (set! (-> s5-1 1 clock) (the-as clock (-> s5-1 2))) + (if (-> s5-1 2) + (set! (-> s5-1 2 mask) (the-as process-mask (-> s5-1 1))) + (set! (-> obj alive-list prev) (the-as dead-pool-heap-rec (-> s5-1 1))) + ) + (set! (-> s5-1 2) (the-as process (-> obj dead-list next))) + (set! (-> obj dead-list next) (the-as dead-pool-heap-rec s5-1)) + (set! (-> s5-1 0) *null-process*) + ) + 0 + (none) + ) + +(defmethod shrink-heap dead-pool-heap ((obj dead-pool-heap) (arg0 process)) + "Shrink the size of a process heap to have 0 free memory. + This is used after the process initializes and all allocations are done." + (when arg0 + (let ((s5-0 (-> arg0 ppointer))) + (when (not (or (logtest? (-> arg0 mask) (process-mask heap-shrunk)) + (and (not (-> arg0 next-state)) (not (-> arg0 state))) + ) + ) + (set! (-> arg0 allocated-length) (&- (-> arg0 heap-cur) (the-as uint (-> arg0 stack)))) + (set! (-> arg0 heap-top) (&-> arg0 stack (-> arg0 allocated-length))) + (if (< (the-as int arg0) (the-as int (gap-location obj (-> obj first-gap)))) + (set! (-> obj first-gap) (find-gap obj (the-as dead-pool-heap-rec s5-0))) + ) + (logior! (-> arg0 mask) (process-mask heap-shrunk)) + ) + (if (= (-> obj first-shrink) s5-0) + (set! (-> obj first-shrink) (the-as dead-pool-heap-rec (-> s5-0 2))) + ) + ) + ) + obj + ) + +(defmethod compact dead-pool-heap ((obj dead-pool-heap) (arg0 int)) + "Relocate processes to remove gaps and increase free memory." + (if (zero? (-> obj heap base)) + (return 0) + ) + (let* ((s4-0 (memory-free obj)) + (v1-5 (memory-total obj)) + (f0-2 (/ (the float s4-0) (the float v1-5))) + ) + (cond + ((< f0-2 0.1) + (set! arg0 1000) + (if (and *debug-segment* (-> *kernel-context* low-memory-message)) + (format *stdcon* "~3LLow Actor Memory~%~0L") + ) + ) + ((< f0-2 0.2) + (set! arg0 (* arg0 4)) + ) + ((< f0-2 0.3) + (set! arg0 (* arg0 2)) + ) + ) + ) + (set! (-> obj compact-count-targ) (the-as uint arg0)) + (set! (-> obj compact-count) (the-as uint 0)) + (while (nonzero? arg0) + (+! arg0 -1) + (let ((v1-19 (-> obj first-shrink))) + (when (not v1-19) + (set! v1-19 (-> obj alive-list next)) + (set! (-> obj first-shrink) v1-19) + ) + (if v1-19 + (shrink-heap obj (-> v1-19 process)) + ) + ) + (let ((s4-1 (-> obj first-gap))) + (when (-> s4-1 next) + (let ((s3-0 (-> s4-1 next process)) + (s2-0 (gap-size obj s4-1)) + ) + (when (nonzero? s2-0) + (when (< s2-0 0) + (break!) + 0 + ) + (shrink-heap obj s3-0) + (relocate s3-0 (- s2-0)) + (set! (-> obj first-gap) (find-gap obj s4-1)) + (+! (-> obj compact-count) 1) + ) + ) + ) + ) + ) + 0 + (none) + ) + +(defmethod churn dead-pool-heap ((obj dead-pool-heap) (arg0 int)) + "Relocate processes to debug process relocation." + (while (nonzero? arg0) + (+! arg0 -1) + (let ((s4-0 (-> obj alive-list next))) + (when s4-0 + (if (or (= (-> obj first-gap) s4-0) + (< (the-as int (gap-location obj s4-0)) (the-as int (gap-location obj (-> obj first-gap)))) + ) + (set! (-> obj first-gap) (-> s4-0 prev)) + ) + (when (= (-> obj first-shrink) s4-0) + (set! (-> obj first-shrink) (-> s4-0 prev)) + (if (not (-> obj first-shrink process)) + (set! (-> obj first-shrink) #f) + ) + ) + (set! (-> s4-0 prev next) (-> s4-0 next)) + (if (-> s4-0 next) + (set! (-> s4-0 next prev) (-> s4-0 prev)) + (set! (-> obj alive-list prev) (-> s4-0 prev)) + ) + (let ((a1-3 (-> obj alive-list prev))) + (let ((v1-19 (-> a1-3 next))) + (set! (-> a1-3 next) s4-0) + (set! (-> s4-0 next) v1-19) + (if v1-19 + (set! (-> v1-19 prev) s4-0) + ) + ) + (set! (-> s4-0 prev) a1-3) + (set! (-> obj alive-list prev) s4-0) + (set! (-> s4-0 process) + (relocate (-> s4-0 process) (&- (gap-location obj a1-3) (the-as uint (&-> (-> s4-0 process) type)))) + ) + ) + ) + ) + ) + 0 + (none) + ) + +(defun method-state ((arg0 type) (arg1 basic)) + "Get a state by name from the method table of a type." + (dotimes (v1-0 (the-as int (-> arg0 allocated-length))) + (let ((a2-2 (the-as basic (-> arg0 method-table v1-0)))) + (if (and (nonzero? (the-as function a2-2)) + (= (-> (the-as function a2-2) type) state) + (= (-> (the-as state a2-2) name) arg1) + ) + (return (the-as state a2-2)) + ) + ) + ) + (the-as state #f) + ) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Process Searching and Iterating +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; goal lambdas don't "capture" variables successfully, so this is a workaround. +(define *global-search-name* (the-as basic #f)) +(define *global-search-count* 0) + +(defun process-by-name ((arg0 string) (arg1 process-tree)) + "Get a process by name." + (set! *global-search-name* arg0) + (the-as process (search-process-tree + arg1 + (lambda ((arg0 process)) (string= (-> arg0 name) (the-as string *global-search-name*))) + ) + ) + ) + +(defun process-not-name ((arg0 string) (arg1 process-tree)) + "Get a process that doesn't have the given name." + (set! *global-search-name* (the-as basic arg0)) + (the-as + process + (search-process-tree + arg1 + (lambda ((arg0 process)) (not (string= (-> arg0 name) (the-as string *global-search-name*)))) + ) + ) + ) + +(defun process-count ((arg0 process-tree)) + "Count the number of processes in the given tree." + (set! *global-search-count* 0) + (iterate-process-tree + arg0 + (lambda ((arg0 process)) (set! *global-search-count* (+ *global-search-count* 1)) #t) + *null-kernel-context* + ) + *global-search-count* + ) + +(defun kill-by-name ((arg0 string) (arg1 process-tree)) + "Kill all processes with the given name." + (local-vars (a0-1 process)) + (while (begin (set! a0-1 (process-by-name arg0 arg1)) a0-1) + (deactivate a0-1) + ) + #f + ) + +(defun kill-by-type ((arg0 type) (arg1 process-tree)) + "Kill all processes with the given type." + (local-vars (a0-1 process-tree)) + (set! *global-search-name* arg0) + (while (begin + (set! a0-1 (search-process-tree arg1 (lambda ((arg0 process)) (= (-> arg0 type) *global-search-name*)))) + a0-1 + ) + (deactivate a0-1) + ) + #f + ) + +(defun kill-not-name ((arg0 string) (arg1 process-tree)) + "Kill all processes, except for ones named this." + (local-vars (a0-1 process)) + (while (begin (set! a0-1 (process-not-name arg0 arg1)) a0-1) + (deactivate a0-1) + ) + #f + ) + +(defun kill-not-type ((arg0 type) (arg1 process-tree)) + "Kill all processes not of the given type." + (local-vars (a0-1 process-tree)) + (set! *global-search-name* arg0) + (while (begin + (set! a0-1 (search-process-tree arg1 (lambda ((arg0 process)) (!= (-> arg0 type) *global-search-name*)))) + a0-1 + ) + (deactivate a0-1) + ) + #f + ) + +(defmethod run-logic? process ((obj process)) + "Should this process be run by the kernel?" + #t + ) + +(defun iterate-process-tree ((arg0 process-tree) (arg1 (function object object)) (arg2 kernel-context)) + "Iterate over the process tree, calling the function on each process." + (let ((s4-0 (or (logtest? (-> arg0 mask) (process-mask process-tree)) (arg1 arg0)))) + (cond + ((= s4-0 'dead) + ;; the function returned dead, don't look at children. + ) + (else + ;; iterate over children too. + (let ((v1-4 (-> arg0 child))) + (while v1-4 + (let ((s3-1 (-> v1-4 0 brother))) + (iterate-process-tree (-> v1-4 0) arg1 arg2) + (set! v1-4 s3-1) + ) + ) + ) + ) + ) + s4-0 + ) + ) + +(defun execute-process-tree ((arg0 process-tree) (arg1 (function object object)) (arg2 kernel-context)) + "Iterate over the process tree, running only if the mask doesn't prevent it. + Update the mask of the process-tree to have kernel-run if and only if we run at least one process." + + ;; start with this cleared + (logclear! (-> arg0 mask) (process-mask kernel-run)) + + ;; prevent run if: + ;; - we are a process-tree + ;; - we are prevent-from-run + ;; - we don't return #t for run-logic? + (let ((s3-0 (or (logtest? (-> arg0 mask) (process-mask process-tree)) + ;; prevent if not both (clear to run and run-logic? = #t) + (not (and (zero? (logand (-> arg2 prevent-from-run) (-> arg0 mask))) (run-logic? arg0))) + (begin (logior! (-> arg0 mask) (process-mask kernel-run)) (arg1 arg0)) + ) + ) + ) + (cond + ((= s3-0 'dead) + ;; don't check children if dead. + ) + (else + (let ((v1-12 (-> arg0 child))) + (while v1-12 + (let ((s4-1 (-> v1-12 0 brother))) + (execute-process-tree (-> v1-12 0) arg1 arg2) + (set! v1-12 s4-1) + ) + ) + ) + ) + ) + s3-0 + ) + ) + +(defun search-process-tree ((arg0 process-tree) (arg1 (function process-tree object))) + "Iterate process tree, returning the process that returns #t first." + (when (zero? (logand (-> arg0 mask) (process-mask process-tree))) + (if (arg1 arg0) + (return arg0) + ) + ) + (let ((v1-5 (-> arg0 child))) + (while v1-5 + (let ((s5-1 (-> v1-5 0 brother))) + (let ((v1-6 (search-process-tree (-> v1-5 0) arg1))) + (if v1-6 + (return v1-6) + ) + ) + (set! v1-5 s5-1) + ) + ) + ) + (the-as process-tree #f) + ) + +(defun kernel-dispatcher () + "Main entry point to GOAL from C++." + + ;; added + ;; outside of all profiler events, set a ROOT event + (profiler-instant-event "ROOT") + + ;; run any listener functions + (when *listener-function* + (set! *enable-method-set* (+ *enable-method-set* 1)) + (let ((t1-0 (reset-and-call (-> *listener-process* main-thread) *listener-function*))) + (if *use-old-listener-print* + (format #t "~D~%" t1-0 t1-0 t1-0) + (format #t "~D #x~X ~F ~A~%" t1-0 t1-0 t1-0 t1-0) + ) + ) + (set! *listener-function* #f) + (set! *enable-method-set* (+ *enable-method-set* -1)) + ) + + ;; for each active proces... + (execute-process-tree + *active-pool* + (lambda ((arg0 process)) + (let ((s5-0 *kernel-context*)) + (case (-> arg0 status) + (('waiting-to-run 'suspended) + ;; we'll run this process + (profiler-start-event (-> arg0 name)) + (set! (-> s5-0 current-process) arg0) + (cond + ((logtest? (-> arg0 mask) (process-mask pause)) + ;; we can be paused, write messages/debug-draw to buffers that aren't cleared + ;; when the game is paused. + (set! *stdcon* *stdcon1*) + (set! *debug-draw-pauseable* #t) + ) + (else + ;; not pausable, write to buffers that clear each frame. + (set! *stdcon* *stdcon0*) + (set! *debug-draw-pauseable* #f) + ) + ) + + ;; run the trans function. + (when (-> arg0 trans-hook) + (let ((s4-0 (new 'process 'cpu-thread arg0 'trans 256 (-> arg0 main-thread stack-top)))) + (reset-and-call s4-0 (-> arg0 trans-hook)) + (delete s4-0) + ) + (when (= (-> arg0 status) 'dead) ;; handle deactivates in trans + (set! (-> s5-0 current-process) #f) + (profiler-end-event) + (return 'dead) + ) + ) + + ;; run the main thread! + (if (logtest? (-> arg0 mask) (process-mask sleep-code)) + (set! (-> arg0 status) 'suspended) + ((-> arg0 main-thread resume-hook) (-> arg0 main-thread)) + ) + + + (cond + ((= (-> arg0 status) 'dead) ;; handle death in main thread. + (set! (-> s5-0 current-process) #f) + (profiler-end-event) + 'dead + ) + (else + ;; run post. + ;; NOTE: post always runs on the dram stack, so you can use ja-post and use the scratchpad for anims. + (when (-> arg0 post-hook) + (let ((s4-1 (new 'process 'cpu-thread arg0 'post 256 *kernel-dram-stack*))) + (reset-and-call s4-1 (-> arg0 post-hook)) + (delete s4-1) + ) + (when (= (-> arg0 status) 'dead) ;; handle death in post + (set! (-> s5-0 current-process) #f) + (profiler-end-event) + (return 'dead) + ) + (set! (-> arg0 status) 'suspended) + ) + ;; done with process. + (set! (-> s5-0 current-process) #f) + (profiler-end-event) + #f + ) + ) + ) + (('dead) + 'dead + ) + ) + ) + ) + *kernel-context* + ) + ) + +#| (defun kernel-dispatcher () "Run the kernel! This is the entry point from C++ to GOAL." @@ -31,4 +1598,765 @@ (set! *listener-function* #f) (+! *enable-method-set* -1) ) - ) \ No newline at end of file + ) + +|# + +(defun sync-dispatcher () + "Run just the listener function. Used for SQL query stuff." + (let ((t9-0 *listener-function*)) + (the-as object (when t9-0 + (set! *listener-function* #f) + (t9-0) + #f + ) + ) + ) + ) + +(defun inspect-process-tree ((arg0 process-tree) (arg1 int) (arg2 int) (arg3 symbol)) + "Print out a process tree diagram." + (print-tree-bitmask arg2 arg1) + (cond + (arg3 + (format #t "__________________~%") + (format + #t + "~S~A~%" + (if (zero? arg1) + "" + "+---" + ) + arg0 + ) + (let ((s2-0 *print-column*)) + (set! *print-column* (the binteger (* arg1 4))) + (inspect arg0) + (set! *print-column* s2-0) + ) + ) + (else + (format + #t + "~S~A~%" + (if (zero? arg1) + "" + "+---" + ) + arg0 + ) + ) + ) + (let ((s2-1 (-> arg0 child))) + (while s2-1 + (inspect-process-tree + (-> s2-1 0) + (+ arg1 1) + (if (not (-> s2-1 0 brother)) + arg2 + (logior arg2 (ash 1 (+ arg1 1))) + ) + arg3 + ) + (set! s2-1 (-> s2-1 0 brother)) + ) + ) + arg0 + ) + +(defmacro set-u128-as-u64! (dst src) + `(set! (-> (the (pointer uint64) (& ,dst))) + ,src + ) + ) + +(defmacro set-u64-from-u128! (dst src) + `(set! ,dst (-> (the (pointer uint64) (& ,src)))) + ) + +(defmacro the-super-u64-fucntion (func) + `(the-as (function uint uint uint uint uint uint object) ,func) + ) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Stack Frame Stuff +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; The GOAL kernel supports dynamic throw and catch. +;; The catch frames are managed per process (you can't throw to a frame outside your process) +;; But otherwise it is fully dynamic. + +(defmethod new catch-frame ((allocation symbol) (type-to-make type) (name symbol) (func function) (param-block (pointer uint64))) + "Run func in a catch frame with the given 8 parameters. + The return value is the result of the function. + The allocation must be an address. + Unlike the original, this only works on the first six parameters, but I think this doesn't matter." + (declare (asm-func object) + (allow-saved-regs) ;; very dangerous! + ) + + (rlet ((pp :reg r13 :type process) + (temp :reg rax :type uint) + (off :reg r15 :type uint) + (sp :reg rsp :type uint) + (s0 :reg rbx :type uint) + (s1 :reg rbp :type uint) + (s2 :reg r10 :type (pointer uint64)) + (s3 :reg r11 :type uint) + (s4 :reg r12 :type uint) + + (xmm8 :reg xmm8 :class fpr) + (xmm9 :reg xmm9 :class fpr) + (xmm10 :reg xmm10 :class fpr) + (xmm11 :reg xmm11 :class fpr) + (xmm12 :reg xmm12 :class fpr) + (xmm13 :reg xmm13 :class fpr) + (xmm14 :reg xmm14 :class fpr) + (xmm15 :reg xmm15 :class fpr) + ) + + ;; we treat the allocation as an address. + (let ((obj (the catch-frame (&+ (the pointer allocation) *gtype-basic-offset*)))) + ;; setup catch frame + (set! (-> obj type) type-to-make) + (set! (-> obj name) name) + ;; get the return address (the compiler won't touch the stack because we're an asm-func) + (.pop temp) + (.push temp) + ;; make it a GOAL address so it fits in 32 bits + (.sub temp off) + ;; store it + (set! (-> obj ra) (the int temp)) + + ;; todo, do we need a stack offset here? + ;; remember the stack pointer + (set! temp sp) + (.sub temp off) + (set! (-> obj sp) (the int temp)) + + ;; back up registers we care about + (.mov :color #f temp s0) + (set-u128-as-u64! (-> obj rreg 0) temp) + (.mov :color #f temp s1) + (set-u128-as-u64! (-> obj rreg 1) temp) + (.mov :color #f temp s2) + (set-u128-as-u64! (-> obj rreg 2) temp) + (.mov :color #f temp s3) + (set-u128-as-u64! (-> obj rreg 3) temp) + (.mov :color #f temp s4) + (set-u128-as-u64! (-> obj rreg 4) temp) + + (.mov :color #f temp xmm8) + (set! (-> obj freg 0) (the-as float temp)) + (.mov :color #f temp xmm9) + (set! (-> obj freg 1) (the-as float temp)) + (.mov :color #f temp xmm10) + (set! (-> obj freg 2) (the-as float temp)) + (.mov :color #f temp xmm11) + (set! (-> obj freg 3) (the-as float temp)) + (.mov :color #f temp xmm12) + (set! (-> obj freg 4) (the-as float temp)) + (.mov :color #f temp xmm13) + (set! (-> obj freg 5) (the-as float temp)) + (.mov :color #f temp xmm14) + (set! (-> obj freg 6) (the-as float temp)) + (.mov :color #f temp xmm15) + (set! (-> obj freg 7) (the-as float temp)) + + ;; push this stack frame + (set! (-> obj next) (-> pp stack-frame-top)) + (set! (-> pp stack-frame-top) obj) + + ;; help coloring, it isn't smart enough to realize it's "safe" to use these registers. + (.push :color #f s3) + (.push :color #f s2) + (.push :color #f s2) + (set! s3 (the uint func)) + (set! s2 param-block) + + ;; todo - are we aligned correctly here? + (let ((ret ((the-super-u64-fucntion s3) + (-> s2 0) + (-> s2 1) + (-> s2 2) + (-> s2 3) + (-> s2 4) + (-> s2 5) + )) + ) + (.pop :color #f s2) + (.pop :color #f s2) + (.pop :color #f s3) + (set! (-> pp stack-frame-top) (-> pp stack-frame-top next)) + (.ret) + (the object ret) + ) + ) + ) + ) + + +(defun throw-dispatch ((obj catch-frame) value) + "Throw the given value to the catch frame. + Only can throw a 64-bit value. The original could throw 128 bits." + (declare (asm-func none)) + + (rlet ((pp :reg r13 :type process) + (temp :reg rax :type uint) + (off :reg r15 :type uint) + (sp :reg rsp :type uint) + (s0 :reg rbx :type uint) + (s1 :reg rbp :type uint) + (s2 :reg r10 :type (pointer uint64)) + (s3 :reg r11 :type uint) + (s4 :reg r12 :type uint) + + (temp-float :reg xmm0 :class fpr) + (xmm8 :reg xmm8 :class fpr) + (xmm9 :reg xmm9 :class fpr) + (xmm10 :reg xmm10 :class fpr) + (xmm11 :reg xmm11 :class fpr) + (xmm12 :reg xmm12 :class fpr) + (xmm13 :reg xmm13 :class fpr) + (xmm14 :reg xmm14 :class fpr) + (xmm15 :reg xmm15 :class fpr) + ) + + ;; pop everything we threw past + (set! (-> pp stack-frame-top) (-> obj next)) + + ;; restore regs we care about. + (set-u64-from-u128! temp (-> obj rreg 0)) + (.mov :color #f s0 temp) + (set-u64-from-u128! temp (-> obj rreg 1)) + (.mov :color #f s1 temp) + (set-u64-from-u128! temp (-> obj rreg 2)) + (.mov :color #f s2 temp) + (set-u64-from-u128! temp (-> obj rreg 3)) + (.mov :color #f s3 temp) + (set-u64-from-u128! temp (-> obj rreg 4)) + (.mov :color #f s4 temp) + + (set! temp-float (-> obj freg 0)) + (.mov :color #f xmm8 temp-float) + (set! temp-float (-> obj freg 1)) + (.mov :color #f xmm9 temp-float) + (set! temp-float (-> obj freg 2)) + (.mov :color #f xmm10 temp-float) + (set! temp-float (-> obj freg 3)) + (.mov :color #f xmm11 temp-float) + (set! temp-float (-> obj freg 4)) + (.mov :color #f xmm12 temp-float) + (set! temp-float (-> obj freg 5)) + (.mov :color #f xmm13 temp-float) + (set! temp-float (-> obj freg 6)) + (.mov :color #f xmm14 temp-float) + (set! temp-float (-> obj freg 7)) + (.mov :color #f xmm15 temp-float) + + ;; set stack pointer + (set! sp (the uint (-> obj sp))) + (.add sp off) + + ;; overwrite our return address + (.pop temp) + (set! temp (the uint (-> obj ra))) + (.add temp off) + (.push temp) + + ;; load the return register + (.mov temp value) + (.ret) + ) + ) + +(defun throw ((name symbol) value) + "Dynamic throw." + (rlet ((pp :reg r13 :type process)) + (let ((cur (-> pp stack-frame-top))) + (while cur + (when (and (eq? (-> cur name) name) (eq? (-> cur type) catch-frame)) + ;; match! + + (throw-dispatch (the catch-frame cur) value) + ) + + (if (eq? (-> cur type) protect-frame) + ;; call the cleanup function + ((-> (the protect-frame cur) exit)) + ) + (set! cur (-> cur next)) + ) + ) + ) + (format 0 "ERROR: throw could not find tag ~A~%" name) + (break) + ) + +(defmethod new protect-frame ((allocation symbol) (type-to-make type) (arg0 (function none))) + "Create a new protect frame, must be on the stack." + (with-pp + (let ((v0-0 (the-as protect-frame (+ (the-as int allocation) 4)))) + (set! (-> v0-0 type) type-to-make) + (set! (-> v0-0 name) 'protect-frame) + (set! (-> v0-0 exit) arg0) + (set! (-> v0-0 next) (-> pp stack-frame-top)) + (set! (-> pp stack-frame-top) v0-0) + v0-0 + ) + ) + ) + +(defun previous-brother ((arg0 process-tree)) + (let ((v1-0 (-> arg0 parent))) + (when v1-0 + (let ((v1-2 (-> v1-0 0 child))) + (if (= v1-2 arg0) + (return (the-as object #f)) + ) + (while v1-2 + (if (= (-> v1-2 0 brother) arg0) + (return (the-as object v1-2)) + ) + (set! v1-2 (-> v1-2 0 brother)) + ) + ) + (the-as (pointer process-tree) #f) + ) + ) + ) + +(defun change-parent ((arg0 process-tree) (arg1 process-tree)) + (let ((a2-0 (-> arg0 parent))) + (when a2-0 + (let ((v1-2 (-> a2-0 0 child))) + (cond + ((= (ppointer->process v1-2) arg0) + (set! (-> a2-0 0 child) (-> arg0 brother)) + ) + (else + (while (!= (ppointer->process (-> v1-2 0 brother)) arg0) + (nop!) + (nop!) + (nop!) + (set! v1-2 (-> v1-2 0 brother)) + ) + (set! (-> v1-2 0 brother) (-> arg0 brother)) + ) + ) + ) + ) + ) + (set! (-> arg0 parent) (-> arg1 ppointer)) + (set! (-> arg0 brother) (-> arg1 child)) + (set! (-> arg1 child) (-> arg0 ppointer)) + arg0 + ) + +(defun change-brother ((arg0 process-tree) (arg1 process-tree)) + (when (and arg0 (!= (-> arg0 brother) arg1) (!= arg0 arg1)) + (let ((a2-1 (-> arg0 parent))) + (when a2-1 + (let ((t0-0 (-> a2-1 0 child)) + (a3-1 (the-as (pointer process-tree) #f)) + (v1-4 (the-as (pointer process-tree) #f)) + ) + (if (= (ppointer->process t0-0) arg0) + (set! a3-1 a2-1) + ) + (if (= (ppointer->process t0-0) arg1) + (set! v1-4 a2-1) + ) + (while (and (-> t0-0 0 brother) (or (not a3-1) (not v1-4))) + (if (= (-> (ppointer->process t0-0) brother) arg1) + (set! v1-4 t0-0) + ) + (if (= (-> (ppointer->process t0-0) brother) arg0) + (set! a3-1 t0-0) + ) + (set! t0-0 (-> t0-0 0 brother)) + ) + (cond + ((or (not a3-1) (not v1-4)) + (return 0) + ) + ((= a3-1 a2-1) + (set! (-> a3-1 5) (the-as process-tree (-> arg0 brother))) + ) + (else + (set! (-> a3-1 4) (the-as process-tree (-> arg0 brother))) + ) + ) + (cond + ((= v1-4 a2-1) + (set! (-> arg0 brother) (the-as (pointer process-tree) (-> v1-4 5))) + (set! (-> v1-4 5) (the-as process-tree (-> arg0 ppointer))) + ) + (else + (set! (-> arg0 brother) (the-as (pointer process-tree) (-> v1-4 4))) + (set! (-> v1-4 4) (the-as process-tree (-> arg0 ppointer))) + ) + ) + ) + ) + ) + ) + arg0 + ) + +(defun change-to-last-brother ((arg0 process-tree)) + (when (and (-> arg0 brother) (-> arg0 parent)) + (let* ((a1-0 (-> arg0 parent)) + (v1-4 (-> a1-0 0 child)) + ) + (cond + ((= (-> v1-4 0) arg0) + (set! (-> a1-0 0 child) (-> arg0 brother)) + ) + (else + (while (!= (-> v1-4 0 brother 0) arg0) + (nop!) + (nop!) + (nop!) + (nop!) + (set! v1-4 (-> v1-4 0 brother)) + ) + (set! (-> v1-4 0 brother) (-> arg0 brother)) + ) + ) + (while (-> v1-4 0 brother) + (nop!) + (nop!) + (nop!) + (nop!) + (set! v1-4 (-> v1-4 0 brother)) + ) + (set! (-> v1-4 0 brother) (-> arg0 ppointer)) + ) + (set! (-> arg0 brother) (the-as (pointer process-tree) #f)) + ) + arg0 + ) + +(defmethod activate process ((obj process) (arg0 process-tree) (arg1 basic) (arg2 pointer)) + + ;; if we got the scratchpad stack, move to the fake scratchpad. + (#when PC_PORT + (when (= arg2 *scratch-memory-top*) + (set! arg2 (&+ *fake-scratchpad-stack* (* 32 1024))) + ) + ) + (set! (-> obj mask) (logclear (-> arg0 mask) (process-mask sleep sleep-code process-tree heap-shrunk))) + (set! (-> obj clock) (-> arg0 clock)) + (set! (-> obj status) 'ready) + (let ((v1-5 (-> *kernel-context* next-pid))) + (set! (-> obj pid) v1-5) + (set! (-> *kernel-context* next-pid) (+ v1-5 1)) + ) + (set! (-> obj top-thread) #f) + (set! (-> obj main-thread) #f) + (set! (-> obj name) (the-as string arg1)) + (let ((v1-10 (&-> obj stack (-> obj type heap-base)))) + (set! (-> obj heap-cur) v1-10) + (set! (-> obj heap-base) v1-10) + ) + (set! (-> obj stack-frame-top) #f) + (mem-set32! (-> obj stack) (the-as int (shr (-> obj type heap-base) 2)) 0) + (set! (-> obj trans-hook) #f) + (set! (-> obj post-hook) #f) + (set! (-> obj event-hook) #f) + (set! (-> obj state) #f) + (set! (-> obj next-state) #f) + (cond + ((logtest? (-> arg0 mask) (process-mask process-tree)) + (set! (-> obj entity) #f) + (set! (-> obj level) *default-level*) + ) + (else + (set! (-> obj entity) (-> (the-as process arg0) entity)) + (set! (-> obj level) (-> (the-as process arg0) level)) + ) + ) + (set! (-> obj connection-list next1) #f) + (set! (-> obj connection-list prev1) #f) + (set! (-> obj main-thread) (new 'process 'cpu-thread obj 'code 256 arg2)) + (change-parent obj arg0) + ) + + +(defun run-function-in-process ((obj process) (func function) a0 a1 a2 a3 a4 a5) + "Switch to the given process and run the function. This is used to initialize a process. + The function will run until it attempts to change state. At the first attempt to change state, + this function will return. The idea is that you use this when you want to initialize a process NOW. + This will then return the value of the function you called!" + (rlet ((pp :reg r13 :type process)) + + (let ((param-array (new 'stack-no-clear 'array 'uint64 6)) + ) + ;; copy params to the stack. + + (set! (-> param-array 0) (the uint64 a0)) + (set! (-> param-array 1) (the uint64 a1)) + (set! (-> param-array 2) (the uint64 a2)) + (set! (-> param-array 3) (the uint64 a3)) + (set! (-> param-array 4) (the uint64 a4)) + (set! (-> param-array 5) (the uint64 a5)) + + (let* ((old-pp pp) + (func-val (begin + ;; set the process + (set! pp obj) + ;; set us as initializing + (set! (-> pp status) 'initialize) + ;; run! + (the object (new 'stack 'catch-frame 'initialize func param-array)) + ))) + ;; the function returned, either through a throw or through actually returning. + ;; the status will give us a clue of what happened. + (case (-> pp status) + (('initialize) + ;; we returned and didn't change status. + (set! (-> pp status) 'initialize-dead) + ;; this means we died, and we should be deactivated. + (deactivate pp) + ) + (('initialize-go) + ;; we returned with a (suspend) or (go) ? not sure + ;; either way, we're ready for next time! + (set! (-> pp status) 'waiting-to-run) + (when (eq? (-> pp pool type) dead-pool-heap) + ;; we can shrink the heap now. + (shrink-heap (the dead-pool-heap (-> pp pool)) pp) + ) + ) + (else + (format 0 "GOT UNKNOWN INIT: ~A~%" (-> pp status)) + ) + ) + ;; restore the old pp + (set! pp old-pp) + func-val + ) + ) + ) + ) + +(defun set-to-run-bootstrap () + "This function is a clever hack. + To reset a thread to running a new function, we stash the arguments as saved registers. + These are then restored by thread-resume on the next run of the kernel. + This stub remaps these saved registers to argument registers. + It also creates a return trampoline to return-from-thread-dead, so if the main thread returns, the + process is properly cleaned up by deactivate." + (declare (asm-func none) + ;;(print-asm) + ) + + (rlet ((s0 :reg rbx :type uint) + (s1 :reg rbp :type uint) + (s2 :reg r10 :type uint) + (s3 :reg r11 :type uint) + (s4 :reg r12 :type uint) + (a0 :reg rdi :type uint) ; ok + (a1 :reg rsi :type uint) ; ok + (a2 :reg rdx :type uint) ; ok + (a3 :reg rcx :type uint) ; ok + (off :reg r15 :type uint) + (a4 :reg r8 :type uint) + (a5 :reg r9 :type uint) + (temp :reg rax) + ) + + + (.mov temp return-from-thread-dead) + (.add temp off) + (.push temp) + + ;; stack is 16 + 8 aligned now + + (.mov :color #f a0 s1) + (.mov :color #f a1 s2) + (.mov :color #f a2 s3) + (.mov :color #f a3 s4) + + (.add :color #f s0 off) + (.jr :color #f s0) + + (.add a4 a4) + (.add a5 a5) + ) + + ) + + +(defun set-to-run ((thread cpu-thread) (func function) a0 a1 a2 a3 a4 a5) + "Set the given thread to call the given function with the given arguments next time it resumes. + Only for main threads. + Once the function returns, the process deactivates." + (let ((proc (-> thread process))) + (set! (-> proc status) 'waiting-to-run) + + ;; we store arguments and the function to call in saved registers + (set! (-> thread rreg 0) (the uint func)) + (set! (-> thread rreg 1) (the uint a0)) + (set! (-> thread rreg 2) (the uint a1)) + (set! (-> thread rreg 3) (the uint a2)) + (set! (-> thread rreg 4) (the uint a3)) + (set! (-> thread rreg 5) (the uint a4)) + (set! (-> thread rreg 6) (the uint a5)) + + ;; and have the thread first call set-to-run-bootstrap, which will properly call + ;; the function with the arguments and install a return trampoline for + ;; deactivating and returning to the kernel on return. + (set! (-> thread pc) (the pointer set-to-run-bootstrap)) + ;; reset sp. + (set! (-> thread sp) (-> thread stack-top)) + ) + ) + +(defmethod deactivate process-tree ((obj process-tree)) + (none) + ) + +;; The defstate macro isn't defined yet, so we do it manually. +(define dead-state + (the (state process) (new 'static 'state + :name 'dead-state + :next #f + :exit #f + :code #f + :trans #f + :post #f + :enter #f + :event #f))) + +(set! (-> dead-state code) (the (function none :behavior process) nothing)) +(define entity-deactivate-handler (the-as (function process entity-actor none) nothing)) + +(defmethod deactivate process ((obj process)) + (with-pp + (when (!= (-> obj status) 'dead) + (set! (-> obj next-state) dead-state) + (if (-> obj entity) + (entity-deactivate-handler obj (the-as entity-actor (-> obj entity))) + ) + (let ((s5-0 pp)) + (set! pp obj) + (let ((s4-0 (-> pp stack-frame-top))) + (while (the-as protect-frame s4-0) + (case (-> s4-0 type) + ((protect-frame state) + ((-> (the-as protect-frame s4-0) exit)) + ) + ) + (set! s4-0 (-> (the-as protect-frame s4-0) next)) + ) + ) + (set! pp s5-0) + ) + (if (!= 0 (the uint process-disconnect)) + (process-disconnect obj) + ) + (let ((v1-12 (-> obj child))) + (while v1-12 + (let ((s5-1 (-> v1-12 0 brother))) + (deactivate (-> v1-12 0)) + (set! v1-12 s5-1) + ) + ) + ) + (return-process (-> obj pool) obj) + (set! (-> obj state) #f) + (set! (-> obj next-state) #f) + (set! (-> obj entity) #f) + (set! (-> obj pid) 0) + (cond + ((= (-> *kernel-context* current-process) obj) + (set! (-> obj status) 'dead) + (let ((temp (the uint return-from-thread))) + (rlet ((off :reg r15 :type uint)) + (+! temp off) + (.push temp) + (.ret) + ) + ) + ) + ((= (-> obj status) 'initialize) + (set! (-> obj status) 'dead) + (throw 'initialize #f) + ) + ) + (set! (-> obj status) 'dead) + ) + 0 + (none) + ) + ) + +(kmemopen global "process-buffers") + +(let ((v0-43 (new 'global 'process "listener" 2048))) + (set! *listener-process* v0-43) + (let ((gp-0 v0-43)) + (set! (-> gp-0 status) 'ready) + (set! (-> gp-0 pid) 1) + (set! (-> gp-0 main-thread) (new 'process 'cpu-thread gp-0 'main 256 (&-> *dram-stack* 14336))) + ) + ) + +(define *null-process* (new 'global 'process "null" 16)) +(define *vis-boot* #f) +(define *kernel-clock* (new 'static 'clock)) +(define *16k-dead-pool* (new 'global 'dead-pool 2 #x4000 "*16k-dead-pool*")) +(define *8k-dead-pool* (new 'global 'dead-pool 2 8192 "*8k-dead-pool*")) +(define *4k-dead-pool* (new 'global 'dead-pool 4 4096 "*4k-dead-pool*")) +(define *target-dead-pool* (new 'global 'dead-pool 2 #xc000 "*target-dead-pool*")) +(define *camera-dead-pool* (new 'global 'dead-pool 7 4096 "*camera-dead-pool*")) +(define *camera-master-dead-pool* (new 'global 'dead-pool 1 8192 "*camera-master-dead-pool*")) +(when *debug-segment* + (define *debug-dead-pool* (new 'debug 'dead-pool-heap "*debug-dead-pool*" 768 #x100000)) + ) +(define *nk-dead-pool* (new 'global 'dead-pool-heap "*nk-dead-pool*" 768 #x181000)) +(define *default-dead-pool* (the-as dead-pool *nk-dead-pool*)) +(define *pickup-dead-pool* (the-as dead-pool *nk-dead-pool*)) +(define *city-dead-pool* (new 'loading-level 'dead-pool-heap "*city-dead-pool*" 256 0)) +(define *dead-pool-list* '(*4k-dead-pool* + *8k-dead-pool* + *16k-dead-pool* + *nk-dead-pool* + *target-dead-pool* + *camera-dead-pool* + *camera-master-dead-pool* + ) + ) +(define *active-pool* (new 'global 'process-tree "active-pool")) + +(change-parent (define *display-pool* (new 'global 'process-tree "display-pool")) *active-pool*) + +(change-parent (define *camera-pool* (new 'global 'process-tree "camera-pool")) *active-pool*) +(set! (-> *camera-pool* mask) (process-mask freeze pause menu progress process-tree camera)) + +(change-parent (define *target-pool* (new 'global 'process-tree "target-pool")) *active-pool*) +(set! (-> *target-pool* mask) (process-mask freeze pause menu progress process-tree)) + +(change-parent (define *entity-pool* (new 'global 'process-tree "entity-pool")) *active-pool*) +(set! (-> *entity-pool* mask) (process-mask freeze pause menu progress process-tree entity)) + +(change-parent (define *mid-pool* (new 'global 'process-tree "mid-pool")) *active-pool*) + +(change-parent (define *pusher-pool* (new 'global 'process-tree "pusher-pool")) *active-pool*) +(set! (-> *pusher-pool* mask) (process-mask freeze pause menu progress process-tree entity)) + +(change-parent (define *bg-pool* (new 'global 'process-tree "bg-pool")) *active-pool*) +(set! (-> *bg-pool* mask) (process-mask freeze pause menu progress process-tree)) + +(change-parent (define *default-pool* (new 'global 'process-tree "default-pool")) *active-pool*) +(set! (-> *default-pool* mask) (process-mask freeze pause menu progress process-tree)) + +(kmemclose) + +(defmacro ps (&key (detail #f)) + `(inspect-process-tree *active-pool* 0 0 ,detail) + ) + + diff --git a/goal_src/jak2/kernel/gstate.gc b/goal_src/jak2/kernel/gstate.gc index 664d614790..2be8748a07 100644 --- a/goal_src/jak2/kernel/gstate.gc +++ b/goal_src/jak2/kernel/gstate.gc @@ -1,7 +1,508 @@ -;;-*-Lisp-*- +;-*-Lisp-*- (in-package goal) ;; name: gstate.gc ;; name in dgo: gstate ;; dgos: KERNEL +#| +Summary of state system: + +A process can be put into a state, using enter-state, or the go macro. +This will set up the process to run the appropriate handler functions defined by the state. +The state handlers are: +- enter : gets run before trans on the first time the state is used. Can be #f. Must return. +- trans : gets run before code each time the code is run. Can be #f. Must return. +- code : main thread. Can suspend. If it returns, the process dies +- exit : gets run when leaving a state. must return. +- event : not sure of the details here yet. + +You can use "go" to change the state of a process. This causes the process main thread execution to be abandoned. +If the main thread has exits/protects on the stack frame, they will be run first to clean up. + +There are several ways to "go" +- go during init: when a process is being initialized with run-function-in-process, you can "go". + this causes the run-function-in-process to return immediately, and the next time the process is dispatched + it will go into the other state. This will automatically set the process to waiting-to-run, + and shrink the process heap, if appropriate + +- go from outside the process. You can temporarily set pp to another process, and have that + process go to another state. The actual go will occur the next time the process is scheduled. + Use the go-process macro to do this. + +- go from a non-main thread in the right process. You can do a go from a temporary thread, like trans or post. + If you do it from post, the go returns and the rest of the post runs. If you do it from any other thread, the temporary thread + is immediately abandonded. Like the previous two, it will defer the actual go until the next time the + process runs. + +- go from the main thread of the main process. This causes the (-> pp state) to change, the stack frames + to be cleaned up, and the old state's exit to be called. It will reset the stack, then run the code. + Unlike the others, this means you "go" immediately. + +The compiler has two special hooks related to states: go-hook and define-state-hook. +These take care of doing a go and a state definition and properly checking types. + +The define-state-hook takes a state object and handlers and defines a global symbol +with the appropriate state type. + +The go-hook calls enter state and sets (-> proc next-state) for the given process. +It type checks the arguments for the entry function. + +|# + +(defmacro go (next-state &rest args) + "Change the state of the current process. + This will only return if this is called within the post thread. + Otherwise, execution stops here and the kernel will run the next state next time." + `(with-pp + (go-hook pp ,next-state ,@args) + ) + ) + +(defmacro go-virtual (state-name &key (proc self) &rest args) + "Same as go, but use a virtual state." + `(go (method-of-object ,proc ,state-name) ,@args) + ) + +(defmacro go-process (proc next-state &rest args) + "Make another process go." + `(with-pp + (protect (pp) + (set! pp ,proc) + (go-hook pp ,next-state ,@args) + ) + ) + ) + +;; run the given function in a process right now. +;; will return to here when: +;; - you return +;; - you deactivate +;; - you go +;; - you throw to 'initialize +(defmacro run-now-in-process (proc func &rest args) + "Run a function in another process right now." + `((the (function _varargs_ object) run-function-in-process) + ,proc ,func ,@args + ) + ) + +;; sets the main thread of the given process to run the given thing. +;; this resets the main thread stack back to the top +(defmacro run-next-time-in-process (proc func &rest args) + "Set up a process to run a function the next time it is scheduled." + `((the (function _varargs_ object) set-to-run) + (-> ,proc main-thread) ,func ,@args + ) + ) + +(defmacro process-spawn-function (proc-type func &key (from *default-dead-pool*) &key (to *default-pool*) &key (name #f) &key (stack-size #x4000) &key (stack *scratch-memory-top*) &rest args) + "Start a new process that runs a function on its main thread. + Returns a pointer to the new process (or #f? on error)." + + (with-gensyms (new-proc) + `(let ((,new-proc (the-as ,proc-type (get-process ,from ,proc-type ,stack-size)))) + (when ,new-proc + ((method-of-type ,proc-type activate) ,new-proc ,to ,(if name name (symbol->string proc-type)) ,stack) + (run-next-time-in-process ,new-proc ,func ,@args) + (the (pointer ,proc-type) (-> ,new-proc ppointer)) + ) + ) + ) + ) + +(defmacro process-spawn (proc-type &key (init #f) &key (from *default-dead-pool*) &key (to *default-pool*) &key (name #f) &key (stack-size #x4000) &key (stack *scratch-memory-top*) &rest args) + "Start a new process and run an init function on it. + Returns a pointer to the new process, or #f (or is it 0?) if something goes wrong." + + (with-gensyms (new-proc) + `(let ((,new-proc (the-as ,proc-type (get-process ,from ,proc-type ,stack-size)))) + (when ,new-proc + ((method-of-type ,proc-type activate) ,new-proc ,to ,(if name name `(quote ,proc-type)) ,stack) + (run-now-in-process ,new-proc ,(if init init (string->symbol (fmt #f "{}-init-by-other" proc-type))) ,@args) + (the (pointer ,proc-type) (-> ,new-proc ppointer)) + ) + ) + ) + ) + +;; display a listing of active processes. +(defmacro ps (&key (detail #f)) + `(inspect-process-tree *active-pool* 0 0 ,detail) + ) + +;; use a compile-time list to keep track of the type of an anonymous behavior. +(seval (define *defstate-type-stack* '())) +(desfun def-state-check-behavior (beh-form beh-type) + "check if code block is an anonymous behavior. needed for anonymous behaviors on defstate." + + (when (and (pair? beh-form) (eq? (first beh-form) 'behavior)) + (push! *defstate-type-stack* beh-type) + ) + ) +(defmacro clear-def-state-stack () + (set! *defstate-type-stack* '()) + `(none) + ) +;; *no-state* is just used for the compiler to know whether a handler was actually set or not +(defmacro defstate (state-name parents + &key (virtual #f) + &key (event *no-state*) + &key (enter *no-state*) + &key (trans *no-state*) + &key (exit *no-state*) + &key (code *no-state*) + &key (post *no-state*) + ) + "Define a new state!" + + (with-gensyms (new-state) + (let ((defstate-type (first parents))) + (when (not (null? *defstate-type-stack*)) + (fmt #t "*defstate-type-stack* leaked! An error probably happened in a previous defstate. stack is: {}" + *defstate-type-stack*) + ) + (set! *defstate-type-stack* '()) + ;; check for default handlers + (let ((default-handlers (assoc defstate-type *default-state-handlers*))) + (when (not (null? default-handlers)) + ;;(fmt #t "found default-handlers for {}: {}\n" defstate-type default-handlers) + ;; event + (set! default-handlers (cadr default-handlers)) + (when (and (eq? event '*no-state*) (car default-handlers)) + (set! event (car default-handlers))) + ;; enter + (set! default-handlers (cdr default-handlers)) + (when (and (eq? enter '*no-state*) (car default-handlers)) + (set! enter (car default-handlers))) + ;; trans + (set! default-handlers (cdr default-handlers)) + (when (and (eq? trans '*no-state*) (car default-handlers)) + (set! trans (car default-handlers))) + ;; exit + (set! default-handlers (cdr default-handlers)) + (when (and (eq? exit '*no-state*) (car default-handlers)) + (set! exit (car default-handlers))) + ;; code + (set! default-handlers (cdr default-handlers)) + (when (and (eq? code '*no-state*) (car default-handlers)) + (set! code (car default-handlers))) + ;; post + (set! default-handlers (cdr default-handlers)) + (when (and (eq? post '*no-state*) (car default-handlers)) + (set! post (car default-handlers))) + + (set! default-handlers (cdr default-handlers)) + ) + ) + (def-state-check-behavior event defstate-type) + (def-state-check-behavior enter defstate-type) + (def-state-check-behavior trans defstate-type) + (def-state-check-behavior exit defstate-type) + (def-state-check-behavior code defstate-type) + (def-state-check-behavior post defstate-type) + `(let ((,new-state (new 'static 'state + :name (quote ,state-name) + :next #f + :exit #f + :code #f + :trans #f + :post #f + :enter #f + :event #f + ) + )) + ;; the compiler will set the fields of the given state and define the symbol. + ;; This way it can check the individual function types, make sure they make sense, and create + ;; a state with the appropriate type. + ,(if virtual + `(define-virtual-state-hook ,state-name ,defstate-type ,new-state ,(eq? virtual 'override) :event ,event :enter ,enter :trans ,trans :exit ,exit :code ,code :post ,post) + `(define-state-hook ,state-name ,defstate-type ,new-state :event ,event :enter ,enter :trans ,trans :exit ,exit :code ,code :post ,post) + ) + + ) + ) + ) + ) + +(defmacro behavior (bindings &rest body) + "Define an anonymous behavior for a process state. This may only be used inside a defstate!" + + (let ((behavior-type (first *defstate-type-stack*))) + (pop! *defstate-type-stack*) + `(lambda :behavior ,behavior-type ,bindings ,@body) + ) + ) + +;; set the default handler functions for a process's state handlers +(seval (define *default-state-handlers* '())) +(defmacro defstatehandler (proc + &key (event #f) + &key (enter #f) + &key (trans #f) + &key (exit #f) + &key (code #f) + &key (post #f)) + (let ((old (assoc proc *default-state-handlers*)) + (new (list proc (list event enter trans exit code post)))) + (if (null? old) + (append!! *default-state-handlers* new) ;; add new set of default handlers + (dolist (hnd *default-state-handlers*) ;; replace old handlers with new ones + (when (eq? (car hnd) old) + (set-car! hnd new) + ) + ) + ) + ) + `(none) + ) + +(defmethod new state + ((allocation symbol) + (type-to-make type) + (name symbol) + (code function) + (trans (function none)) + (enter function) + (exit (function none)) + (event (function process int symbol event-message-block object))) + "Allocate a new state. It seems like this isn't really used much and most states are + statically allocated and as a result don't have the constructor called." + (let ((obj (object-new allocation type-to-make (the-as int (-> type-to-make size))))) + (set! (-> obj name) name) + (set! (-> obj next) #f) + (set! (-> obj exit) exit) + (set! (-> obj code) code) + (set! (-> obj trans) trans) + (set! (-> obj post) #f) + (set! (-> obj enter) enter) + (set! (-> obj event) event) + obj + ) + ) + +(defun inherit-state ((child state) (parent state)) + "Copy handler functions from parent to child" + (cond + ((nonzero? parent) + (set! (-> child exit) (-> parent exit)) + (set! (-> child code) (-> parent code)) + (set! (-> child trans) (-> parent trans)) + (set! (-> child post) (-> parent post)) + (set! (-> child enter) (-> parent enter)) + (set! (-> child event) (-> parent event)) + ) + (else + ;; Note: this is added to let us defstate on a child before the parent. + ;; The child won't be usable like this, but it will prevent a crash. + (format 0 "[STATE ERROR] inherit-state got a null parent state. Child is ~A~%" (-> child name)) + ) + ) + + child + ) + +(defmethod print state ((obj state)) + "Print a state." + (format '#t "#<~A ~A @ #x~X>" (-> obj type) (-> obj name) obj) + obj + ) + +(define-extern enter-state (function object object object object object object object)) +(defun enter-state (arg0 arg1 arg2 arg3 arg4 arg5) + "Make the process stored in pp enter the state in pp next-state" + (with-pp + ;; unsleep us + (process-mask-clear! (-> pp mask) sleep sleep-code) + ;; mark as going + (process-mask-set! (-> pp mask) going) + (cond + ((= (-> pp status) 'initialize) + ;; did a go during initialize. + ;; remove the old trans hook, if there was one + (set! (-> pp trans-hook) #f) + ;; set us up to run enter-state again, the next time we're scheduled. + (set-to-run (-> pp main-thread) enter-state arg0 arg1 arg2 arg3 arg4 arg5) + ;; tell the kernel that we did a go during init + (set! (-> pp status) 'initialize-go) + ;; abandon this thread, go back to what initialized us! + (throw 'initialize #t) + #t + ) + ((!= (-> *kernel-context* current-process) pp) + ;; we aren't actually in process pp right now. + ;; so set us up to go in the next run + (let ((status-backup (-> pp status))) + (set! (-> pp trans-hook) #f) + ;; will set waiting-to-run + (set-to-run (-> pp main-thread) enter-state arg0 arg1 arg2 arg3 arg4 arg5) + ;; restore the old status. + (set! (-> pp status) status-backup) + #t + ) + ) + ((= (-> pp main-thread) (-> pp top-thread)) + ;; we are in the right process, and in the main thread! + ;; we will do a nonlocal control transfer to the new state's code. + ;; the new state can then suspend and get back to the kernel dispatcher lambda + ;; like normal. + + ;; change state! + (set! (-> pp state) (-> pp next-state)) + + ;; do exits + (let ((frame (-> pp stack-frame-top))) + (while frame + (case (-> frame type) + ((protect-frame state) + ((-> (the-as protect-frame frame) exit)) + ) + ) + (set! frame (-> frame next)) + ) + ) + + ;; done with going, clear the mask + (process-mask-clear! (-> pp mask) going) + + ;; now, update the process: + (let ((new-state (-> pp state))) + ;; event hook from the current state + (set! (-> pp event-hook) (-> new-state event)) + ;; if we have an exit, push it onto the stack frame + ;; and also blow away the old stack frame + (if (-> new-state exit) + (set! (-> pp stack-frame-top) new-state) + (set! (-> pp stack-frame-top) #f) + ) + (set! (-> pp post-hook) (-> new-state post)) + (set! (-> pp trans-hook) (-> new-state trans)) + + + ;; start up the new state. First run the enter function + (let ((enter-func (-> new-state enter))) + (if enter-func + ((the (function _varargs_ none) enter-func) arg0 arg1 arg2 arg3 arg4 arg5) + ) + ) + + ;; run the trans function before the code. + (let ((trans-func (-> new-state trans))) + (if trans-func + (trans-func) + ) + ) + ;; now we run the code, but in a tricky way. + ;; we need to: + ;; - make sure that when this code returns, we do a deactivate + ;; - reset the stack to the top, so we can't just call the code. + (rlet ((temp) + (func) + (sp :reg rsp :type uint) + (off :reg r15 :type uint) + (carg0 :reg rdi) + (carg1 :reg rsi) + (carg2 :reg rdx) + (carg3 :reg rcx)) + ;; prepare args + ;; compiler will likely have these on the stack, we need to get them in regs + ;; before messing with the stack. + (.mov carg0 arg0) + (.mov carg1 arg1) + (.mov carg2 arg2) + (.mov carg3 arg3) + + ;; get the main code as an x86-64 pointer + (.mov func (-> new-state code)) + (.add func off) + ;; reset the stack (scary) + (.mov sp (-> pp main-thread stack-top)) + (.add sp off) + ;; push the return trampoline for when code returns. + (.mov temp return-from-thread-dead) ;; will deactivate + (.add temp off) + (.push temp) + ;; and call! + (.jr func) + ;; stupid hack so the compiler doesn't throw away these registers. + (.add carg0 carg1) + (.add carg2 carg3) + #f ;; can't get here + ) + ) + ) + (else + ;; not in the main-thread. + ;; so we set up the main thread to try again. + (set! (-> pp trans-hook) #f) + (set-to-run (-> pp main-thread) + enter-state arg0 arg1 arg2 arg3 arg4 arg5) + (when (!= (-> pp top-thread name) 'post) + ;; abandon this one too. + ;; NOTE - this is different from GOAL. + ;; GOAL installs this as the return address for this function and returns normally. + ;; but we don't because I don't have an easy way to find where to stick this. + ;; I can't see how this makes a difference, as all non-main threads seem + ;; temporary, but if this turns out to be false, we will need to change this. + (rlet ((temp) + (off :reg r15 :type uint :reset-here #t)) + (.mov temp return-from-thread) ;; could probably just call this... + (.add temp off) + (.push temp) + (.ret) + #f ;; can't get here + ) + ) + ) + ) + ) + ) + +(kmemopen global "event-queue") + +(let ((v1-3 (new 'global 'event-message-block-array 64))) + (set! (-> v1-3 length) 0) + (define *event-queue* v1-3) + ) + +(kmemclose) + +(defun send-event-function ((arg0 process-tree) (arg1 event-message-block)) + (with-pp + (when (and arg0 (!= (-> arg0 type) process-tree) (-> (the-as process arg0) event-hook) (-> arg1 from)) + (let ((gp-0 pp)) + (set! pp (the-as process arg0)) + (let ((v0-0 ((-> (the-as process arg0) event-hook) (-> arg1 from 0) (-> arg1 num-params) (-> arg1 message) arg1))) + (set! pp gp-0) + v0-0 + ) + ) + ) + ) + ) + +(defmethod send-all! event-message-block-array ((obj event-message-block-array)) + (dotimes (s5-0 (-> obj length)) + (let* ((a1-0 (-> obj data s5-0)) + (a0-2 (handle->process (-> a1-0 to-handle))) + ) + (if (and a0-2 (handle->process (-> a1-0 form-handle))) + (send-event-function a0-2 a1-0) + ) + ) + ) + (set! (-> obj length) 0) + 0 + (none) + ) + +(defun looping-code () + (until #f + (suspend) + ) + #f + ) + + + + diff --git a/goal_src/jak2/kernel/gstring-h.gc b/goal_src/jak2/kernel/gstring-h.gc index 7b3331ed05..be82621eb9 100644 --- a/goal_src/jak2/kernel/gstring-h.gc +++ b/goal_src/jak2/kernel/gstring-h.gc @@ -5,3 +5,10 @@ ;; name in dgo: gstring-h ;; dgos: KERNEL +(define-extern *string-tmp-str* string) +(define-extern *temp-string* string) +(define-extern *stdcon0* string) +(define-extern *stdcon1* string) +(define-extern *stdcon* string) +(define-extern *debug-draw-pauseable* symbol) +(define-extern string= (function string string symbol)) diff --git a/goal_src/jak2/kernel/gstring.gc b/goal_src/jak2/kernel/gstring.gc index 6abdec3321..57dd3d4246 100644 --- a/goal_src/jak2/kernel/gstring.gc +++ b/goal_src/jak2/kernel/gstring.gc @@ -5,3 +5,708 @@ ;; name in dgo: gstring ;; dgos: KERNEL +(defmethod length string ((obj string)) + (let ((v1-0 (-> obj data))) + (while (nonzero? (-> v1-0 0)) + (nop!) + (nop!) + (nop!) + (set! v1-0 (&-> v1-0 1)) + ) + (&- v1-0 (the-as uint (-> obj data))) + ) + ) + +(defmethod asize-of string ((obj string)) + (+ (-> obj allocated-length) 1 (-> string size)) + ) + +(defun copy-string<-string ((arg0 string) (arg1 string)) + (let ((v1-0 (-> arg0 data))) + (let ((a1-1 (-> arg1 data))) + (while (nonzero? (-> a1-1 0)) + (set! (-> v1-0 0) (-> a1-1 0)) + (set! v1-0 (&-> v1-0 1)) + (set! a1-1 (&-> a1-1 1)) + ) + ) + (set! (-> v1-0 0) (the-as uint 0)) + ) + arg0 + ) + +(defmethod new string ((allocation symbol) (type-to-make type) (arg0 int) (arg1 string)) + (cond + (arg1 + (let* ((s2-1 (max (length arg1) arg0)) + (a0-4 (object-new allocation type-to-make (+ s2-1 1 (-> type-to-make size)))) + ) + (set! (-> a0-4 allocated-length) s2-1) + (copy-string<-string a0-4 arg1) + ) + ) + (else + (let ((v0-2 (object-new allocation type-to-make (+ arg0 1 (-> type-to-make size))))) + (set! (-> v0-2 allocated-length) arg0) + v0-2 + ) + ) + ) + ) + +(defun string= ((arg0 string) (arg1 string)) + (let ((a2-0 (-> arg0 data)) + (v1-0 (-> arg1 data)) + ) + (if (or (zero? arg0) (zero? arg1)) + (return #f) + ) + (while (and (nonzero? (-> a2-0 0)) (nonzero? (-> v1-0 0))) + (if (!= (-> a2-0 0) (-> v1-0 0)) + (return #f) + ) + (set! a2-0 (&-> a2-0 1)) + (set! v1-0 (&-> v1-0 1)) + ) + (and (zero? (-> a2-0 0)) (zero? (-> v1-0 0))) + ) + ) + +(defun string-prefix= ((arg0 string) (arg1 string)) + (let ((v1-0 (-> arg0 data))) + (let ((a2-0 (-> arg1 data))) + (if (or (zero? arg0) (zero? arg1)) + (return #f) + ) + (while (and (nonzero? (-> v1-0 0)) (nonzero? (-> a2-0 0))) + (if (!= (-> v1-0 0) (-> a2-0 0)) + (return #f) + ) + (set! v1-0 (&-> v1-0 1)) + (set! a2-0 (&-> a2-0 1)) + ) + ) + (zero? (-> v1-0 0)) + ) + ) + +(defun charp-prefix= ((arg0 (pointer uint8)) (arg1 (pointer uint8))) + (while (and (nonzero? (-> arg0 0)) (nonzero? (-> arg1 0))) + (if (!= (-> arg0 0) (-> arg1 0)) + (return #f) + ) + (set! arg0 (&-> arg0 1)) + (set! arg1 (&-> arg1 1)) + ) + (zero? (-> arg0 0)) + ) + +(defun string-suffix= ((arg0 string) (arg1 string)) + (let ((s5-0 (-> arg0 data)) + (gp-0 (-> arg1 data)) + ) + (if (or (zero? arg0) (zero? arg1)) + (return #f) + ) + (let ((s4-0 (length arg0)) + (v1-5 (length arg1)) + ) + (if (< s4-0 v1-5) + (return #f) + ) + (let ((v1-7 (&+ s5-0 (- s4-0 v1-5)))) + (while (and (nonzero? (-> v1-7 0)) (nonzero? (-> gp-0 0))) + (if (!= (-> v1-7 0) (-> gp-0 0)) + (return #f) + ) + (set! v1-7 (&-> v1-7 1)) + (set! gp-0 (&-> gp-0 1)) + ) + (zero? (-> v1-7 0)) + ) + ) + ) + ) + +(defun string-position ((arg0 string) (arg1 string)) + (let ((s5-0 0) + (s4-0 (-> arg1 data)) + ) + (while (nonzero? (-> s4-0 0)) + (if (charp-prefix= (-> arg0 data) s4-0) + (return s5-0) + ) + (+! s5-0 1) + (set! s4-0 (&-> s4-0 1)) + ) + ) + -1 + ) + +(defun string-charp= ((arg0 string) (arg1 (pointer uint8))) + (let ((v1-0 (-> arg0 data))) + (while (and (nonzero? (-> v1-0 0)) (nonzero? (-> arg1 0))) + (if (!= (-> v1-0 0) (-> arg1 0)) + (return #f) + ) + (set! v1-0 (&-> v1-0 1)) + (set! arg1 (&-> arg1 1)) + ) + (and (zero? (-> v1-0 0)) (zero? (-> arg1 0))) + ) + ) + +;; definition for function name= +;; ERROR: function was not converted to expressions. Cannot decompile. + + +(defun copyn-string<-charp ((arg0 string) (arg1 (pointer uint8)) (arg2 int)) + (let ((v1-0 (-> arg0 data))) + (dotimes (a3-0 arg2) + (set! (-> v1-0 0) (-> arg1 0)) + (set! v1-0 (&-> v1-0 1)) + (set! arg1 (&-> arg1 1)) + ) + (set! (-> v1-0 0) (the-as uint 0)) + ) + arg0 + ) + +(defun string<-charp ((arg0 string) (arg1 (pointer uint8))) + (let ((v1-0 (-> arg0 data))) + (while (nonzero? (-> arg1 0)) + (set! (-> v1-0 0) (-> arg1 0)) + (set! v1-0 (&-> v1-0 1)) + (set! arg1 (&-> arg1 1)) + ) + (set! (-> v1-0 0) (the-as uint 0)) + ) + arg0 + ) + +(defun charp<-string ((arg0 (pointer uint8)) (arg1 string)) + (let ((v1-0 (-> arg1 data))) + (while (nonzero? (-> v1-0 0)) + (set! (-> arg0 0) (-> v1-0 0)) + (set! arg0 (&-> arg0 1)) + (set! v1-0 (&-> v1-0 1)) + ) + ) + (set! (-> arg0 0) (the-as uint 0)) + 0 + ) + +(defun copyn-charp<-string ((arg0 (pointer uint8)) (arg1 string) (arg2 int)) + (let ((v1-0 (-> arg1 data))) + (while (and (nonzero? (-> v1-0 0)) (< 1 arg2)) + (set! (-> arg0 0) (-> v1-0 0)) + (set! arg0 (&-> arg0 1)) + (set! v1-0 (&-> v1-0 1)) + (set! arg2 (+ arg2 -1)) + ) + ) + (while (> arg2 0) + (set! (-> arg0 0) (the-as uint 0)) + (set! arg0 (&-> arg0 1)) + (set! arg2 (+ arg2 -1)) + ) + 0 + (none) + ) + +(defun copy-charp<-charp ((arg0 (pointer uint8)) (arg1 (pointer uint8))) + (while (nonzero? (-> arg1 0)) + (set! (-> arg0 0) (-> arg1 0)) + (set! arg0 (&-> arg0 1)) + (set! arg1 (&-> arg1 1)) + ) + (set! (-> arg0 0) (the-as uint 0)) + arg0 + ) + +(defun cat-string<-string ((arg0 string) (arg1 string)) + (let ((v1-0 (-> arg0 data))) + (let ((a1-1 (-> arg1 data))) + (while (nonzero? (-> v1-0 0)) + (nop!) + (nop!) + (nop!) + (set! v1-0 (&-> v1-0 1)) + ) + (while (nonzero? (-> a1-1 0)) + (set! (-> v1-0 0) (-> a1-1 0)) + (set! v1-0 (&-> v1-0 1)) + (set! a1-1 (&-> a1-1 1)) + ) + ) + (set! (-> v1-0 0) (the-as uint 0)) + ) + arg0 + ) + +(defun catn-string<-charp ((arg0 string) (arg1 (pointer uint8)) (arg2 int)) + (let ((v1-0 (-> arg0 data))) + (while (nonzero? (-> v1-0 0)) + (nop!) + (nop!) + (nop!) + (set! v1-0 (&-> v1-0 1)) + ) + (dotimes (a3-2 arg2) + (set! (-> v1-0 0) (-> arg1 0)) + (set! v1-0 (&-> v1-0 1)) + (set! arg1 (&-> arg1 1)) + ) + (set! (-> v1-0 0) (the-as uint 0)) + ) + arg0 + ) + +(defun cat-string<-string_to_charp ((arg0 string) (arg1 string) (arg2 (pointer uint8))) + (let ((v1-0 (-> arg1 data)) + (v0-0 (-> arg0 data)) + ) + (while (nonzero? (-> v0-0 0)) + (nop!) + (nop!) + (nop!) + (set! v0-0 (&-> v0-0 1)) + ) + (while (and (>= (the-as int arg2) (the-as int v1-0)) (nonzero? (-> v1-0 0))) + (set! (-> v0-0 0) (-> v1-0 0)) + (set! v0-0 (&-> v0-0 1)) + (set! v1-0 (&-> v1-0 1)) + ) + (set! (-> v0-0 0) (the-as uint 0)) + v0-0 + ) + ) + +(defun append-character-to-string ((arg0 string) (arg1 uint8)) + (let ((v1-0 (-> arg0 data))) + (while (nonzero? (-> v1-0 0)) + (nop!) + (nop!) + (nop!) + (set! v1-0 (&-> v1-0 1)) + ) + (set! (-> v1-0 0) (the-as uint arg1)) + (set! (-> v1-0 1) (the-as uint 0)) + ) + 0 + 0 + ) + +(defun charp-basename ((arg0 (pointer uint8))) + (let ((v1-0 arg0)) + (while (nonzero? (-> v1-0 0)) + (set! v1-0 (&-> v1-0 1)) + ) + (while (< (the-as int arg0) (the-as int v1-0)) + (set! v1-0 (&-> v1-0 -1)) + (if (or (= (-> v1-0 0) 47) (= (-> v1-0 0) 92)) + (return (&-> v1-0 1)) + ) + ) + ) + arg0 + ) + +(defun clear ((arg0 string)) + (set! (-> arg0 data 0) (the-as uint 0)) + arg0 + ) + +(defun string arg0 data v1-4) (-> arg1 data v1-4)) + (return #t) + ) + ((< (-> arg1 data v1-4) (-> arg0 data v1-4)) + (return #f) + ) + ) + ) + ) + #f + ) + +(defun string>? ((arg0 string) (arg1 string)) + (let ((s4-1 (min (length arg0) (length arg1)))) + (dotimes (v1-4 s4-1) + (cond + ((< (-> arg0 data v1-4) (-> arg1 data v1-4)) + (return #f) + ) + ((< (-> arg1 data v1-4) (-> arg0 data v1-4)) + (return #t) + ) + ) + ) + ) + #f + ) + +(defun string<=? ((arg0 string) (arg1 string)) + (let ((s4-1 (min (length arg0) (length arg1)))) + (dotimes (v1-4 s4-1) + (cond + ((< (-> arg0 data v1-4) (-> arg1 data v1-4)) + (return #t) + ) + ((< (-> arg1 data v1-4) (-> arg0 data v1-4)) + (return #f) + ) + ) + ) + ) + #t + ) + +(defun string>=? ((arg0 string) (arg1 string)) + (let ((s4-1 (min (length arg0) (length arg1)))) + (dotimes (v1-4 s4-1) + (cond + ((< (-> arg0 data v1-4) (-> arg1 data v1-4)) + (return #f) + ) + ((< (-> arg1 data v1-4) (-> arg0 data v1-4)) + (return #t) + ) + ) + ) + ) + #t + ) + +(define *string-tmp-str* (new 'global 'string 128 (the-as string #f))) + +(defun string-skip-to-char ((arg0 (pointer uint8)) (arg1 uint)) + (while (and (nonzero? (-> arg0 0)) (!= (-> arg0 0) arg1)) + (set! arg0 (&-> arg0 1)) + ) + arg0 + ) + +(defun string-cat-to-last-char ((arg0 string) (arg1 string) (arg2 uint)) + (let ((s4-0 (&-> (the-as (pointer uint8) arg1) 3))) + (let ((v1-0 (string-skip-to-char (-> arg1 data) arg2))) + (when (= (-> v1-0 0) arg2) + (until (!= (-> v1-0 0) arg2) + (set! s4-0 v1-0) + (set! v1-0 (string-skip-to-char (&-> v1-0 1) arg2)) + ) + ) + ) + (cat-string<-string_to_charp arg0 arg1 s4-0) + ) + ) + +(defun string-skip-whitespace ((arg0 (pointer uint8))) + (while (and (nonzero? (-> arg0 0)) (or (= (-> arg0 0) 32) (= (-> arg0 0) 9) (= (-> arg0 0) 13) (= (-> arg0 0) 10))) + (set! arg0 (&-> arg0 1)) + ) + arg0 + ) + +(defun string-suck-up! ((arg0 string) (arg1 (pointer uint8))) + (when (!= arg1 (-> arg0 data)) + (let ((v1-2 (-> arg0 data))) + (while (nonzero? (-> arg1 0)) + (set! (-> v1-2 0) (-> arg1 0)) + (set! v1-2 (&-> v1-2 1)) + (set! arg1 (&-> arg1 1)) + ) + (set! (-> v1-2 0) (the-as uint 0)) + ) + 0 + ) + #f + ) + +(defun string-strip-leading-whitespace! ((arg0 string)) + (let ((a1-0 (string-skip-whitespace (-> arg0 data)))) + (string-suck-up! arg0 a1-0) + ) + #f + ) + +(defun string-strip-trailing-whitespace! ((arg0 string)) + (when (nonzero? (length arg0)) + (let ((v1-6 (&+ (-> arg0 data) (+ (length arg0) -1)))) + (while (and (>= (the-as int v1-6) (the-as int (-> arg0 data))) + (or (= (-> v1-6 0) 32) (= (-> v1-6 0) 9) (= (-> v1-6 0) 13) (= (-> v1-6 0) 10)) + ) + (set! v1-6 (&-> v1-6 -1)) + ) + (set! (-> v1-6 1) (the-as uint 0)) + ) + 0 + ) + #f + ) + +(defun string-strip-whitespace! ((arg0 string)) + (string-strip-trailing-whitespace! arg0) + (string-strip-leading-whitespace! arg0) + #f + ) + +(defun string-upcase ((arg0 string) (arg1 string)) + (let* ((a0-1 (-> arg0 data)) + (a3-0 (-> a0-1 0)) + (a2-0 1) + (v1-0 0) + ) + (while (nonzero? a3-0) + (if (and (>= a3-0 (the-as uint 97)) (>= (the-as uint 122) a3-0)) + (+! a3-0 -32) + ) + (set! (-> arg1 data v1-0) a3-0) + (set! a3-0 (-> a0-1 a2-0)) + (+! a2-0 1) + (+! v1-0 1) + ) + (set! (-> arg1 data v1-0) (the-as uint 0)) + ) + 0 + (none) + ) + +(defun string-get-arg!! ((arg0 string) (arg1 string)) + (let ((s4-0 (string-skip-whitespace (-> arg1 data)))) + (cond + ((= (-> s4-0 0) 34) + (let ((s4-1 (&-> s4-0 1))) + (let ((v1-3 s4-1)) + (while (and (nonzero? (-> s4-1 0)) (!= (-> s4-1 0) 34)) + (set! s4-1 (&-> s4-1 1)) + ) + (copyn-string<-charp arg0 v1-3 (&- s4-1 (the-as uint v1-3))) + ) + (if (= (-> s4-1 0) 34) + (set! s4-1 (&-> s4-1 1)) + ) + (let ((a1-3 (string-skip-whitespace s4-1))) + (string-suck-up! arg1 a1-3) + ) + ) + (return #t) + ) + ((nonzero? (-> s4-0 0)) + (let ((v1-11 s4-0)) + (while (and (nonzero? (-> s4-0 0)) (!= (-> s4-0 0) 32) (!= (-> s4-0 0) 9) (!= (-> s4-0 0) 13) (!= (-> s4-0 0) 10)) + (set! s4-0 (&-> s4-0 1)) + ) + (copyn-string<-charp arg0 v1-11 (&- s4-0 (the-as uint v1-11))) + ) + (let ((a1-9 (string-skip-whitespace s4-0))) + (string-suck-up! arg1 a1-9) + ) + (return #t) + ) + ) + ) + #f + ) + +(defun string->int ((arg0 string)) + (let ((a0-1 (-> arg0 data)) + (v0-0 0) + (v1-0 #f) + ) + (cond + ((= (-> a0-1 0) 35) + (let ((a0-2 (&-> a0-1 1))) + (cond + ((or (= (-> a0-2 0) 120) (= (-> a0-2 0) 88)) + (let ((a0-3 (&-> a0-2 1))) + (when (= (-> a0-3 1) 45) + (set! v1-0 #t) + (set! a0-3 (&-> a0-3 1)) + ) + (while (or (and (>= (-> a0-3 0) (the-as uint 48)) (>= (the-as uint 57) (-> a0-3 0))) + (and (>= (-> a0-3 0) (the-as uint 65)) (>= (the-as uint 70) (-> a0-3 0))) + (and (>= (-> a0-3 0) (the-as uint 97)) (>= (the-as uint 102) (-> a0-3 0))) + ) + (cond + ((and (>= (-> a0-3 0) (the-as uint 65)) (>= (the-as uint 70) (-> a0-3 0))) + (set! v0-0 (the-as int (+ (-> a0-3 0) -55 (* v0-0 16)))) + ) + ((and (>= (-> a0-3 0) (the-as uint 97)) (>= (the-as uint 102) (-> a0-3 0))) + (set! v0-0 (the-as int (+ (-> a0-3 0) -87 (* v0-0 16)))) + ) + (else + (set! v0-0 (the-as int (+ (-> a0-3 0) -48 (* v0-0 16)))) + ) + ) + (set! a0-3 (&-> a0-3 1)) + ) + ) + ) + ((or (= (-> a0-2 0) 98) (= (-> a0-2 0) 66)) + (let ((a0-4 (&-> a0-2 1))) + (while (and (>= (-> a0-4 0) (the-as uint 48)) (>= (the-as uint 49) (-> a0-4 0))) + (set! v0-0 (the-as int (+ (-> a0-4 0) -48 (* v0-0 2)))) + (set! a0-4 (&-> a0-4 1)) + ) + ) + ) + ) + ) + ) + (else + (when (= (-> a0-1 1) 45) + (set! v1-0 #t) + (set! a0-1 (&-> a0-1 1)) + ) + (while (and (>= (-> a0-1 0) (the-as uint 48)) (>= (the-as uint 57) (-> a0-1 0))) + (set! v0-0 (the-as int (+ (-> a0-1 0) -48 (* 10 v0-0)))) + (set! a0-1 (&-> a0-1 1)) + ) + ) + ) + (cond + (v1-0 + (- v0-0) + ) + (else + (empty) + v0-0 + ) + ) + ) + ) + +(defun string->float ((arg0 string)) + (let ((a0-1 (-> arg0 data)) + (f0-0 0.0) + (v1-0 #f) + ) + (when (= (-> a0-1 0) 45) + (set! v1-0 #t) + (set! a0-1 (&-> a0-1 1)) + ) + (while (and (>= (-> a0-1 0) (the-as uint 48)) (>= (the-as uint 57) (-> a0-1 0))) + (set! f0-0 (+ (* 10.0 f0-0) (the float (+ (-> a0-1 0) -48)))) + (set! a0-1 (&-> a0-1 1)) + ) + (when (= (-> a0-1 0) 46) + (set! a0-1 (&-> a0-1 1)) + (let ((a2-4 #xf4240) + (a1-12 0) + ) + (while (and (>= (-> a0-1 0) (the-as uint 48)) (>= (the-as uint 57) (-> a0-1 0))) + (+! a1-12 (* (+ (-> a0-1 0) -48) (the-as uint a2-4))) + (set! a2-4 (/ a2-4 10)) + (set! a0-1 (&-> a0-1 1)) + ) + (+! f0-0 (* 0.0000001 (the float a1-12))) + ) + ) + (when (= (-> a0-1 0) 101) + (let ((a1-16 (&-> a0-1 1)) + (f1-5 0.0) + (a0-2 #f) + ) + (cond + ((= (-> a1-16 0) 45) + (set! a0-2 #t) + (set! a1-16 (&-> a1-16 1)) + ) + ((= (-> a1-16 0) 43) + (set! a1-16 (&-> a1-16 1)) + ) + ) + (while (and (>= (-> a1-16 0) (the-as uint 48)) (>= (the-as uint 57) (-> a1-16 0))) + (set! f1-5 (+ (* 10.0 f1-5) (the float (+ (-> a1-16 0) -48)))) + (set! a1-16 (&-> a1-16 1)) + ) + (when (!= f1-5 0.0) + (let ((f2-6 1.0)) + (cond + (a0-2 + (dotimes (a0-3 (the int f1-5)) + (set! f2-6 (* 0.1 f2-6)) + (nop!) + (nop!) + ) + ) + (else + (dotimes (a0-6 (the int f1-5)) + (set! f2-6 (* 10.0 f2-6)) + (nop!) + (nop!) + ) + ) + ) + (set! f0-0 (* f0-0 f2-6)) + ) + ) + ) + ) + (if v1-0 + (- f0-0) + f0-0 + ) + ) + ) + +(defun string-get-int32!! ((arg0 (pointer int32)) (arg1 string)) + (cond + ((string-get-arg!! *string-tmp-str* arg1) + (set! (-> arg0 0) (string->int *string-tmp-str*)) + #t + ) + (else + #f + ) + ) + ) + +(defun string-get-float!! ((arg0 (pointer float)) (arg1 string)) + (cond + ((string-get-arg!! *string-tmp-str* arg1) + (set! (-> arg0 0) (string->float *string-tmp-str*)) + #t + ) + (else + #f + ) + ) + ) + +(defun string-get-flag!! ((arg0 (pointer symbol)) (arg1 string) (arg2 string) (arg3 string)) + (cond + ((string-get-arg!! *string-tmp-str* arg1) + (cond + ((or (string= *string-tmp-str* arg2) (string= *string-tmp-str* arg3)) + (set! (-> arg0 0) (string= *string-tmp-str* arg2)) + #t + ) + (else + #f + ) + ) + ) + (else + #f + ) + ) + ) + +(kmemopen global "gstring-globals") + +(define *debug-draw-pauseable* #f) +(define *stdcon0* (new 'global 'string #x4000 (the-as string #f))) +(define *stdcon1* (new 'global 'string #x4000 (the-as string #f))) +(define *stdcon* *stdcon0*) +(define *temp-string* (new 'global 'string 2048 (the-as string #f))) + +(kmemclose) + + + + diff --git a/goalc/compiler/Compiler.h b/goalc/compiler/Compiler.h index 64404e4950..546583cb1d 100644 --- a/goalc/compiler/Compiler.h +++ b/goalc/compiler/Compiler.h @@ -220,6 +220,7 @@ class Compiler { bool is_structure(const TypeSpec& ts); bool is_bitfield(const TypeSpec& ts); bool is_pair(const TypeSpec& ts); + bool is_symbol(const TypeSpec& ts); std::vector get_list_as_vector(const goos::Object& o, goos::Object* rest_out = nullptr, int max_length = -1); diff --git a/goalc/compiler/Util.cpp b/goalc/compiler/Util.cpp index 0e9f9d8ea3..f4b57fd969 100644 --- a/goalc/compiler/Util.cpp +++ b/goalc/compiler/Util.cpp @@ -336,6 +336,10 @@ bool Compiler::is_pair(const TypeSpec& ts) { return m_ts.tc(m_ts.make_typespec("pair"), ts); } +bool Compiler::is_symbol(const TypeSpec& ts) { + return m_ts.tc(m_ts.make_typespec("symbol"), ts); +} + bool Compiler::get_true_or_false(const goos::Object& form, const goos::Object& boolean) { // todo try other things. if (boolean.is_symbol()) { diff --git a/goalc/compiler/compilation/Atoms.cpp b/goalc/compiler/compilation/Atoms.cpp index 3d491a129c..792c9d3d19 100644 --- a/goalc/compiler/compilation/Atoms.cpp +++ b/goalc/compiler/compilation/Atoms.cpp @@ -380,7 +380,7 @@ Val* Compiler::compile_get_symbol_value(const goos::Object& form, } auto ts = existing_symbol->second; - auto sext = m_ts.lookup_type(ts)->get_load_signed(); + auto sext = m_ts.lookup_type_allow_partial_def(ts)->get_load_signed(); auto fe = env->function_env(); auto sym = fe->alloc_val(name, m_ts.make_typespec("symbol")); auto re = fe->alloc_val(sym, ts, sext); diff --git a/goalc/compiler/compilation/Static.cpp b/goalc/compiler/compilation/Static.cpp index b3c9e3297c..379acb925e 100644 --- a/goalc/compiler/compilation/Static.cpp +++ b/goalc/compiler/compilation/Static.cpp @@ -139,7 +139,8 @@ void Compiler::compile_static_structure_inline(const goos::Object& form, deref_info.sign_extend); } - } else if (is_structure(field_info.type) || is_pair(field_info.type)) { + } else if (is_structure(field_info.type) || is_pair(field_info.type) || + is_symbol(field_info.type)) { if (is_pair(field_info.type)) { ASSERT(!field_info.field.is_inline()); } diff --git a/goalc/compiler/compilation/Type.cpp b/goalc/compiler/compilation/Type.cpp index c730125754..30b438a043 100644 --- a/goalc/compiler/compilation/Type.cpp +++ b/goalc/compiler/compilation/Type.cpp @@ -162,7 +162,8 @@ void Compiler::generate_field_description(const goos::Object& form, format_args.push_back(get_field_of_structure(type, reg, f.name(), env)->to_gpr(form, env)); } else if (m_ts.tc(m_ts.make_typespec("basic"), f.type()) || m_ts.tc(m_ts.make_typespec("binteger"), f.type()) || - m_ts.tc(m_ts.make_typespec("pair"), f.type())) { + m_ts.tc(m_ts.make_typespec("pair"), f.type()) || + m_ts.tc(m_ts.make_typespec("symbol"), f.type())) { // basic, binteger, pair str_template += fmt::format("{}{}: ~A~%", tabs, f.name()); format_args.push_back(get_field_of_structure(type, reg, f.name(), env)->to_gpr(form, env)); diff --git a/scripts/decomp_progress.py b/scripts/decomp_progress.py index c63dde71e7..96f504ff14 100644 --- a/scripts/decomp_progress.py +++ b/scripts/decomp_progress.py @@ -50,7 +50,7 @@ def main(): file_stats = [] total_gc_files = 0 excluded_files = {"game_dgos.gc", "all_files.gc", "goal-lib.gc", "ocean-trans-tables.gc", "ocean-frames.gc", - "ocean-tables.gc"} + "ocean-tables.gc", "kernel-defs.gc", "compiler-setup.gc"} modified = set() for fn in all_files: diff --git a/test/goalc/test_with_game.cpp b/test/goalc/test_with_game.cpp index ac15620139..1f47f885d8 100644 --- a/test/goalc/test_with_game.cpp +++ b/test/goalc/test_with_game.cpp @@ -961,6 +961,14 @@ TEST(Jak1TypeConsistency, TypeConsistency) { compiler.run_test_no_load("test/goalc/source_templates/with_game/test-build-all-code.gc"); } +TEST(Jak2TypeConsistency, TypeConsistency) { + Compiler compiler(GameVersion::Jak2); + compiler.enable_throw_on_redefines(); + add_expected_type_mismatches(compiler); + compiler.run_test_no_load("decompiler/config/jak2/all-types.gc"); + compiler.run_test_no_load("test/goalc/source_templates/with_game/test-build-all-code.gc"); +} + struct VectorFloatRegister { float x = 0; float y = 0;