jak-project/goal_src/engine/dma/dma-disasm.gc
ManDude 946284c05d
add goal enum utils to standard libs (#740)
* add goal enum utils to standard libs

* Update .gitattributes
2021-08-09 19:18:53 -04:00

629 lines
28 KiB
Common Lisp

;;-*-Lisp-*-
(in-package goal)
;; name: dma-disasm.gc
;; name in dgo: dma-disasm
;; dgos: GAME, ENGINE
;; Debug tool to print out a DMA list.
(deftype vif-disasm-element (structure)
((mask uint32 :offset-assert 0)
(tag vif-cmd-32 :offset-assert 4)
(val uint32 :offset-assert 8)
(print uint32 :offset-assert 12)
(string1 string :offset-assert 16)
(string2 string :offset-assert 20)
)
:method-count-assert 9
:size-assert #x18
:flag-assert #x900000018
)
(define *vif-disasm-table*
(new 'static 'boxed-array :type vif-disasm-element :length 34
(new 'static 'vif-disasm-element :mask #x7f :string1 "nop")
(new 'static 'vif-disasm-element :mask #x7f :tag #x1 :print #x2 :string1 "stcycl")
(new 'static 'vif-disasm-element :mask #x7f :tag #x2 :print #x1 :string1 "offset" :string2 "offset")
(new 'static 'vif-disasm-element :mask #x7f :tag #x3 :print #x1 :string1 "base" :string2 "base")
(new 'static 'vif-disasm-element :mask #x7f :tag #x4 :print #x1 :string1 "itop" :string2 "addr")
(new 'static 'vif-disasm-element :mask #x7f :tag #x5 :print #x1 :string1 "stmod" :string2 "mode")
(new 'static 'vif-disasm-element :mask #x7f :tag #x6 :print #x1 :string1 "mskpath3" :string2 "mask")
(new 'static 'vif-disasm-element :mask #x7f :tag #x7 :print #x1 :string1 "mark" :string2 "mark")
(new 'static 'vif-disasm-element :mask #x7f :tag #x10 :string1 "flushe")
(new 'static 'vif-disasm-element :mask #x7f :tag #x11 :string1 "flush")
(new 'static 'vif-disasm-element :mask #x7f :tag #x13 :string1 "flusha")
(new 'static 'vif-disasm-element :mask #x7f :tag #x14 :print #x1 :string1 "mscal" :string2 "addr")
(new 'static 'vif-disasm-element :mask #x7f :tag #x17 :string1 "mscnt")
(new 'static 'vif-disasm-element :mask #x7f :tag #x15 :print #x1 :string1 "mscalf" :string2 "addr")
(new 'static 'vif-disasm-element :mask #x7f :tag #x20 :print #x3 :string1 "stmask" :string2 "mask")
(new 'static 'vif-disasm-element :mask #x7f :tag #x30 :print #x4 :string1 "strow" :string2 "row")
(new 'static 'vif-disasm-element :mask #x7f :tag #x31 :print #x4 :string1 "stcol" :string2 "col")
(new 'static 'vif-disasm-element :mask #x7f :tag #x4a :print #x5 :string1 "mpg")
(new 'static 'vif-disasm-element :mask #x7f :tag #x50 :print #x6 :string1 "direct")
(new 'static 'vif-disasm-element :mask #x7f :tag #x51 :print #x6 :string1 "directhl")
(new 'static 'vif-disasm-element :mask #x6f :tag #x60 :val #x10 :print #x7 :string1 "unpack-s-32")
(new 'static 'vif-disasm-element :mask #x6f :tag #x61 :val #x8 :print #x7 :string1 "unpack-s-16")
(new 'static 'vif-disasm-element :mask #x6f :tag #x62 :val #x4 :print #x7 :string1 "unpack-s-8")
(new 'static 'vif-disasm-element :mask #x6f :tag #x64 :val #x8 :print #x7 :string1 "unpack-v2-32")
(new 'static 'vif-disasm-element :mask #x6f :tag #x65 :val #x4 :print #x7 :string1 "unpack-v2-16")
(new 'static 'vif-disasm-element :mask #x6f :tag #x66 :val #x2 :print #x7 :string1 "unpack-v2-8")
(new 'static 'vif-disasm-element :mask #x6f :tag #x68 :val #xc :print #x7 :string1 "unpack-v3-32")
(new 'static 'vif-disasm-element :mask #x6f :tag #x69 :val #x6 :print #x7 :string1 "unpack-v3-16")
(new 'static 'vif-disasm-element :mask #x6f :tag #x6a :val #x3 :print #x7 :string1 "unpack-v3-8")
(new 'static 'vif-disasm-element :mask #x6f :tag #x6c :val #x10 :print #x7 :string1 "unpack-v4-32")
(new 'static 'vif-disasm-element :mask #x6f :tag #x6d :val #x8 :print #x7 :string1 "unpack-v4-16")
(new 'static 'vif-disasm-element :mask #x6f :tag #x6e :val #x4 :print #x7 :string1 "unpack-v4-8")
(new 'static 'vif-disasm-element :mask #x6f :tag #x6f :val #x2 :print #x7 :string1 "unpack-v4-5")
(new 'static 'vif-disasm-element :print #x8)))
(defun disasm-vif-details ((stream symbol) (data (pointer uint8)) (kind vif-cmd) (count int))
(cond
((= kind (vif-cmd unpack-v4-8))
(let ((data-ptr (&-> data 4)))
(dotimes (i count)
(format stream " #x~X: #x~2X #x~2X #x~2X #x~2X~%"
(+ (+ (shl i 2) 4) (the-as int data))
(-> data-ptr (shl i 2))
(-> data-ptr (+ (shl i 2) 1))
(-> data-ptr (+ (shl i 2) 2))
(-> data-ptr (+ (shl i 2) 3))
)
)
)
)
((= kind (vif-cmd unpack-s-8))
(let ((s3-1 (&-> data 4)))
(dotimes (s2-1 count)
;; this is messed up and I think somebody put a parenthesis in
;; the wrong spot. I believe the format below only has one
;; format argument but should have 2.
(format stream " #x~X: #x~2x~%"
(+ (+ s2-1 4) (the-as int data))
count
)
;; the actual assembly is very strange here.
(let ((v1-21 (-> s3-1 (* 3 s2-1))))
)
(let ((v1-26 (-> s3-1 (+ (* 3 s2-1) 1))))
)
)
)
)
((= kind (vif-cmd unpack-v4-32))
(let ((s3-2 (the-as (pointer uint32) (&-> data 4))))
(dotimes (s2-2 count)
(format stream " #x~X: #x~8x #x~8x #x~8x #x~8x~%"
(+ (+ (shl s2-2 4) 4) (the-as int data))
(-> s3-2 (shl s2-2 2))
(-> s3-2 (+ (shl s2-2 2) 1))
(-> s3-2 (+ (shl s2-2 2) 2))
(-> s3-2 (+ (shl s2-2 2) 3))
)
)
)
)
((= kind (vif-cmd unpack-v4-16))
(let ((s3-3 (the-as (pointer uint16) (&-> data 4))))
(dotimes (s2-3 count)
(format stream " #x~X: #x~4x #x~4x #x~4x #x~4x~%"
(+ (+ (shl s2-3 3) 4) (the-as int data))
(-> s3-3 (shl s2-3 2))
(-> s3-3 (+ (shl s2-3 2) 1))
(-> s3-3 (+ (shl s2-3 2) 2))
(-> s3-3 (+ (shl s2-3 2) 3))
)
)
)
)
((= kind (vif-cmd unpack-v3-32))
(let ((s3-4 (the-as (pointer uint32) (&-> data 4))))
(dotimes (s2-4 count)
(format stream " #x~X: #x~8x #x~8x #x~8x~%"
(+ (+ (* 12 s2-4) 4) (the-as int data))
(-> (&+ s3-4 (* 12 s2-4)) 0)
(-> s3-4 (+ (* 3 s2-4) 1))
(-> s3-4 (+ (* 3 s2-4) 2))
)
)
)
)
((= kind (vif-cmd unpack-v3-16))
(let ((s3-5 (the-as (pointer uint16) (&-> data 4))))
(dotimes (s2-5 count)
(format stream " #x~X: #x~4x #x~4x #x~4x~%"
(+ (+ (* 6 s2-5) 4) (the-as int data))
(-> (&+ s3-5 (* 6 s2-5)) 0)
(-> s3-5 (+ (* 3 s2-5) 1))
(-> s3-5 (+ (* 3 s2-5) 2))
)
)
)
)
((= kind (vif-cmd unpack-v2-16))
(let ((s3-6 (the-as (pointer uint16) (&-> data 4))))
(dotimes (s2-6 count)
(format stream " #x~X: #x~4x #x~4x~%"
(+ (+ (shl s2-6 2) 4) (the-as int data))
(-> (&+ s3-6 (* 6 s2-6)) 0)
(-> s3-6 (+ (* 3 s2-6) 1))
)
)
)
)
(else
(format stream " #x~X: Data format #b~b not yet supported, add it for yourself!~%"
(&-> data 4)
kind
)
)
)
#f
)
(defun disasm-vif-tag ((data (pointer vif-tag)) (words int) (stream symbol) (details symbol))
"Print out a vif code and the immediate data.
Will print stuff until the number of words has been reached.
Returns how many bytes we overshot by."
(local-vars
(cmd vif-cmd)
(data-ptr (pointer vif-tag))
(data-idx int)
(unpack-imm vif-unpack-imm)
)
(let ((byte-idx 0))
(while (< byte-idx (shl words 2))
(let ((packet-size 4)) ;; default packet size is 32-bits.
(let ((first-tag (-> data 0)))
;; print the packet's address.
(format stream " #x~X:" data)
;; iterate through the disasm table, looking for a match
(dotimes (cmd-template-idx (-> *vif-disasm-table* length))
(set! cmd (-> first-tag cmd))
;; check the command against the table's mask and tag
(when (= (logand cmd (the-as uint (-> *vif-disasm-table* cmd-template-idx mask)))
(-> *vif-disasm-table* cmd-template-idx tag)
)
(let* ((print-kind (-> *vif-disasm-table* cmd-template-idx print))
(v0-1 (cond
((zero? print-kind)
;; just the name and irq bit.
(format stream " (~s :irq ~D)~%"
(-> *vif-disasm-table* cmd-template-idx string1)
(-> first-tag irq)
)
)
((= print-kind 1)
;; name and immediate register value.
(format stream " (~s :irq ~D :~s #x~X)~%"
(-> *vif-disasm-table* cmd-template-idx string1)
(-> first-tag irq)
(-> *vif-disasm-table* cmd-template-idx string2)
(-> first-tag imm)
)
)
((= print-kind 2)
;; name and stcycl immediate
(let ((stcycl-imm (the-as vif-stcycl-imm (-> first-tag imm))))
(format stream " (~s :irq ~D :wl ~D :cl ~D)~%"
(-> *vif-disasm-table* cmd-template-idx string1)
(-> first-tag irq)
(-> stcycl-imm wl)
(-> stcycl-imm cl)
)
)
)
((= print-kind 3)
;; name and a single word of extra data
(set! packet-size 8) ;; 4 + 4 = 8 byte packet.
(format stream " (~s :irq ~D :~s #x~X)~%"
(-> *vif-disasm-table* cmd-template-idx string1)
(-> first-tag irq)
(-> *vif-disasm-table* cmd-template-idx string2)
(-> data 1)
)
)
((= print-kind 4)
;; 4x 1 word extra data
(set! packet-size 20) ;; 4 + 16 = 20 byte packet.
(format stream " (~s :irq ~D :~s "
(-> *vif-disasm-table* cmd-template-idx string1)
(-> first-tag irq)
(-> *vif-disasm-table* cmd-template-idx string2)
)
(format stream "#x~X #x~X #x~X #x~X)~%"
(-> data 1)
(-> data 2)
(-> data 3)
(-> data 4)
)
)
((= print-kind 5)
(format stream " (~s :irq ~D :instructions #x~D :addr #x~X)~%"
(-> *vif-disasm-table* cmd-template-idx string1)
(-> first-tag irq)
(-> first-tag num)
(-> first-tag imm)
)
)
((= print-kind 6)
;; imm is quadword count.
;; This packet size calculation is wrong.
;; this doesn't seem to be a decompiler error, this matches
;; the assembly, but makes no sense.
(set! packet-size
(the-as int (if (-> first-tag imm)
#x100000
(shl (-> first-tag imm) 4)
)
)
)
(format stream " (~s :irq ~D :qwc #x~D)~%"
(-> *vif-disasm-table* cmd-template-idx string1)
(-> first-tag irq)
(-> first-tag imm)
)
;; loop over data and print it.
(set! data-ptr (&-> data 1))
(set! data-idx 0)
(while (< data-idx (the-as int (-> first-tag imm)))
(format stream " #x~X: #x~8x #x~8x #x~8x #x~8x~%"
(+ (+ (shl data-idx 4) 4) (the-as int data))
(-> data-ptr (shl data-idx 2))
(-> data-ptr (+ (shl data-idx 2) 1))
(-> data-ptr (+ (shl data-idx 2) 2))
(-> data-ptr (+ (shl data-idx 2) 3))
)
(set! data-idx (+ data-idx 1))
)
#f
)
((= print-kind 7)
(set! packet-size
(the-as int
(+
(logand
-4
(+ (* (-> *vif-disasm-table* cmd-template-idx val)
(the-as uint (-> first-tag num)))
3
)
)
4
)
)
)
(set! unpack-imm (the-as vif-unpack-imm (-> first-tag imm)))
(format stream " (~s :irq ~D :num ~D :addr #x~X "
(-> *vif-disasm-table* cmd-template-idx string1)
(-> first-tag irq)
(-> first-tag num)
(-> unpack-imm addr)
)
(format stream ":msk ~D :flg ~D :usn ~D [skip ~d])~%"
(-> first-tag msk)
(-> unpack-imm flg)
(-> unpack-imm usn)
(the-as uint packet-size)
)
(if details
(disasm-vif-details
stream
(the-as (pointer uint8) data)
(logand cmd (vif-cmd cmd-mask))
(the-as int (-> first-tag num))
)
)
)
((= print-kind 8)
(format stream " (*unknown* vif-tag #x~X)~%"
(-> first-tag cmd)
)
)
)
)
)
)
;; we matched, skip to the end.
(set! cmd-template-idx (-> *vif-disasm-table* length))
)
)
)
;; increment counters.
(+! byte-idx packet-size)
(&+! data packet-size)
)
)
(- byte-idx (shl words 2))
)
)
(defun disasm-dma-tag ((arg0 dma-tag) (arg1 symbol))
(format arg1 "(dma-tag ")
(format arg1 "~s" (enum->string dma-tag-id (-> arg0 id)))
(if (> (the-as uint (-> arg0 addr)) 0)
(format arg1 " :addr #x~8x" (-> arg0 addr))
)
(if (> (the-as uint (-> arg0 qwc)) 0)
(format arg1 " :qwc ~d" (-> arg0 qwc))
)
(if (> (the-as uint (-> arg0 spr)) 0)
(format arg1 " :spr ~d" (-> arg0 spr))
)
(if (> (the-as uint (-> arg0 irq)) 0)
(format arg1 " :irq ~d" (-> arg0 irq))
)
(if (> (the-as uint (-> arg0 pce)) 0)
(format arg1 " :pce ~d" (-> arg0 pce))
)
(format arg1 ")~%")
(none)
)
;; this is unused.
(define *dma-disasm* #t)
(defmacro disasm-dma-buffer (buff)
`(disasm-dma-list (the dma-packet (-> ,buff data-buffer)) 'details #t #t 0)
)
;; NOTE: the decompiler currently outputs something nicer looking for the nexted conds,
;; but keeping the old version with nicer comments for now.
(defun disasm-dma-list ((data dma-packet) (mode symbol) (verbose symbol) (stream symbol) (expected-size int))
"Disassemble a dma list, starting from the given packet."
(local-vars
(addr object)
(data-2 dma-packet)
(qwc int)
(ra-1 object)
(ra-2 object)
(call-depth int)
(current-tag dma-tag)
)
;; this is a little messed up because of stack spills.
(set! data-2 data)
(let ((mode-2 mode)
(verbose-2 verbose)
(stream-2 stream)
(expected-size-2 expected-size)
)
(if verbose-2
(format stream-2 "~%--- ~X -----------------------------~%" data-2)
)
;; the end-condition will get set to #t when the end of the chain is reached,
;; or 'error if invalid data is found.
(let ((end-condition #f))
;; statistics
(let ((total-qwc 0)
(total-tags 0)
)
(set! addr 0)
(set! qwc 0)
;; for the "call" feature
(set! ra-1 0)
(set! ra-2 1)
(set! call-depth -1)
;; the tag we're currently exploring.
(set! current-tag (new 'static 'dma-tag))
;; loop until tag is done
(while (not end-condition)
;; first, we should verify that the data pointer is valid so we don't crash
(cond
((not (valid? data-2 (the-as type #f) "dma-list tag pointer" #t stream-2))
(format stream-2 "ERROR: dma-list tag pointer invalid~%")
(set! end-condition 'error)
)
(else
;; load the tag
(set! current-tag (-> data-2 dma))
;; check the address. it is unset on the first pass so we skip this check then.
(when (not (or (zero? total-tags)
(valid? addr (the-as type #f) "dma-list data pointer" #t stream-2)
)
)
(format stream-2 "ERROR: dma-list data pointer invalid~%")
(set! end-condition 'error)
)
;; check that the tag's value makes sense.
(when (nonzero? (logand #x3ff0000 (the-as int current-tag)))
(format stream-2 "ERROR: dma tag has data in reserved bits ~X~%")
(set! end-condition 'error)
)
)
)
;; next, disassembly the dma-tag.
;; only do it if verbose is set, or we have encountered an error.
(when (or verbose-2 (= end-condition 'error))
(format stream-2 "#x~8x: " data-2)
(cond
((zero? call-depth)
(format stream-2 " ")
)
((= call-depth 1)
(format stream-2 " ")
)
)
(disasm-dma-tag current-tag stream-2)
)
;; now the dma data.
(if end-condition
(none) ;; do nothing if we want to end.
(cond
;; check if we are an addr in addr field dma tag.
((or (zero? (+ (the-as uint (-> current-tag id)) (the-as uint -3))) ;; ref
(zero? (+ (the-as uint (-> current-tag id)) (the-as uint -4))) ;; refs
(zero? (-> current-tag id)) ;; refe
)
;; set addresss and qwc from the tag.
(set! addr (-> current-tag addr))
(set! qwc (the-as int (-> current-tag qwc)))
;; optionally disassemble vif tags.
(if mode-2
;; I don't quite understand this. The first thing is for the tag transferred due to tte.
;; but I don't understand what the v0-9 offset is.
(let ((v0-9 (disasm-vif-tag (&-> data-2 vif0) 2 stream-2 (= mode-2 'details))))
(disasm-vif-tag
(the-as (pointer vif-tag) (+ (the-as uint addr) (the-as uint v0-9)))
(the-as int (- (shl (the-as int qwc) 2) (the-as uint (sar v0-9 2))))
stream-2
(= mode-2 'details)
)
)
)
;; move on to next dma-packet. it is adjacent in memory for these modes.
(set! data-2 (the-as dma-packet (&-> (the-as (pointer uint64) data-2) 2)))
;; if we were a refe, it is now the end!
(if (zero? (-> current-tag id)) ;; check refe.
(set! end-condition #t)
)
)
(else
(cond
((= (-> current-tag id) (dma-tag-id cnt))
;; cnt: address is after tag, next tag is after data.
;; get the address from after the tag
(set! addr (&-> (the-as (pointer uint64) data-2) 2))
;; qwc from the tag
(set! qwc (the-as int (-> current-tag qwc)))
;; disassemble vif.
(if mode-2
(disasm-vif-tag
(the-as (pointer vif-tag) (&-> (the-as (pointer uint64) data-2) 1))
(the-as int (+ (shl (the-as int qwc) 2) 2))
stream-2
(= mode-2 'details)
)
)
;; next data is after vif data.
(set! data-2 (the-as dma-packet (+ (the-as uint data-2)
(the-as uint (shl (the-as int (+ (the-as uint qwc)
(the-as uint 1)))
4)
)
)
)
)
)
((= (-> current-tag id) (dma-tag-id next))
;; address after tag and qwc in tag.
(set! addr (&-> (the-as (pointer uint64) data-2) 2))
(set! qwc (the-as int (-> current-tag qwc)))
(if mode-2
(disasm-vif-tag
(the-as (pointer vif-tag) (&-> (the-as (pointer uint64) data-2) 1))
(the-as int (+ (shl (the-as int qwc) 2) 2))
stream-2
(= mode-2 'details)
)
)
;; addr is the next tag. check for infinite loop before continuing.
(when (= data-2 (-> current-tag addr))
(format stream-2 "ERROR: next tag creates infinite loop.~%")
(set! end-condition 'error)
)
(set! data-2 (the-as dma-packet (-> current-tag addr)))
)
(else
(cond
((= (-> current-tag id) (dma-tag-id call))
;; this "calls" a DMA chain, which should then return to here.
;; the stack is only two deep.
(set! addr (&-> (the-as (pointer uint64) data-2) 2))
(set! qwc (the-as int (-> current-tag qwc)))
(if mode-2
(disasm-vif-tag (the-as (pointer vif-tag) (&-> (the-as (pointer uint64) data-2) 1))
(the-as int (+ (shl (the-as int qwc) 2) 2))
stream-2
(= mode-2 'details)
)
)
(set! data-2 (the-as dma-packet (-> current-tag addr)))
;; increment stack
(set! call-depth (+ call-depth 1))
;; and store the return.
(cond
((zero? call-depth)
(set! ra-1 (&+ (the pointer addr) qwc))
)
(else
(set! ra-2 (&+ (the pointer addr) qwc))
)
)
)
((= (-> current-tag id) (dma-tag-id ret))
;; return from a "called" dma chain.
(set! addr (&-> (the-as (pointer uint64) data-2) 2))
(set! qwc (the-as int (-> current-tag qwc)))
(if mode-2
(disasm-vif-tag (the-as (pointer vif-tag) (&-> (the-as (pointer uint64) data-2) 1))
(the-as int (+ (shl (the-as int qwc) 2) 2))
stream-2
(= mode-2 'details)
)
)
;; restore the address from the stack
;; likely a case.
(let ((v1-123 call-depth))
(cond
((zero? v1-123)
(set! data-2 (the-as dma-packet ra-1))
)
((= v1-123 1)
(set! data-2 (the-as dma-packet ra-2))
)
(else
(set! end-condition #t)
)
)
)
(set! call-depth (+ call-depth -1))
)
((= (-> current-tag id) (dma-tag-id end))
(set! addr (&-> (the-as (pointer uint64) data-2) 2))
(set! qwc (the-as int (-> current-tag qwc)))
(set! end-condition #t)
(if mode-2
(disasm-vif-tag (the-as (pointer vif-tag) (&-> (the-as (pointer uint64) data-2) 1))
(the-as int (+ (shl (the-as int qwc) 2) 2))
stream-2
(= mode-2 'details)
)
)
)
(else
(format stream-2 "ERROR: Unknown DMA TAG command.~%")
(set! end-condition 'error)
)
)
)
)
)
)
)
;; increment stats
(+! total-qwc qwc)
(+! total-tags 1)
;; end, if we hit the tag limit.
(if (and (>= expected-size-2 0) (>= total-tags expected-size-2))
(set! end-condition #t)
)
)
(when (or verbose-2 (= end-condition 'error))
(format stream-2 "NOTICE: Total tags: ~d~%" total-tags)
(format stream-2 "NOTICE: Total QWC: ~d~%" total-qwc)
(format stream-2 "--------------------------------~%~%")
)
)
(!= end-condition 'error)
)
)
)