Imported Upstream version 9.20
[platform/upstream/7zip.git] / CPP / 7zip / Compress / Rar3Vm.cpp
1 // Rar3Vm.cpp\r
2 // According to unRAR license, this code may not be used to develop\r
3 // a program that creates RAR archives\r
4 \r
5 /*\r
6 Note:\r
7   Due to performance considerations Rar VM may set Flags C incorrectly\r
8   for some operands (SHL x, 0, ... ).\r
9   Check implementation of concrete VM command\r
10   to see if it sets flags right.\r
11 */\r
12 \r
13 #include "StdAfx.h"\r
14 \r
15 #include "../../../C/7zCrc.h"\r
16 #include "../../../C/Alloc.h"\r
17 \r
18 #include "Rar3Vm.h"\r
19 \r
20 namespace NCompress {\r
21 namespace NRar3 {\r
22 \r
23 UInt32 CMemBitDecoder::ReadBits(int numBits)\r
24 {\r
25   UInt32 res = 0;\r
26   for (;;)\r
27   {\r
28     Byte b = _bitPos < _bitSize ? _data[_bitPos >> 3] : 0;\r
29     int avail = (int)(8 - (_bitPos & 7));\r
30     if (numBits <= avail)\r
31     {\r
32       _bitPos += numBits;\r
33       return res | (b >> (avail - numBits)) & ((1 << numBits) - 1);\r
34     }\r
35     numBits -= avail;\r
36     res |= (UInt32)(b & ((1 << avail) - 1)) << numBits;\r
37     _bitPos += avail;\r
38   }\r
39 }\r
40 \r
41 UInt32 CMemBitDecoder::ReadBit() { return ReadBits(1); }\r
42 \r
43 namespace NVm {\r
44 \r
45 static const UInt32 kStackRegIndex = kNumRegs - 1;\r
46 \r
47 static const UInt32 FLAG_C = 1;\r
48 static const UInt32 FLAG_Z = 2;\r
49 static const UInt32 FLAG_S = 0x80000000;\r
50 \r
51 static const Byte CF_OP0 = 0;\r
52 static const Byte CF_OP1 = 1;\r
53 static const Byte CF_OP2 = 2;\r
54 static const Byte CF_OPMASK = 3;\r
55 static const Byte CF_BYTEMODE = 4;\r
56 static const Byte CF_JUMP = 8;\r
57 static const Byte CF_PROC = 16;\r
58 static const Byte CF_USEFLAGS = 32;\r
59 static const Byte CF_CHFLAGS = 64;\r
60 \r
61 static Byte kCmdFlags[]=\r
62 {\r
63   /* CMD_MOV   */ CF_OP2 | CF_BYTEMODE,\r
64   /* CMD_CMP   */ CF_OP2 | CF_BYTEMODE | CF_CHFLAGS,\r
65   /* CMD_ADD   */ CF_OP2 | CF_BYTEMODE | CF_CHFLAGS,\r
66   /* CMD_SUB   */ CF_OP2 | CF_BYTEMODE | CF_CHFLAGS,\r
67   /* CMD_JZ    */ CF_OP1 | CF_JUMP | CF_USEFLAGS,\r
68   /* CMD_JNZ   */ CF_OP1 | CF_JUMP | CF_USEFLAGS,\r
69   /* CMD_INC   */ CF_OP1 | CF_BYTEMODE | CF_CHFLAGS,\r
70   /* CMD_DEC   */ CF_OP1 | CF_BYTEMODE | CF_CHFLAGS,\r
71   /* CMD_JMP   */ CF_OP1 | CF_JUMP,\r
72   /* CMD_XOR   */ CF_OP2 | CF_BYTEMODE | CF_CHFLAGS,\r
73   /* CMD_AND   */ CF_OP2 | CF_BYTEMODE | CF_CHFLAGS,\r
74   /* CMD_OR    */ CF_OP2 | CF_BYTEMODE | CF_CHFLAGS,\r
75   /* CMD_TEST  */ CF_OP2 | CF_BYTEMODE | CF_CHFLAGS,\r
76   /* CMD_JS    */ CF_OP1 | CF_JUMP | CF_USEFLAGS,\r
77   /* CMD_JNS   */ CF_OP1 | CF_JUMP | CF_USEFLAGS,\r
78   /* CMD_JB    */ CF_OP1 | CF_JUMP | CF_USEFLAGS,\r
79   /* CMD_JBE   */ CF_OP1 | CF_JUMP | CF_USEFLAGS,\r
80   /* CMD_JA    */ CF_OP1 | CF_JUMP | CF_USEFLAGS,\r
81   /* CMD_JAE   */ CF_OP1 | CF_JUMP | CF_USEFLAGS,\r
82   /* CMD_PUSH  */ CF_OP1,\r
83   /* CMD_POP   */ CF_OP1,\r
84   /* CMD_CALL  */ CF_OP1 | CF_PROC,\r
85   /* CMD_RET   */ CF_OP0 | CF_PROC,\r
86   /* CMD_NOT   */ CF_OP1 | CF_BYTEMODE,\r
87   /* CMD_SHL   */ CF_OP2 | CF_BYTEMODE | CF_CHFLAGS,\r
88   /* CMD_SHR   */ CF_OP2 | CF_BYTEMODE | CF_CHFLAGS,\r
89   /* CMD_SAR   */ CF_OP2 | CF_BYTEMODE | CF_CHFLAGS,\r
90   /* CMD_NEG   */ CF_OP1 | CF_BYTEMODE | CF_CHFLAGS,\r
91   /* CMD_PUSHA */ CF_OP0,\r
92   /* CMD_POPA  */ CF_OP0,\r
93   /* CMD_PUSHF */ CF_OP0 | CF_USEFLAGS,\r
94   /* CMD_POPF  */ CF_OP0 | CF_CHFLAGS,\r
95   /* CMD_MOVZX */ CF_OP2,\r
96   /* CMD_MOVSX */ CF_OP2,\r
97   /* CMD_XCHG  */ CF_OP2 | CF_BYTEMODE,\r
98   /* CMD_MUL   */ CF_OP2 | CF_BYTEMODE,\r
99   /* CMD_DIV   */ CF_OP2 | CF_BYTEMODE,\r
100   /* CMD_ADC   */ CF_OP2 | CF_BYTEMODE | CF_USEFLAGS | CF_CHFLAGS ,\r
101   /* CMD_SBB   */ CF_OP2 | CF_BYTEMODE | CF_USEFLAGS | CF_CHFLAGS ,\r
102   /* CMD_PRINT */ CF_OP0\r
103 };\r
104 \r
105 CVm::CVm(): Mem(NULL) {}\r
106 \r
107 bool CVm::Create()\r
108 {\r
109   if (Mem == NULL)\r
110     Mem = (Byte *)::MyAlloc(kSpaceSize + 4);\r
111   return (Mem != NULL);\r
112 }\r
113 \r
114 CVm::~CVm()\r
115 {\r
116   ::MyFree(Mem);\r
117 }\r
118 \r
119 // CVm::Execute can change CProgram object: it clears progarm if VM returns error.\r
120 \r
121 bool CVm::Execute(CProgram *prg, const CProgramInitState *initState,\r
122     CBlockRef &outBlockRef, CRecordVector<Byte> &outGlobalData)\r
123 {\r
124   memcpy(R, initState->InitR, sizeof(initState->InitR));\r
125   R[kStackRegIndex] = kSpaceSize;\r
126   R[kNumRegs] = 0;\r
127   Flags = 0;\r
128 \r
129   UInt32 globalSize = MyMin((UInt32)initState->GlobalData.Size(), kGlobalSize);\r
130   if (globalSize != 0)\r
131     memcpy(Mem + kGlobalOffset, &initState->GlobalData[0], globalSize);\r
132   UInt32 staticSize = MyMin((UInt32)prg->StaticData.Size(), kGlobalSize - globalSize);\r
133   if (staticSize != 0)\r
134     memcpy(Mem + kGlobalOffset + globalSize, &prg->StaticData[0], staticSize);\r
135 \r
136   bool res = true;\r
137   #ifdef RARVM_STANDARD_FILTERS\r
138   if (prg->StandardFilterIndex >= 0)\r
139     ExecuteStandardFilter(prg->StandardFilterIndex);\r
140   else\r
141   #endif\r
142   {\r
143     res = ExecuteCode(prg);\r
144     if (!res)\r
145       prg->Commands[0].OpCode = CMD_RET;\r
146   }\r
147   UInt32 newBlockPos = GetFixedGlobalValue32(NGlobalOffset::kBlockPos) & kSpaceMask;\r
148   UInt32 newBlockSize = GetFixedGlobalValue32(NGlobalOffset::kBlockSize) & kSpaceMask;\r
149   if (newBlockPos + newBlockSize >= kSpaceSize)\r
150     newBlockPos = newBlockSize = 0;\r
151   outBlockRef.Offset = newBlockPos;\r
152   outBlockRef.Size = newBlockSize;\r
153 \r
154   outGlobalData.Clear();\r
155   UInt32 dataSize = GetFixedGlobalValue32(NGlobalOffset::kGlobalMemOutSize);\r
156   dataSize = MyMin(dataSize, kGlobalSize - kFixedGlobalSize);\r
157   if (dataSize != 0)\r
158   {\r
159     dataSize += kFixedGlobalSize;\r
160     outGlobalData.Reserve(dataSize);\r
161     for (UInt32 i = 0; i < dataSize; i++)\r
162       outGlobalData.Add(Mem[kGlobalOffset + i]);\r
163   }\r
164   return res;\r
165 }\r
166 \r
167 \r
168 #define SET_IP(IP) \\r
169   if ((IP) >= numCommands) return true; \\r
170   if (--maxOpCount <= 0) return false; \\r
171   cmd = commands + (IP);\r
172 \r
173 #define GET_FLAG_S_B(res) (((res) & 0x80) ? FLAG_S : 0)\r
174 #define SET_IP_OP1 { UInt32 val = GetOperand32(&cmd->Op1); SET_IP(val); }\r
175 #define FLAGS_UPDATE_SZ Flags = res == 0 ? FLAG_Z : res & FLAG_S\r
176 #define FLAGS_UPDATE_SZ_B Flags = (res & 0xFF) == 0 ? FLAG_Z : GET_FLAG_S_B(res)\r
177 \r
178 UInt32 CVm::GetOperand32(const COperand *op) const\r
179 {\r
180   switch(op->Type)\r
181   {\r
182     case OP_TYPE_REG: return R[op->Data];\r
183     case OP_TYPE_REGMEM: return GetValue32(&Mem[(op->Base + R[op->Data]) & kSpaceMask]);\r
184     default: return op->Data;\r
185   }\r
186 }\r
187 \r
188 void CVm::SetOperand32(const COperand *op, UInt32 val)\r
189 {\r
190   switch(op->Type)\r
191   {\r
192     case OP_TYPE_REG: R[op->Data] = val; return;\r
193     case OP_TYPE_REGMEM: SetValue32(&Mem[(op->Base + R[op->Data]) & kSpaceMask], val); return;\r
194   }\r
195 }\r
196 \r
197 Byte CVm::GetOperand8(const COperand *op) const\r
198 {\r
199   switch(op->Type)\r
200   {\r
201     case OP_TYPE_REG: return (Byte)R[op->Data];\r
202     case OP_TYPE_REGMEM: return Mem[(op->Base + R[op->Data]) & kSpaceMask];;\r
203     default: return (Byte)op->Data;\r
204   }\r
205 }\r
206 \r
207 void CVm::SetOperand8(const COperand *op, Byte val)\r
208 {\r
209   switch(op->Type)\r
210   {\r
211     case OP_TYPE_REG: R[op->Data] = (R[op->Data] & 0xFFFFFF00) | val; return;\r
212     case OP_TYPE_REGMEM: Mem[(op->Base + R[op->Data]) & kSpaceMask] = val; return;\r
213   }\r
214 }\r
215 \r
216 UInt32 CVm::GetOperand(bool byteMode, const COperand *op) const\r
217 {\r
218   if (byteMode)\r
219     return GetOperand8(op);\r
220   return GetOperand32(op);\r
221 }\r
222 \r
223 void CVm::SetOperand(bool byteMode, const COperand *op, UInt32 val)\r
224 {\r
225   if (byteMode)\r
226     SetOperand8(op, (Byte)(val & 0xFF));\r
227   else\r
228     SetOperand32(op, val);\r
229 }\r
230 \r
231 bool CVm::ExecuteCode(const CProgram *prg)\r
232 {\r
233   Int32 maxOpCount = 25000000;\r
234   const CCommand *commands = &prg->Commands[0];\r
235   const CCommand *cmd = commands;\r
236   UInt32 numCommands = prg->Commands.Size();\r
237   for (;;)\r
238   {\r
239     switch(cmd->OpCode)\r
240     {\r
241       #ifndef RARVM_NO_VM\r
242       \r
243       case CMD_MOV:\r
244         SetOperand32(&cmd->Op1, GetOperand32(&cmd->Op2));\r
245         break;\r
246       case CMD_MOVB:\r
247         SetOperand8(&cmd->Op1, GetOperand8(&cmd->Op2));\r
248         break;\r
249       case CMD_CMP:\r
250         {\r
251           UInt32 v1 = GetOperand32(&cmd->Op1);\r
252           UInt32 res = v1 - GetOperand32(&cmd->Op2);\r
253           Flags = res == 0 ? FLAG_Z : (res > v1) | (res & FLAG_S);\r
254         }\r
255         break;\r
256       case CMD_CMPB:\r
257         {\r
258           Byte v1 = GetOperand8(&cmd->Op1);\r
259           Byte res = v1 - GetOperand8(&cmd->Op2);\r
260           res &= 0xFF;\r
261           Flags = res == 0 ? FLAG_Z : (res > v1) | GET_FLAG_S_B(res);\r
262         }\r
263         break;\r
264       case CMD_ADD:\r
265         {\r
266           UInt32 v1 = GetOperand32(&cmd->Op1);\r
267           UInt32 res = v1 + GetOperand32(&cmd->Op2);\r
268           SetOperand32(&cmd->Op1, res);\r
269           Flags = (res < v1) | (res == 0 ? FLAG_Z : (res & FLAG_S));\r
270         }\r
271         break;\r
272       case CMD_ADDB:\r
273         {\r
274           Byte v1 = GetOperand8(&cmd->Op1);\r
275           Byte res = v1 + GetOperand8(&cmd->Op2);\r
276           res &= 0xFF;\r
277           SetOperand8(&cmd->Op1, (Byte)res);\r
278           Flags = (res < v1) | (res == 0 ? FLAG_Z : GET_FLAG_S_B(res));\r
279         }\r
280         break;\r
281       case CMD_ADC:\r
282         {\r
283           UInt32 v1 = GetOperand(cmd->ByteMode, &cmd->Op1);\r
284           UInt32 FC = (Flags & FLAG_C);\r
285           UInt32 res = v1 + GetOperand(cmd->ByteMode, &cmd->Op2) + FC;\r
286           if (cmd->ByteMode)\r
287             res &= 0xFF;\r
288           SetOperand(cmd->ByteMode, &cmd->Op1, res);\r
289           Flags = (res < v1 || res == v1 && FC) | (res == 0 ? FLAG_Z : (res & FLAG_S));\r
290         }\r
291         break;\r
292       case CMD_SUB:\r
293         {\r
294           UInt32 v1 = GetOperand32(&cmd->Op1);\r
295           UInt32 res = v1 - GetOperand32(&cmd->Op2);\r
296           SetOperand32(&cmd->Op1, res);\r
297           Flags = res == 0 ? FLAG_Z : (res > v1) | (res & FLAG_S);\r
298         }\r
299         break;\r
300       case CMD_SUBB:\r
301         {\r
302           UInt32 v1 = GetOperand8(&cmd->Op1);\r
303           UInt32 res = v1 - GetOperand8(&cmd->Op2);\r
304           SetOperand8(&cmd->Op1, (Byte)res);\r
305           Flags = res == 0 ? FLAG_Z : (res > v1) | (res & FLAG_S);\r
306         }\r
307         break;\r
308       case CMD_SBB:\r
309         {\r
310           UInt32 v1 = GetOperand(cmd->ByteMode, &cmd->Op1);\r
311           UInt32 FC = (Flags & FLAG_C);\r
312           UInt32 res = v1 - GetOperand(cmd->ByteMode, &cmd->Op2) - FC;\r
313           // Flags = res == 0 ? FLAG_Z : (res > v1 || res == v1 && FC) | (res & FLAG_S);\r
314           if (cmd->ByteMode)\r
315             res &= 0xFF;\r
316           SetOperand(cmd->ByteMode, &cmd->Op1, res);\r
317           Flags = (res > v1 || res == v1 && FC) | (res == 0 ? FLAG_Z : (res & FLAG_S));\r
318         }\r
319         break;\r
320       case CMD_INC:\r
321         {\r
322           UInt32 res = GetOperand32(&cmd->Op1) + 1;\r
323           SetOperand32(&cmd->Op1, res);\r
324           FLAGS_UPDATE_SZ;\r
325         }\r
326         break;\r
327       case CMD_INCB:\r
328         {\r
329           Byte res = GetOperand8(&cmd->Op1) + 1;\r
330           SetOperand8(&cmd->Op1, res);;\r
331           FLAGS_UPDATE_SZ_B;\r
332         }\r
333         break;\r
334       case CMD_DEC:\r
335         {\r
336           UInt32 res = GetOperand32(&cmd->Op1) - 1;\r
337           SetOperand32(&cmd->Op1, res);\r
338           FLAGS_UPDATE_SZ;\r
339         }\r
340         break;\r
341       case CMD_DECB:\r
342         {\r
343           Byte res = GetOperand8(&cmd->Op1) - 1;\r
344           SetOperand8(&cmd->Op1, res);;\r
345           FLAGS_UPDATE_SZ_B;\r
346         }\r
347         break;\r
348       case CMD_XOR:\r
349         {\r
350           UInt32 res = GetOperand32(&cmd->Op1) ^ GetOperand32(&cmd->Op2);\r
351           SetOperand32(&cmd->Op1, res);\r
352           FLAGS_UPDATE_SZ;\r
353         }\r
354         break;\r
355       case CMD_XORB:\r
356         {\r
357           Byte res = GetOperand8(&cmd->Op1) ^ GetOperand8(&cmd->Op2);\r
358           SetOperand8(&cmd->Op1, res);\r
359           FLAGS_UPDATE_SZ_B;\r
360         }\r
361         break;\r
362       case CMD_AND:\r
363         {\r
364           UInt32 res = GetOperand32(&cmd->Op1) & GetOperand32(&cmd->Op2);\r
365           SetOperand32(&cmd->Op1, res);\r
366           FLAGS_UPDATE_SZ;\r
367         }\r
368         break;\r
369       case CMD_ANDB:\r
370         {\r
371           Byte res = GetOperand8(&cmd->Op1) & GetOperand8(&cmd->Op2);\r
372           SetOperand8(&cmd->Op1, res);\r
373           FLAGS_UPDATE_SZ_B;\r
374         }\r
375         break;\r
376       case CMD_OR:\r
377         {\r
378           UInt32 res = GetOperand32(&cmd->Op1) | GetOperand32(&cmd->Op2);\r
379           SetOperand32(&cmd->Op1, res);\r
380           FLAGS_UPDATE_SZ;\r
381         }\r
382         break;\r
383       case CMD_ORB:\r
384         {\r
385           Byte res = GetOperand8(&cmd->Op1) | GetOperand8(&cmd->Op2);\r
386           SetOperand8(&cmd->Op1, res);\r
387           FLAGS_UPDATE_SZ_B;\r
388         }\r
389         break;\r
390       case CMD_TEST:\r
391         {\r
392           UInt32 res = GetOperand32(&cmd->Op1) & GetOperand32(&cmd->Op2);\r
393           FLAGS_UPDATE_SZ;\r
394         }\r
395         break;\r
396       case CMD_TESTB:\r
397         {\r
398           Byte res = GetOperand8(&cmd->Op1) & GetOperand8(&cmd->Op2);\r
399           FLAGS_UPDATE_SZ_B;\r
400         }\r
401         break;\r
402       case CMD_NOT:\r
403         SetOperand(cmd->ByteMode, &cmd->Op1, ~GetOperand(cmd->ByteMode, &cmd->Op1));\r
404         break;\r
405       case CMD_NEG:\r
406         {\r
407           UInt32 res = 0 - GetOperand32(&cmd->Op1);\r
408           SetOperand32(&cmd->Op1, res);\r
409           Flags = res == 0 ? FLAG_Z : FLAG_C | (res & FLAG_S);\r
410         }\r
411         break;\r
412       case CMD_NEGB:\r
413         {\r
414           Byte res = (Byte)(0 - GetOperand8(&cmd->Op1));\r
415           SetOperand8(&cmd->Op1, res);\r
416           Flags = res == 0 ? FLAG_Z : FLAG_C | GET_FLAG_S_B(res);\r
417         }\r
418         break;\r
419 \r
420       case CMD_SHL:\r
421         {\r
422           UInt32 v1 = GetOperand32(&cmd->Op1);\r
423           int v2 = (int)GetOperand32(&cmd->Op2);\r
424           UInt32 res = v1 << v2;\r
425           SetOperand32(&cmd->Op1, res);\r
426           Flags = (res == 0 ? FLAG_Z : (res & FLAG_S)) | ((v1 << (v2 - 1)) & 0x80000000 ? FLAG_C : 0);\r
427         }\r
428         break;\r
429       case CMD_SHLB:\r
430         {\r
431           Byte v1 = GetOperand8(&cmd->Op1);\r
432           int v2 = (int)GetOperand8(&cmd->Op2);\r
433           Byte res = (Byte)(v1 << v2);\r
434           SetOperand8(&cmd->Op1, res);\r
435           Flags = (res == 0 ? FLAG_Z : GET_FLAG_S_B(res)) | ((v1 << (v2 - 1)) & 0x80 ? FLAG_C : 0);\r
436         }\r
437         break;\r
438       case CMD_SHR:\r
439         {\r
440           UInt32 v1 = GetOperand32(&cmd->Op1);\r
441           int v2 = (int)GetOperand32(&cmd->Op2);\r
442           UInt32 res = v1 >> v2;\r
443           SetOperand32(&cmd->Op1, res);\r
444           Flags = (res == 0 ? FLAG_Z : (res & FLAG_S)) | ((v1 >> (v2 - 1)) & FLAG_C);\r
445         }\r
446         break;\r
447       case CMD_SHRB:\r
448         {\r
449           Byte v1 = GetOperand8(&cmd->Op1);\r
450           int v2 = (int)GetOperand8(&cmd->Op2);\r
451           Byte res = (Byte)(v1 >> v2);\r
452           SetOperand8(&cmd->Op1, res);\r
453           Flags = (res == 0 ? FLAG_Z : GET_FLAG_S_B(res)) | ((v1 >> (v2 - 1)) & FLAG_C);\r
454         }\r
455         break;\r
456       case CMD_SAR:\r
457         {\r
458           UInt32 v1 = GetOperand32(&cmd->Op1);\r
459           int v2 = (int)GetOperand32(&cmd->Op2);\r
460           UInt32 res = UInt32(((Int32)v1) >> v2);\r
461           SetOperand32(&cmd->Op1, res);\r
462           Flags= (res == 0 ? FLAG_Z : (res & FLAG_S)) | ((v1 >> (v2 - 1)) & FLAG_C);\r
463         }\r
464         break;\r
465       case CMD_SARB:\r
466         {\r
467           Byte v1 = GetOperand8(&cmd->Op1);\r
468           int v2 = (int)GetOperand8(&cmd->Op2);\r
469           Byte res = (Byte)(((signed char)v1) >> v2);\r
470           SetOperand8(&cmd->Op1, res);\r
471           Flags= (res == 0 ? FLAG_Z : GET_FLAG_S_B(res)) | ((v1 >> (v2 - 1)) & FLAG_C);\r
472         }\r
473         break;\r
474 \r
475       case CMD_JMP:\r
476         SET_IP_OP1;\r
477         continue;\r
478       case CMD_JZ:\r
479         if ((Flags & FLAG_Z) != 0)\r
480         {\r
481           SET_IP_OP1;\r
482           continue;\r
483         }\r
484         break;\r
485       case CMD_JNZ:\r
486         if ((Flags & FLAG_Z) == 0)\r
487         {\r
488           SET_IP_OP1;\r
489           continue;\r
490         }\r
491         break;\r
492       case CMD_JS:\r
493         if ((Flags & FLAG_S) != 0)\r
494         {\r
495           SET_IP_OP1;\r
496           continue;\r
497         }\r
498         break;\r
499       case CMD_JNS:\r
500         if ((Flags & FLAG_S) == 0)\r
501         {\r
502           SET_IP_OP1;\r
503           continue;\r
504         }\r
505         break;\r
506       case CMD_JB:\r
507         if ((Flags & FLAG_C) != 0)\r
508         {\r
509           SET_IP_OP1;\r
510           continue;\r
511         }\r
512         break;\r
513       case CMD_JBE:\r
514         if ((Flags & (FLAG_C | FLAG_Z)) != 0)\r
515         {\r
516           SET_IP_OP1;\r
517           continue;\r
518         }\r
519         break;\r
520       case CMD_JA:\r
521         if ((Flags & (FLAG_C | FLAG_Z)) == 0)\r
522         {\r
523           SET_IP_OP1;\r
524           continue;\r
525         }\r
526         break;\r
527       case CMD_JAE:\r
528         if ((Flags & FLAG_C) == 0)\r
529         {\r
530           SET_IP_OP1;\r
531           continue;\r
532         }\r
533         break;\r
534       \r
535       case CMD_PUSH:\r
536         R[kStackRegIndex] -= 4;\r
537         SetValue32(&Mem[R[kStackRegIndex] & kSpaceMask], GetOperand32(&cmd->Op1));\r
538         break;\r
539       case CMD_POP:\r
540         SetOperand32(&cmd->Op1, GetValue32(&Mem[R[kStackRegIndex] & kSpaceMask]));\r
541         R[kStackRegIndex] += 4;\r
542         break;\r
543       case CMD_CALL:\r
544         R[kStackRegIndex] -= 4;\r
545         SetValue32(&Mem[R[kStackRegIndex] & kSpaceMask], (UInt32)(cmd - commands + 1));\r
546         SET_IP_OP1;\r
547         continue;\r
548 \r
549       case CMD_PUSHA:\r
550         {\r
551           for (UInt32 i = 0, SP = R[kStackRegIndex] - 4; i < kNumRegs; i++, SP -= 4)\r
552             SetValue32(&Mem[SP & kSpaceMask], R[i]);\r
553           R[kStackRegIndex] -= kNumRegs * 4;\r
554         }\r
555         break;\r
556       case CMD_POPA:\r
557         {\r
558           for (UInt32 i = 0, SP = R[kStackRegIndex]; i < kNumRegs; i++, SP += 4)\r
559             R[kStackRegIndex - i] = GetValue32(&Mem[SP & kSpaceMask]);\r
560         }\r
561         break;\r
562       case CMD_PUSHF:\r
563         R[kStackRegIndex] -= 4;\r
564         SetValue32(&Mem[R[kStackRegIndex]&kSpaceMask], Flags);\r
565         break;\r
566       case CMD_POPF:\r
567         Flags = GetValue32(&Mem[R[kStackRegIndex] & kSpaceMask]);\r
568         R[kStackRegIndex] += 4;\r
569         break;\r
570       \r
571       case CMD_MOVZX:\r
572         SetOperand32(&cmd->Op1, GetOperand8(&cmd->Op2));\r
573         break;\r
574       case CMD_MOVSX:\r
575         SetOperand32(&cmd->Op1, (UInt32)(Int32)(signed char)GetOperand8(&cmd->Op2));\r
576         break;\r
577       case CMD_XCHG:\r
578         {\r
579           UInt32 v1 = GetOperand(cmd->ByteMode, &cmd->Op1);\r
580           SetOperand(cmd->ByteMode, &cmd->Op1, GetOperand(cmd->ByteMode, &cmd->Op2));\r
581           SetOperand(cmd->ByteMode, &cmd->Op2, v1);\r
582         }\r
583         break;\r
584       case CMD_MUL:\r
585         {\r
586           UInt32 res = GetOperand32(&cmd->Op1) * GetOperand32(&cmd->Op2);\r
587           SetOperand32(&cmd->Op1, res);\r
588         }\r
589         break;\r
590       case CMD_MULB:\r
591         {\r
592           Byte res = GetOperand8(&cmd->Op1) * GetOperand8(&cmd->Op2);\r
593           SetOperand8(&cmd->Op1, res);\r
594         }\r
595         break;\r
596       case CMD_DIV:\r
597         {\r
598           UInt32 divider = GetOperand(cmd->ByteMode, &cmd->Op2);\r
599           if (divider != 0)\r
600           {\r
601             UInt32 res = GetOperand(cmd->ByteMode, &cmd->Op1) / divider;\r
602             SetOperand(cmd->ByteMode, &cmd->Op1, res);\r
603           }\r
604         }\r
605         break;\r
606       \r
607       #endif\r
608       \r
609       case CMD_RET:\r
610         {\r
611           if (R[kStackRegIndex] >= kSpaceSize)\r
612             return true;\r
613           UInt32 ip = GetValue32(&Mem[R[kStackRegIndex] & kSpaceMask]);\r
614           SET_IP(ip);\r
615           R[kStackRegIndex] += 4;\r
616           continue;\r
617         }\r
618       case CMD_PRINT:\r
619         break;\r
620     }\r
621     cmd++;\r
622     --maxOpCount;\r
623   }\r
624 }\r
625 \r
626 \r
627 //////////////////////////////////////////////////////\r
628 // Read program\r
629 \r
630 UInt32 ReadEncodedUInt32(CMemBitDecoder &inp)\r
631 {\r
632   switch(inp.ReadBits(2))\r
633   {\r
634     case 0:\r
635       return inp.ReadBits(4);\r
636     case 1:\r
637     {\r
638       UInt32 v = inp.ReadBits(4);\r
639       if (v == 0)\r
640         return 0xFFFFFF00 | inp.ReadBits(8);\r
641       else\r
642         return (v << 4) | inp.ReadBits(4);\r
643     }\r
644     case 2:\r
645       return inp.ReadBits(16);\r
646     default:\r
647       return inp.ReadBits(32);\r
648   }\r
649 }\r
650 \r
651 void CVm::DecodeArg(CMemBitDecoder &inp, COperand &op, bool byteMode)\r
652 {\r
653   if (inp.ReadBit())\r
654   {\r
655     op.Type = OP_TYPE_REG;\r
656     op.Data = inp.ReadBits(kNumRegBits);\r
657   }\r
658   else if (inp.ReadBit() == 0)\r
659   {\r
660     op.Type = OP_TYPE_INT;\r
661     if (byteMode)\r
662       op.Data = inp.ReadBits(8);\r
663     else\r
664       op.Data = ReadEncodedUInt32(inp);\r
665   }\r
666   else\r
667   {\r
668     op.Type = OP_TYPE_REGMEM;\r
669     if (inp.ReadBit() == 0)\r
670     {\r
671       op.Data = inp.ReadBits(kNumRegBits);\r
672       op.Base = 0;\r
673     }\r
674     else\r
675     {\r
676       if (inp.ReadBit() == 0)\r
677         op.Data = inp.ReadBits(kNumRegBits);\r
678       else\r
679         op.Data = kNumRegs;\r
680       op.Base = ReadEncodedUInt32(inp);\r
681     }\r
682   }\r
683 }\r
684 \r
685 void CVm::ReadVmProgram(const Byte *code, UInt32 codeSize, CProgram *prg)\r
686 {\r
687   CMemBitDecoder inp;\r
688   inp.Init(code, codeSize);\r
689 \r
690   prg->StaticData.Clear();\r
691   if (inp.ReadBit())\r
692   {\r
693     UInt32 dataSize = ReadEncodedUInt32(inp) + 1;\r
694     for (UInt32 i = 0; inp.Avail() && i < dataSize; i++)\r
695       prg->StaticData.Add((Byte)inp.ReadBits(8));\r
696   }\r
697   while (inp.Avail())\r
698   {\r
699     prg->Commands.Add(CCommand());\r
700     CCommand *cmd = &prg->Commands.Back();\r
701     if (inp.ReadBit() == 0)\r
702       cmd->OpCode = (ECommand)inp.ReadBits(3);\r
703     else\r
704       cmd->OpCode = (ECommand)(8 + inp.ReadBits(5));\r
705     if (kCmdFlags[cmd->OpCode] & CF_BYTEMODE)\r
706       cmd->ByteMode = (inp.ReadBit()) ? true : false;\r
707     else\r
708       cmd->ByteMode = 0;\r
709     int opNum = (kCmdFlags[cmd->OpCode] & CF_OPMASK);\r
710     if (opNum > 0)\r
711     {\r
712       DecodeArg(inp, cmd->Op1, cmd->ByteMode);\r
713       if (opNum == 2)\r
714         DecodeArg(inp, cmd->Op2, cmd->ByteMode);\r
715       else\r
716       {\r
717         if (cmd->Op1.Type == OP_TYPE_INT && (kCmdFlags[cmd->OpCode] & (CF_JUMP | CF_PROC)))\r
718         {\r
719           int Distance = cmd->Op1.Data;\r
720           if (Distance >= 256)\r
721             Distance -= 256;\r
722           else\r
723           {\r
724             if (Distance >= 136)\r
725               Distance -= 264;\r
726             else if (Distance >= 16)\r
727               Distance -= 8;\r
728             else if (Distance >= 8)\r
729               Distance -= 16;\r
730             Distance += prg->Commands.Size() - 1;\r
731           }\r
732           cmd->Op1.Data = Distance;\r
733         }\r
734       }\r
735     }\r
736     if (cmd->ByteMode)\r
737     {\r
738       switch (cmd->OpCode)\r
739       {\r
740         case CMD_MOV: cmd->OpCode = CMD_MOVB; break;\r
741         case CMD_CMP: cmd->OpCode = CMD_CMPB; break;\r
742         case CMD_ADD: cmd->OpCode = CMD_ADDB; break;\r
743         case CMD_SUB: cmd->OpCode = CMD_SUBB; break;\r
744         case CMD_INC: cmd->OpCode = CMD_INCB; break;\r
745         case CMD_DEC: cmd->OpCode = CMD_DECB; break;\r
746         case CMD_XOR: cmd->OpCode = CMD_XORB; break;\r
747         case CMD_AND: cmd->OpCode = CMD_ANDB; break;\r
748         case CMD_OR: cmd->OpCode = CMD_ORB; break;\r
749         case CMD_TEST: cmd->OpCode = CMD_TESTB; break;\r
750         case CMD_NEG: cmd->OpCode = CMD_NEGB; break;\r
751         case CMD_SHL: cmd->OpCode = CMD_SHLB; break;\r
752         case CMD_SHR: cmd->OpCode = CMD_SHRB; break;\r
753         case CMD_SAR: cmd->OpCode = CMD_SARB; break;\r
754         case CMD_MUL: cmd->OpCode = CMD_MULB; break;\r
755       }\r
756     }\r
757   }\r
758 }\r
759 \r
760 #ifdef RARVM_STANDARD_FILTERS\r
761 \r
762 enum EStandardFilter\r
763 {\r
764   SF_E8,\r
765   SF_E8E9,\r
766   SF_ITANIUM,\r
767   SF_RGB,\r
768   SF_AUDIO,\r
769   SF_DELTA,\r
770   SF_UPCASE\r
771 };\r
772 \r
773 struct StandardFilterSignature\r
774 {\r
775   UInt32 Length;\r
776   UInt32 CRC;\r
777   EStandardFilter Type;\r
778 }\r
779 kStdFilters[]=\r
780 {\r
781   {  53, 0xad576887, SF_E8 },\r
782   {  57, 0x3cd7e57e, SF_E8E9 },\r
783   { 120, 0x3769893f, SF_ITANIUM },\r
784   {  29, 0x0e06077d, SF_DELTA },\r
785   { 149, 0x1c2c5dc8, SF_RGB },\r
786   { 216, 0xbc85e701, SF_AUDIO },\r
787   {  40, 0x46b9c560, SF_UPCASE }\r
788 };\r
789 \r
790 static int FindStandardFilter(const Byte *code, UInt32 codeSize)\r
791 {\r
792   UInt32 crc = CrcCalc(code, codeSize);\r
793   for (int i = 0; i < sizeof(kStdFilters) / sizeof(kStdFilters[0]); i++)\r
794   {\r
795     StandardFilterSignature &sfs = kStdFilters[i];\r
796     if (sfs.CRC == crc && sfs.Length == codeSize)\r
797       return i;\r
798   }\r
799   return -1;\r
800 }\r
801 \r
802 #endif\r
803 \r
804 void CVm::PrepareProgram(const Byte *code, UInt32 codeSize, CProgram *prg)\r
805 {\r
806   Byte xorSum = 0;\r
807   for (UInt32 i = 1; i < codeSize; i++)\r
808     xorSum ^= code[i];\r
809 \r
810   prg->Commands.Clear();\r
811   #ifdef RARVM_STANDARD_FILTERS\r
812   prg->StandardFilterIndex = -1;\r
813   #endif\r
814 \r
815   if (xorSum == code[0] && codeSize > 0)\r
816   {\r
817     #ifdef RARVM_STANDARD_FILTERS\r
818     prg->StandardFilterIndex = FindStandardFilter(code, codeSize);\r
819     if (prg->StandardFilterIndex >= 0)\r
820       return;\r
821     #endif\r
822     // 1 byte for checksum\r
823     ReadVmProgram(code + 1, codeSize - 1, prg);\r
824   }\r
825   prg->Commands.Add(CCommand());\r
826   CCommand *cmd = &prg->Commands.Back();\r
827   cmd->OpCode = CMD_RET;\r
828 }\r
829 \r
830 void CVm::SetMemory(UInt32 pos, const Byte *data, UInt32 dataSize)\r
831 {\r
832   if (pos < kSpaceSize && data != Mem + pos)\r
833     memmove(Mem + pos, data, MyMin(dataSize, kSpaceSize - pos));\r
834 }\r
835 \r
836 #ifdef RARVM_STANDARD_FILTERS\r
837 \r
838 static void E8E9Decode(Byte *data, UInt32 dataSize, UInt32 fileOffset, bool e9)\r
839 {\r
840   if (dataSize <= 4)\r
841     return;\r
842   dataSize -= 4;\r
843   const UInt32 kFileSize = 0x1000000;\r
844   Byte cmpByte2 = (e9 ? 0xE9 : 0xE8);\r
845   for (UInt32 curPos = 0; curPos < dataSize;)\r
846   {\r
847     Byte curByte = *(data++);\r
848     curPos++;\r
849     if (curByte == 0xE8 || curByte == cmpByte2)\r
850     {\r
851       UInt32 offset = curPos + fileOffset;\r
852       UInt32 addr = (Int32)GetValue32(data);\r
853       if (addr < kFileSize)\r
854         SetValue32(data, addr - offset);\r
855       else if ((Int32)addr < 0 && (Int32)(addr + offset) >= 0)\r
856         SetValue32(data, addr + kFileSize);\r
857       data += 4;\r
858       curPos += 4;\r
859     }\r
860   }\r
861 }\r
862 \r
863 static inline UInt32 ItaniumGetOpType(const Byte *data, int bitPos)\r
864 {\r
865   return (data[(unsigned int)bitPos >> 3] >> (bitPos & 7)) & 0xF;\r
866 }\r
867 \r
868 \r
869 static void ItaniumDecode(Byte *data, UInt32 dataSize, UInt32 fileOffset)\r
870 {\r
871   UInt32 curPos = 0;\r
872   fileOffset >>= 4;\r
873   while (curPos < dataSize - 21)\r
874   {\r
875     int b = (data[0] & 0x1F) - 0x10;\r
876     if (b >= 0)\r
877     {\r
878       static Byte kCmdMasks[16] = {4,4,6,6,0,0,7,7,4,4,0,0,4,4,0,0};\r
879       Byte cmdMask = kCmdMasks[b];\r
880       if (cmdMask != 0)\r
881         for (int i = 0; i < 3; i++)\r
882           if (cmdMask & (1 << i))\r
883           {\r
884             int startPos = i * 41 + 18;\r
885             if (ItaniumGetOpType(data, startPos + 24) == 5)\r
886             {\r
887               const UInt32 kMask = 0xFFFFF;\r
888               Byte *p = data + ((unsigned int)startPos >> 3);\r
889               UInt32 bitField =  ((UInt32)p[0]) | ((UInt32)p[1] <<  8) | ((UInt32)p[2] << 16);\r
890               int inBit = (startPos & 7);\r
891               UInt32 offset = (bitField >> inBit) & kMask;\r
892               UInt32 andMask = ~(kMask << inBit);\r
893               bitField = ((offset - fileOffset) & kMask) << inBit;\r
894               for (int j = 0; j < 3; j++)\r
895               {\r
896                 p[j] &= andMask;\r
897                 p[j] |= bitField;\r
898                 andMask >>= 8;\r
899                 bitField >>= 8;\r
900               }\r
901             }\r
902           }\r
903     }\r
904     data += 16;\r
905     curPos += 16;\r
906     fileOffset++;\r
907   }\r
908 }\r
909 \r
910 static void DeltaDecode(Byte *data, UInt32 dataSize, UInt32 numChannels)\r
911 {\r
912   UInt32 srcPos = 0;\r
913   UInt32 border = dataSize * 2;\r
914   for (UInt32 curChannel = 0; curChannel < numChannels; curChannel++)\r
915   {\r
916     Byte prevByte = 0;\r
917     for (UInt32 destPos = dataSize + curChannel; destPos < border; destPos += numChannels)\r
918       data[destPos] = (prevByte = prevByte - data[srcPos++]);\r
919   }\r
920 }\r
921 \r
922 static void RgbDecode(Byte *srcData, UInt32 dataSize, UInt32 width, UInt32 posR)\r
923 {\r
924   Byte *destData = srcData + dataSize;\r
925   const UInt32 numChannels = 3;\r
926   for (UInt32 curChannel = 0; curChannel < numChannels; curChannel++)\r
927   {\r
928     Byte prevByte = 0;\r
929     \r
930     for (UInt32 i = curChannel; i < dataSize; i+= numChannels)\r
931     {\r
932       unsigned int predicted;\r
933       if (i < width)\r
934         predicted = prevByte;\r
935       else\r
936       {\r
937         unsigned int upperLeftByte = destData[i - width];\r
938         unsigned int upperByte = destData[i - width + 3];\r
939         predicted = prevByte + upperByte - upperLeftByte;\r
940         int pa = abs((int)(predicted - prevByte));\r
941         int pb = abs((int)(predicted - upperByte));\r
942         int pc = abs((int)(predicted - upperLeftByte));\r
943         if (pa <= pb && pa <= pc)\r
944           predicted = prevByte;\r
945         else\r
946           if (pb <= pc)\r
947             predicted = upperByte;\r
948           else\r
949             predicted = upperLeftByte;\r
950       }\r
951       destData[i] = prevByte = (Byte)(predicted - *(srcData++));\r
952     }\r
953   }\r
954   if (dataSize < 3)\r
955     return;\r
956   for (UInt32 i = posR, border = dataSize - 2; i < border; i += 3)\r
957   {\r
958     Byte g = destData[i + 1];\r
959     destData[i] = destData[i] + g;\r
960     destData[i + 2] = destData[i + 2] + g;\r
961   }\r
962 }\r
963 \r
964 static void AudioDecode(Byte *srcData, UInt32 dataSize, UInt32 numChannels)\r
965 {\r
966   Byte *destData = srcData + dataSize;\r
967   for (UInt32 curChannel = 0; curChannel < numChannels; curChannel++)\r
968   {\r
969     UInt32 prevByte = 0, prevDelta = 0, dif[7];\r
970     Int32 D1 = 0, D2 = 0, D3;\r
971     Int32 K1 = 0, K2 = 0, K3 = 0;\r
972     memset(dif, 0, sizeof(dif));\r
973     \r
974     for (UInt32 i = curChannel, byteCount = 0; i < dataSize; i += numChannels, byteCount++)\r
975     {\r
976       D3 = D2;\r
977       D2 = prevDelta - D1;\r
978       D1 = prevDelta;\r
979       \r
980       UInt32 predicted = 8 * prevByte + K1 * D1 + K2 * D2 + K3 * D3;\r
981       predicted = (predicted >> 3) & 0xFF;\r
982       \r
983       UInt32 curByte = *(srcData++);\r
984       \r
985       predicted -= curByte;\r
986       destData[i] = (Byte)predicted;\r
987       prevDelta = (UInt32)(Int32)(signed char)(predicted - prevByte);\r
988       prevByte = predicted;\r
989       \r
990       Int32 D = ((Int32)(signed char)curByte) << 3;\r
991       \r
992       dif[0] += abs(D);\r
993       dif[1] += abs(D - D1);\r
994       dif[2] += abs(D + D1);\r
995       dif[3] += abs(D - D2);\r
996       dif[4] += abs(D + D2);\r
997       dif[5] += abs(D - D3);\r
998       dif[6] += abs(D + D3);\r
999       \r
1000       if ((byteCount & 0x1F) == 0)\r
1001       {\r
1002         UInt32 minDif = dif[0], numMinDif = 0;\r
1003         dif[0] = 0;\r
1004         for (int j = 1; j < sizeof(dif) / sizeof(dif[0]); j++)\r
1005         {\r
1006           if (dif[j] < minDif)\r
1007           {\r
1008             minDif = dif[j];\r
1009             numMinDif = j;\r
1010           }\r
1011           dif[j] = 0;\r
1012         }\r
1013         switch (numMinDif)\r
1014         {\r
1015           case 1: if (K1 >= -16) K1--; break;\r
1016           case 2: if (K1 <   16) K1++; break;\r
1017           case 3: if (K2 >= -16) K2--; break;\r
1018           case 4: if (K2 <   16) K2++; break;\r
1019           case 5: if (K3 >= -16) K3--; break;\r
1020           case 6: if (K3 <   16) K3++; break;\r
1021         }\r
1022       }\r
1023     }\r
1024   }\r
1025 }\r
1026 \r
1027 static UInt32 UpCaseDecode(Byte *data, UInt32 dataSize)\r
1028 {\r
1029   UInt32 srcPos = 0, destPos = dataSize;\r
1030   while (srcPos < dataSize)\r
1031   {\r
1032     Byte curByte = data[srcPos++];\r
1033     if (curByte == 2 && (curByte = data[srcPos++]) != 2)\r
1034       curByte -= 32;\r
1035     data[destPos++] = curByte;\r
1036   }\r
1037   return destPos - dataSize;\r
1038 }\r
1039 \r
1040 void CVm::ExecuteStandardFilter(int filterIndex)\r
1041 {\r
1042   UInt32 dataSize = R[4];\r
1043   if (dataSize >= kGlobalOffset)\r
1044     return;\r
1045   EStandardFilter filterType = kStdFilters[filterIndex].Type;\r
1046 \r
1047   switch (filterType)\r
1048   {\r
1049     case SF_E8:\r
1050     case SF_E8E9:\r
1051       E8E9Decode(Mem, dataSize, R[6], (filterType == SF_E8E9));\r
1052       break;\r
1053     case SF_ITANIUM:\r
1054       ItaniumDecode(Mem, dataSize, R[6]);\r
1055       break;\r
1056     case SF_DELTA:\r
1057       if (dataSize >= kGlobalOffset / 2)\r
1058         break;\r
1059       SetBlockPos(dataSize);\r
1060       DeltaDecode(Mem, dataSize, R[0]);\r
1061       break;\r
1062     case SF_RGB:\r
1063       if (dataSize >= kGlobalOffset / 2)\r
1064         break;\r
1065       {\r
1066         UInt32 width = R[0];\r
1067         if (width <= 3)\r
1068           break;\r
1069         SetBlockPos(dataSize);\r
1070         RgbDecode(Mem, dataSize, width, R[1]);\r
1071       }\r
1072       break;\r
1073     case SF_AUDIO:\r
1074       if (dataSize >= kGlobalOffset / 2)\r
1075         break;\r
1076       SetBlockPos(dataSize);\r
1077       AudioDecode(Mem, dataSize, R[0]);\r
1078       break;\r
1079     case SF_UPCASE:\r
1080       if (dataSize >= kGlobalOffset / 2)\r
1081         break;\r
1082       UInt32 destSize = UpCaseDecode(Mem, dataSize);\r
1083       SetBlockSize(destSize);\r
1084       SetBlockPos(dataSize);\r
1085       break;\r
1086   }\r
1087 }\r
1088 \r
1089 #endif\r
1090 \r
1091 }}}\r