mirror of
https://github.com/open-goal/jak-project
synced 2026-06-12 05:27:57 -04:00
Add the STR RPC to overlord and game code (#134)
* work in progress streaming rpc, simple test is working * actually add the test * debug windows failure * windows fix maybe * windows 2 * use str-load-status * update types
This commit is contained in:
@@ -5,3 +5,130 @@
|
||||
;; name in dgo: load-dgo
|
||||
;; dgos: GAME, ENGINE
|
||||
|
||||
(deftype load-dgo-msg (structure)
|
||||
((rsvd uint16 :offset-assert 0)
|
||||
(result uint16 :offset-assert 2)
|
||||
(b1 uint32 :offset-assert 4)
|
||||
(b2 uint32 :offset-assert 8)
|
||||
(bt uint32 :offset-assert 12)
|
||||
;(name uint128 :offset-assert 16)
|
||||
(name uint8 16 :offset-assert 16)
|
||||
(address uint32 :offset 4)
|
||||
)
|
||||
:method-count-assert 9
|
||||
:size-assert #x20
|
||||
:flag-assert #x900000020
|
||||
)
|
||||
|
||||
#|
|
||||
struct RPC_Dgo_Cmd {
|
||||
uint16_t rsvd;
|
||||
uint16_t result;
|
||||
uint32_t buffer1;
|
||||
uint32_t buffer2;
|
||||
uint32_t buffer_heap_top;
|
||||
char name[16];
|
||||
};
|
||||
|#
|
||||
|
||||
(deftype load-chunk-msg (structure)
|
||||
((rsvd uint16 :offset-assert 0)
|
||||
(result uint16 :offset-assert 2)
|
||||
(address uint32 :offset-assert 4)
|
||||
(section uint32 :offset-assert 8)
|
||||
(maxlen uint32 :offset-assert 12)
|
||||
(id uint32 :offset 4)
|
||||
(basename uint8 48 :offset-assert 16)
|
||||
)
|
||||
:method-count-assert 9
|
||||
:size-assert #x40
|
||||
:flag-assert #x900000040
|
||||
)
|
||||
|
||||
#|
|
||||
struct RPC_Str_Cmd {
|
||||
u16 rsvd; // 0, seems unused
|
||||
u16 result; // 2, return code. see STR_RPC_RESULT_XXX
|
||||
u32 ee_addr; // 4, GOAL address to load to.
|
||||
s32 chunk_id; // 8, chunk ID for chunked file. Use -1 to load a non-chunked file, which gets the
|
||||
// whole file.
|
||||
u32 length; // 12, length that was actually loaded
|
||||
char name[64]; // file name
|
||||
};
|
||||
|#
|
||||
|
||||
(deftype dgo-header (structure)
|
||||
((length uint32 :offset-assert 0)
|
||||
(rootname uint8 60 :offset-assert 4)
|
||||
)
|
||||
:method-count-assert 9
|
||||
:size-assert #x40
|
||||
:flag-assert #x900000040
|
||||
)
|
||||
|
||||
#|
|
||||
struct DgoHeader {
|
||||
u32 object_count;
|
||||
char name[60];
|
||||
};
|
||||
|#
|
||||
|
||||
(define-extern *load-dgo-rpc* rpc-buffer-pair)
|
||||
(when (= 0 (the int *load-dgo-rpc*))
|
||||
;; we need to allocate the rpc buffers
|
||||
(set! *load-dgo-rpc* (new 'global 'rpc-buffer-pair (the uint 32) (the uint 1) 3)) ;; todo, constants
|
||||
(define *load-str-rpc* (new 'global 'rpc-buffer-pair (the uint 64) (the uint 1) 4)) ;; todo, constants
|
||||
(define *play-str-rpc* (new 'global 'rpc-buffer-pair (the uint 64) (the uint 2) 5))
|
||||
(define *load-str-lock* '#f)
|
||||
(define *que-str-lock* '#f)
|
||||
(define *dgo-name* (new 'global 'string 64 (the string '#f)))
|
||||
)
|
||||
|
||||
(defun str-load ((name string) (chunk-id int) (address pointer) (len int))
|
||||
"Begin a streaming load if possible!
|
||||
We must be able to grab the lock, and no streaming load in progress.
|
||||
Return if we actually start the load."
|
||||
;; call method 13
|
||||
(when (or (check-busy *load-str-rpc*)
|
||||
*load-str-lock*
|
||||
)
|
||||
(return-from #f '#f)
|
||||
)
|
||||
;; ok, we are good to start a load. begin by adding an element to the RPC buffer
|
||||
(let ((cmd (the load-chunk-msg (add-element *load-str-rpc*))))
|
||||
(set! (-> cmd result) 666)
|
||||
(set! (-> cmd address) address)
|
||||
(set! (-> cmd section) chunk-id)
|
||||
(set! (-> cmd maxlen) len)
|
||||
(charp<-string (-> cmd basename) name)
|
||||
(call *load-str-rpc* (the uint 0) (the pointer cmd) (the uint 32))
|
||||
(set! *load-str-lock* '#t)
|
||||
(set! *que-str-lock* '#t)
|
||||
'#t
|
||||
)
|
||||
)
|
||||
|
||||
(defun str-load-status ((length-out (pointer int32)))
|
||||
"Check the status of the str load.
|
||||
The 'busy status indicates it is still going
|
||||
The 'error status indicates the load failed.
|
||||
The 'complete status means the load is finished, and length-out contains the loaded length."
|
||||
|
||||
;; still going..
|
||||
(if (check-busy *load-str-rpc*)
|
||||
(return-from #f 'busy)
|
||||
)
|
||||
|
||||
;; not busy, we can free the lock
|
||||
(set! *load-str-lock* '#f)
|
||||
(set! *que-str-lock* '#t)
|
||||
;; grab the response
|
||||
(let ((response (the load-chunk-msg (pop-last-received *load-str-rpc*))))
|
||||
(if (= 1 (-> response result))
|
||||
(return-from #f 'error)
|
||||
)
|
||||
;; no error!
|
||||
(set! (-> length-out) (the int (-> response maxlen)))
|
||||
'complete
|
||||
)
|
||||
)
|
||||
@@ -5,3 +5,230 @@
|
||||
;; name in dgo: rpc-h
|
||||
;; dgos: GAME, ENGINE
|
||||
|
||||
;; an RPC buffer is a container of elements to send to the IOP.
|
||||
;; each element is size elt-size, and there are maximum of elt-count elements
|
||||
;; it is possible to use fewer elements than elt-count.
|
||||
;; the buffer is 64-byte aligned.
|
||||
(deftype rpc-buffer (basic)
|
||||
((elt-size uint32 :offset-assert 4)
|
||||
(elt-count uint32 :offset-assert 8)
|
||||
(elt-used uint32 :offset-assert 12)
|
||||
(busy basic :offset-assert 16) ;; are we being sent currently?
|
||||
(base pointer :offset-assert 20) ;; 64-byte aligned buffer of elts.
|
||||
;; I suspect this was 16-byte aligned for DMA purposes.
|
||||
(data uint8 :dynamic :offset 32)
|
||||
)
|
||||
(:methods
|
||||
(new (symbol type uint uint) rpc-buffer 0)
|
||||
)
|
||||
:method-count-assert 9
|
||||
:size-assert #x20
|
||||
:flag-assert #x900000020
|
||||
)
|
||||
|
||||
(defmethod new rpc-buffer ((allocation symbol) (type-to-make type) (elt-size uint) (elt-count uint))
|
||||
"Create a new rpc-buffer with room for elt-count elements of elt-size.
|
||||
The element array is 64-byte aligned."
|
||||
;; we make room for a buffer of size elt-size * elt-count that is _64 byte_ aligned.
|
||||
(let ((obj (object-new (the int (+ (-> type-to-make size) 63 (* elt-size elt-count))))))
|
||||
(set! (-> obj elt-size) elt-size)
|
||||
(set! (-> obj elt-count) elt-count)
|
||||
(set! (-> obj elt-used) 0)
|
||||
(set! (-> obj busy) '#f)
|
||||
;(set! (-> obj base) (logand -64 (+ (the uint obj) 28 63)))
|
||||
;; base is the 64-byte aligned buffer.
|
||||
(set! (-> obj base) (the pointer (logand -64 (+ (the uint (-> obj data)) 63))))
|
||||
obj
|
||||
)
|
||||
)
|
||||
|
||||
;; An RPC buffer pair is a pair of two buffers that implement double buffering.
|
||||
;; The "current" buffer is the one being loaded on the EE.
|
||||
;; The other is referred to as the active buffer.
|
||||
;; This also supports receiving data, though it just gives you a plain pointer.
|
||||
(deftype rpc-buffer-pair (basic)
|
||||
((buffer rpc-buffer 2 :offset-assert 4) ;; the two buffers
|
||||
(current rpc-buffer :offset-assert 12) ;; the buffer being loaded
|
||||
(last-recv-buffer pointer :offset-assert 16) ;; the last reply
|
||||
(rpc-port int32 :offset-assert 20) ;; the RPC port number
|
||||
)
|
||||
:method-count-assert 15
|
||||
:size-assert #x18
|
||||
:flag-assert #xf00000018
|
||||
(:methods
|
||||
(new (symbol type uint uint int) rpc-buffer-pair 0)
|
||||
(call (rpc-buffer-pair uint pointer uint) int 9)
|
||||
(add-element (rpc-buffer-pair) pointer 10)
|
||||
(decrement-elt-used (rpc-buffer-pair) int 11)
|
||||
(sync (rpc-buffer-pair symbol) int 12)
|
||||
(check-busy (rpc-buffer-pair) symbol 13)
|
||||
(pop-last-received (rpc-buffer-pair) pointer 14)
|
||||
)
|
||||
)
|
||||
|
||||
(defmethod new rpc-buffer-pair ((allocation symbol) (type-to-make type) (elt-size uint) (elt-count uint) (rpc-port int))
|
||||
"Create a new rpc-buffer-pair"
|
||||
(let ((obj (object-new)))
|
||||
(set! (-> obj buffer 0) (new 'global 'rpc-buffer elt-size elt-count))
|
||||
(set! (-> obj buffer 1) (new 'global 'rpc-buffer elt-size elt-count))
|
||||
(set! (-> obj current) (-> obj buffer 0))
|
||||
(set! (-> obj last-recv-buffer) (the pointer '#f))
|
||||
(set! (-> obj rpc-port) rpc-port)
|
||||
obj
|
||||
)
|
||||
)
|
||||
|
||||
;; method 12
|
||||
(defmethod sync rpc-buffer-pair ((obj rpc-buffer-pair) (print-stall-warning symbol))
|
||||
"Wait for the in progress RPC to complete."
|
||||
(let ((active-buffer (if (= (-> obj buffer 0) (-> obj current))
|
||||
(-> obj buffer 1)
|
||||
(-> obj buffer 0))
|
||||
)
|
||||
)
|
||||
|
||||
(when (-> active-buffer busy)
|
||||
;; the flag is set, meaning we should check.
|
||||
(cond
|
||||
((!= 0 (rpc-busy? (-> obj rpc-port)))
|
||||
;; we're busy
|
||||
(if print-stall-warning
|
||||
(format 0 "STALL: waiting for IOP on RPC port #~D~%" (-> obj rpc-port))
|
||||
)
|
||||
(while (!= 0 (rpc-busy? (-> obj rpc-port)))
|
||||
;; real game has a bunch of nops
|
||||
(+ 1 2 3)
|
||||
)
|
||||
)
|
||||
(else
|
||||
;; not actually busy, clear the flag!
|
||||
(set! (-> active-buffer busy) '#f)
|
||||
(set! (-> active-buffer elt-used) 0)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
)
|
||||
0
|
||||
)
|
||||
|
||||
;; method 13
|
||||
(defmethod check-busy rpc-buffer-pair ((obj rpc-buffer-pair))
|
||||
"Is the currently running RPC still busy?"
|
||||
(let ((active-buffer (if (= (-> obj buffer 0) (-> obj current))
|
||||
(-> obj buffer 1)
|
||||
(-> obj buffer 0)
|
||||
)))
|
||||
(when (-> active-buffer busy)
|
||||
(if (!= 0 (rpc-busy? (-> obj rpc-port)))
|
||||
(return-from #f '#t)
|
||||
)
|
||||
(set! (-> active-buffer busy) '#f)
|
||||
(set! (-> active-buffer elt-count) 0)
|
||||
)
|
||||
)
|
||||
'#f
|
||||
)
|
||||
|
||||
(defmacro hack-rpc-call (a0 a1 a2 a3 a4 a5 a6)
|
||||
`(begin
|
||||
(rpc-call-p1 ,a0 ,a1 ,a2)
|
||||
(rpc-call-p2 ,a3 ,a4 ,a5 ,a6)
|
||||
)
|
||||
)
|
||||
|
||||
;; method 9
|
||||
(defmethod call rpc-buffer-pair ((obj rpc-buffer-pair) (fno uint) (recv-buff pointer) (recv-size uint))
|
||||
"Call an RPC. This is an async RPC. Use check-busy or sync to see if it's done."
|
||||
(when (!= 0 (-> obj current elt-used))
|
||||
;; when we have used elements
|
||||
(format 0 "call rpc-buffer-pair with ~D elts~%" (-> obj current elt-used))
|
||||
|
||||
;; make sure the previous buffer is done
|
||||
(let ((active-buffer (if (= (-> obj buffer 0) (-> obj current))
|
||||
(-> obj buffer 1)
|
||||
(-> obj buffer 0))))
|
||||
(when (-> active-buffer busy)
|
||||
;; we think the active buffer may be busy.
|
||||
;; first lets just do a simple check
|
||||
(cond
|
||||
((!= 0 (rpc-busy? (-> obj rpc-port)))
|
||||
;; busy! print an error and stall!
|
||||
(format 0 "STALL: waiting for IOP on RPC port #~D~%" (-> obj rpc-port))
|
||||
(while (!= 0 (rpc-busy? (-> obj rpc-port)))
|
||||
(+ 1 2 3)
|
||||
)
|
||||
)
|
||||
(else
|
||||
;; not busy.
|
||||
(set! (-> active-buffer busy) '#f)
|
||||
(set! (-> active-buffer elt-used) 0)
|
||||
)
|
||||
)
|
||||
)
|
||||
;; now we've cleared the last RPC call, we can do another
|
||||
(let ((current-buffer (-> obj current)))
|
||||
;; rpc_channel, fno, async, send_buff, send_size, recv_buff, recv_size
|
||||
(format 0 "recv-size is ~D~%" recv-size)
|
||||
(hack-rpc-call (-> obj rpc-port)
|
||||
fno
|
||||
(the uint 1)
|
||||
(the uint (-> current-buffer base))
|
||||
(the int (* (-> current-buffer elt-used) (-> current-buffer elt-size)))
|
||||
(the uint recv-buff)
|
||||
(the int recv-size)
|
||||
)
|
||||
(set! (-> current-buffer busy) '#t)
|
||||
(set! (-> obj last-recv-buffer) recv-buff)
|
||||
(set! (-> obj current) active-buffer)
|
||||
)
|
||||
)
|
||||
)
|
||||
0
|
||||
)
|
||||
|
||||
|
||||
;; method 14
|
||||
(defmethod pop-last-received rpc-buffer-pair ((obj rpc-buffer-pair))
|
||||
(let ((result (-> obj last-recv-buffer)))
|
||||
(set! (-> obj last-recv-buffer) (the rpc-buffer '#f))
|
||||
result
|
||||
)
|
||||
)
|
||||
|
||||
;; method 10
|
||||
(defmethod add-element rpc-buffer-pair ((obj rpc-buffer-pair))
|
||||
"Add an element, and return a pointer to the element.
|
||||
If we are out of elements, flush by doing an RPC call.
|
||||
DANGER: this uses all arguments of 0. If you want something else, flush it yourself.
|
||||
If we're RPC 0 and we do this auto-flush, print a warning.
|
||||
"
|
||||
(let ((current-buffer (-> obj current)))
|
||||
(when (= (-> current-buffer elt-count) (-> current-buffer elt-used))
|
||||
;; oops, we're full.
|
||||
(if (= 0 (-> obj rpc-port))
|
||||
;; if we're RPC 0, this is evidently a situation to warn about.
|
||||
(format 0 "WARNING: too many sound commands queued~%")
|
||||
)
|
||||
;; otherwise we just flush
|
||||
;; seems kinda dangerous. these could be the wrong parameters...
|
||||
(call obj (the uint 0) (the pointer 0) (the uint 0))
|
||||
;; update the current-buffer.
|
||||
(set! current-buffer (-> obj current))
|
||||
)
|
||||
(let ((result (&+ (-> current-buffer base) (* (-> current-buffer elt-size) (-> current-buffer elt-used)))))
|
||||
(+! (-> current-buffer elt-used) 1)
|
||||
result
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
;; method 11
|
||||
(defmethod decrement-elt-used rpc-buffer-pair ((obj rpc-buffer-pair))
|
||||
"If elt-used > 0, decrease it by one."
|
||||
(when (> (-> obj current elt-used) 0)
|
||||
(-! (-> obj current elt-used) 1)
|
||||
)
|
||||
0
|
||||
)
|
||||
@@ -53,3 +53,4 @@
|
||||
;; todo print-game-text
|
||||
;; todo disable-level-text-file-loading
|
||||
;; todo enable-level-text-file-loading
|
||||
|
||||
Reference in New Issue
Block a user