[NTVDM]
[reactos.git] / lib / 3rdparty / softx86 / doc / softx86.txt
1
2 Softx86 - Stock Software Intel 80x86 emulation library
3
4 Introduction
5 ============
6
7 Softx86 is designed to be a simple library that one can
8 link into their program for 80x86 emulation purposes.
9 The primary goals of this project are:
10
11 * accurate emulation
12 * reasonably sized memory footprint
13 * modular design that closely models the actual
14 hardware signals (Clocking, I/O, addressable
15 memory, #INT and #NMI) using callbacks
16 set up by the program
17
18 The Softx86 library itself is designed to emulate ONLY
19 the CPU, none of the surrounding hardware. Just like
20 the real thing the program using Softx86 is responsible
21 for providing RAM, I/O, and #INT/#NMI signals as
22 appropriate, and making the necessary calls to cycle the CPU.
23
24
25 +-----------------------------------------------+
26 | SOFTX86 LIBRARY IN A TYPICAL EMULATOR PROJECT |
27 +-----------------------------------------------+
28
29
30 +-----------------------------------+
31 | APPLICATION AS THE "MOTHERBOARD", |
32 | "POWER SUPPLY", "RAM", AND "I/O |
33 | DEVICES". | +------------------------------+
34 | | | SOFTX86 LIBRARY AS THE "CPU" |
35 | MAIN LOOP | | |
36 | | | | CPU INSTRUCTION EXECUTIONEER |
37 | +-----------------------------------------------------------^ | |
38 | | | | |
39 | ADDRESSABLE RAM EMULATION<=====================(callback)==============| |
40 | | | | |
41 | I/O PORT EMULATION<============================(callback)==============/ |
42 | | | |
43 | INTERRUPT SIGNAL SOURCE | | |
44 | \/ \/ | | |
45 | #INT LINE MODERATOR --------------------->| #NMI handler |
46 | \/ | | |
47 | ---------------------------------------->| #INT handler |
48 | | | |
49 +-----------------------------------+ +------------------------------+
50
51 Softx86 is like a lone CPU, it needs a "motherboard" to interact with.
52
53
54 The Softx86 API uses context structures to represent
55 a CPU, rather than internal variables, so that
56 it is possible to emulate more than one CPU
57 simultaneously using the same library. This also
58 puts the CPU's entire state in one location for both
59 Softx86 and the application using it, so that use
60 of numerous API calls are generally not needed to get
61 or set the CPU state (although some members should
62 not be modified directly).
63
64 STEP 1: CREATING A CPU
65 ======================
66
67 To create a CPU, you first need to allocate a region
68 of memory of size sizeof(softx86_ctx).
69
70 ----- C example -----
71 softx86_ctx* cpu;
72 cpu = (softx86_ctx*)(malloc(sizeof(softx86_ctx)));
73
74 ----- C++ example -----
75 softx86_ctx* cpu;
76 cpu = new softx86_ctx;
77
78 Using memset() to fill the contents of that area with NULL
79 is advised but not absolutely necessary.
80
81 Then call API function softx86_init(), passing it the address
82 of the context structure and a constant from softx86.h
83 that represents which version of the 80x86 the context
84 structure represents.
85
86 ------ C/C++ example -----
87 /* I want this CPU to act like a 80286. */
88 /* Act appropriately if there is an error. */
89 ser=softx86_init(cpu,SX86_CPULEVEL_80286);
90 if (ser == 0) {
91 printf("ERROR: Unable to initialize CPU\n");
92 return 0;
93 }
94
95 STEP 2: SET UP CALLBACKS
96 ========================
97
98 You've made the CPU but now it needs to know who to call
99 for fetching RAM and I/O. Set that up now.
100
101 WARNING: This is grossly oversimplified, not including code
102 to properly handle requests that fall outside the
103 1MB range! YES IT CAN HAPPEN, ESPECIALLY IF EMULATING
104 THE 286 WHICH CAN ADDRESS 16MB! See Softx86dbg source
105 code for a better example!
106
107 ----- C/C++ example -----
108 /* can hold the entire 1MB range supported by the 8086 */
109 unsigned char BIG_HUNK[1024*1024];
110
111 /* called when reading memory */
112 void on_read_memory(void* _ctx,sx86_udword address,sx86_ubyte *buf,int size)
113 {
114 memcpy(buf,BIG_HUNK+address,size);
115 }
116
117 /* called when writing memory */
118 void on_write_memory(void* _ctx,sx86_udword address,sx86_ubyte *buf,int size)
119 {
120 memcpy(BIG_HUNK+address,buf,size);
121 }
122
123 /* called when reading an I/O port */
124 void on_read_io(void* _ctx,sx86_udword address,sx86_ubyte *buf,int size)
125 {
126 /* in this example there are no I/O ports */
127 memset(buf,0xFF,size);
128 }
129
130 /* called when writing an I/O port */
131 void on_write_io(void* _ctx,sx86_udword address,sx86_ubyte *buf,int size)
132 {
133 /* in this example there are no I/O ports */
134 }
135
136 /* function to set up the callbacks for given cpu */
137 void setup_callbacks(softx86_ctx *cpu)
138 {
139 cpu.callbacks->on_read_memory = on_read_memory;
140 cpu.callbacks->on_write_memory = on_write_memory;
141 cpu.callbacks->on_read_io = on_read_io;
142 cpu.callbacks->on_write_io = on_write_io;
143 }
144
145 Once everything is set up, you must call softx86_reset()
146 to reset the CPU.
147
148 ----- C/C++ example -----
149 softx86_reset(cpu);
150
151 STEP 3: GIVE THE CPU SOMETHING TO EXECUTE
152 =========================================
153
154 For the CPU to execute something it must have valid instructions.
155 There are many sources that these can be obtained, starting with
156 simple MS-DOS .COM executables (which can be found on any web site
157 dedicated to ancient DOS programs and games), or perhaps whatever
158 happens to be on the hard drive of that old dusty 286 in the corner,
159 although you can also find some on many Windows 95/98-based systems.
160 By .COM executables I DO NOT MEAN COM/OLE ActiveX controls!
161 Anyway, best results can be obtained by using a COM program that is
162 simple (doesn't rely too much on direct hardware access or performs
163 a simple function).
164
165 Loading a COM executable into your program's simulated RAM should be
166 easy:
167
168 ----- C/C++ example -----
169 void load_code()
170 {
171 FILE *comfile;
172
173 comfile = fopen("whatever.com","rb");
174 if (!comfile) {
175 printf("Unable to load program!\n");
176 return 0;
177 }
178
179 /* remember BIG_HUNK[] ? */
180 /* 0x100 offset because of PSP segment and such */
181 /* CS:IP = 0x1000:0x0100 */
182 /* (0x1000 << 4) + 0x100 = 0x10100 */
183 fread(BIG_HUNK+0x10100,65280,1,comfile);
184 fclose(comfile);
185
186 /* set up a PSP and such... */
187 /* I'll leave it up to you as an exercise :) */
188
189 /* done */
190 return 1;
191 }
192
193 Now that there is code to execute, we need to tell the CPU where
194 to start executing. To do this, we must redirect the INSTRUCTION
195 POINTER.
196
197 NOTE: The instruction pointer variables can be accessed directly
198 through the context structure to obtain the values and
199 the cached information (such as precalculated segment->linear
200 conversion). HOWEVER you must not modify these values
201 directly!
202
203 ----- C/C++ example -----
204 softx86_set_instruction_ptr(cpu,0x1000,0x100);
205
206 Depending on the program, it is most likely that you will want
207 to set up the stack pointer as well so that the program has a
208 usable stack.
209
210 NOTE: The stack pointer must NOT be modified directly, although
211 the values can be readily obtained through the context
212 structure!
213
214 ----- C/C++ example -----
215 softx86_set_stack_ptr(cpu,0x1000,0xFFF8);
216
217 Many DOS programs assume that DOS will set the segment registers
218 so that CS = DS = ES. Set the segment registers appropriately:
219
220 NOTE: The segment registers carry with them a cached copy of
221 various state information (limits, precalculated
222 segment->linear conversion, protected mode bits, etc).
223 They may be directly accessed through the context
224 structure but must not be modified directly!
225
226 ----- C/C++ example -----
227 softx86_setsegval(cpu,SX86_SREG_DS,0x1000); /* set DS */
228 softx86_setsegval(cpu,SX86_SREG_ES,0x1000); /* set ES */
229
230 STEP 4: CYCLING THE CPU
231 =======================
232
233 Execution doesn't happen unless your program explicitly wants
234 it to happen, which it does by calling softx86_step(). For
235 each call to softx86_step(), one instruction is executed
236 and then control is returned to your program. The return
237 value indicates success or failure.
238
239 ----- C/C++ example -----
240 x=softx86_step(cpu);
241 if (x == 0) {
242 printf("The CPU failed to execute or recognize an instruction\n");
243 }
244
245 During the call, softx86_step() is likely to call your
246 memory and I/O callback routines (in fact it's guaranteed).
247 When softx86_step() returns the instruction pointer values
248 and all registers affected will be updated according to
249 whatever instruction was executed. On return, the instruction
250 pointer points to the next instruction that will be executed,
251 or points to the offending instruction if an error occured.
252
253
254 * FOR MORE DETAILS REFER TO THE SOFTX86DBG SOURCE CODE, OR
255 E-MAIL ME AT jcampbell@mdjk.com.
256
257
258 FREEING/DISPOSING OF A CPU
259 ==========================
260
261 Disposing of a CPU is simple. Call softx86_free()
262 and then free the memory you allocated for that
263 structure. That's it.
264
265 NOTE: It is very important to call softx86_free()
266 because Softx86 itself allocates memory
267 for CPU emulation purposes associated with
268 that CPU. Freeing the structure and not
269 calling softx86_free() may (very likely!)
270 result in memory leaks!
271
272 ----- C example -----
273 softx86_free(cpu);
274 free(cpu);
275
276 ----- C/C++ example ----
277 softx86_free(cpu);
278 delete cpu;
279
280
281 HOW TO SIGNAL AN EXTERNAL HARDWARE INTERRUPT
282 ============================================
283
284 Use API call softx86_ext_hw_signal() to emulate an #INT
285 signal from hardware.
286
287 NOTE: Do not assume that this function will succeed.
288 If softx86_ext_hw_signal() has already been called
289 but the CPU has not yet acknowledged the interrupt,
290 softx86_ext_hw_signal() will return 0 (error).
291 Otherwise, it will return 1 (success). If you plan
292 on emulating a platform with multiple interrupt
293 signals possible, consider writing a wrapper for
294 this call in your program that moderates the
295 signals somehow. For example, write a portion of
296 your program that emulates the Programmable
297 Interrupt Controller in IBM PC/XT/AT+ hardware, then
298 write a general function your code can call that
299 emulates IRQ signals being sent to the PIC. The PIC
300 emulator can then act as a moderator that uses this
301 function to pass the IRQ signals to the CPU.
302
303 ----- C/C++ example, signalling INT 9 (PC/XT/AT+ IRQ 1) from hardware -----
304 int i,j;
305
306 /* loop until we can signal the interrupt we want */
307 /* ---or, until CPU failure */
308 i=1;
309 j=0;
310 while (i && !j) {
311 i=softx86_step(cpu);
312 if (i) j=softx86_ext_hw_signal(cpu,9);
313 }
314
315 HOW TO SIGNAL AN EXTERNAL NMI (NON-MASKABLE) HARDWARE INTERRUPT
316 ===============================================================
317
318 Use API call softx86_ext_hw_nmi_signal() to emulate an
319 #NMI signal from hardware.
320
321 NOTE: Do not assume that this function will succeed.
322 If #NMI has already been activated but the CPU
323 has not yet acknowledged the interrupt, this
324 function will return 0 (error). Otherwise, it
325 will return 1 (success).
326
327 NOTE: This signal is processed immediately, even if
328 #INT is active. #NMI has higher priority than
329 #INT.
330
331 ----- C/C++ example, signalling NMI from hardware -----
332 int i,j;
333
334 /* loop until we can signal the interrupt we want */
335 /* ---or, until CPU failure */
336 i=1;
337 j=0;
338 while (i && !j) {
339 i=softx86_step(cpu);
340 if (i) j=softx86_ext_hw_nmi_signal(cpu);
341 }
342
343 HOW TO FORCE SOFTWARE INTERRUPT
344 ===============================
345
346 calling softx86_int_sw_signal() causes the CPU to branch to
347 an interrupt as if the INT instruction had just been executed
348 in the program.
349
350 NOTE: If the CPU is in protected mode, this function may fail
351 if the interrupt is invalid according to the IDT or
352 GDT/LDT!
353
354 ----- C/C++ example, software interrupt INT 21h -----
355 softx86_int_sw_signal(cpu,0x21);
356
357 HOW TO USE THE DECOMPILER
358 =========================
359
360 In addition to providing the ability to execute instructions,
361 Softx86 provides an alternate API call to decompile instructions
362 at an alternate instruction pointer referred to as the
363 "decompiler instruction pointer".
364
365 First, you set the decompiler IP using
366 softx86_set_instruction_dec_ptr() or
367 softx86_decompile_exec_cs_ip().
368
369 NOTE: The decompiler instruction pointer, like the
370 CPU instruction pointer, MUST NOT BE MODIFIED
371 DIRECTLY!
372
373 ----- C/C++ example, setting decompile CS:IP to specific values -----
374 sx86_udword seg,ofs;
375 softx86_set_instruction_dec_ptr(cpu,seg,ofs);
376
377 ----- C/C++ example, setting decompiler CS:IP == current instruction pointer -----
378 softx86_decompile_exec_cs_ip(cpu);
379
380 Then, you call softx86_decompile() for each instruction, providing
381 for it a char array that is 256 bytes wide or larger.
382
383 ----- C/C++ example, decompiling 10 instructions following CS:IP -----
384 char asmbuf[256];
385 int j;
386
387 softx86_decompile_exec_cs_ip(cpu);
388 for (j=0;j < 10;j++) {
389 if (!softx86_decompile(cpu,asmbuf)) {
390 printf("*decompiler error!\n");
391 j=10; /* terminate the loop */
392 }
393 else {
394 printf("%s\n",asmbuf);
395 }
396 }
397
398 HOW TO DETERMINE THE SOFTX86 VERSION
399 ====================================
400
401 Obtaining the version of the Softx86 library your
402 program is using is simple. Have 3 integers ready
403 and call softx86_getversion() like this:
404
405 int x,major,minor,subminor;
406
407 x=softx86_getversion(&major,&minor,&subminor);
408 if (!x) {
409 printf("Unable to obtain version!\n");
410 return 1;
411 }
412
413 You can then determine if you're using the version
414 you were compiled for by matching them against the
415 constants (defined in softx86.h) SOFTX86_VERSION_HI,
416 SOFTX86_VERSION_LO, and SOFTX86_VERSION_SUBLO.
417
418 if (major != SOFTX86_VERSION_HI ||
419 minor != SOFTX86_VERSION_LO ||
420 subminor != SOFTX86_VERSION_SUBLO) {
421 printf("ERROR: Version mismatch!\n");
422 return 1;
423 }
424
425 HOW TO CHANGE THE GENERAL REGISTERS
426 ===================================
427
428 All of the general registers (AX,BX,CX,DX,SI,DI,BP,SP)
429 are of type softx86_regval, which is a union that allows
430 either partial or complete access of the contents
431 (no matter what the native byte order is).
432
433 The general registers reside in
434 cpu->state.general_reg[], an array of softx86_regval
435 values which may be indexed using the constants defined in
436 softx86.h:
437
438 SX86_REG_AX,SX86_REG_BX,SX86_REG_CX,SX86_REG_DX,
439 SX86_REG_SI,SX86_REG_DI,SX86_REG_BP,SX86_REG_SP.
440
441 NOTE: You must not modify the SP register directly!
442 Use softx86_set_stack_ptr()
443
444 Thus, you can read/modify AX like this:
445
446 cpu->state.general_reg[SX86_REG_AX].w.lo
447
448 or EAX like this:
449
450 cpu->state.general_reg[SX86_REG_AX].val
451
452 or AH like this:
453
454 cpu->state.general_reg[SX86_REG_AX].b.hi
455
456 *------------------------------------*
457 | softx86_regval union naming scheme |
458 *------------------------------------*
459 general_reg[idx].val = the entire 32 bit register (e.g. EAX)
460 general_reg[idx].w.lo = the lower 16 bit portion (e.g. AX bits 0-15)
461 general_reg[idx].w.hi = the upper 16 bit portion (bits 16-31)
462 general_reg[idx].b.lo = the lower 8 bit portion (e.g. AL bits 0-7)
463 general_reg[idx].b.hi = the upper 8 bit portion (e.g. AH bits 8-15)
464 general_reg[idx].b.extra[0] = bits 16-23 as a byte
465 general_reg[idx].b.extra[1] = bits 24-31 as a byte
466
467 HOW TO CHANGE THE INSTRUCTION POINTER
468 =====================================
469
470 The instruction pointer lies in cpu->state.reg_ip (IP)
471 and cpu->state.segment_reg[SX86_SREG_CS].val (CS).
472
473 NOTE: You should NOT change this value directly! Use
474 softx86_set_instruction_ptr() or
475 softx86_set_near_instruction_ptr() instead!
476 Changing reg_ip directly may cause problems
477 with instruction prefetch emulation or
478 any portion of the code that caches the
479 instruction pointer.
480
481 cpu->state.reg_ip is a DWORD value that represents the
482 current offset in the code segment.
483 cpu->state.segment_reg[SX86_SREG_CS].val represents the
484 actual value of the CS segment register, along with the
485 precalculated linear and limit values for the segment/selector.
486
487 To change both CS and IP to known values, call
488 softx86_set_instruction_ptr() with the new CS:IP values.
489
490 NOTE: softx86_set_instruction_ptr() and
491 softx86_set_near_instruction_ptr() will return
492 0 (error) if the CS:IP values are invalid.
493
494 ----- C/C++ example -----
495 x=softx86_set_instruction_ptr(cpu,0xF000,0xFFF0);
496 if (!x) {
497 printf("Failed to set instruction pointer!\n");
498 return 1;
499 }
500
501 If you only wish to change IP without changing CS,
502 you may call softx86_set_near_instruction_ptr().
503
504 ----- C/C++ example -----
505 x=softx86_set_near_instruction_ptr(cpu,0x100);
506 if (!x) {
507 printf("Failed to set instruction pointer!\n");
508 return 1;
509 }
510
511 HOW TO CHANGE THE SEGMENT REGISTERS
512 ===================================
513
514 The segment registers are stored in the context structure
515 under cpu->state.segment_reg[] and are of type
516 softx86_segregval. softx86_segregval is a special structure
517 that holds the value of the register as well as hidden
518 cached values related to the segment register.
519
520 typedef struct {
521 sx86_uword val; /* visible to program---the value */
522 sx86_udword cached_linear; /* hidden---linear address of segment */
523 sx86_udword cached_limit; /* hidden---limit of segment */
524 } softx86_segregval;
525
526 A particular segment value may be obtained by using
527 constants SX86_SREG_ES, SX86_SREG_CS, SX86_SREG_DS,
528 SX86_SREG_SS as indices to the array.
529
530 NOTE: Do NOT modify these values directly. Use
531 softx86_setsegval() instead!
532
533 To retrieve the current value in CS for example:
534
535 segment = cpu->state.segment_reg[SX86_SREG_CS].val;
536
537 To set the value of CS:
538
539 x=softx86_setsegval(cpu,SX86_SREG_CS,0xF000);
540 if (!x) {
541 printf("Unable to set CS!\n");
542 return 1;
543 }
544
545 NOTE: softx86_setsegval() will return 0 (error)
546 if the CS value is invalid.