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:
water111
2020-11-22 12:59:55 -05:00
committed by GitHub
parent 460ec874bb
commit 19b8bb81c9
27 changed files with 749 additions and 126 deletions
+127
View File
@@ -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
)
)
+227
View File
@@ -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
)
+1
View File
@@ -53,3 +53,4 @@
;; todo print-game-text
;; todo disable-level-text-file-loading
;; todo enable-level-text-file-loading