mirror of
https://github.com/open-goal/jak-project
synced 2026-05-27 08:09:29 -04:00
d317497c64
* decomp/goal-lib: Implement all fixed point macros * decompiler/goalc: Support half-word and byte parallel extend ops * decompiler/goalc: Support all parallel compare instructions * decompiler/goalc: Wire up PPACH and parallel bitwise operations * goalc: Add emitter tests * decomp: finalize `main-collide` * codacy lint
333 lines
8.8 KiB
Common Lisp
Vendored
333 lines
8.8 KiB
Common Lisp
Vendored
;; This file should contain an implementation for all macros that the decompiler uses in its output.
|
|
|
|
(defun ash ((value int) (shift-amount int))
|
|
"Arithmetic shift value by shift-amount.
|
|
A positive shift-amount will shift to the left and a negative will shift to the right.
|
|
"
|
|
;; OpenGOAL does not support ash in the compiler, so we implement it here as an inline function.
|
|
(declare (inline))
|
|
(if (> shift-amount 0)
|
|
(shl value shift-amount)
|
|
(sar value (- shift-amount))
|
|
)
|
|
)
|
|
|
|
(defmacro suspend()
|
|
'(none)
|
|
)
|
|
|
|
(defmacro empty-form ()
|
|
'(none)
|
|
)
|
|
|
|
(defmacro .sync.l ()
|
|
`(none))
|
|
|
|
(defmacro make-u128 (upper lower)
|
|
`(rlet ((result :class i128)
|
|
(upper-xmm :class i128)
|
|
(lower-xmm :class i128))
|
|
(.mov upper-xmm ,upper)
|
|
(.mov lower-xmm ,lower)
|
|
(.pcpyld result upper-xmm lower-xmm)
|
|
(the uint result)
|
|
)
|
|
)
|
|
|
|
(defmacro init-vf0-vector ()
|
|
"Initializes the VF0 vector which is a constant vector in the VU set to <0,0,0,1>"
|
|
`(.lvf vf0 (new 'static 'vector :x 0.0 :y 0.0 :z 0.0 :w 1.0))
|
|
)
|
|
|
|
(defconstant SYM_TO_STRING_OFFSET #xff38)
|
|
(defmacro symbol->string (sym)
|
|
"Convert a symbol to a goal string."
|
|
`(-> (the-as (pointer string) (+ SYM_TO_STRING_OFFSET (the-as int ,sym))))
|
|
)
|
|
|
|
(defmacro new-stack-matrix0 ()
|
|
"Get a new matrix on the stack that's set to zero."
|
|
`(let ((mat (new 'stack-no-clear 'matrix)))
|
|
(set! (-> mat quad 0) (the-as uint128 0))
|
|
(set! (-> mat quad 1) (the-as uint128 0))
|
|
(set! (-> mat quad 2) (the-as uint128 0))
|
|
(set! (-> mat quad 3) (the-as uint128 0))
|
|
mat
|
|
)
|
|
)
|
|
|
|
(defmacro new-stack-vector0 ()
|
|
"Get a stack vector that's set to 0.
|
|
This is more efficient than (new 'stack 'vector) because
|
|
this doesn't call the constructor."
|
|
`(let ((vec (new 'stack-no-clear 'vector)))
|
|
(set! (-> vec quad) (the-as uint128 0))
|
|
vec
|
|
)
|
|
)
|
|
|
|
(defmacro new-stack-quaternion0 ()
|
|
"Get a stack quaternion that's set to 0.
|
|
This is more efficient than (new 'stack 'quaternion) because
|
|
this doesn't call the constructor."
|
|
`(let ((q (new 'stack-no-clear 'quaternion)))
|
|
(set! (-> q quad) (the-as uint128 0))
|
|
q
|
|
)
|
|
)
|
|
|
|
|
|
(defmacro with-pp (&rest body)
|
|
`(rlet ((pp :reg r13 :reset-here #t :type process))
|
|
,@body)
|
|
)
|
|
|
|
(defmacro fabs (x)
|
|
`(if (< (the float ,x) 0)
|
|
(- (the float ,x))
|
|
(the float ,x))
|
|
)
|
|
|
|
(defconstant PI (the-as float #x40490fda))
|
|
(defconstant MINUS_PI (the-as float #xc0490fda))
|
|
|
|
(defmacro handle->process (handle)
|
|
;; the actual implementation is more clever than this.
|
|
;; Checks PID.
|
|
`(if (-> ,handle process)
|
|
(let ((proc (-> (-> ,handle process))))
|
|
(if (= (-> ,handle pid) (-> proc pid))
|
|
proc
|
|
)
|
|
)
|
|
)
|
|
)
|
|
|
|
(defmacro defbehavior (name process-type bindings &rest body)
|
|
(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 b! (pred destination &key (delay '()) &key (likely-delay '()))
|
|
"Branch!"
|
|
;; evaluate the predicate
|
|
`(let ((should-branch ,pred))
|
|
;; normal delay slot:
|
|
,delay
|
|
(when should-branch
|
|
,likely-delay
|
|
(goto ,destination)
|
|
)
|
|
)
|
|
)
|
|
|
|
|
|
(defglobalconstant METER_LENGTH 4096.0)
|
|
|
|
(defmacro meters (x)
|
|
"Convert number to meters.
|
|
If the input is a constant float or integer, the result will be a
|
|
compile time constant float. Otherwise, it will not be constant."
|
|
|
|
;; we don't have enough constant propagation for the compiler to figure this out.
|
|
(cond
|
|
((float? x)
|
|
(* METER_LENGTH x)
|
|
)
|
|
((integer? x)
|
|
(* METER_LENGTH x)
|
|
)
|
|
(#t
|
|
`(* METER_LENGTH ,x)
|
|
)
|
|
)
|
|
)
|
|
|
|
(defglobalconstant DEGREES_PER_ROT 65536.0)
|
|
|
|
(defmacro degrees (x)
|
|
"Convert number to degrees unit.
|
|
Will keep a constant float/int constant."
|
|
(cond
|
|
((or (float? x) (integer? x))
|
|
(* DEGREES_PER_ROT (/ (+ 0.0 x) 360.0))
|
|
)
|
|
(#t
|
|
`(* (/ (the float ,x) 360.0)
|
|
DEGREES_PER_ROT
|
|
)
|
|
)
|
|
)
|
|
)
|
|
|
|
(defglobalconstant TICKS_PER_SECOND 300) ;; 5 t/frame @ 60fps, 6 t/frame @ 50fps
|
|
|
|
(defmacro seconds (x)
|
|
"Convert number to seconds unit."
|
|
(cond
|
|
((float? x)
|
|
(* 1.0 TICKS_PER_SECOND x)
|
|
)
|
|
((integer? x)
|
|
(* TICKS_PER_SECOND x)
|
|
)
|
|
(#t
|
|
`(* 1.0 TICKS_PER_SECOND ,x)
|
|
)
|
|
)
|
|
)
|
|
|
|
;; maybe rename to "velocity"?
|
|
(defmacro vel-tick (vel)
|
|
"turn a velocity value into a per-tick value"
|
|
`(* (/ 1.0 ,TICKS_PER_SECOND) ,vel)
|
|
)
|
|
|
|
(defmacro copy-and-set-field (original field-name field-value)
|
|
`(let ((temp-copy ,original))
|
|
(set! (-> temp-copy ,field-name) ,field-value)
|
|
temp-copy
|
|
)
|
|
)
|
|
|
|
(defmacro set-vector! (v xv yv zv wv)
|
|
"Set all fields in a vector"
|
|
(with-gensyms (vec)
|
|
`(let ((vec ,v))
|
|
(set! (-> vec x) ,xv)
|
|
(set! (-> vec y) ,yv)
|
|
(set! (-> vec z) ,zv)
|
|
(set! (-> vec w) ,wv)))
|
|
)
|
|
|
|
(defmacro go (next-state &rest args)
|
|
`(with-pp
|
|
(set! (-> pp next-state) ,next-state)
|
|
((the (function _varargs_ object) enter-state) ,@args)
|
|
)
|
|
)
|
|
|
|
(defmacro static-sound-name (str)
|
|
"Convert a string constant to a static sound-name."
|
|
|
|
;; all this is done at compile-time so we can come up with 2
|
|
;; 64-bit constants to use
|
|
(when (> (string-length str) 16)
|
|
(error "static-sound-name got a string that is too long")
|
|
)
|
|
(let ((lo-val 0)
|
|
(hi-val 0)
|
|
)
|
|
(dotimes (i (string-length str))
|
|
(if (>= i 8)
|
|
(+! hi-val (ash (string-ref str i) (* 8 (- i 8))))
|
|
(+! lo-val (ash (string-ref str i) (* 8 i)))
|
|
)
|
|
)
|
|
`(new 'static 'sound-name :lo ,lo-val :hi ,hi-val)
|
|
)
|
|
)
|
|
|
|
(defmacro vftoi4.xyzw (dst src)
|
|
"convert to 28.4 integer. This does the multiply while the number is still
|
|
a float. This will have issues for very large floats, but it seems like this
|
|
is how PCSX2 does it as well, so maybe it's right?
|
|
NOTE: this is the only version of the instruction used in Jak 1, so we
|
|
don't need to worry about masks."
|
|
|
|
`(begin
|
|
(rlet ((temp :class vf))
|
|
(set! temp 16.0)
|
|
(.mul.x.vf temp ,src temp)
|
|
(.ftoi.vf ,dst temp)
|
|
)
|
|
)
|
|
)
|
|
|
|
(defmacro vftoi12.xyzw (dst src)
|
|
"convert to 20.12 integer. This does the multiply while the number is still
|
|
a float. This will have issues for very large floats, but it seems like this
|
|
is how PCSX2 does it as well, so maybe it's right?
|
|
NOTE: this is the only version of the instruction used in Jak 1, so we
|
|
don't need to worry about masks."
|
|
|
|
`(begin
|
|
(rlet ((temp :class vf))
|
|
(set! temp 4096.0)
|
|
(.mul.x.vf temp ,src temp)
|
|
(.ftoi.vf ,dst temp)
|
|
)
|
|
)
|
|
)
|
|
|
|
(defmacro vftoi15.xyzw (dst src)
|
|
"convert to 17.15 integer. This does the multiply while the number is still
|
|
a float. This will have issues for very large floats, but it seems like this
|
|
is how PCSX2 does it as well, so maybe it's right?
|
|
NOTE: this is the only version of the instruction used in Jak 1, so we
|
|
don't need to worry about masks."
|
|
|
|
`(begin
|
|
(rlet ((temp :class vf))
|
|
(set! temp 32768.0)
|
|
(.mul.x.vf temp ,src temp)
|
|
(.ftoi.vf ,dst temp)
|
|
)
|
|
)
|
|
)
|
|
|
|
(defmacro vitof4.xyzw (dst src)
|
|
"convert from a 28.4 integer. This does the multiply while the number is still
|
|
a float. This will have issues for very large floats, but it seems like this
|
|
is how PCSX2 does it as well, so maybe it's right?
|
|
NOTE: this is the only version of the instruction used in Jak 1, so we
|
|
don't need to worry about masks."
|
|
|
|
`(begin
|
|
(rlet ((temp :class vf))
|
|
(set! temp 0.0625)
|
|
(.mul.x.vf temp ,src temp)
|
|
(.ftoi.vf ,dst temp)
|
|
)
|
|
)
|
|
)
|
|
|
|
(defmacro vitof12.xyzw (dst src)
|
|
"convert from a 20.12 integer. This does the multiply while the number is still
|
|
a float. This will have issues for very large floats, but it seems like this
|
|
is how PCSX2 does it as well, so maybe it's right?
|
|
NOTE: this is the only version of the instruction used in Jak 1, so we
|
|
don't need to worry about masks."
|
|
|
|
`(begin
|
|
(rlet ((temp :class vf))
|
|
(set! temp 0.000244140625)
|
|
(.mul.x.vf temp ,src temp)
|
|
(.ftoi.vf ,dst temp)
|
|
)
|
|
)
|
|
)
|
|
|
|
(defmacro vitof15.xyzw (dst src)
|
|
"convert from a 17.15 integer. This does the multiply while the number is still
|
|
a float. This will have issues for very large floats, but it seems like this
|
|
is how PCSX2 does it as well, so maybe it's right?
|
|
NOTE: this is the only version of the instruction used in Jak 1, so we
|
|
don't need to worry about masks."
|
|
|
|
`(begin
|
|
(rlet ((temp :class vf))
|
|
(set! temp 0.000030517578125)
|
|
(.mul.x.vf temp ,src temp)
|
|
(.ftoi.vf ,dst temp)
|
|
)
|
|
)
|
|
)
|