jak-project/goal_src/jak1/engine/dma/dma.gc
Tyler Wilding 5e987cc0e2
jak2: overlord rework (#2544)
Fixes #2545
Fixes #2546
Fixes #2547
Fixes #2548
Fixes #2549
Fixes #2550
Fixes #2551
Fixes #2552
Fixes #2553
Fixes #2554
Fixes #2555
Fixes #2556
Fixes #2557
Fixes #2558
Fixes #2559
Fixes #2560
Fixes #2561
Fixes #2562
Fixes #2563
Fixes #2564
Fixes #2565
Fixes #2567
Fixes #2566
Fixes #2568
Fixes #2569
Fixes #2570
Fixes #2522
Fixes #2571

---------

Co-authored-by: water <awaterford111445@gmail.com>
Co-authored-by: Hat Kid <6624576+Hat-Kid@users.noreply.github.com>
Co-authored-by: ManDude <7569514+ManDude@users.noreply.github.com>
2023-04-29 16:13:57 -04:00

432 lines
12 KiB
Common Lisp

;;-*-Lisp-*-
(in-package goal)
;; name: dma.gc
;; name in dgo: dma
;; dgos: GAME, ENGINE
;; DMA send and sync functions:
;; These functions transfer existing DMA chains and buffers.
;; In general, there are two types:
;; - plain transfer of a buffer (like, src, dst, size)
;; - chain, a data structure the PS2 DMA hardware can traverse, just give it a starting address.
;; The plan for OpenGOAL is to patch things higher up, likely in renderers or buffers.
;; So we would replace a (dma-send-chain ...) in a render with
;; (generic-dma-pc-port ...) that will pass the data along to the PC renderer.
;; these functions are just here for completeness and won't work properly on PC.
;; DMA sync functions will succeed and return instantly, so it's safe to leave those in.
(defun dma-sync-hang ((bank dma-bank))
"Hang here until the dma transfer is completed.
This is worse than the dma-sync-fast because it ends
up spamming the DMA bank register more often, and reduces
the speed of the DMA transfer.
This function is unused."
(#cond
(INSTANT_DMA
0)
(#t
(while (nonzero? (-> bank chcr str))
(nop!)
)
)
)
(none)
)
(defun dma-sync-crash ((arg0 dma-bank))
"Wait for DMA to finish for a while, then crash if we can't.
This function is unused."
(#cond
(INSTANT_DMA
(return 0)
)
)
(let ((v1-0 5000000))
(while (nonzero? (-> arg0 chcr str))
(if (zero? v1-0)
(crash!)
(+! v1-0 -1)
)
)
(none)
)
)
(defmacro make-madr-addr (addr)
"Convert an address to one suitable for MADR.
This works with Main RAM (all mappings) and for spad"
`(let ((int-addr (the-as int ,addr)))
(logior (logand #xfffffff int-addr) ;; mask off spad/mapping bits
(if (= (logand #x70000000 int-addr) #x70000000)
#x80000000 ;; if we're in spad, set high bit.
0 ;; otherwise nothing.
)
)
)
)
(defun dma-send ((arg0 dma-bank) (madr uint) (qwc uint))
"Send DMA given an address and a quadword count.
The madr can be in main memory or scratchpad.
This is appropriate for VIF0/GIF transfers.
It can be used for VIF1, but will do VIF -> madr, which is probably
not what you want."
(dma-sync (the-as pointer arg0) 0 0)
(flush-cache 0)
(.sync.l)
(set! (-> arg0 madr) (make-madr-addr madr))
(set! (-> arg0 qwc) qwc)
(.sync.l)
;; begin DMA!
(set! (-> arg0 chcr) (new 'static 'dma-chcr :str 1))
(.sync.l)
(none)
)
(defun dma-send-chain ((arg0 dma-bank-source) (tadr uint))
"Send DMA! tadr should be a tag address, possibly in spad ram.
This is useful for sending to VIF.
Tag transfer is enabled, and DIR is set so a VIF1 transfer
goes from tadr -> VIF."
(dma-sync (the-as pointer arg0) 0 0)
(flush-cache 0)
(.sync.l)
(set! (-> arg0 qwc) 0)
;; adjust address
(set! (-> arg0 tadr) (make-madr-addr tadr))
(.sync.l)
;;(set! (-> arg0 chcr) 325)
(set! (-> arg0 chcr)
(new 'static 'dma-chcr
:dir 1 ;; from memory
:mod 1 ;; source chain
:tte 1 ;; send tags
:str 1) ;; go!
)
(.sync.l)
(none)
)
(defun dma-send-chain-no-tte ((arg0 dma-bank-source) (arg1 uint))
"Send DMA chain! TTE bit is not set, don't transfer tags.
This is never used."
(dma-sync (the-as pointer arg0) 0 0)
(flush-cache 0)
(.sync.l)
(set! (-> arg0 qwc) 0)
(set! (-> arg0 tadr) (make-madr-addr arg1))
(.sync.l)
(set! (-> arg0 chcr)
(new 'static 'dma-chcr
:dir 1 ;; from memory
:mod 1 ;; source chain
:tte 0 ;; no tags
:str 1) ;; go!
)
(.sync.l)
(none)
)
(defun dma-send-chain-no-flush ((arg0 dma-bank-source) (arg1 uint))
"Send DMA chain! But don't flush the cache, so be careful here. TTE enable."
(local-vars (v0-1 int))
(dma-sync (the-as pointer arg0) 0 0)
(.sync.l)
(set! (-> arg0 qwc) 0)
(set! (-> arg0 tadr) arg1)
(.sync.l)
;;(set! (-> arg0 chcr) 325)
(set! (-> arg0 chcr)
(new 'static 'dma-chcr
:dir 1
:mod 1
:tte 1
:str 1)
)
(.sync.l)
(none)
)
(defun dma-send-to-spr ((sadr uint) (madr uint) (qwc uint) (sync symbol))
"Transfer data to spr"
(local-vars (s5-0 dma-bank-spr))
(#when PC_PORT
(return 0)
)
(set! s5-0 SPR_TO_BANK)
(dma-sync (the-as pointer s5-0) 0 0)
(flush-cache 0)
(.sync.l)
(set! (-> s5-0 madr) (logand #xfffffff (the-as int madr)))
(set! (-> s5-0 sadr) (logand #xfffffff (the-as int sadr)))
(set! (-> s5-0 qwc) qwc)
(.sync.l)
(set! (-> s5-0 chcr) (new 'static 'dma-chcr :str 1))
(.sync.l)
(if sync
(dma-sync (the-as pointer s5-0) 0 0))
(none)
)
(defun dma-send-to-spr-no-flush ((sadr uint) (madr uint) (qwc uint) (sync symbol))
"Transfer to spr. Doesn't flush the cache first, so be careful."
(local-vars (s5-0 dma-bank-spr))
(#when PC_PORT
(return 0)
)
(set! s5-0 SPR_TO_BANK)
(dma-sync (the-as pointer s5-0) 0 0)
(.sync.l)
(set! (-> s5-0 madr) (logand #xfffffff (the-as int madr)))
(set! (-> s5-0 sadr) (logand #xfffffff (the-as int sadr)))
(set! (-> s5-0 qwc) qwc)
(.sync.l)
(set! (-> s5-0 chcr) (new 'static 'dma-chcr :str 1))
(.sync.l)
(if sync (dma-sync (the-as pointer s5-0) 0 0))
(none)
)
(defun dma-send-from-spr ((madr uint) (sadr uint) (qwc uint) (sync symbol))
"Transfer from spr."
(local-vars (s5-0 dma-bank-spr))
(#when PC_PORT
(return 0)
)
(set! s5-0 SPR_FROM_BANK)
(dma-sync (the-as pointer s5-0) 0 0)
(flush-cache 0)
(.sync.l)
(set! (-> s5-0 madr) (logand #xfffffff (the-as int madr)))
(set! (-> s5-0 sadr) (logand #xfffffff (the-as int sadr)))
(set! (-> s5-0 qwc) qwc)
(.sync.l)
(set! (-> s5-0 chcr) (new 'static 'dma-chcr :str 1))
(.sync.l)
(if sync (dma-sync (the-as pointer s5-0) 0 0))
(none)
)
(defun dma-send-from-spr-no-flush ((madr uint) (sadr uint) (qwc uint) (sync symbol))
"Transfer from spr, don't flush the cache."
(local-vars (s5-0 dma-bank-spr))
(#when PC_PORT
(return 0)
)
(set! s5-0 SPR_FROM_BANK)
(dma-sync (the-as pointer s5-0) 0 0)
(.sync.l)
(set! (-> s5-0 madr) (logand #xfffffff (the-as int madr)))
(set! (-> s5-0 sadr) (logand #xfffffff (the-as int sadr)))
(set! (-> s5-0 qwc) qwc)
(.sync.l)
(set! (-> s5-0 chcr) (new 'static 'dma-chcr :str 1))
(.sync.l)
(if sync (dma-sync (the-as pointer s5-0) 0 0))
(none)
)
(defun dma-initialize ()
"Due to a bug in the PS2 hardware, you must always disable the DMAtag mismatch
error. This is done here."
(#when PC_PORT
(return 0)
)
(set! (-> (the-as vif-bank #x10003800) err me0) 1)
(set! (-> (the-as vif-bank #x10003c00) err me0) 1)
(none)
)
(defun clear-vu0-mem ()
"Set the vu0 data memory to 0xabadbeef. This uses the slow EE mapping of VU memory.
Will crash on PC Port."
(let ((v1-0 VU0_DATA_MEM_MAP))
(dotimes (a0-0 1024)
(set! (-> v1-0 a0-0) #xabadbeef)
)
)
(none)
)
(defun clear-vu1-mem ()
"Set the vu1 data memory to 0xabadbeef. This uses the slow EE mapping of VU memory.
Will crash on PC Port."
(let ((v1-0 VU1_DATA_MEM_MAP))
(dotimes (a0-0 4096)
(set! (-> v1-0 a0-0) #xabadbeef)
)
)
(none)
)
(defun dump-vu1-mem ()
"Print VU1 memory to runtime stdout.
Will crash on PC Port."
(local-vars (s5-0 int) (gp-0 (pointer uint32)))
(set! gp-0 (the (pointer uint32) #x1100c000))
(set! s5-0 0)
(while (< s5-0 1024) ;; 1k quadwords
(format 0 "~4,'0X: ~8,'0X ~8,'0X ~8,'0X ~8,'0X"
s5-0
(-> gp-0 (shl s5-0 2))
(-> gp-0 (+ (shl s5-0 2) 1))
(-> gp-0 (+ (shl s5-0 2) 2))
(-> gp-0 (+ (shl s5-0 2) 3))
)
(format 0 " ~F ~F ~F ~F ~%"
(-> gp-0 (shl s5-0 2))
(-> gp-0 (+ (shl s5-0 2) 1))
(-> gp-0 (+ (shl s5-0 2) 2))
(-> gp-0 (+ (shl s5-0 2) 3))
)
(set! s5-0 (+ s5-0 1))
)
(none)
)
(defun dump-vu1-range ((start uint) (total-count uint))
"Print part of VU1 memory to runtime stdout.
Will crash on PC Port."
(local-vars (s3-0 int) (s2-0 int) (s4-0 (pointer uint32)))
(set! s4-0 (the (pointer uint32) #x1100c000))
(set! s3-0 0)
(while (< s3-0 (the-as int total-count))
(set! s2-0 (+ s3-0 (the-as int start)))
(format 0 "~4,'0X: ~8x ~8x ~8x ~8x"
s2-0
(-> s4-0 (shl s2-0 2))
(-> s4-0 (+ (shl s2-0 2) 1))
(-> s4-0 (+ (shl s2-0 2) 2))
(-> s4-0 (+ (shl s2-0 2) 3))
)
(format 0 " ~F ~F ~F ~F ~%"
(-> s4-0 (shl s2-0 2))
(-> s4-0 (+ (shl s2-0 2) 1))
(-> s4-0 (+ (shl s2-0 2) 2))
(-> s4-0 (+ (shl s2-0 2) 3))
)
(set! s3-0 (+ s3-0 1))
)
'#f
)
;; if we send junk DMA data due to an engine bug, the PS2 will eventually time out the transfer.
;; in this case, the main loop will attempt to reset everything to hopefully recover.
;; this value is the PAL/NTSC constant for the Sony reset functions.
;; if we switch PAL/NTSC during runtime, we should update this.
(define *video-reset-parm* 2)
(defun reset-vif1-path ()
"When things go wrong, totally reset vif1."
;; inspect the banks
(#unless PC_PORT
((method-of-type dma-bank-vif inspect) VIF1_DMA_BANK)
((method-of-type vif-bank inspect) VIF1_BANK)
)
;; sceGsResetPath
(reset-path)
;; sceGsResetGraph
(reset-graph 1 1 *video-reset-parm* 1)
(format 0 "gkernel: vif1 path reset!~%")
(none)
)
(defun ultimate-memcpy ((dst pointer) (src pointer) (size-bytes uint))
"The Fastest Memory Copy, for larger transfers.
Memory is copied in ascending order, in 4 kB blocks.
The size should be a multiple of 16 bytes."
(#cond
(PC_PORT
;; on PC Port, just call C mem-move, it's the fastest.
(__mem-move dst src size-bytes)
)
(else
;; ultimate-memcpy works by DMAing to the scratchpad and back.
;; surprisingly this seems to be the fastest memcpy on larger
;; transfers. This is a nice example of how DMA is used from GOAL.
(let ((spr-to-bank (the-as dma-bank-spr #x1000d400))
(spr-from-bank (the-as dma-bank-spr #x1000d000))
(qwc-remaining (shr size-bytes 4))
)
;; Flush all data in the dcache to main memory. DMA bypasses the dcache.
(flush-cache 0)
;; Complete all pending DMA transfers using the spad.
;; (this uses the Sony library DMA sync, which is bad)
(dma-sync (the-as pointer spr-to-bank) 0 0)
(dma-sync (the-as pointer spr-from-bank) 0 0)
;; transfer loop
(while (> qwc-remaining 0)
;; copy up to 1024 quadwords - limited by the 4kB spad size.
(let ((qwc-transferred-now (the-as int qwc-remaining)))
(if (< (the-as uint 1024) (the-as uint qwc-transferred-now))
(set! qwc-transferred-now 1024)
)
(set! qwc-remaining (- qwc-remaining (the-as uint qwc-transferred-now)))
;; set up the "to spad" transfer
(.sync.l)
(set! (-> spr-to-bank madr) (the-as uint src))
(set! (-> spr-to-bank sadr) (the-as uint 0))
(set! (-> spr-to-bank qwc) (the-as uint qwc-transferred-now))
(.sync.l)
;; activate!
(set! (-> spr-to-bank chcr) (new 'static 'dma-chcr :str #x1))
(.sync.l)
;; wait for it to finish...
(dma-sync (the-as pointer spr-to-bank) 0 0)
(&+! src (shl qwc-transferred-now 4))
;; and copy back...
(set! (-> spr-from-bank madr) (the-as uint dst))
(set! (-> spr-from-bank sadr) (the-as uint 0))
(set! (-> spr-from-bank qwc) (the-as uint qwc-transferred-now))
(.sync.l)
(set! (-> spr-from-bank chcr) (new 'static 'dma-chcr :str #x1))
(.sync.l)
(dma-sync (the-as pointer spr-from-bank) 0 0)
(&+! dst (shl qwc-transferred-now 4))
)
)
)
(let ((v0-4 0))
)
)
)
(none)
)
(defun symlink2 ()
"symlink2 is a handwritten assembly version of the v2 linking routine.
it is not ported because the OpenGOAL linker has its own implementation already."
(segfault)
(none)
)
(defun symlink3 ()
"symlink3 is a handwritten assembly version of the v3 linking routine.
OpenGOAL uses a different format for v3, customized for x86-64, so this is not
needed. The C++ implementation is plenty fast enough"
(segfault)
(none)
)
;; configuration required to work around hardware bug on the PS2.
;; doesn't do anything important
(dma-initialize)