mirror of
https://github.com/open-goal/jak-project.git
synced 2024-11-27 08:20:47 +00:00
Clean up asm for format (#164)
* clean up asm for format * fix windows * remove rpc-call hack * revert cmake version change * clang format * asm fix for windows
This commit is contained in:
parent
3355df809a
commit
5cb6368b9a
@ -111,4 +111,4 @@ add_subdirectory("third-party/zydis")
|
||||
# windows memory management lib
|
||||
IF (WIN32)
|
||||
add_subdirectory(third-party/mman)
|
||||
ENDIF ()
|
||||
ENDIF ()
|
||||
|
@ -3,3 +3,7 @@ add_library(common_util
|
||||
FileUtil.cpp
|
||||
DgoWriter.cpp
|
||||
Timer.cpp)
|
||||
|
||||
IF(UNIX)
|
||||
target_link_libraries(common_util stdc++fs)
|
||||
ENDIF()
|
||||
|
@ -4,20 +4,47 @@
|
||||
|
||||
;; GOAL Runtime assembly functions. These exist only in the x86 version of GOAL.
|
||||
|
||||
;; declaration of the extern "C" function format_impl
|
||||
extern format_impl
|
||||
|
||||
SECTION .TEXT
|
||||
|
||||
;; This _format function which will be exported to the GOAL symbol table at runtime start as "_format"
|
||||
;; This function accepts 8 GOAL arguments and puts them on the stack, then calls format_impl and passes
|
||||
;; a pointer to this array of GOAL arguments as the argument. The reason for this is that GOAL and
|
||||
;; the standard System V ABI used in Linux are different for 8 argument function calls.
|
||||
|
||||
global _format_win32
|
||||
_format_win32:
|
||||
; GOAL will call with regs RDI, RSI, RDX, RCX, R8, R9, R10, R11
|
||||
;; this function pushes all 8 OpenGOAL registers into a stack array.
|
||||
;; then it calls the function pointed to by rax with a pointer to this array.
|
||||
;; it returns the return value of the called function.
|
||||
global _stack_call_linux
|
||||
_stack_call_linux:
|
||||
pop rax
|
||||
; align stack
|
||||
sub rsp, 8
|
||||
; create stack array of arguments
|
||||
push r11
|
||||
push r10
|
||||
push r9
|
||||
push r8
|
||||
push rcx
|
||||
push rdx
|
||||
push rsi
|
||||
push rdi
|
||||
; set first argument
|
||||
mov rdi, rsp
|
||||
; call function
|
||||
call rax
|
||||
; restore arguments (probably don't need to really do this...)
|
||||
pop rdi
|
||||
pop rsi
|
||||
pop rdx
|
||||
pop rcx
|
||||
pop r8
|
||||
pop r9
|
||||
pop r10
|
||||
pop r11
|
||||
; restore stack
|
||||
add rsp, 8
|
||||
; return!
|
||||
ret
|
||||
|
||||
;; windows implementation of stack_call
|
||||
global _stack_call_win32
|
||||
_stack_call_win32:
|
||||
pop rax
|
||||
; to make sure the stack frame is aligned
|
||||
sub rsp, 8
|
||||
|
||||
@ -36,7 +63,7 @@ _format_win32:
|
||||
sub rsp, 32
|
||||
|
||||
; call C function to do format, result will go in RAX
|
||||
call format_impl
|
||||
call rax
|
||||
add rsp, 32
|
||||
|
||||
; restore
|
||||
@ -52,47 +79,6 @@ _format_win32:
|
||||
add rsp, 8
|
||||
ret
|
||||
|
||||
global _format_linux
|
||||
_format_linux:
|
||||
; GOAL will call with regs RDI, RSI, RDX, RCX, R8, R9, R10, R11
|
||||
|
||||
; to make sure the stack frame is aligned
|
||||
sub rsp, 8
|
||||
|
||||
; push all registers and create the register array on the stack
|
||||
push r11
|
||||
push r10
|
||||
push r9
|
||||
push r8
|
||||
push rcx
|
||||
push rdx
|
||||
push rsi
|
||||
push rdi
|
||||
|
||||
; set the first argument register to the stack argument array
|
||||
mov rdi, rsp
|
||||
|
||||
; call C function to do format, result will go in RAX
|
||||
call format_impl
|
||||
|
||||
; restore
|
||||
; (note - this could probably just be add rsp 72, we don't care about the value of these register)
|
||||
pop rdi
|
||||
pop rsi
|
||||
pop rdx
|
||||
pop rcx
|
||||
pop r8
|
||||
pop r9
|
||||
pop r10
|
||||
pop r11
|
||||
add rsp, 8
|
||||
ret
|
||||
;; NOTE: calling format has a _lot_ of indirection...
|
||||
;; symbol table lookup to find the GOAL "format" symbol value
|
||||
;; run the GOAL-to-C trampoline (on GOAL heap) to jump to this _format
|
||||
;; run this wrapper to call the real format_impl
|
||||
|
||||
|
||||
;; The _call_goal_asm function is used to call a GOAL function from C.
|
||||
;; It supports up to 3 arguments and a return value.
|
||||
;; This should be called with the arguments:
|
||||
|
@ -50,38 +50,31 @@ s32 RpcCall(s32 rpcChannel,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
// Terrible hack! Remove soon!
|
||||
|
||||
namespace {
|
||||
struct RpcCallArgCache {
|
||||
s32 rpcChannel;
|
||||
u32 fno;
|
||||
u32 async;
|
||||
} rpc_arg_cache;
|
||||
struct GoalStackArgs {
|
||||
u64 args[8];
|
||||
template <typename T>
|
||||
T get_as(int i) {
|
||||
static_assert(sizeof(T) <= 8, "arg size");
|
||||
T result;
|
||||
memcpy(&result, args + i, sizeof(T));
|
||||
return result;
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
void RpcCall_wrapper_part1(s32 rpcChannel, u32 fno, u32 async) {
|
||||
rpc_arg_cache.rpcChannel = rpcChannel;
|
||||
rpc_arg_cache.fno = fno;
|
||||
rpc_arg_cache.async = async;
|
||||
}
|
||||
|
||||
u64 RpcCall_wrapper_part2(u64 send_buff, s32 send_size, u64 recv_buff, s32 recv_size) {
|
||||
return RpcCall_wrapper(rpc_arg_cache.rpcChannel, rpc_arg_cache.fno, rpc_arg_cache.async,
|
||||
send_buff, send_size, recv_buff, recv_size);
|
||||
}
|
||||
|
||||
/*!
|
||||
* GOAL Wrapper for RpcCall.
|
||||
*/
|
||||
u64 RpcCall_wrapper(s32 rpcChannel,
|
||||
u32 fno,
|
||||
u32 async,
|
||||
u64 send_buff,
|
||||
s32 send_size,
|
||||
u64 recv_buff,
|
||||
s32 recv_size) {
|
||||
fprintf(stderr, "size in c is %d\n", recv_size);
|
||||
u64 RpcCall_wrapper(void* _args) {
|
||||
GoalStackArgs* args = (GoalStackArgs*)_args;
|
||||
auto rpcChannel = args->get_as<s32>(0);
|
||||
auto fno = args->get_as<u32>(1);
|
||||
auto async = args->get_as<u32>(2);
|
||||
auto send_buff = args->get_as<u64>(3);
|
||||
auto send_size = args->get_as<s32>(4);
|
||||
auto recv_buff = args->get_as<u64>(5);
|
||||
auto recv_size = args->get_as<s32>(6);
|
||||
return sceSifCallRpc(&cd[rpcChannel], fno, async, Ptr<u8>(send_buff).c(), send_size,
|
||||
Ptr<u8>(recv_buff).c(), recv_size, nullptr, nullptr);
|
||||
}
|
||||
|
@ -19,16 +19,7 @@ void load_and_link_dgo_from_c(const char* name, Ptr<kheapinfo> heap, u32 linkFla
|
||||
void load_and_link_dgo(u64 name_gstr, u64 heap_info, u64 flag, u64 buffer_size);
|
||||
void StopIOP();
|
||||
|
||||
u64 RpcCall_wrapper(s32 rpcChannel,
|
||||
u32 fno,
|
||||
u32 async,
|
||||
u64 send_buff,
|
||||
s32 send_size,
|
||||
u64 recv_buff,
|
||||
s32 recv_size);
|
||||
u64 RpcCall_wrapper(void* args);
|
||||
u32 RpcBusy(s32 channel);
|
||||
void LoadDGOTest();
|
||||
|
||||
void RpcCall_wrapper_part1(s32 rpcChannel, u32 fno, u32 async);
|
||||
u64 RpcCall_wrapper_part2(u64 send_buff, s32 send_size, u64 recv_buff, s32 recv_size);
|
||||
#endif // JAK_V2_KDGO_H
|
||||
|
@ -315,6 +315,10 @@ u64 make_string_from_c(const char* c_str) {
|
||||
return mem;
|
||||
}
|
||||
|
||||
/*!
|
||||
* This creates an OpenGOAL function from a C++ function. Only 6 arguments can be accepted.
|
||||
* But calling this function is very fast and doesn't use the stack.
|
||||
*/
|
||||
Ptr<Function> make_function_from_c_linux(void* func) {
|
||||
// allocate a function object on the global heap
|
||||
auto mem = Ptr<u8>(
|
||||
@ -393,16 +397,59 @@ ret
|
||||
return mem.cast<Function>();
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
void _stack_call_linux();
|
||||
void _stack_call_win32();
|
||||
}
|
||||
|
||||
Ptr<Function> make_stack_arg_function_from_c_linux(void* func) {
|
||||
// allocate a function object on the global heap
|
||||
auto mem = Ptr<u8>(
|
||||
alloc_heap_object(s7.offset + FIX_SYM_GLOBAL_HEAP, *(s7 + FIX_SYM_FUNCTION_TYPE), 0x40));
|
||||
auto f = (uint64_t)func;
|
||||
auto target_function = (u8*)&f;
|
||||
auto trampoline_function_addr = _stack_call_linux;
|
||||
auto trampoline = (u8*)&trampoline_function_addr;
|
||||
|
||||
// movabs rax, target_function
|
||||
int offset = 0;
|
||||
mem.c()[offset++] = 0x48;
|
||||
mem.c()[offset++] = 0xb8;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
mem.c()[offset++] = target_function[i];
|
||||
}
|
||||
|
||||
// push rax
|
||||
mem.c()[offset++] = 0x50;
|
||||
|
||||
// movabs rax, trampoline
|
||||
mem.c()[offset++] = 0x48;
|
||||
mem.c()[offset++] = 0xb8;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
mem.c()[offset++] = trampoline[i];
|
||||
}
|
||||
|
||||
// jmp rax
|
||||
mem.c()[offset++] = 0xff;
|
||||
mem.c()[offset++] = 0xe0;
|
||||
|
||||
// CacheFlush(mem, 0x34);
|
||||
|
||||
return mem.cast<Function>();
|
||||
}
|
||||
|
||||
/*!
|
||||
* Create a GOAL function from a C function. This calls a windows function, but doesn't scramble
|
||||
* the argument order. It's supposed to be used with _format_win32 which assumes GOAL order.
|
||||
*/
|
||||
Ptr<Function> make_function_for_format_from_c_win32(void* func) {
|
||||
Ptr<Function> make_stack_arg_function_from_c_win32(void* func) {
|
||||
// allocate a function object on the global heap
|
||||
auto mem = Ptr<u8>(
|
||||
alloc_heap_object(s7.offset + FIX_SYM_GLOBAL_HEAP, *(s7 + FIX_SYM_FUNCTION_TYPE), 0x80));
|
||||
auto f = (uint64_t)func;
|
||||
auto fp = (u8*)&f;
|
||||
auto trampoline_function_addr = _stack_call_win32;
|
||||
auto trampoline = (u8*)&trampoline_function_addr;
|
||||
|
||||
int i = 0;
|
||||
// we will put the function address in RAX with a movabs rax, imm8
|
||||
@ -412,13 +459,20 @@ Ptr<Function> make_function_for_format_from_c_win32(void* func) {
|
||||
mem.c()[i++] = fp[j];
|
||||
}
|
||||
|
||||
// push rax
|
||||
mem.c()[i++] = 0x50;
|
||||
|
||||
// we will put the function address in RAX with a movabs rax, imm8
|
||||
mem.c()[i++] = 0x48;
|
||||
mem.c()[i++] = 0xb8;
|
||||
for (int j = 0; j < 8; j++) {
|
||||
mem.c()[i++] = trampoline[j];
|
||||
}
|
||||
|
||||
/*
|
||||
* sub rsp, 40
|
||||
* call rax
|
||||
* add rsp, 40
|
||||
* ret
|
||||
* jmp rax
|
||||
*/
|
||||
for (auto x : {0x48, 0x83, 0xEC, 0x28, 0xFF, 0xD0, 0x48, 0x83, 0xC4, 0x28, 0xC3}) {
|
||||
for (auto x : {0xFF, 0xE0}) {
|
||||
mem.c()[i++] = x;
|
||||
}
|
||||
|
||||
@ -439,6 +493,14 @@ Ptr<Function> make_function_from_c(void* func) {
|
||||
#endif
|
||||
}
|
||||
|
||||
Ptr<Function> make_stack_arg_function_from_c(void* func) {
|
||||
#ifdef __linux__
|
||||
return make_stack_arg_function_from_c_linux(func);
|
||||
#elif _WIN32
|
||||
return make_stack_arg_function_from_c_win32(func);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*!
|
||||
* Create a GOAL function which does nothing and immediately returns.
|
||||
*/
|
||||
@ -471,6 +533,8 @@ Ptr<Function> make_zero_func() {
|
||||
* Given a C function and a name, create a GOAL function and store it in the symbol with the given
|
||||
* name. This effectively creates a global GOAL function with the given name which calls the given C
|
||||
* function.
|
||||
*
|
||||
* This work on both Linux and Windows, but only supports up to 6 arguments.
|
||||
*/
|
||||
Ptr<Function> make_function_symbol_from_c(const char* name, void* f) {
|
||||
auto sym = intern_from_c(name);
|
||||
@ -480,12 +544,12 @@ Ptr<Function> make_function_symbol_from_c(const char* name, void* f) {
|
||||
}
|
||||
|
||||
/*!
|
||||
* Given a C function and a name, create a GOAL function and store it in the symbol with the given
|
||||
* name. This is designed for _format_win32, which is special because it takes 8 arguments.
|
||||
* Like make_function_symbol_from_c, but all 8 GOAL arguments are put into an array on the stack.
|
||||
* The address of this array is passed as the first and only argument to f.
|
||||
*/
|
||||
Ptr<Function> make_format_function_symbol_from_c_win32(const char* name, void* f) {
|
||||
Ptr<Function> make_stack_arg_function_symbol_from_c(const char* name, void* f) {
|
||||
auto sym = intern_from_c(name);
|
||||
auto func = make_function_for_format_from_c_win32(f);
|
||||
auto func = make_stack_arg_function_from_c(f);
|
||||
sym->value = func.offset;
|
||||
return func;
|
||||
}
|
||||
@ -1587,15 +1651,6 @@ s32 test_function(s32 arg0, s32 arg1, s32 arg2, s32 arg3) {
|
||||
return arg0 + 2 * arg1 + 3 * arg2 + 4 * arg3;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
// defined in asm_funcs. It calls format_impl and sets up arguments correctly.
|
||||
#ifdef __linux__
|
||||
void _format_linux();
|
||||
#elif _WIN32
|
||||
void _format_win32();
|
||||
#endif
|
||||
}
|
||||
|
||||
/*!
|
||||
* Initializes the GOAL Heap, GOAL Symbol Table, GOAL Funcdamental Types, loads the GOAL kernel,
|
||||
* exports Machine functions, loads the game engine, and calls "play" to initialize the engine.
|
||||
@ -1845,11 +1900,7 @@ s32 InitHeapAndSymbol() {
|
||||
make_function_symbol_from_c("load", (void*)load);
|
||||
make_function_symbol_from_c("loado", (void*)loado);
|
||||
make_function_symbol_from_c("unload", (void*)unload);
|
||||
#ifdef __linux__
|
||||
make_function_symbol_from_c("_format", (void*)_format_linux);
|
||||
#elif _WIN32
|
||||
make_format_function_symbol_from_c_win32("_format", (void*)_format_win32);
|
||||
#endif
|
||||
make_stack_arg_function_symbol_from_c("_format", (void*)format_impl);
|
||||
|
||||
// allocations
|
||||
make_function_symbol_from_c("malloc", (void*)alloc_heap_memory);
|
||||
|
@ -101,6 +101,7 @@ u64 load(u32 file_name_in, u32 heap_in);
|
||||
u64 loado(u32 file_name_in, u32 heap_in);
|
||||
u64 unload(u32 name);
|
||||
Ptr<Function> make_function_symbol_from_c(const char* name, void* f);
|
||||
Ptr<Function> make_stack_arg_function_symbol_from_c(const char* name, void* f);
|
||||
u64 call_goal_function_by_name(const char* name);
|
||||
u64 call_method_of_type_arg2(u32 arg, Ptr<Type> type, u32 method_id, u32 a1, u32 a2);
|
||||
Ptr<Type> alloc_and_init_type(Ptr<Symbol> sym, u32 method_count);
|
||||
|
@ -25,8 +25,5 @@ void InitSoundScheme() {
|
||||
make_function_symbol_from_c("rpc-call", (void*)RpcCall_wrapper);
|
||||
make_function_symbol_from_c("rpc-busy?", (void*)RpcBusy);
|
||||
make_function_symbol_from_c("test-load-dgo-c", (void*)LoadDGOTest);
|
||||
|
||||
// terrible hack!
|
||||
make_function_symbol_from_c("rpc-call-p1", (void*)RpcCall_wrapper_part1);
|
||||
make_function_symbol_from_c("rpc-call-p2", (void*)RpcCall_wrapper_part2);
|
||||
make_stack_arg_function_symbol_from_c("rpc-call", (void*)RpcCall_wrapper);
|
||||
}
|
@ -130,12 +130,6 @@
|
||||
'#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))
|
||||
@ -170,7 +164,7 @@
|
||||
(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)
|
||||
(rpc-call (-> obj rpc-port)
|
||||
fno
|
||||
(the uint 1)
|
||||
(the uint (-> current-buffer base))
|
||||
|
@ -154,6 +154,3 @@
|
||||
|
||||
(define-extern rpc-call (function int uint uint uint int uint int uint))
|
||||
(define-extern rpc-busy? (function int uint))
|
||||
;; hack! remove!
|
||||
(define-extern rpc-call-p1 (function int uint uint none))
|
||||
(define-extern rpc-call-p2 (function uint int uint int uint))
|
Loading…
Reference in New Issue
Block a user