2 Softx86 - Stock Software Intel 80x86 emulation library
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:
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
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.
25 +-----------------------------------------------+
26 | SOFTX86 LIBRARY IN A TYPICAL EMULATOR PROJECT |
27 +-----------------------------------------------+
30 +-----------------------------------+
31 | APPLICATION AS THE "MOTHERBOARD", |
32 | "POWER SUPPLY", "RAM", AND "I/O |
33 | DEVICES". | +------------------------------+
34 | | | SOFTX86 LIBRARY AS THE "CPU" |
36 | | | | CPU INSTRUCTION EXECUTIONEER |
37 | +-----------------------------------------------------------^ | |
39 | ADDRESSABLE RAM EMULATION<=====================(callback)==============| |
41 | I/O PORT EMULATION<============================(callback)==============/ |
43 | INTERRUPT SIGNAL SOURCE | | |
45 | #INT LINE MODERATOR --------------------->| #NMI handler |
47 | ---------------------------------------->| #INT handler |
49 +-----------------------------------+ +------------------------------+
51 Softx86 is like a lone CPU, it needs a "motherboard" to interact with.
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).
64 STEP 1: CREATING A CPU
65 ======================
67 To create a CPU, you first need to allocate a region
68 of memory of size sizeof(softx86_ctx).
72 cpu = (softx86_ctx*)(malloc(sizeof(softx86_ctx)));
74 ----- C++ example -----
76 cpu = new softx86_ctx;
78 Using memset() to fill the contents of that area with NULL
79 is advised but not absolutely necessary.
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
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);
91 printf("ERROR: Unable to initialize CPU\n");
95 STEP 2: SET UP CALLBACKS
96 ========================
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.
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!
107 ----- C/C++ example -----
108 /* can hold the entire 1MB range supported by the 8086 */
109 unsigned char BIG_HUNK[1024*1024];
111 /* called when reading memory */
112 void on_read_memory(void* _ctx,sx86_udword address,sx86_ubyte *buf,int size)
114 memcpy(buf,BIG_HUNK+address,size);
117 /* called when writing memory */
118 void on_write_memory(void* _ctx,sx86_udword address,sx86_ubyte *buf,int size)
120 memcpy(BIG_HUNK+address,buf,size);
123 /* called when reading an I/O port */
124 void on_read_io(void* _ctx,sx86_udword address,sx86_ubyte *buf,int size)
126 /* in this example there are no I/O ports */
127 memset(buf,0xFF,size);
130 /* called when writing an I/O port */
131 void on_write_io(void* _ctx,sx86_udword address,sx86_ubyte *buf,int size)
133 /* in this example there are no I/O ports */
136 /* function to set up the callbacks for given cpu */
137 void setup_callbacks(softx86_ctx *cpu)
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;
145 Once everything is set up, you must call softx86_reset()
148 ----- C/C++ example -----
151 STEP 3: GIVE THE CPU SOMETHING TO EXECUTE
152 =========================================
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
165 Loading a COM executable into your program's simulated RAM should be
168 ----- C/C++ example -----
173 comfile = fopen("whatever.com","rb");
175 printf("Unable to load program!\n");
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);
186 /* set up a PSP and such... */
187 /* I'll leave it up to you as an exercise :) */
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
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
203 ----- C/C++ example -----
204 softx86_set_instruction_ptr(cpu,0x1000,0x100);
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
210 NOTE: The stack pointer must NOT be modified directly, although
211 the values can be readily obtained through the context
214 ----- C/C++ example -----
215 softx86_set_stack_ptr(cpu,0x1000,0xFFF8);
217 Many DOS programs assume that DOS will set the segment registers
218 so that CS = DS = ES. Set the segment registers appropriately:
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!
226 ----- C/C++ example -----
227 softx86_setsegval(cpu,SX86_SREG_DS,0x1000); /* set DS */
228 softx86_setsegval(cpu,SX86_SREG_ES,0x1000); /* set ES */
230 STEP 4: CYCLING THE CPU
231 =======================
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.
239 ----- C/C++ example -----
242 printf("The CPU failed to execute or recognize an instruction\n");
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.
254 * FOR MORE DETAILS REFER TO THE SOFTX86DBG SOURCE CODE, OR
255 E-MAIL ME AT jcampbell@mdjk.com.
258 FREEING/DISPOSING OF A CPU
259 ==========================
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.
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!
272 ----- C example -----
276 ----- C/C++ example ----
281 HOW TO SIGNAL AN EXTERNAL HARDWARE INTERRUPT
282 ============================================
284 Use API call softx86_ext_hw_signal() to emulate an #INT
285 signal from hardware.
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.
303 ----- C/C++ example, signalling INT 9 (PC/XT/AT+ IRQ 1) from hardware -----
306 /* loop until we can signal the interrupt we want */
307 /* ---or, until CPU failure */
312 if (i) j=softx86_ext_hw_signal(cpu,9);
315 HOW TO SIGNAL AN EXTERNAL NMI (NON-MASKABLE) HARDWARE INTERRUPT
316 ===============================================================
318 Use API call softx86_ext_hw_nmi_signal() to emulate an
319 #NMI signal from hardware.
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).
327 NOTE: This signal is processed immediately, even if
328 #INT is active. #NMI has higher priority than
331 ----- C/C++ example, signalling NMI from hardware -----
334 /* loop until we can signal the interrupt we want */
335 /* ---or, until CPU failure */
340 if (i) j=softx86_ext_hw_nmi_signal(cpu);
343 HOW TO FORCE SOFTWARE INTERRUPT
344 ===============================
346 calling softx86_int_sw_signal() causes the CPU to branch to
347 an interrupt as if the INT instruction had just been executed
350 NOTE: If the CPU is in protected mode, this function may fail
351 if the interrupt is invalid according to the IDT or
354 ----- C/C++ example, software interrupt INT 21h -----
355 softx86_int_sw_signal(cpu,0x21);
357 HOW TO USE THE DECOMPILER
358 =========================
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".
365 First, you set the decompiler IP using
366 softx86_set_instruction_dec_ptr() or
367 softx86_decompile_exec_cs_ip().
369 NOTE: The decompiler instruction pointer, like the
370 CPU instruction pointer, MUST NOT BE MODIFIED
373 ----- C/C++ example, setting decompile CS:IP to specific values -----
375 softx86_set_instruction_dec_ptr(cpu,seg,ofs);
377 ----- C/C++ example, setting decompiler CS:IP == current instruction pointer -----
378 softx86_decompile_exec_cs_ip(cpu);
380 Then, you call softx86_decompile() for each instruction, providing
381 for it a char array that is 256 bytes wide or larger.
383 ----- C/C++ example, decompiling 10 instructions following CS:IP -----
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 */
394 printf("%s\n",asmbuf);
398 HOW TO DETERMINE THE SOFTX86 VERSION
399 ====================================
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:
405 int x,major,minor,subminor;
407 x=softx86_getversion(&major,&minor,&subminor);
409 printf("Unable to obtain version!\n");
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.
418 if (major != SOFTX86_VERSION_HI ||
419 minor != SOFTX86_VERSION_LO ||
420 subminor != SOFTX86_VERSION_SUBLO) {
421 printf("ERROR: Version mismatch!\n");
425 HOW TO CHANGE THE GENERAL REGISTERS
426 ===================================
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).
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
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.
441 NOTE: You must not modify the SP register directly!
442 Use softx86_set_stack_ptr()
444 Thus, you can read/modify AX like this:
446 cpu->state.general_reg[SX86_REG_AX].w.lo
450 cpu->state.general_reg[SX86_REG_AX].val
454 cpu->state.general_reg[SX86_REG_AX].b.hi
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
467 HOW TO CHANGE THE INSTRUCTION POINTER
468 =====================================
470 The instruction pointer lies in cpu->state.reg_ip (IP)
471 and cpu->state.segment_reg[SX86_SREG_CS].val (CS).
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
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.
487 To change both CS and IP to known values, call
488 softx86_set_instruction_ptr() with the new CS:IP values.
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.
494 ----- C/C++ example -----
495 x=softx86_set_instruction_ptr(cpu,0xF000,0xFFF0);
497 printf("Failed to set instruction pointer!\n");
501 If you only wish to change IP without changing CS,
502 you may call softx86_set_near_instruction_ptr().
504 ----- C/C++ example -----
505 x=softx86_set_near_instruction_ptr(cpu,0x100);
507 printf("Failed to set instruction pointer!\n");
511 HOW TO CHANGE THE SEGMENT REGISTERS
512 ===================================
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.
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 */
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.
530 NOTE: Do NOT modify these values directly. Use
531 softx86_setsegval() instead!
533 To retrieve the current value in CS for example:
535 segment = cpu->state.segment_reg[SX86_SREG_CS].val;
537 To set the value of CS:
539 x=softx86_setsegval(cpu,SX86_SREG_CS,0xF000);
541 printf("Unable to set CS!\n");
545 NOTE: softx86_setsegval() will return 0 (error)
546 if the CS value is invalid.