Merge pull request #4 from xTVaser/windows-nodeci

Current Windows Build
This commit is contained in:
water111 2020-09-04 20:09:47 -04:00 committed by GitHub
commit 44f0fd054d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
46 changed files with 932 additions and 138 deletions

5
.editorconfig Normal file
View file

@ -0,0 +1,5 @@
# Rules in this file were initially inferred by Visual Studio IntelliCode from the C:\Users\xtvas\Repositories\jak-project codebase based on best match to current usage at 2020-08-28
# You can modify the rules from these initially generated values to suit your own policies
# You can learn more about editorconfig here: https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference
[*.cs]

View file

@ -1,5 +1,5 @@
name: Linux Workflow
on: [push]
on: [push, pull_request]
jobs:
build:
@ -20,6 +20,7 @@ jobs:
make -j
- name: Test Project with gTest
run: ./test.sh
timeout-minutes: 5
- name: Check Clang-Formatting
run: |
chmod +x ./third-party/run-clang-format/run-clang-format.py

3
.vs/.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
*
!.gitignore
!launch.vs.json

45
.vs/launch.vs.json Normal file
View file

@ -0,0 +1,45 @@
{
// https://docs.microsoft.com/en-us/cpp/build/launch-vs-schema-reference-cpp?view=vs-2019
"version": "0.2.1",
"defaults": {},
"configurations": [
{
"type": "default",
"project": "CMakeLists.txt",
"projectTarget": "goalc-test.exe (bin\\goalc-test.exe)",
"name": "Run Tests - Summary",
"args": ["--gtest_brief=1"],
"env": {
"NEXT_DIR": "${projectDir}"
}
},
{
"type": "default",
"project": "CMakeLists.txt",
"projectTarget": "goalc-test.exe (bin\\goalc-test.exe)",
"name": "Run Tests - Verbose",
"args": ["--gtest_brief=0"],
"env": {
"NEXT_DIR": "${projectDir}"
}
},
{
"type": "default",
"project": "CMakeLists.txt",
"projectTarget": "gk.exe (bin\\gk.exe)",
"name": "Run Game"
},
{
"type": "default",
"project": "CMakeLists.txt",
"projectTarget": "goalc.exe (bin\\goalc.exe)",
"name": "Build Compiler"
},
{
"type": "default",
"project": "CMakeLists.txt",
"projectTarget": "decompiler.exe (bin\\decompiler.exe)",
"name": "Build Decompiler"
}
]
}

View file

@ -4,11 +4,34 @@ project(jak)
set(CMAKE_CXX_STANDARD 14)
# Set default compile flags for GCC
# optimization level can be set here. Note that game/ overwrites this for building game C++ code.
set(CMAKE_CXX_FLAGS "-O0 -ggdb -Wall \
-Wextra -Wcast-align -Wcast-qual -Wdisabled-optimization -Wformat=2 \
-Winit-self -Wmissing-include-dirs -Woverloaded-virtual \
-Wredundant-decls -Wshadow -Wsign-promo ")
if(CMAKE_COMPILER_IS_GNUCXX)
message(STATUS "GCC detected, adding compile flags")
set(CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} \
-Wall \
-Winit-self \
-Wextra \
-Wcast-align \
-Wcast-qual \
-Wdisabled-optimization \
-Wformat=2 \
-Wmissing-include-dirs \
-Woverloaded-virtual \
-Wredundant-decls \
-Wshadow \
-Wsign-promo")
else()
set(CMAKE_CXX_FLAGS "/EHsc")
endif(CMAKE_COMPILER_IS_GNUCXX)
IF (WIN32)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
ENDIF()
# includes relative to top level jak-project folder
include_directories(./)
@ -39,3 +62,8 @@ add_subdirectory(third-party/minilzo)
# build format library
add_subdirectory(third-party/fmt)
# windows memory management lib
IF (WIN32)
add_subdirectory(third-party/mman)
ENDIF()

22
CMakeSettings.json Normal file
View file

@ -0,0 +1,22 @@
{
"configurations": [
{
"name": "x64-Debug",
"generator": "Ninja",
"configurationType": "Debug",
"inheritEnvironments": [ "msvc_x64_x64" ],
"buildRoot": "${projectDir}\\out\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"ctestCommandArgs": "",
"variables": [
{
"name": "INSTALL_GTEST",
"value": "True",
"type": "BOOL"
}
]
}
]
}

191
README.md
View file

