OpenGothic
Open source reimplementation of Gothic I and II
Loading...
Searching...
No Matches
cpu32.cpp
Go to the documentation of this file.
1#include "cpu32.h"
2
3#include <Tempest/Log>
4#include <unordered_set>
5#include <cstring>
6
7#include "directmemory.h"
8
9using namespace Tempest;
10using namespace Compatibility;
11
12Cpu32::Cpu32(DirectMemory& owner, Mem32& mem32):owner(owner), mem32(mem32){
13 stack.reserve(1024);
14 }
15
16void Cpu32::exec(const ptr32_t basePc, const uint8_t* ins, size_t len) {
17 std::vector<uint8_t> dbg{ins, ins+len};
18
19 ptr32_t pc = 0;
20 while(pc<len) {
21 if(exec1Byte(basePc, pc, ins, len))
22 pc += 1;
23 else if(exec2Byte(basePc, pc, ins, len))
24 pc += 2;
25 else if(exec3Byte(basePc, pc, ins, len))
26 pc += 3;
27 else {
28 auto op = readUInt32(ins, pc, len);
29 Log::e("ASMINT: unknown instruction: ", int32_t(op));
30 return;
31 }
32 }
33 stack.clear();
34 }
35
36bool Cpu32::exec1Byte(const ptr32_t basePc, ptr32_t& pc, const uint8_t* ins, size_t len) {
37 enum Op1:uint8_t {
38 ASMINT_OP_movImToEAX = 184, //0xB8
39 ASMINT_OP_movImToECX = 185, //0xB9
40 ASMINT_OP_movImToEDX = 186, //0xBA
41 ASMINT_OP_pushIm = 104, //0x68
42 ASMINT_OP_call = 232, //0xE8
43 ASMINT_OP_retn = 195, //0xC3
44 ASMINT_OP_nop = 144, //0x90
45 ASMINT_OP_jmp = 233, //0xE9
46 ASMINT_OP_cld = 252, //0xFC
47 ASMINT_OP_repz = 243, //0xF3
48 ASMINT_OP_cmpsb = 166, //0xA6
49 ASMINT_OP_pushEAX = 80, //0x50
50 ASMINT_OP_pushECX = 81, //0x51
51 ASMINT_OP_popEAX = 88, //0x58
52 ASMINT_OP_popECX = 89, //0x59
53 ASMINT_OP_pusha = 96, //0x60
54 ASMINT_OP_popa = 97, //0x61
55 ASMINT_OP_movMemToEAX = 161, //0xA1
56 ASMINT_OP_movALToMem = 162, //0xA2
57 };
58
59 const Op1 op = Op1(readUInt8(ins, pc, len));
60 switch(op) {
61 case ASMINT_OP_pushIm: {
62 auto imm = readUInt32(ins, pc+1, len);
63 stack.push_back(imm);
64 pc += 4;
65 return true;
66 }
67 case ASMINT_OP_movImToEAX:{
68 eax = readUInt32(ins, pc+1, len);
69 pc += 4;
70 return true;
71 }
72 case ASMINT_OP_movImToECX: {
73 ecx = readUInt32(ins, pc+1, len);
74 pc += 4;
75 return true;
76 }
77 case ASMINT_OP_movImToEDX: {
78 edx = readUInt32(ins, pc+1, len);
79 pc += 4;
80 return true;
81 }
82 case ASMINT_OP_call: {
83 ptr32_t calle = readUInt32(ins, pc+1, len);
84 calle = basePc+pc+calle+5;
85 pc += 4;
86 callFunction(calle);
87 return true;
88 }
89 case ASMINT_OP_retn: {
90 //NOTE: 0xC3 is regular return
91 pc = ptr32_t(len);
92 return true;
93 }
94 case ASMINT_OP_nop:
95 break;
96 case ASMINT_OP_movMemToEAX: {
97 ptr32_t ptr = readUInt32(ins, pc+1, len);
98 pc += 4;
99 eax = uint32_t(mem32.readInt(ptr));
100 return true;
101 }
102 case ASMINT_OP_pushEAX: {
103 stack.push_back(eax);
104 return true;
105 }
106 case ASMINT_OP_jmp:
107 case ASMINT_OP_cld:
108 case ASMINT_OP_repz:
109 case ASMINT_OP_cmpsb:
110 case ASMINT_OP_pushECX:
111 case ASMINT_OP_popEAX:
112 case ASMINT_OP_popECX:
113 case ASMINT_OP_pusha:
114 case ASMINT_OP_popa:
115 case ASMINT_OP_movALToMem:
116 Log::e("ASMINT: unimplemented instruction: ", int32_t(op));
117 return true;
118 }
119 return false;
120 }
121
122bool Cpu32::exec2Byte(const ptr32_t basePc, ptr32_t& pc, const uint8_t* ins, size_t len) {
123 enum Op2:uint16_t {
124 ASMINT_OP_movEAXToMem = 1417, //0x0589
125 ASMINT_OP_movEAXToAL = 138, //0x008A
126 ASMINT_OP_movCLToEAX = 2184, //0x0888
127 ASMINT_OP_floatStoreToMem = 7641, //0x1DD9
128 ASMINT_OP_addImToESP = 50307, //0xC483
129 ASMINT_OP_movMemToECX = 3467, //0x0D8B
130 ASMINT_OP_movMemToCL = 3466, //0x0D8A
131 ASMINT_OP_movMemToEDX = 5515, //0x158B
132 ASMINT_OP_movMemToEDI = 15755, //0x3D8B
133 ASMINT_OP_movMemToESI = 13707, //0x358B
134 ASMINT_OP_movECXtoEAX = 49547, //0xC18B
135 ASMINT_OP_movESPtoEAX = 50315, //0xC48B
136 ASMINT_OP_movEAXtoECX = 49545, //0xC189
137 ASMINT_OP_movEBXtoEAX = 55433, //0xD889
138 ASMINT_OP_movEBPtoEAX = 50571, //0xC58B
139 ASMINT_OP_movEDItoEAX = 51083, //0xC78B
140 ASMINT_OP_addImToEAX = 49283, //0xC083
141 };
142
143 const Op2 op = Op2(readUInt16(ins, pc, len));
144 switch(op) {
145 case ASMINT_OP_movEAXToMem: {
146 ptr32_t mem = readUInt32(ins, pc+2, len);
147 pc += 4;
148 mem32.writeInt(mem, int32_t(eax));
149 return true;
150 }
151 case ASMINT_OP_addImToESP: {
152 //TODO: propper stack maintanance
153 uint8_t imm = readUInt8(ins, pc+2, len);
154 pc += 1;
155 esp += imm;
156 return true;
157 }
158 case ASMINT_OP_movMemToECX: {
159 ptr32_t ptr = readUInt32(ins, pc+2, len);
160 pc += 4;
161 ecx = uint32_t(mem32.readInt(ptr));
162 return true;
163 }
164 case ASMINT_OP_movEAXToAL:
165 case ASMINT_OP_movCLToEAX:
166 case ASMINT_OP_floatStoreToMem:
167 case ASMINT_OP_movMemToCL:
168 case ASMINT_OP_movMemToEDX:
169 case ASMINT_OP_movMemToEDI:
170 case ASMINT_OP_movMemToESI:
171 case ASMINT_OP_movECXtoEAX:
172 case ASMINT_OP_movESPtoEAX:
173 case ASMINT_OP_movEAXtoECX:
174 case ASMINT_OP_movEBXtoEAX:
175 case ASMINT_OP_movEBPtoEAX:
176 case ASMINT_OP_movEDItoEAX:
177 case ASMINT_OP_addImToEAX:
178 Log::e("ASMINT: unimplemented instruction: ", int32_t(op));
179 return true;
180 }
181 return false;
182 }
183
184bool Cpu32::exec3Byte(const ptr32_t basePc, ptr32_t& pc, const uint8_t* ins, size_t len) {
185 enum Op3:uint32_t {
186 ASMINT_OP_setzMem = 365583, //0x05940F
187 };
188 const Op3 op = Op3(readUInt24(ins, pc, len));
189 switch(op) {
190 case ASMINT_OP_setzMem:
191 Log::e("ASMINT: unimplemented instruction: ", int32_t(op));
192 return true;
193 }
194 return false;
195 }
196
197uint8_t Cpu32::readUInt8(const uint8_t* code, size_t offset, size_t len) {
198 if(offset>=len)
199 return 0;
200 uint8_t ret = 0;
201 std::memcpy(&ret, code+offset, sizeof(uint8_t));
202 return ret;
203 }
204
205uint16_t Cpu32::readUInt16(const uint8_t* code, size_t offset, size_t len) {
206 if(offset+2>len)
207 return 0;
208 uint16_t ret = 0;
209 std::memcpy(&ret, code+offset, sizeof(uint16_t));
210 return ret;
211 }
212
213uint32_t Cpu32::readUInt24(const uint8_t* code, size_t offset, size_t len) {
214 if(offset+3>len)
215 return 0;
216 uint32_t ret = 0;
217 std::memcpy(&ret, code+offset, 3);
218 return ret;
219 }
220
221uint32_t Cpu32::readUInt32(const uint8_t* code, size_t offset, size_t len) {
222 if(offset+4>len)
223 return 0;
224 uint32_t ret = 0;
225 std::memcpy(&ret, code+offset, sizeof(uint32_t));
226 return ret;
227 }
228
229void Cpu32::register_stdcall_inner(ptr32_t addr, std::function<void (Cpu32&)> f) {
230 stdcall_Overrides[addr] = std::move(f);
231 }
232
233void Cpu32::callFunction(ptr32_t func) {
234 if(func==0x1) {
235 //TODO: better representation of *.dll in memory
236 Log::d("ASMINT_OP_call: won't invoke function obtained by GetProcAddress ");
237 return;
238 }
239
240 auto fn = stdcall_Overrides.find(func);
241 if(fn!=stdcall_Overrides.end()) {
242 fn->second(*this);
243 return;
244 }
245
246 static std::unordered_set<ptr32_t> once;
247 if(!once.insert(func).second)
248 return;
249
250 auto str = owner.demangleAddress(func);
251 if(!str.empty())
252 Log::d("ASMINT_OP_call: unknown external function: \"", str, "\"");else
253 Log::d("ASMINT_OP_call: unknown external function: ", func);
254 }
255
256std::string Cpu32::popString() {
257 if(stack.size()==0)
258 return 0;
259 auto ptr = stack.back();
260 stack.pop_back();
261
262 auto mem = mem32.deref<const zString>(ptr);
263 if(mem==nullptr || mem->len<=0)
264 return "";
265 auto chr = reinterpret_cast<const char*>(mem32.deref(mem->ptr, uint32_t(mem->len)));
266 if(chr==nullptr)
267 return "";
268 std::string ret(chr, size_t(mem->len));
269 return ret;
270 }
Definition cpu32.h:14
void exec(const ptr32_t basePc, const uint8_t *code, size_t len)
Definition cpu32.cpp:16
Compatibility::ptr32_t ptr32_t
Definition cpu32.h:16
Cpu32(DirectMemory &owner, Mem32 &mem32)
Definition cpu32.cpp:12
auto demangleAddress(uint32_t addr) -> std::string_view
Definition mem32.h:12
int32_t readInt(ptr32_t address)
Definition mem32.cpp:205
auto deref(ptr32_t address) -> std::tuple< void *, uint32_t >
Definition mem32.cpp:163
void writeInt(ptr32_t address, int32_t v)
Definition mem32.cpp:191
uint32_t ptr32_t