Building cicoparser:
- building on OSX or linux
- download source file:
https://github.com/gabonator/Projects/b ... s/main.cpp
-
brew install capstone or
apt-get install capstone
-
g++ -std=c++17 main.cpp -I/opt/homebrew/Cellar/capstone/5.0.1/include/ -L/opt/homebrew/Cellar/capstone/5.0.1/lib -lcapstone.5 -o cicodis
- building on windows:
- it has no dependencies besides of capstone, so with basic Visual Studio skills it should be fairly easy:
https://github.com/capstone-engine/caps ... UILDING.md
- To build capstone you will need cmake:
https://cmake.org/download/
Building individual games (Stargoose for example,
https://github.com/gabonator/Projects/t ... elib/goose):
- install SDL2 framework (used by all games for creating windows, rendering framebuffer and keyboard input), check here:
https://github.com/libsdl-org/SDL/releases or here:
https://wiki.libsdl.org/SDL2/Installation
- download the game binaries:
- on linux or osx run:
https://github.com/gabonator/Projects/b ... e/fetch.sh
- on windows: just find some place where you can download the game, goose.exe should have 53684 bytes, there should be also 6 resource files (bird1.x, bird2.x, blox.x, intro.x, newbird.x, podz1.x). Place all files into "dos" directory
- in goose.cpp fix the path to game resources:
load("/Users/gabrielvalky/Documents/git/Projects/CicoJit/gamelib/goose/dos", "GOOSE.EXE", 53684);
- building and running the game
- linux/osx: run
run.sh, it just calls
g++ -std=c++17 cicorun/goose.cpp cicorun/main.cpp `pkg-config --libs --cflags sdl2` -o goose
- windows: create visual studio project and place all files there, add SDL2 dependency into your project (header and library files, set library/header search paths)
Converting game into c++:
./cicodis $PWD/dos/GOOSE.EXE -asm -coverage -tree \
-jumptable 1000:104e 1000:105a 35 callwords indirect \
-simplestack -ctx -recursive start,1000:05fc,1000:04b7,1000:05ce
There are two required parameters: input executable file (goose.exe) and a method (or comma separated methods) you want to disassemble (start,1000:05fc,1000:04b7,1000:05ce). Providing just
start is sufficient, those 3 extra addresses are the interrupt handlers which are not called by the game anyway, but I included them to make the "coverage report" look better. Coverage report is a separate topic, I don't want to go much into details. Check it here:
https://rawgit.valky.eu/gabonator/Proje ... erage.html
Extra switches used in this example:
-
-asm generate assembly code before each C++ function
-
-coverage generate extra comments for the coverage generator tool (the byte range for a specific method)
-
-tree dump call tree (not yet used anywhere)
-
-recursive dump all identified methods recursively, remove if you want to disassemble a single method
-
-segofscomment show method address in comment
-
-jumptable 1000:104e 1000:105a 35 callwords indirect replaces indirect call at 1000:104e with a switch with address table with 35 entries placed at 1000:105a
A single disassembled method will look like this:
Code: Select all
/* Assembly listing of 1000:33b3 sub_133b3()
sub_133b3 PROC
1000:33b3 50 push ax
1000:33b4 a1 f8 94 mov ax, word ptr [0x94f8]
1000:33b7 8e c0 mov es, ax
1000:33b9 bf 00 00 mov di, 0
1000:33bc ba ce 03 mov dx, 0x3ce
1000:33bf 58 pop ax
1000:33c0 8a e0 mov ah, al
1000:33c2 b0 00 mov al, 0
1000:33c4 ef out dx, ax
1000:33c5 b8 01 0f mov ax, 0xf01
1000:33c8 ef out dx, ax
1000:33c9 fc cld
1000:33ca b9 a0 0f mov cx, 0xfa0
1000:33cd f3 ab rep stosw word ptr es:[di], ax
1000:33cf b8 00 00 mov ax, 0
1000:33d2 ef out dx, ax
1000:33d3 fe c0 inc al
1000:33d5 ef out dx, ax
1000:33d6 c3 ret
sub_133b3 ENDP
*/
void sub_133b3()
{
// coverage: 0x35b3-0x35d7 method sub_133b3
push(ax);
ax = memoryAGet16(ds, 0x94f8);
es = ax;
di = 0x0000;
dx = 0x03ce;
ax = pop();
ah = al;
al = 0x00;
out(dx, ax);
ax = 0x0f01;
out(dx, ax);
flags.direction = false;
cx = 0x0fa0;
rep_stosw<MemAuto, DirAuto>();
ax = 0x0000;
out(dx, ax);
al++;
out(dx, ax);
}