@ -1,22 +1,96 @@
Build Status
--------------
# Jak Project
![Linux Workflow](https://github.com/water111/jak-project/workflows/Linux%20Workflow/badge.svg?branch=master)
Project Structure
----------------------
Requirements:
## Table of Contents
<!-- toc -->
- [Jak Project](#jak-project)
- [Table of Contents](#table-of-contents)
- [Requirements](#requirements)
- [Getting Started - Linux (Ubuntu)](#getting-started---linux-ubuntu)
- [Getting Started - Windows](#getting-started---windows)
- [Project Layout](#project-layout)
- [Design](#design)
- [Current State](#current-state)
- [Coding Guidelines](#coding-guidelines)
- [TODOs](#todos)
- [Project Description](#project-description)
- [GOAL Decompiler](#goal-decompiler)
- [GOAL Runtime](#goal-runtime)
- [GOAL Compiler](#goal-compiler)
- [Asset Extraction Tool](#asset-extraction-tool)
- [Asset Packing Tool](#asset-packing-tool)
<!-- tocstop -->
## Requirements
- `cmake` for build system
- `clang-format` for formatting code (there is already a `.clang-format` provided)
- `gtest` for testing. (Run `git submodule update --init --recursive` to check out the repository)
- `nasm` for assembling x86. There isn't much x86 assembly so if there's a better way to do this for windows, we can change.
- `nasm` for assembling x86
- Third party libraries (`nlohmann/json`, `minilzo`, and `linenoise`) are provided in the `third-party` folder
Setup (for Ubuntu):
```
## Getting Started - Linux (Ubuntu)
Install Packages and Init Repository
```bash
sudo apt install gcc make cmake build-essential g++ nasm clang-format
git submodule update --init --recursive
```
Layout:
Compile
```bash
mkdir build && cd build && cmake .. && make -j
```
Run Tests
```bash
./test.sh
```
## Getting Started - Windows
Install Visual Studio 2019 and get the C++ and CMake tools via the Visual Studio Installer
On Windows, it's recommended to get Scoop to use as a package manager, making the follow steps _much_ easier. Follow the steps on the bottom of the homepage here https://scoop.sh/
Once Scoop is installed, run the following command:
```ps
scoop install llvm nasm
```
Initialize the repository's third-party dependencies:
```bash
git submodule update --init --recursive
```
Open the project as a CMake project, browse for the root level `CMakeLists.txt`:
![](./doc/imgs/open-cmake-vs.png)
In the toolbar, you should be able to select an individual component to compile, or combine within the root CMakeLists.txt. In the future we will pre-define configurations to make this easier.
![](./doc/imgs/cmake-build-vs.png)
You may also wish to view the files that pertain to each CMake target, rather than the project as it is normally:
![](./doc/imgs/cmake-target-view.png)
TODO
- more steps to follow as we actually figure it out!
- running tests
- etc
## Project Layout
- `goalc` is the GOAL compiler
- `gs` contains GOOS code for parts of GOOS implemented in GOOS
- `gc` contains GOAL code for parts of GOAL implemented in GOAL (must generate no machine code, just defining macros)
@ -33,8 +107,10 @@ Layout:
- `tests` will contain all tests
- `asset_tool` will contain the asset packer/unpacker
Design:
## Design
(if anybody has better ideas, feel free to suggest improvements! This is just a rough plan for now)
- All C++ code should build from the top-level `cmake`.
- All C++ applications (GOAL compiler, asset extractor, asset packer, runtime, test) should have a script in the top level which launches them.
- All file paths should be relative to the `jak` folder.
@ -53,10 +129,12 @@ Design:
- `./gs.sh` : run a goos interpreter in interactive mode
- `./decomp.sh : run the decompiler
Current state:
## Current State
- GOAL compiler just implements the GOOS Scheme Macro Language. Running `./gc.sh` just loads the GOOS library (`goalc/gs/goos-lib.gs`) and then goes into an interactive mode. Use `(exit)` to exit.
- `./test.sh` runs tests for some game C++ code, for GOOS, for the reader, for the listener connection, and for some early emitter stuff.
- The runtime boots in `fakeiso` mode which will load some dummy files. Then the C Kernel (`game/kernel`) will load the `KERNEL.CGO` and `GAME.CGO` files, which are from the "proof of concept" GOAL compiler. If you run `./gk.sh`, you should see it load stuff, then print:
```
calling play!
~~ HACK ~~ : fake play has been called
@ -65,13 +143,16 @@ InitCheckListener
kernel: machine started
```
where the `~~ HACK ~~` message is from code in `KERNEL.CGO`.
Code Guidelines:
## Coding Guidelines
- Avoid warnings
- Use asserts over throwing exceptions in game code (throwing exceptions from C code called by GOAL code is sketchy)
TODOS:
## TODOs
- Build on Windows!
- Networking
- File paths
@ -99,11 +180,10 @@ TODOS:
- Clean up decompiler print spam, finish up the CFG stuff
- Decompiler document
Project Description
-----------------------
## Project Description
This project is to port Jak 1 (NTSC, "black label" version) to PC. The strategy is to:
- recompile for x86 to get much better performance than emulation
- create human-reabable GOAL source code that can be modified
- create a GOAL compiler for x86-64 which supports live patching of code like the original
@ -111,6 +191,7 @@ This project is to port Jak 1 (NTSC, "black label" version) to PC. The strategy
- unpack assets in a format that can be modified
There are 6 components to this project
- GOAL decompiler. The result will be manually cleaned up for running on a PC.
- GOAL compiler for x86-64.
- Game source code, made from cleaning up the result of the GOAL decompiler.
@ -119,25 +200,27 @@ There are 6 components to this project
- Asset packing tool.
The process to build the port will be
- Build data extraction tool, GOAL compiler, and GOAL runtime library (all written in C++)
- Run the GOAL compiler on the game source code to build the game engine
- Run asset extraction on the game disc to get level data, textures, geometry data, music...
- Run the asset packing tool to combine the unpacked assets with the compiled game engine to create the game!
Some statistics:
- Estimated ~500k lines of GOAL code
- 10410 functions
- 5451 functions with no control flow (no branching, loops, if/else, short-circuiting boolean operators, gotos, etc)
The rough timeline is to finish sometime in 2022. If it looks like this is impossible, the project will be abandoned. But I have already spent about 4 months preparing to start this and seems doable. I also have some background in compilers, and familiarity with PS2 (worked on DobieStation PS2 emulator) / MIPS in general (wrote a PS1 emulator). I think the trick will be making good automated tools - the approach taken for SM64 and other N64 decompilations is way too labor-intensive to work.
### GOAL Decompiler
GOAL Decompiler
------------------
The decompiler is in progress, at
https://github.com/water111/jak-disassembler
Here is the plan for writing the decompiler:
- [x] Decode the CGO/DGO format.
- [x] Decode the linking data format.
- [x] Identify all code and disassemble
@ -151,10 +234,10 @@ Here is the plan for writing the decompiler:
- [ ] Variable map and scoping
- [ ] S-expression construction (expression stack)
### GOAL Runtime
GOAL Runtime
--------------
The "runtime" will be a replacement for all of the C/C++ code of the original game. There is C/C++ code that runs on the main processor (EE) and the separate I/O processor (IOP).
- The "C Kernel", which runs on the EE and contains
- [ ] File I/O (for debugging, not used by retail game)
- [x] Initialization to boostrap the GOAL Kernel and start the game engine
@ -184,8 +267,8 @@ The "Sony libraries" are a simple wrapper around my `system` library, which impl
Likely there will be sound/graphics code in here at some point, but this part is not fully planned yet.
GOAL Compiler
---------------
### GOAL Compiler
The GOAL compiler will target x86-64. At first just Linux. There is a macro language called GOOS which is basically just Scheme but with a few bonus features.
The compiler will reuse a significant amount of code from my existing LISP compiler for x86-64. I have a very bad WIP combination which is capable of building a modified `gkernel.gc` for x86 as a proof of concept. It can create and run functions in threads.
@ -195,6 +278,7 @@ An important part of the compiler is the test suite. Without tests the compiler
The major components are
- [ ] GOAL-IR, a typed linear intermediate representation for GOAL code
- [ ] "Environment"
- [ ] "Ref"
- [ ] Constant propagation of integers/floats
@ -202,6 +286,7 @@ The major components are
- [ ] Ref `in_gpr`
- [ ] The type system
- [ ] Type inheritance
- [ ] Integer/float/pointer types (value semantics)
- [ ] Reference types
@ -220,41 +305,41 @@ The major components are
- [ ] Built-in types in the GOAL runtime/C Kernel
- [x] The GOOS Macro Language
- [x] S-expression parser (the "Reader")
- [x] Reader text db (for error messages that point to a specific line)
- [x] Scheme interpreter
- [ ] Front-end (convert s-expressions (a tree structure) to GOAL-IR (a linear representation))
- [ ] Parsing helpers
- [ ] Macro expansion
- [ ] Control flow/block forms
- [ ] Type definitions
- [ ] Inline assembly forms
- [ ] Function/method call
- [ ] Math forms
- [ ] Lexical scoping (immediate application of `lambda`)
- [ ] Function inlining (slightly different scoping rules of immediate `lambda`)
- [ ] Function/macro definition
- [ ] Static Objects
- [ ] Parsing helpers
- [ ] Macro expansion
- [ ] Control flow/block forms
- [ ] Type definitions
- [ ] Inline assembly forms
- [ ] Function/method call
- [ ] Math forms
- [ ] Lexical scoping (immediate application of `lambda`)
- [ ] Function inlining (slightly different scoping rules of immediate `lambda`)
- [ ] Function/macro definition
- [ ] Static Objects
- [ ] Regsiter allocation
- [ ] Analysis
- [ ] Allocation
- [ ] Stack spilling
- [ ] `xmm` and `gpr` promotion/demotions for EE 128-bit register usage
- [ ] Analysis
- [ ] Allocation
- [ ] Stack spilling
- [ ] `xmm` and `gpr` promotion/demotions for EE 128-bit register usage
- [ ] Codegen / Emitter (convert GOAL-IR + register allocations to x86 object file format)
- [ ] Emitter (convert GOAL-IR to instructions)
- [ ] x86-64 instruction generation (actually generate the machine code)
- [ ] Linking data
- [ ] 64-bit GPR
- [ ] 32-bit float
- [ ] 128-bit GPR
- [ ] 32-bit float x4 vector register
- [ ] function prologue/epilogue
- [ ] stack spilling
- [ ] static object and static object links
- [ ] Emitter (convert GOAL-IR to instructions)
- [ ] x86-64 instruction generation (actually generate the machine code)
- [ ] Linking data
- [ ] 64-bit GPR
- [ ] 32-bit float
- [ ] 128-bit GPR
- [ ] 32-bit float x4 vector register
- [ ] function prologue/epilogue
- [ ] stack spilling
- [ ] static object and static object links
- [ ] Listener/REPL
- [ ] Network connection
@ -264,14 +349,14 @@ The major components are
- [ ] Expand single macro debugging feature
- [ ] Interface for running gtests
### Asset Extraction Tool
Asset Extraction Tool
-----------------------
Not started yet. The simplest version of this tool is just to use the decompiler logic to turn the level/art-group/texture/TXT files into GOAL source code, and copy all STR/sound/visibility files, as these don't need to be modified.
Eventually this should export to a more useful format.
File formats:
- [ ] Art group (a GOAL object format)
- There may be more formats related to art groups.
- [ ] Texture page (a GOAL object format)
@ -286,12 +371,8 @@ File formats:
- [ ] Loading screen image
- [ ] save game icon (I do not care about this)
### Asset Packing Tool
Asset Packing Tool
-----------------------
Packs together all assets/compiled code/runtime into a format that can be played. The simplest version to go with the simplest extraction tool will just pass the level/art-group/texture/TXT files to the compiler, and copy STR/sound/visbility files into the fakeiso. Then pack in CGOs/DGOs.
It's important that the asset extraction/packing can be automated so we can avoid distributing the assets, which are large and probably not supposed to be distributed.

View file

@ -3,6 +3,7 @@
#include "TypeSystem.h"
#include "type_util.h"
#include <cassert>
#include <stdexcept>
TypeSystem::TypeSystem() {
// the "none" and "_type_" types are included by default.

BIN
doc/imgs/cmake-build-vs.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
doc/imgs/open-cmake-vs.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View file

@ -1,11 +1,33 @@
# We define our own compilation flags here.
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_FLAGS "-O0 -ggdb -Wall \
-Wextra -Wcast-align -Wcast-qual -Wdisabled-optimization -Wformat=2 \
-Winit-self -Wmissing-include-dirs -Woverloaded-virtual \
-Wredundant-decls -Wshadow -Wsign-promo ")
# Set default compile flags for GCC
# optimization level can be set here. Note that game/ overwrites this for building game C++ code.
if(CMAKE_COMPILER_IS_GNUCXX)
message(STATUS "GCC detected, adding compile flags")
set(CMAKE_CXX_FLAGS
"${CMAKE_CXX_FLAGS} \
-ggdb \
-Wall \
-Winit-self \
-Wextra \
-Wcast-align \
-Wcast-qual \
-Wdisabled-optimization \
-Wformat=2 \
-Wmissing-include-dirs \
-Woverloaded-virtual \
-Wredundant-decls \
-Wshadow \
-Wsign-promo")
else()
set(CMAKE_CXX_FLAGS "/EHsc")
endif(CMAKE_COMPILER_IS_GNUCXX)
enable_language(ASM_NASM)
set(CMAKE_ASM_NASM_SOURCE_FILE_EXTENSIONS ${CMAKE_ASM_NASM_SOURCE_FILE_EXTENSIONS} asm)
set(CMAKE_ASM_NASM_COMPILE_OBJECT "<CMAKE_ASM_NASM_COMPILER> <INCLUDES> <FLAGS> -f ${CMAKE_ASM_NASM_OBJECT_FORMAT} -o <OBJECT> <SOURCE>")
set_source_files_properties(kernel/asm_funcs.asm PROPERTIES COMPILE_FLAGS "-g")
set(RUNTIME_SOURCE
runtime.cpp
system/SystemThread.cpp
@ -18,7 +40,7 @@ set(RUNTIME_SOURCE
sce/sif_ee.cpp
sce/iop.cpp
sce/stubs.cpp
kernel/asm_funcs.nasm
kernel/asm_funcs.asm
kernel/fileio.cpp
kernel/kboot.cpp
kernel/kdgo.cpp
@ -54,4 +76,11 @@ add_executable(gk ${RUNTIME_SOURCE} main.cpp)
# can be used to test other things.
add_library(runtime ${RUNTIME_SOURCE})
target_link_libraries(gk pthread)
IF (WIN32)
# set stuff for windows
target_link_libraries(gk mman)
ELSE()
# set stuff for other systems
target_link_libraries(gk pthread)
ENDIF()

View file

@ -14,8 +14,46 @@ SECTION .TEXT
;; 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
_format:
global _format_win32
_format_win32:
; 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 rcx, rsp
sub rsp, 32
; call C function to do format, result will go in RAX
call format_impl
add rsp, 32
; 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
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
@ -55,8 +93,6 @@ _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:
@ -67,9 +103,9 @@ _format:
;; - address of the symbol table
;; - GOAL memory space offset
global _call_goal_asm
global _call_goal_asm_linux
_call_goal_asm:
_call_goal_asm_linux:
;; x86 saved registers we need to modify for GOAL should be saved
push r13
push r14
@ -96,3 +132,56 @@ _call_goal_asm:
pop r14
pop r13
ret
;; 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:
;; - first goal arg
;; - second goal arg
;; - third goal arg
;; - address of function to call
;; - address of the symbol table
;; - GOAL memory space offset
global _call_goal_asm_win32
_call_goal_asm_win32:
push rdx ; 8
push rbx ; 16
push rbp ; 24
push rsi ; 32
push rdi ; 40
push r8 ; 48
push r9 ; 56
push r10 ; 64
push r11 ; 72
push r12 ; 80
push r13 ; 88
push r14 ; 96
push r15 ; 104
mov rdi, rcx ;; rdi is GOAL first argument, rcx is windows first argument
mov rsi, rdx ;; rsi is GOAL second argument, rdx is windows second argument
mov rdx, r8 ;; rdx is GOAL third argument, r8 is windows third argument
mov r13, r9 ;; r13 is GOAL fp, r9 is windows fourth argument
mov r15, [rsp + 144] ;; symbol table
mov r14, [rsp + 152] ;; offset
call r13
pop r15
pop r14
pop r13
pop r12
pop r11
pop r10
pop r9
pop r8
pop rdi
pop rsi
pop rbp
pop rbx
pop rdx
ret

View file

@ -4,7 +4,6 @@
* DONE!
*/
#include <unistd.h>
#include <cstring>
#include "common/common_types.h"
#include "game/sce/libscf.h"
@ -14,6 +13,13 @@
#include "ksocket.h"
#include "klisten.h"
#ifdef _WIN32
#include "Windows.h"
#include <io.h>
#elif __linux__
#include <unistd.h>
#endif
using namespace ee;
// Level to load on boot
@ -128,14 +134,21 @@ void KernelCheckAndDispatch() {
// dispatch the kernel
//(**kernel_dispatcher)();
call_goal(Ptr<Function>(kernel_dispatcher->value), 0, 0, 0, s7.offset, g_ee_main_mem);
// TODO-WINDOWS
#ifdef __linux__
ClearPending();
#endif
// if the listener function changed, it means the kernel ran it, so we should notify compiler.
if (MasterDebug && ListenerFunction->value != old_listener) {
SendAck();
}
usleep(1000); // todo - remove this
#ifdef _WIN32
Sleep(1000); // todo - remove this
#elif __linux__
usleep(1000);
#endif
}
}

View file

@ -27,6 +27,8 @@ void kdsnetm_init_globals() {
protoBlock.reset();
}
// TODO-WINDOWS
#ifdef __linux__
/*!
* Register GOAL DECI2 Protocol Driver with DECI2 service
* DONE, EXACT
@ -220,3 +222,4 @@ void GoalProtoStatus() {
Msg(6, "gproto: got %d %d\n", protoBlock.most_recent_event, protoBlock.most_recent_param);
Msg(6, "gproto: %d %d\n", protoBlock.last_receive_size, protoBlock.send_remaining);
}
#endif

View file

@ -51,6 +51,8 @@ extern GoalProtoBlock protoBlock;
*/
void kdsnetm_init_globals();
// TODO-WINDOWS
#ifdef __linux__
/*!
* Register GOAL DECI2 Protocol Driver with DECI2 service
* DONE, EXACT
@ -63,6 +65,8 @@ void InitGoalProto();
*/
void ShutdownGoalProto();
#endif
/*!
* Handle a DECI2 Protocol Event for the GOAL Proto.
* Called by the DECI2 Protocol driver
@ -70,6 +74,8 @@ void ShutdownGoalProto();
*/
void GoalProtoHandler(int event, int param, void* data);
// TODO-WINDOWS
#ifdef __linux__
/*!
* Low level DECI2 send
* Will block until send is complete.
@ -77,6 +83,7 @@ void GoalProtoHandler(int event, int param, void* data);
* removed
*/
s32 SendFromBufferD(s32 p1, u64 p2, char* data, s32 size);
#endif
/*!
* Print GOAL Protocol status

View file

@ -71,7 +71,10 @@ void ClearPending() {
Ptr<char> msg = OutputBufArea.cast<char>() + sizeof(GoalMessageHeader);
auto size = strlen(msg.c());
// note - if size is ever greater than 2^16 this will cause an issue.
// TODO-WINDOWS
#ifdef __linux__
SendFromBuffer(msg.c(), size);
#endif
clear_output();
}
@ -84,7 +87,10 @@ void ClearPending() {
if (send_size > 64000) {
send_size = 64000;
}
// TODO-WINDOWS
#ifdef __linux__
SendFromBufferD(2, 0, msg, send_size);
#endif
size -= send_size;
msg += send_size;
}
@ -103,9 +109,11 @@ void ClearPending() {
*/
void SendAck() {
if (MasterDebug) {
#ifdef __linux__
SendFromBufferD(u16(ListenerMessageKind::MSG_ACK), protoBlock.msg_id,
AckBufArea + sizeof(GoalMessageHeader),
strlen(AckBufArea + sizeof(GoalMessageHeader)));
#endif
}
}

View file

@ -329,7 +329,10 @@ int InitMachine() {
// }
if (MasterDebug) { // connect to GOAL compiler
// TODO-WINDOWS
#ifdef __linux__
InitGoalProto();
#endif
}
printf("InitSound\n");
@ -359,7 +362,10 @@ int ShutdownMachine() {
StopIOP();
CloseListener();
ShutdownSound();
// TODO-WINDOWS
#ifdef __linux__
ShutdownGoalProto();
#endif
Msg(6, "kernel: machine shutdown");
return 0;
}

View file

@ -62,28 +62,59 @@ void output_unload(const char* name);
*/
void output_segment_load(const char* name, Ptr<u8> link_block, u32 flags);
#ifdef __linux__
/*!
* Print to the GOAL print buffer from C
*/
void cprintf(const char* format, ...) __attribute__((format(printf, 1, 2)));
#elif _WIN32
/*!
* Print to the GOAL print buffer from C
*/
void cprintf(const char* format, ...);
#endif
#ifdef __linux__
/*!
* Print directly to the C stdout
* The "k" parameter is ignored, so this is just like printf
*/
void Msg(s32 k, const char* format, ...) __attribute__((format(printf, 2, 3)));
#elif _WIN32
/*!
* Print directly to the C stdout
* The "k" parameter is ignored, so this is just like printf
*/
void Msg(s32 k, const char* format, ...);
#endif
#ifdef __linux__
/*!
* Print directly to the C stdout
* This is identical to Msg.
*/
void MsgWarn(const char* format, ...) __attribute__((format(printf, 1, 2)));
#elif _WIN32
/*!
* Print directly to the C stdout
* This is identical to Msg.
*/
void MsgWarn(const char* format, ...);
#endif
#ifdef __linux__
/*!
* Print directly to the C stdout
* This is identical to Msg.
*/
void MsgErr(const char* format, ...) __attribute__((format(printf, 1, 2)));
#elif _WIN32
/*!
* Print directly to the C stdout
* This is identical to Msg.
*/
void MsgErr(const char* format, ...);
#endif
/*!
* Reverse string in place.

View file

@ -313,13 +313,7 @@ u64 make_string_from_c(const char* c_str) {
return mem;
}
/*!
* Create a GOAL function from a C function. This doesn't export it as a global function, it just
* creates a function object on the global heap.
*
* The implementation is to create a simple trampoline function which jumps to the C function.
*/
Ptr<Function> make_function_from_c(void* func) {
Ptr<Function> make_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));
@ -344,6 +338,68 @@ Ptr<Function> make_function_from_c(void* func) {
return mem.cast<Function>();
}
/*!
* Create a GOAL function from a C function. This doesn't export it as a global function, it just
* creates a function object on the global heap.
*
* The implementation is to create a simple trampoline function which jumps to the C function.
*/
Ptr<Function> make_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;
int i = 0;
// 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++] = fp[j];
}
/*
* push rdi
* push rsi
* push rdx
* push rcx
* pop r9
* pop r8
* pop rdx
* pop rcx
*
* sub rsp, 40
* call rax
* add rsp, 40
* ret
*/
for (auto x : {0x57, 0x56, 0x52, 0x51, 0x41, 0x59, 0x41, 0x58, 0x5A, 0x59, 0x48,
0x83, 0xEC, 0x28, 0xFF, 0xD0, 0x48, 0x83, 0xC4, 0x28, 0xC3}) {
mem.c()[i++] = x;
}
// the C function's ret will return to the caller of this trampoline.
// CacheFlush(mem, 0x34);
return mem.cast<Function>();
}
/*!
* Create a GOAL function from a C function. This doesn't export it as a global function, it just
* creates a function object on the global heap.
*
* The implementation is to create a simple trampoline function which jumps to the C function.
*/
Ptr<Function> make_function_from_c(void* func) {
#ifdef __linux__
return make_function_from_c_linux(func);
#elif _WIN32
return make_function_from_c_win32(func);
#endif
}
/*!
* Create a GOAL function which does nothing and immediately returns.
*/
@ -890,8 +946,12 @@ u64 method_set(u32 type_, u32 method_id, u32 method) {
}
extern "C" {
// defined in asm_funcs.nasm
uint64_t _call_goal_asm(u64 a0, u64 a1, u64 a2, void* fptr, void* st_ptr, void* offset);
// defined in asm_funcs.asm
#ifdef __linux__
uint64_t _call_goal_asm_linux(u64 a0, u64 a1, u64 a2, void* fptr, void* st_ptr, void* offset);
#elif _WIN32
uint64_t _call_goal_asm_win32(u64 a0, u64 a1, u64 a2, void* fptr, void* st_ptr, void* offset);
#endif
}
/*!
@ -900,7 +960,11 @@ uint64_t _call_goal_asm(u64 a0, u64 a1, u64 a2, void* fptr, void* st_ptr, void*
u64 call_goal(Ptr<Function> f, u64 a, u64 b, u64 c, u64 st, void* offset) {
auto st_ptr = (void*)((uint8_t*)(offset) + st);
void* fptr = f.c();
return _call_goal_asm(a, b, c, fptr, st_ptr, offset);
#ifdef __linux__
return _call_goal_asm_linux(a, b, c, fptr, st_ptr, offset);
#elif _WIN32
return _call_goal_asm_win32(a, b, c, fptr, st_ptr, offset);
#endif
}
/*!
@ -1453,7 +1517,11 @@ s32 test_function(s32 arg0, s32 arg1, s32 arg2, s32 arg3) {
extern "C" {
// defined in asm_funcs. It calls format_impl and sets up arguments correctly.
void _format();
#ifdef __linux__
void _format_linux();
#elif _WIN32
void _format_win32();
#endif
}
/*!
@ -1705,8 +1773,11 @@ 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);
make_function_symbol_from_c("_format", (void*)_format);
#ifdef __linux__
make_function_symbol_from_c("_format", (void*)_format_linux);
#elif _WIN32
make_function_symbol_from_c("_format", (void*)_format_win32);
#endif
// allocations
make_function_symbol_from_c("malloc", (void*)alloc_heap_memory);

View file

@ -52,6 +52,8 @@ u32 ReceiveToBuffer(char* buff) {
return msg_size;
}
// TODO-WINDOWS
#ifdef __linux__
/*!
* Do a DECI2 send and block until it is complete.
* The message type is OUTPUT
@ -60,6 +62,7 @@ u32 ReceiveToBuffer(char* buff) {
s32 SendFromBuffer(char* buff, s32 size) {
return SendFromBufferD(u16(ListenerMessageKind::MSG_OUTPUT), 0, buff, size);
}
#endif
/*!
* Just prepare the Ack buffer, doesn't actually connect.

View file

@ -73,6 +73,7 @@ void fake_iso_init_globals() {
//! will hold prefix for the source folder.
static const char* next_dir = nullptr;
static const char* fake_iso_path = nullptr;
/*!
* Initialize the file system.
@ -80,13 +81,15 @@ static const char* next_dir = nullptr;
int FS_Init(u8* buffer) {
(void)buffer;
// get path to next/. Will be set in the gk.sh launch script.
next_dir = std::getenv("NEXT_DIR"); // todo windows?
next_dir = std::getenv("NEXT_DIR");
assert(next_dir);
// get path to next/data/fake_iso.txt, the map file.
char fakeiso_path[512];
strcpy(fakeiso_path, next_dir);
strcat(fakeiso_path, "/game/fake_iso.txt"); // todo windows paths?
fake_iso_path = std::getenv("FAKE_ISO_PATH");
assert(fake_iso_path);
strcat(fakeiso_path, fake_iso_path);
// open the map.
FILE* fp = fopen(fakeiso_path, "r");
@ -191,7 +194,9 @@ static const char* get_file_path(FileRecord* fr) {
assert(fr->location < fake_iso_entry_count);
static char path_buffer[1024];
strcpy(path_buffer, next_dir);
#ifdef __linux__
strcat(path_buffer, "/");
#endif
strcat(path_buffer, fake_iso_entries[fr->location].file_path);
return path_buffer;
}

View file

@ -3,8 +3,15 @@
* Setup and launcher for the runtime.
*/
#ifdef __linux__
#include <unistd.h>
#include <sys/mman.h>
#elif _WIN32
#include <io.h>
#include <third-party/mman/mman.h>
#include <Windows.h>
#endif
#include <cstring>
#include "runtime.h"
@ -38,21 +45,29 @@
u8* g_ee_main_mem = nullptr;
/*!
* TODO-WINDOWS
* runtime.cpp - Deci2Listener has been disabled for now, pending rewriting for Windows.
*/
namespace {
/*!
* SystemThread function for running the DECI2 communication with the GOAL compiler.
*/
void deci2_runner(SystemThreadInterface& interface) {
void deci2_runner(SystemThreadInterface& iface) {
// TODO-WINDOWS
#ifdef __linux__
// callback function so the server knows when to give up and shutdown
std::function<bool()> shutdown_callback = [&]() { return interface.get_want_exit(); };
std::function<bool()> shutdown_callback = [&]() { return iface.get_want_exit(); };
// create and register server
Deci2Server server(shutdown_callback);
ee::LIBRARY_sceDeci2_register(&server);
// now its ok to continue with initialization
interface.initialization_complete();
iface.initialization_complete();
// in our own thread, wait for the EE to register the first protocol driver
printf("[DECI2] waiting for EE to register protos\n");
@ -64,7 +79,7 @@ void deci2_runner(SystemThreadInterface& interface) {
printf("[DECI2] waiting for listener...\n");
bool saw_listener = false;
while (!interface.get_want_exit()) {
while (!iface.get_want_exit()) {
if (server.check_for_listener()) {
if (!saw_listener) {
printf("[DECI2] Connected!\n");
@ -77,6 +92,7 @@ void deci2_runner(SystemThreadInterface& interface) {
usleep(50000);
}
}
#endif
}
// EE System
@ -95,7 +111,7 @@ constexpr int GOAL_ARGC = 4;
/*!
* SystemThread Function for the EE (PS2 Main CPU)
*/
void ee_runner(SystemThreadInterface& interface) {
void ee_runner(SystemThreadInterface& iface) {
// Allocate Main RAM. Must have execute enabled.
if (EE_MEM_LOW_MAP) {
g_ee_main_mem =
@ -109,7 +125,7 @@ void ee_runner(SystemThreadInterface& interface) {
if (g_ee_main_mem == (u8*)(-1)) {
printf(" Failed to initialize main memory! %s\n", strerror(errno));
interface.initialization_complete();
iface.initialization_complete();
return;
}
@ -118,7 +134,7 @@ void ee_runner(SystemThreadInterface& interface) {
(double)EE_MAIN_MEM_SIZE / (1 << 20));
printf("[EE] Initialization complete!\n");
interface.initialization_complete();
iface.initialization_complete();
printf("[EE] Run!\n");
memset((void*)g_ee_main_mem, 0, EE_MAIN_MEM_SIZE);
@ -145,13 +161,13 @@ void ee_runner(SystemThreadInterface& interface) {
munmap(g_ee_main_mem, EE_MAIN_MEM_SIZE);
// after main returns, trigger a shutdown.
interface.trigger_shutdown();
iface.trigger_shutdown();
}
/*!
* SystemThread function for running the IOP (separate I/O Processor)
*/
void iop_runner(SystemThreadInterface& interface) {
void iop_runner(SystemThreadInterface& iface) {
IOP iop;
printf("\n\n\n[IOP] Restart!\n");
iop.reset_allocator();
@ -174,7 +190,7 @@ void iop_runner(SystemThreadInterface& interface) {
// ssound
// stream
interface.initialization_complete();
iface.initialization_complete();
printf("[IOP] Wait for OVERLORD to be started...\n");
iop.wait_for_overlord_start_cmd();
@ -195,7 +211,7 @@ void iop_runner(SystemThreadInterface& interface) {
iop.signal_overlord_init_finish();
// IOP Kernel loop
while (!interface.get_want_exit() && !iop.want_exit) {
while (!iface.get_want_exit() && !iop.want_exit) {
// the IOP kernel just runs at full blast, so we only run the IOP when the EE is waiting on the
// IOP. Each time the EE is waiting on the IOP, it will run an iteration of the IOP kernel.
iop.wait_run_iop();
@ -220,7 +236,10 @@ void exec_runtime(int argc, char** argv) {
// step 1: sce library prep
iop::LIBRARY_INIT();
ee::LIBRARY_INIT_sceCd();
// TODO-WINDOWS
#ifdef __linux__
ee::LIBRARY_INIT_sceDeci2();
#endif
ee::LIBRARY_INIT_sceSif();
// step 2: system prep

View file

@ -12,17 +12,23 @@
namespace ee {
namespace {
// TODO-WINDOWS
#ifdef __linux__
constexpr int MAX_DECI2_PROTOCOLS = 4;
Deci2Driver protocols[MAX_DECI2_PROTOCOLS]; // info for each deci2 protocol registered
int protocol_count; // number of registered protocols
Deci2Driver* sending_driver; // currently sending protocol driver
::Deci2Server* server; // the server to send data to
#endif
} // namespace
/*!
/*
* Initialize the library.
*/
void LIBRARY_INIT_sceDeci2() {
// TODO-WINDOWS
#ifdef __linux__
// reset protocols
for (auto& p : protocols) {
p = Deci2Driver();
@ -30,12 +36,15 @@ void LIBRARY_INIT_sceDeci2() {
protocol_count = 0;
server = nullptr;
sending_driver = nullptr;
#endif
}
/*!
* Run any pending requested sends.
*/
void LIBRARY_sceDeci2_run_sends() {
// TODO-WINDOWS
#ifdef __linux__
for (auto& prot : protocols) {
if (prot.active && prot.pending_send == 'H') {
sending_driver = &prot;
@ -45,13 +54,17 @@ void LIBRARY_sceDeci2_run_sends() {
(prot.handler)(DECI2_WRITEDONE, 0, prot.opt);
}
}
#endif
}
/*!
* Register a Deci2Server with this library.
*/
void LIBRARY_sceDeci2_register(::Deci2Server* s) {
// TODO-WINDOWS
#ifdef __linux__
server = s;
#endif
}
/*!
@ -60,6 +73,8 @@ void LIBRARY_sceDeci2_register(::Deci2Server* s) {
* I don't know why it's like this.
*/
s32 sceDeci2Open(u16 protocol, void* opt, void (*handler)(s32 event, s32 param, void* opt)) {
// TODO-WINDOWS
#ifdef __linux__
server->lock();
Deci2Driver drv;
drv.protocol = protocol;
@ -78,14 +93,20 @@ s32 sceDeci2Open(u16 protocol, void* opt, void (*handler)(s32 event, s32 param,
}
return drv.id;
#elif _WIN32
return 0;
#endif
}
/*!
* Deactivate a DECI2 protocol by socket descriptor.
*/
s32 sceDeci2Close(s32 s) {
// TODO-WINDOWS
#ifdef __linux__
assert(s - 1 < protocol_count);
protocols[s - 1].active = false;
#endif
return 1;
}
@ -93,9 +114,12 @@ s32 sceDeci2Close(s32 s) {
* Start a send.
*/
s32 sceDeci2ReqSend(s32 s, char dest) {
// TODO-WINDOWS
#ifdef __linux__
assert(s - 1 < protocol_count);
auto& proto = protocols[s - 1];
proto.pending_send = dest;
#endif
return 0;
}
@ -104,6 +128,8 @@ s32 sceDeci2ReqSend(s32 s, char dest) {
* Returns after data is copied.
*/
s32 sceDeci2ExRecv(s32 s, void* buf, u16 len) {
// TODO-WINDOWS
#ifdef __linux__
assert(s - 1 < protocol_count);
protocols[s - 1].recv_size = len;
auto avail = protocols[s - 1].available_to_receive;
@ -114,12 +140,17 @@ s32 sceDeci2ExRecv(s32 s, void* buf, u16 len) {
printf("[DECI2] Error: ExRecv %d, only %d available!\n", len, avail);
return -1;
}
#elif _WIN32
return 0;
#endif
}
/*!
* Do a send.
*/
s32 sceDeci2ExSend(s32 s, void* buf, u16 len) {
// TODO-WINDOWS
#ifdef __linux__
assert(s - 1 < protocol_count);
if (!sending_driver) {
printf("sceDeci2ExSend called at illegal time!\n");
@ -131,6 +162,8 @@ s32 sceDeci2ExSend(s32 s, void* buf, u16 len) {
server->send_data(buf, len);
return len;
#elif _WIN32
return 0;
#endif
}
} // namespace ee

View file

@ -4,6 +4,9 @@
* Works with deci2.cpp (sceDeci2) to implement the networking on target
*/
// TODO-WINDOWS
#ifdef __linux__
#include <cstdio>
#include <sys/socket.h>
#include <netinet/tcp.h>
@ -262,3 +265,4 @@ void Deci2Server::accept_thread_func() {
}
}
}
#endif

View file

@ -4,6 +4,7 @@
* Works with deci2.cpp (sceDeci2) to implement the networking on target
*/
#ifdef __linux__
#ifndef JAK1_DECI2SERVER_H
#define JAK1_DECI2SERVER_H
@ -51,3 +52,4 @@ class Deci2Server {
};
#endif // JAK1_DECI2SERVER_H
#endif

View file

@ -83,7 +83,7 @@ void* bootstrap_thread_func(void* x) {
void SystemThread::start(std::function<void(SystemThreadInterface&)> f) {
printf("# Initialize %s...\n", name.c_str());
function = f;
pthread_create(&thread, nullptr, bootstrap_thread_func, this);
thread = std::thread(bootstrap_thread_func, this);
running = true;
// and wait for initialization
@ -99,8 +99,7 @@ void SystemThread::start(std::function<void(SystemThreadInterface&)> f) {
* Join a system thread
*/
void SystemThread::join() {
void* x;
pthread_join(thread, &x);
thread.join();
running = false;
}
@ -136,6 +135,8 @@ void SystemThreadInterface::trigger_shutdown() {
thread.manager->shutdown();
}
// TODO-Windows
#ifdef __linux__
#include <sys/time.h>
#include <sys/resource.h>
@ -165,3 +166,4 @@ void SystemThreadInterface::report_perf_stats() {
thread.last_collection_nanoseconds = current_ns;
}
}
#endif

View file

@ -8,10 +8,11 @@
#include <string>
#include <functional>
#include <pthread.h>
#include <array>
#include <mutex>
#include <thread>
#include <condition_variable>
#include "Timer.h"
constexpr int MAX_SYSTEM_THREADS = 16;
@ -39,7 +40,7 @@ class SystemThread {
friend void* bootstrap_thread_func(void* thd);
std::string name = "invalid";
pthread_t thread;
std::thread thread;
SystemThreadManager* manager;
std::function<void(SystemThreadInterface&)> function;
bool initialization_complete = false;

View file

@ -1,6 +1,7 @@
#ifndef JAK_DECIM_COMMON_H
#define JAK_DECIM_COMMON_H
#include "common/common_types.h"
struct Deci2Driver {
u16 protocol = 0;
void* opt = nullptr;

View file

@ -1,6 +1,10 @@
#include "iop_thread.h"
#ifdef __linux__
#include <unistd.h>
#elif _WIN32
#include <io.h>
#endif
#include "SystemThread.h"
//#include "shared_config.h"
//#include "ps2/SCE_IOP.h"

1
gc.sh
View file

@ -4,4 +4,5 @@
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
export NEXT_DIR=$DIR
export FAKE_ISO_PATH=/game/fake_iso.txt
$DIR/build/goalc/goalc "$@"

1
gk.sh
View file

@ -4,4 +4,5 @@
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
export NEXT_DIR=$DIR
export FAKE_ISO_PATH=/game/fake_iso.txt
$DIR/build/game/gk "$@"

View file

@ -1,9 +1,18 @@
add_subdirectory(util)
add_subdirectory(goos)
add_subdirectory(listener)
IF (WIN32)
# TODO - implement windows listener
message("Windows Listener Not Implemented!")
ELSE()
add_subdirectory(listener)
ENDIF()
add_subdirectory(emitter)
add_executable(goalc main.cpp
compiler/Compiler.cpp)
IF (WIN32)
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
ENDIF()
target_link_libraries(goalc util goos type_system)

View file

@ -6,7 +6,13 @@
* The CodeTester can't be used for tests requiring the full GOAL language/linking.
*/
#ifdef __linux__
#include <sys/mman.h>
#elif _WIN32
#include <third-party/mman/mman.h>
#endif
#include <cstdio>
#include "CodeTester.h"
#include "IGen.h"

View file

@ -652,13 +652,13 @@ bool Reader::try_token_as_integer(const Token& tok, Object& obj) {
return false;
}
}
uint64_t v = 0;
try {
std::size_t end = 0;
v = std::stoll(tok.text, &end);
if (end != tok.text.size())
if (end != tok.text.size()) {
return false;
}
obj = Object::make_integer(v);
return true;
} catch (std::exception& e) {

View file

@ -3,6 +3,9 @@
* The Listener can connect to a Deci2Server for debugging.
*/
// TODO-Windows
#ifdef __linux__
#include <stdexcept>
#include <netinet/tcp.h>
#include <arpa/inet.h>
@ -239,3 +242,4 @@ void Listener::receive_func() {
}
} // namespace listener
#endif

View file

@ -4,4 +4,5 @@
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
export NEXT_DIR=$DIR
export FAKE_ISO_PATH=/game/fake_iso.txt
$DIR/build/test/goalc-test --gtest_color=yes "$@"

View file

@ -1,3 +1,5 @@
enable_testing()
add_executable(goalc-test
test_main.cpp
test_test.cpp
@ -14,4 +16,11 @@ add_executable(goalc-test
test_emitter_integer_math.cpp
)
target_link_libraries(goalc-test goos util listener runtime emitter type_system gtest)
IF (WIN32)
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
# TODO - implement windows listener
message("Windows Listener Not Implemented!")
target_link_libraries(goalc-test mman goos util runtime emitter type_system gtest)
ELSE()
target_link_libraries(goalc-test goos util listener runtime emitter type_system gtest)
ENDIF()

View file

@ -946,6 +946,8 @@ TEST(GoosObject, char_to_string) {
* Test the EmptyListObject
*/
TEST(GoosObject, EmptyList) {
// TODO-Windows
#ifdef __linux__
// create two empty lists
Object nil = EmptyListObject::make_new();
Object nil2 = EmptyListObject::make_new();
@ -965,6 +967,7 @@ TEST(GoosObject, EmptyList) {
// check print and inspect
EXPECT_EQ(nil.print(), "()");
EXPECT_EQ(nil.inspect(), "[empty list] ()\n");
#endif
}
/*!

View file

@ -157,7 +157,8 @@ TEST(Kernel, ftoa) {
ftoa(buffer, 1., 1, ' ', 1, 0);
EXPECT_EQ("1.0", std::string(buffer));
ftoa(buffer, 0.f / 0.f, 1, ' ', 4, 0);
float zero = 0.0f;
ftoa(buffer, 0.f / zero, 1, ' ', 4, 0);
EXPECT_EQ("NaN", std::string(buffer));
ftoa(buffer, 1., 8, ' ', 1, 0);
@ -166,7 +167,7 @@ TEST(Kernel, ftoa) {
ftoa(buffer, -1., 8, '0', 1, 0);
EXPECT_EQ("0000-1.0", std::string(buffer));
ftoa(buffer, 0.f / 0.f, 8, ' ', 4, 0);
ftoa(buffer, 0.f / zero, 8, ' ', 4, 0);
EXPECT_EQ(" NaN", std::string(buffer));
ftoa(buffer, 0.1, 1, ' ', 4, 0);

View file

@ -1,3 +1,5 @@
#ifdef __linux__
#include "gtest/gtest.h"
#include "goalc/listener/Listener.h"
#include "game/system/Deci2Server.h"
@ -124,3 +126,5 @@ TEST(Listener, ListenerMultipleDecis) {
l.disconnect();
}
}
#endif

1
third-party/mman/CMakeLists.txt vendored Normal file
View file

@ -0,0 +1 @@
add_library(mman SHARED mman.c)

180
third-party/mman/mman.c vendored Normal file
View file

@ -0,0 +1,180 @@
#include <windows.h>
#include <errno.h>
#include <io.h>
#include "mman.h"
#ifndef FILE_MAP_EXECUTE
#define FILE_MAP_EXECUTE 0x0020
#endif /* FILE_MAP_EXECUTE */
static int __map_mman_error(const DWORD err, const int deferr)
{
if (err == 0)
return 0;
//TODO: implement
return err;
}
static DWORD __map_mmap_prot_page(const int prot)
{
DWORD protect = 0;
if (prot == PROT_NONE)
return protect;
if ((prot & PROT_EXEC) != 0)
{
protect = ((prot & PROT_WRITE) != 0) ?
PAGE_EXECUTE_READWRITE : PAGE_EXECUTE_READ;
}
else
{
protect = ((prot & PROT_WRITE) != 0) ?
PAGE_READWRITE : PAGE_READONLY;
}
return protect;
}
static DWORD __map_mmap_prot_file(const int prot)
{
DWORD desiredAccess = 0;
if (prot == PROT_NONE)
return desiredAccess;
if ((prot & PROT_READ) != 0)
desiredAccess |= FILE_MAP_READ;
if ((prot & PROT_WRITE) != 0)
desiredAccess |= FILE_MAP_WRITE;
if ((prot & PROT_EXEC) != 0)
desiredAccess |= FILE_MAP_EXECUTE;
return desiredAccess;
}
void* mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off)
{
HANDLE fm, h;
void * map = MAP_FAILED;
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4293)
#endif
const DWORD dwFileOffsetLow = (sizeof(off_t) <= sizeof(DWORD)) ?
(DWORD)off : (DWORD)(off & 0xFFFFFFFFL);
const DWORD dwFileOffsetHigh = (sizeof(off_t) <= sizeof(DWORD)) ?
(DWORD)0 : (DWORD)((off >> 32) & 0xFFFFFFFFL);
const DWORD protect = __map_mmap_prot_page(prot);
const DWORD desiredAccess = __map_mmap_prot_file(prot);
const off_t maxSize = off + (off_t)len;
const DWORD dwMaxSizeLow = (sizeof(off_t) <= sizeof(DWORD)) ?
(DWORD)maxSize : (DWORD)(maxSize & 0xFFFFFFFFL);
const DWORD dwMaxSizeHigh = (sizeof(off_t) <= sizeof(DWORD)) ?
(DWORD)0 : (DWORD)((maxSize >> 32) & 0xFFFFFFFFL);
#ifdef _MSC_VER
#pragma warning(pop)
#endif
errno = 0;
if (len == 0
/* Unsupported flag combinations */
|| (flags & MAP_FIXED) != 0
/* Usupported protection combinations */
|| prot == PROT_EXEC)
{
errno = EINVAL;
return MAP_FAILED;
}
h = ((flags & MAP_ANONYMOUS) == 0) ?
(HANDLE)_get_osfhandle(fildes) : INVALID_HANDLE_VALUE;
if ((flags & MAP_ANONYMOUS) == 0 && h == INVALID_HANDLE_VALUE)
{
errno = EBADF;
return MAP_FAILED;
}
fm = CreateFileMapping(h, NULL, protect, dwMaxSizeHigh, dwMaxSizeLow, NULL);
if (fm == NULL)
{
errno = __map_mman_error(GetLastError(), EPERM);
return MAP_FAILED;
}
map = MapViewOfFile(fm, desiredAccess, dwFileOffsetHigh, dwFileOffsetLow, len);
CloseHandle(fm);
if (map == NULL)
{
errno = __map_mman_error(GetLastError(), EPERM);
return MAP_FAILED;
}
return map;
}
int munmap(void *addr, size_t len)
{
if (UnmapViewOfFile(addr))
return 0;
errno = __map_mman_error(GetLastError(), EPERM);
return -1;
}
int mprotect(void *addr, size_t len, int prot)
{
DWORD newProtect = __map_mmap_prot_page(prot);
DWORD oldProtect = 0;
if (VirtualProtect(addr, len, newProtect, &oldProtect))
return 0;
errno = __map_mman_error(GetLastError(), EPERM);
return -1;
}
int msync(void *addr, size_t len, int flags)
{
if (FlushViewOfFile(addr, len))
return 0;
errno = __map_mman_error(GetLastError(), EPERM);
return -1;
}
int mlock(const void *addr, size_t len)
{
if (VirtualLock((LPVOID)addr, len))
return 0;
errno = __map_mman_error(GetLastError(), EPERM);
return -1;
}
int munlock(const void *addr, size_t len)
{
if (VirtualUnlock((LPVOID)addr, len))
return 0;
errno = __map_mman_error(GetLastError(), EPERM);
return -1;
}

57
third-party/mman/mman.h vendored Normal file
View file

@ -0,0 +1,57 @@
/*
* sys/mman.h
* mman-win32
*/
#ifndef _SYS_MMAN_H_
#define _SYS_MMAN_H_
#ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later.
#define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target other versions of Windows.
#endif
/* All the headers include this file. */
#ifndef _MSC_VER
#include <_mingw.h>
#endif
#include <sys/types.h>
#ifdef __cplusplus
extern "C" {
#endif
#define PROT_NONE 0
#define PROT_READ 1
#define PROT_WRITE 2
#define PROT_EXEC 4
#define MAP_FILE 0
#define MAP_SHARED 1
#define MAP_PRIVATE 2
#define MAP_POPULATE 0x08000
#define MAP_TYPE 0xf
#define MAP_FIXED 0x10
#define MAP_ANONYMOUS 0x20
#define MAP_32BIT 0x40 /* Only give out 32-bit addresses. */
#define MAP_ANON MAP_ANONYMOUS
#define MAP_FAILED ((void *)-1)
/* Flags for msync. */
#define MS_ASYNC 1
#define MS_SYNC 2
#define MS_INVALIDATE 4
void* mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off);
int munmap(void *addr, size_t len);
int mprotect(void *addr, size_t len, int prot);
int msync(void *addr, size_t len, int flags);
int mlock(const void *addr, size_t len);
int munlock(const void *addr, size_t len);
#ifdef __cplusplus
};
#endif
#endif /* _SYS_MMAN_H_ */