Subversion Repositories Kolibri OS

Rev

Rev 8523 | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. // 8086tiny: a tiny, highly functional, highly portable PC emulator/VM
  2. // Copyright 2013-14, Adrian Cable (adrian.cable@gmail.com) - http://www.megalith.co.uk/8086tiny
  3. //
  4. // Revision 1.25
  5. //
  6. // This work is licensed under the MIT License. See included LICENSE.TXT.
  7.  
  8. #include <time.h>
  9. //#include <sys/timeb.h>
  10.  
  11. struct timeb
  12. {
  13.     time_t              time;           /* Seconds since the epoch      */
  14.     unsigned short      millitm;
  15.     short               timezone;
  16.     short               dstflag;
  17. };
  18.  
  19. static int ftime(struct timeb* tp)
  20. {
  21.         unsigned counter = 0;
  22.         __asm__ volatile("int $0x40" : "=a"(counter) : "a"(26), "b"(9));
  23.         tp->millitm = (counter % 100) * 10;
  24.         return 0;
  25. }
  26.  
  27. /*
  28. #include <memory.h>
  29. */
  30. #ifndef _WIN32
  31. #include <unistd.h>
  32. #include <fcntl.h>
  33. #endif
  34.  
  35. #ifndef NO_GRAPHICS
  36. #include <SDL.h>
  37. #endif
  38.  
  39. // Emulator system constants
  40. #define IO_PORT_COUNT 0x10000
  41. #define RAM_SIZE 0x10FFF0
  42. #define REGS_BASE 0xF0000
  43. #define VIDEO_RAM_SIZE 0x10000
  44.  
  45. // Graphics/timer/keyboard update delays (explained later)
  46. #ifndef GRAPHICS_UPDATE_DELAY
  47. #define GRAPHICS_UPDATE_DELAY 36000 //1000 times
  48. #endif
  49. #define KEYBOARD_TIMER_UPDATE_DELAY 2000 //1000 times
  50.  
  51. // 16-bit register decodes
  52. #define REG_AX 0
  53. #define REG_CX 1
  54. #define REG_DX 2
  55. #define REG_BX 3
  56. #define REG_SP 4
  57. #define REG_BP 5
  58. #define REG_SI 6
  59. #define REG_DI 7
  60.  
  61. #define REG_ES 8
  62. #define REG_CS 9
  63. #define REG_SS 10
  64. #define REG_DS 11
  65.  
  66. #define REG_ZERO 12
  67. #define REG_SCRATCH 13
  68.  
  69. // 8-bit register decodes
  70. #define REG_AL 0
  71. #define REG_AH 1
  72. #define REG_CL 2
  73. #define REG_CH 3
  74. #define REG_DL 4
  75. #define REG_DH 5
  76. #define REG_BL 6
  77. #define REG_BH 7
  78.  
  79. // FLAGS register decodes
  80. #define FLAG_CF 40
  81. #define FLAG_PF 41
  82. #define FLAG_AF 42
  83. #define FLAG_ZF 43
  84. #define FLAG_SF 44
  85. #define FLAG_TF 45
  86. #define FLAG_IF 46
  87. #define FLAG_DF 47
  88. #define FLAG_OF 48
  89.  
  90. // Lookup tables in the BIOS binary
  91. #define TABLE_XLAT_OPCODE 8
  92. #define TABLE_XLAT_SUBFUNCTION 9
  93. #define TABLE_STD_FLAGS 10
  94. #define TABLE_PARITY_FLAG 11
  95. #define TABLE_BASE_INST_SIZE 12
  96. #define TABLE_I_W_SIZE 13
  97. #define TABLE_I_MOD_SIZE 14
  98. #define TABLE_COND_JUMP_DECODE_A 15
  99. #define TABLE_COND_JUMP_DECODE_B 16
  100. #define TABLE_COND_JUMP_DECODE_C 17
  101. #define TABLE_COND_JUMP_DECODE_D 18
  102. #define TABLE_FLAGS_BITFIELDS 19
  103.  
  104. // Bitfields for TABLE_STD_FLAGS values
  105. #define FLAGS_UPDATE_SZP 1
  106. #define FLAGS_UPDATE_AO_ARITH 2
  107. #define FLAGS_UPDATE_OC_LOGIC 4
  108.  
  109. // Helper macros
  110.  
  111. // Decode mod, r_m and reg fields in instruction
  112. #define DECODE_RM_REG scratch2_uint = 4 * !i_mod, \
  113.                                           op_to_addr = rm_addr = i_mod < 3 ? SEGREG(seg_override_en ? seg_override : bios_table_lookup[scratch2_uint + 3][i_rm], bios_table_lookup[scratch2_uint][i_rm], regs16[bios_table_lookup[scratch2_uint + 1][i_rm]] + bios_table_lookup[scratch2_uint + 2][i_rm] * i_data1+) : GET_REG_ADDR(i_rm), \
  114.                                           op_from_addr = GET_REG_ADDR(i_reg), \
  115.                                           i_d && (scratch_uint = op_from_addr, op_from_addr = rm_addr, op_to_addr = scratch_uint)
  116.  
  117. // Return memory-mapped register location (offset into mem array) for register #reg_id
  118. #define GET_REG_ADDR(reg_id) (REGS_BASE + (i_w ? 2 * reg_id : 2 * reg_id + reg_id / 4 & 7))
  119.  
  120. // Returns number of top bit in operand (i.e. 8 for 8-bit operands, 16 for 16-bit operands)
  121. #define TOP_BIT 8*(i_w + 1)
  122.  
  123. // Opcode execution unit helpers
  124. #define OPCODE ;break; case
  125. #define OPCODE_CHAIN ; case
  126.  
  127. // [I]MUL/[I]DIV/DAA/DAS/ADC/SBB helpers
  128. #define MUL_MACRO(op_data_type,out_regs) (set_opcode(0x10), \
  129.                                                                                   out_regs[i_w + 1] = (op_result = CAST(op_data_type)mem[rm_addr] * (op_data_type)*out_regs) >> 16, \
  130.                                                                                   regs16[REG_AX] = op_result, \
  131.                                                                                   set_OF(set_CF(op_result - (op_data_type)op_result)))
  132. #define DIV_MACRO(out_data_type,in_data_type,out_regs) (scratch_int = CAST(out_data_type)mem[rm_addr]) && !(scratch2_uint = (in_data_type)(scratch_uint = (out_regs[i_w+1] << 16) + regs16[REG_AX]) / scratch_int, scratch2_uint - (out_data_type)scratch2_uint) ? out_regs[i_w+1] = scratch_uint - scratch_int * (*out_regs = scratch2_uint) : pc_interrupt(0)
  133. #define DAA_DAS(op1,op2,mask,min) set_AF((((scratch2_uint = regs8[REG_AL]) & 0x0F) > 9) || regs8[FLAG_AF]) && (op_result = regs8[REG_AL] op1 6, set_CF(regs8[FLAG_CF] || (regs8[REG_AL] op2 scratch2_uint))), \
  134.                                                                   set_CF((((mask & 1 ? scratch2_uint : regs8[REG_AL]) & mask) > min) || regs8[FLAG_CF]) && (op_result = regs8[REG_AL] op1 0x60)
  135. #define ADC_SBB_MACRO(a) OP(a##= regs8[FLAG_CF] +), \
  136.                                                  set_CF(regs8[FLAG_CF] && (op_result == op_dest) || (a op_result < a(int)op_dest)), \
  137.                                                  set_AF_OF_arith()
  138.  
  139. // Execute arithmetic/logic operations in emulator memory/registers
  140. #define R_M_OP(dest,op,src) (i_w ? op_dest = CAST(unsigned short)dest, op_result = CAST(unsigned short)dest op (op_source = CAST(unsigned short)src) \
  141.                                                                  : (op_dest = dest, op_result = dest op (op_source = CAST(unsigned char)src)))
  142. #define MEM_OP(dest,op,src) R_M_OP(mem[dest],op,mem[src])
  143. #define OP(op) MEM_OP(op_to_addr,op,op_from_addr)
  144.  
  145. // Increment or decrement a register #reg_id (usually SI or DI), depending on direction flag and operand size (given by i_w)
  146. #define INDEX_INC(reg_id) (regs16[reg_id] -= (2 * regs8[FLAG_DF] - 1)*(i_w + 1))
  147.  
  148. // Helpers for stack operations
  149. #define R_M_PUSH(a) (i_w = 1, R_M_OP(mem[SEGREG(REG_SS, REG_SP, --)], =, a))
  150. #define R_M_POP(a) (i_w = 1, regs16[REG_SP] += 2, R_M_OP(a, =, mem[SEGREG(REG_SS, REG_SP, -2+)]))
  151.  
  152. // Convert segment:offset to linear address in emulator memory space
  153. #define SEGREG(reg_seg,reg_ofs,op) 16 * regs16[reg_seg] + (unsigned short)(op regs16[reg_ofs])
  154.  
  155. // Returns sign bit of an 8-bit or 16-bit operand
  156. #define SIGN_OF(a) (1 & (i_w ? CAST(short)a : a) >> (TOP_BIT - 1))
  157.  
  158. // Reinterpretation cast
  159. #define CAST(a) *(a*)&
  160.  
  161. // Keyboard driver for console. This may need changing for UNIX/non-UNIX platforms
  162. #ifdef _WIN32
  163. #define KEYBOARD_DRIVER kbhit() && (mem[0x4A6] = getch(), pc_interrupt(7))
  164. #else
  165. //#define KEYBOARD_DRIVER read(0, mem + 0x4A6, 1) && (int8_asap = (mem[0x4A6] == 0x1B), pc_interrupt(7))
  166.  
  167. #define KEYBOARD_DRIVER kbhit() && (mem[0x4A6] = getch(), pc_interrupt(7))
  168. #endif
  169.  
  170. // Keyboard driver for SDL
  171. #ifdef NO_GRAPHICS
  172. #define SDL_KEYBOARD_DRIVER KEYBOARD_DRIVER
  173. #else
  174. #define SDL_KEYBOARD_DRIVER sdl_screen ? SDL_PollEvent(&sdl_event) && (sdl_event.type == SDL_KEYDOWN || sdl_event.type == SDL_KEYUP) && (scratch_uint = sdl_event.key.keysym.unicode, scratch2_uint = sdl_event.key.keysym.mod, CAST(short)mem[0x4A6] = 0x400 + 0x800*!!(scratch2_uint & KMOD_ALT) + 0x1000*!!(scratch2_uint & KMOD_SHIFT) + 0x2000*!!(scratch2_uint & KMOD_CTRL) + 0x4000*(sdl_event.type == SDL_KEYUP) + ((!scratch_uint || scratch_uint > 0x7F) ? sdl_event.key.keysym.sym : scratch_uint), pc_interrupt(7)) : (KEYBOARD_DRIVER)
  175. #endif
  176.  
  177. // Global variable definitions
  178. unsigned char mem[RAM_SIZE], io_ports[IO_PORT_COUNT], *opcode_stream, *regs8, i_rm, i_w, i_reg, i_mod, i_mod_size, i_d, i_reg4bit, raw_opcode_id, xlat_opcode_id, extra, rep_mode, seg_override_en, rep_override_en, trap_flag, int8_asap, scratch_uchar, io_hi_lo, *vid_mem_base, spkr_en, bios_table_lookup[20][256];
  179. unsigned short *regs16, reg_ip, seg_override, file_index, wave_counter;
  180. unsigned int op_source, op_dest, rm_addr, op_to_addr, op_from_addr, i_data0, i_data1, i_data2, scratch_uint, scratch2_uint, inst_counter, set_flags_type, GRAPHICS_X, GRAPHICS_Y, pixel_colors[16], vmem_ctr;
  181. int op_result, disk[3], scratch_int;
  182. time_t clock_buf;
  183. struct timeb ms_clock;
  184.  
  185. #ifndef NO_GRAPHICS
  186. SDL_AudioSpec sdl_audio = {44100, AUDIO_U8, 1, 0, 128};
  187. SDL_AudioSpec sdl_audio_obt = {44100, AUDIO_U8, 1, 0, 128};
  188. SDL_Surface *sdl_screen;
  189. SDL_Event sdl_event;
  190. unsigned short vid_addr_lookup[VIDEO_RAM_SIZE], cga_colors[4] = {0 /* Black */, 0x1F1F /* Cyan */, 0xE3E3 /* Magenta */, 0xFFFF /* White */};
  191. #endif
  192.  
  193. // Helper functions
  194.  
  195. // Set carry flag
  196. char set_CF(int new_CF)
  197. {
  198.         return regs8[FLAG_CF] = !!new_CF;
  199. }
  200.  
  201. // Set auxiliary flag
  202. char set_AF(int new_AF)
  203. {
  204.         return regs8[FLAG_AF] = !!new_AF;
  205. }
  206.  
  207. // Set overflow flag
  208. char set_OF(int new_OF)
  209. {
  210.         return regs8[FLAG_OF] = !!new_OF;
  211. }
  212.  
  213. // Set auxiliary and overflow flag after arithmetic operations
  214. char set_AF_OF_arith()
  215. {
  216.         set_AF((op_source ^= op_dest ^ op_result) & 0x10);
  217.         if (op_result == op_dest)
  218.                 return set_OF(0);
  219.         else
  220.                 return set_OF(1 & (regs8[FLAG_CF] ^ op_source >> (TOP_BIT - 1)));
  221. }
  222.  
  223. // Assemble and return emulated CPU FLAGS register in scratch_uint
  224. void make_flags()
  225. {
  226.         scratch_uint = 0xF002; // 8086 has reserved and unused flags set to 1
  227.         for (int i = 9; i--;)
  228.                 scratch_uint += regs8[FLAG_CF + i] << bios_table_lookup[TABLE_FLAGS_BITFIELDS][i];
  229. }
  230.  
  231. // Set emulated CPU FLAGS register from regs8[FLAG_xx] values
  232. void set_flags(int new_flags)
  233. {
  234.         for (int i = 9; i--;)
  235.                 regs8[FLAG_CF + i] = !!(1 << bios_table_lookup[TABLE_FLAGS_BITFIELDS][i] & new_flags);
  236. }
  237.  
  238. // Convert raw opcode to translated opcode index. This condenses a large number of different encodings of similar
  239. // instructions into a much smaller number of distinct functions, which we then execute
  240. void set_opcode(unsigned char opcode)
  241. {
  242.         xlat_opcode_id = bios_table_lookup[TABLE_XLAT_OPCODE][raw_opcode_id = opcode];
  243.         extra = bios_table_lookup[TABLE_XLAT_SUBFUNCTION][opcode];
  244.         i_mod_size = bios_table_lookup[TABLE_I_MOD_SIZE][opcode];
  245.         set_flags_type = bios_table_lookup[TABLE_STD_FLAGS][opcode];
  246. }
  247.  
  248. // Execute INT #interrupt_num on the emulated machine
  249. char pc_interrupt(unsigned char interrupt_num)
  250. {
  251.         set_opcode(0xCD); // Decode like INT
  252.  
  253.         make_flags();
  254.         R_M_PUSH(scratch_uint);
  255.         R_M_PUSH(regs16[REG_CS]);
  256.         R_M_PUSH(reg_ip);
  257.         MEM_OP(REGS_BASE + 2 * REG_CS, =, 4 * interrupt_num + 2);
  258.         R_M_OP(reg_ip, =, mem[4 * interrupt_num]);
  259.  
  260.         return regs8[FLAG_TF] = regs8[FLAG_IF] = 0;
  261. }
  262.  
  263. // AAA and AAS instructions - which_operation is +1 for AAA, and -1 for AAS
  264. int AAA_AAS(char which_operation)
  265. {
  266.         return (regs16[REG_AX] += 262 * which_operation*set_AF(set_CF(((regs8[REG_AL] & 0x0F) > 9) || regs8[FLAG_AF])), regs8[REG_AL] &= 0x0F);
  267. }
  268.  
  269. #ifndef NO_GRAPHICS
  270. void audio_callback(void *data, unsigned char *stream, int len)
  271. {
  272.         for (int i = 0; i < len; i++)
  273.                 stream[i] = (spkr_en == 3) && CAST(unsigned short)mem[0x4AA] ? -((54 * wave_counter++ / CAST(unsigned short)mem[0x4AA]) & 1) : sdl_audio.silence;
  274.  
  275.         spkr_en = io_ports[0x61] & 3;
  276. }
  277. #endif
  278.  
  279. #include <conio.h>
  280. #define kbhit con_kbhit
  281. #define getch con_getch
  282.  
  283. // Emulator entry point
  284. int main(int argc, char **argv)
  285. {
  286.     con_set_title("8086tiny");
  287.     //freopen("OUT", "w" ,stdout);
  288. #ifndef NO_GRAPHICS
  289.         // Initialise SDL
  290.         SDL_Init(SDL_INIT_AUDIO);
  291.         sdl_audio.callback = audio_callback;
  292. #ifdef _WIN32
  293.         sdl_audio.samples = 512;
  294. #endif
  295.         SDL_OpenAudio(&sdl_audio, &sdl_audio_obt);
  296. #endif
  297.  
  298.         // regs16 and reg8 point to F000:0, the start of memory-mapped registers. CS is initialised to F000
  299.         regs16 = (unsigned short *)(regs8 = mem + REGS_BASE);
  300.         regs16[REG_CS] = 0xF000;
  301.  
  302.         // Trap flag off
  303.         regs8[FLAG_TF] = 0;
  304.  
  305.         // Set DL equal to the boot device: 0 for the FD, or 0x80 for the HD. Normally, boot from the FD.
  306.         // But, if the HD image file is prefixed with @, then boot from the HD
  307.         regs8[REG_DL] = ((argc > 3) && (*argv[3] == '@')) ? argv[3]++, 0x80 : 0;
  308.  
  309.         // Open BIOS (file id disk[2]), floppy disk image (disk[1]), and hard disk image (disk[0]) if specified
  310.         for (file_index = 3; file_index;)
  311.                 disk[--file_index] = *++argv ? open(*argv, 32898) : 0;
  312.  
  313.         // Set CX:AX equal to the hard disk image size, if present
  314.         CAST(unsigned)regs16[REG_AX] = *disk ? lseek(*disk, 0, 2) >> 9 : 0;
  315.  
  316.         // Load BIOS image into F000:0100, and set IP to 0100
  317.         read(disk[2], regs8 + (reg_ip = 0x100), 0xFF00);
  318.  
  319.         // Load instruction decoding helper table
  320.         for (int i = 0; i < 20; i++)
  321.                 for (int j = 0; j < 256; j++)
  322.                         bios_table_lookup[i][j] = regs8[regs16[0x81 + i] + j];
  323.  
  324.         // Instruction execution loop. Terminates if CS:IP = 0:0
  325.         for (; opcode_stream = mem + 16 * regs16[REG_CS] + reg_ip, opcode_stream != mem;)
  326.         {
  327.                 // Set up variables to prepare for decoding an opcode
  328.                 set_opcode(*opcode_stream);
  329.  
  330.                 // Extract i_w and i_d fields from instruction
  331.                 i_w = (i_reg4bit = raw_opcode_id & 7) & 1;
  332.                 i_d = i_reg4bit / 2 & 1;
  333.  
  334.                 // Extract instruction data fields
  335.                 i_data0 = CAST(short)opcode_stream[1];
  336.                 i_data1 = CAST(short)opcode_stream[2];
  337.                 i_data2 = CAST(short)opcode_stream[3];
  338.  
  339.                 // seg_override_en and rep_override_en contain number of instructions to hold segment override and REP prefix respectively
  340.                 if (seg_override_en)
  341.                         seg_override_en--;
  342.                 if (rep_override_en)
  343.                         rep_override_en--;
  344.  
  345.                 // i_mod_size > 0 indicates that opcode uses i_mod/i_rm/i_reg, so decode them
  346.                 if (i_mod_size)
  347.                 {
  348.                         i_mod = (i_data0 & 0xFF) >> 6;
  349.                         i_rm = i_data0 & 7;
  350.                         i_reg = i_data0 / 8 & 7;
  351.  
  352.                         if ((!i_mod && i_rm == 6) || (i_mod == 2))
  353.                                 i_data2 = CAST(short)opcode_stream[4];
  354.                         else if (i_mod != 1)
  355.                                 i_data2 = i_data1;
  356.                         else // If i_mod is 1, operand is (usually) 8 bits rather than 16 bits
  357.                                 i_data1 = (char)i_data1;
  358.  
  359.                         DECODE_RM_REG;
  360.                 }
  361.  
  362.                 // Instruction execution unit
  363.                 switch (xlat_opcode_id)
  364.                 {
  365.                         OPCODE_CHAIN 0: // Conditional jump (JAE, JNAE, etc.)
  366.                                 // i_w is the invert flag, e.g. i_w == 1 means JNAE, whereas i_w == 0 means JAE
  367.                                 scratch_uchar = raw_opcode_id / 2 & 7;
  368.                                 reg_ip += (char)i_data0 * (i_w ^ (regs8[bios_table_lookup[TABLE_COND_JUMP_DECODE_A][scratch_uchar]] || regs8[bios_table_lookup[TABLE_COND_JUMP_DECODE_B][scratch_uchar]] || regs8[bios_table_lookup[TABLE_COND_JUMP_DECODE_C][scratch_uchar]] ^ regs8[bios_table_lookup[TABLE_COND_JUMP_DECODE_D][scratch_uchar]]))
  369.                         OPCODE 1: // MOV reg, imm
  370.                                 i_w = !!(raw_opcode_id & 8);
  371.                                 R_M_OP(mem[GET_REG_ADDR(i_reg4bit)], =, i_data0)
  372.                         OPCODE 3: // PUSH regs16
  373.                                 R_M_PUSH(regs16[i_reg4bit])
  374.                         OPCODE 4: // POP regs16
  375.                                 R_M_POP(regs16[i_reg4bit])
  376.                         OPCODE 2: // INC|DEC regs16
  377.                                 i_w = 1;
  378.                                 i_d = 0;
  379.                                 i_reg = i_reg4bit;
  380.                                 DECODE_RM_REG;
  381.                                 i_reg = extra
  382.                         OPCODE_CHAIN 5: // INC|DEC|JMP|CALL|PUSH
  383.                                 if (i_reg < 2) // INC|DEC
  384.                                         MEM_OP(op_from_addr, += 1 - 2 * i_reg +, REGS_BASE + 2 * REG_ZERO),
  385.                                         op_source = 1,
  386.                                         set_AF_OF_arith(),
  387.                                         set_OF(op_dest + 1 - i_reg == 1 << (TOP_BIT - 1)),
  388.                                         (xlat_opcode_id == 5) && (set_opcode(0x10), 0); // Decode like ADC
  389.                                 else if (i_reg != 6) // JMP|CALL
  390.                                         i_reg - 3 || R_M_PUSH(regs16[REG_CS]), // CALL (far)
  391.                                         i_reg & 2 && R_M_PUSH(reg_ip + 2 + i_mod*(i_mod != 3) + 2*(!i_mod && i_rm == 6)), // CALL (near or far)
  392.                                         i_reg & 1 && (regs16[REG_CS] = CAST(short)mem[op_from_addr + 2]), // JMP|CALL (far)
  393.                                         R_M_OP(reg_ip, =, mem[op_from_addr]),
  394.                                         set_opcode(0x9A); // Decode like CALL
  395.                                 else // PUSH
  396.                                         R_M_PUSH(mem[rm_addr])
  397.                         OPCODE 6: // TEST r/m, imm16 / NOT|NEG|MUL|IMUL|DIV|IDIV reg
  398.                                 op_to_addr = op_from_addr;
  399.  
  400.                                 switch (i_reg)
  401.                                 {
  402.                                         OPCODE_CHAIN 0: // TEST
  403.                                                 set_opcode(0x20); // Decode like AND
  404.                                                 reg_ip += i_w + 1;
  405.                                                 R_M_OP(mem[op_to_addr], &, i_data2)
  406.                                         OPCODE 2: // NOT
  407.                                                 OP(=~)
  408.                                         OPCODE 3: // NEG
  409.                                                 OP(=-);
  410.                                                 op_dest = 0;
  411.                                                 set_opcode(0x28); // Decode like SUB
  412.                                                 set_CF(op_result > op_dest)
  413.                                         OPCODE 4: // MUL
  414.                                                 i_w ? MUL_MACRO(unsigned short, regs16) : MUL_MACRO(unsigned char, regs8)
  415.                                         OPCODE 5: // IMUL
  416.                                                 i_w ? MUL_MACRO(short, regs16) : MUL_MACRO(char, regs8)
  417.                                         OPCODE 6: // DIV
  418.                                                 i_w ? DIV_MACRO(unsigned short, unsigned, regs16) : DIV_MACRO(unsigned char, unsigned short, regs8)
  419.                                         OPCODE 7: // IDIV
  420.                                                 i_w ? DIV_MACRO(short, int, regs16) : DIV_MACRO(char, short, regs8);
  421.                                 }
  422.                         OPCODE 7: // ADD|OR|ADC|SBB|AND|SUB|XOR|CMP AL/AX, immed
  423.                                 rm_addr = REGS_BASE;
  424.                                 i_data2 = i_data0;
  425.                                 i_mod = 3;
  426.                                 i_reg = extra;
  427.                                 reg_ip--;
  428.                         OPCODE_CHAIN 8: // ADD|OR|ADC|SBB|AND|SUB|XOR|CMP reg, immed
  429.                                 op_to_addr = rm_addr;
  430.                                 regs16[REG_SCRATCH] = (i_d |= !i_w) ? (char)i_data2 : i_data2;
  431.                                 op_from_addr = REGS_BASE + 2 * REG_SCRATCH;
  432.                                 reg_ip += !i_d + 1;
  433.                                 set_opcode(0x08 * (extra = i_reg));
  434.                         OPCODE_CHAIN 9: // ADD|OR|ADC|SBB|AND|SUB|XOR|CMP|MOV reg, r/m
  435.                                 switch (extra)
  436.                                 {
  437.                                         OPCODE_CHAIN 0: // ADD
  438.                                                 OP(+=),
  439.                                                 set_CF(op_result < op_dest)
  440.                                         OPCODE 1: // OR
  441.                                                 OP(|=)
  442.                                         OPCODE 2: // ADC
  443.                                                 ADC_SBB_MACRO(+)
  444.                                         OPCODE 3: // SBB
  445.                                                 ADC_SBB_MACRO(-)
  446.                                         OPCODE 4: // AND
  447.                                                 OP(&=)
  448.                                         OPCODE 5: // SUB
  449.                                                 OP(-=),
  450.                                                 set_CF(op_result > op_dest)
  451.                                         OPCODE 6: // XOR
  452.                                                 OP(^=)
  453.                                         OPCODE 7: // CMP
  454.                                                 OP(-),
  455.                                                 set_CF(op_result > op_dest)
  456.                                         OPCODE 8: // MOV
  457.                                                 OP(=);
  458.                                 }
  459.                         OPCODE 10: // MOV sreg, r/m | POP r/m | LEA reg, r/m
  460.                                 if (!i_w) // MOV
  461.                                         i_w = 1,
  462.                                         i_reg += 8,
  463.                                         DECODE_RM_REG,
  464.                                         OP(=);
  465.                                 else if (!i_d) // LEA
  466.                                         seg_override_en = 1,
  467.                                         seg_override = REG_ZERO,
  468.                                         DECODE_RM_REG,
  469.                                         R_M_OP(mem[op_from_addr], =, rm_addr);
  470.                                 else // POP
  471.                                         R_M_POP(mem[rm_addr])
  472.                         OPCODE 11: // MOV AL/AX, [loc]
  473.                                 i_mod = i_reg = 0;
  474.                                 i_rm = 6;
  475.                                 i_data1 = i_data0;
  476.                                 DECODE_RM_REG;
  477.                                 MEM_OP(op_from_addr, =, op_to_addr)
  478.                         OPCODE 12: // ROL|ROR|RCL|RCR|SHL|SHR|???|SAR reg/mem, 1/CL/imm (80186)
  479.                                 scratch2_uint = SIGN_OF(mem[rm_addr]),
  480.                                 scratch_uint = extra ? // xxx reg/mem, imm
  481.                                         ++reg_ip,
  482.                                         (char)i_data1
  483.                                 : // xxx reg/mem, CL
  484.                                         i_d
  485.                                                 ? 31 & regs8[REG_CL]
  486.                                 : // xxx reg/mem, 1
  487.                                         1;
  488.                                 if (scratch_uint)
  489.                                 {
  490.                                         if (i_reg < 4) // Rotate operations
  491.                                                 scratch_uint %= i_reg / 2 + TOP_BIT,
  492.                                                 R_M_OP(scratch2_uint, =, mem[rm_addr]);
  493.                                         if (i_reg & 1) // Rotate/shift right operations
  494.                                                 R_M_OP(mem[rm_addr], >>=, scratch_uint);
  495.                                         else // Rotate/shift left operations
  496.                                                 R_M_OP(mem[rm_addr], <<=, scratch_uint);
  497.                                         if (i_reg > 3) // Shift operations
  498.                                                 set_opcode(0x10); // Decode like ADC
  499.                                         if (i_reg > 4) // SHR or SAR
  500.                                                 set_CF(op_dest >> (scratch_uint - 1) & 1);
  501.                                 }
  502.  
  503.                                 switch (i_reg)
  504.                                 {
  505.                                         OPCODE_CHAIN 0: // ROL
  506.                                                 R_M_OP(mem[rm_addr], += , scratch2_uint >> (TOP_BIT - scratch_uint));
  507.                                                 set_OF(SIGN_OF(op_result) ^ set_CF(op_result & 1))
  508.                                         OPCODE 1: // ROR
  509.                                                 scratch2_uint &= (1 << scratch_uint) - 1,
  510.                                                 R_M_OP(mem[rm_addr], += , scratch2_uint << (TOP_BIT - scratch_uint));
  511.                                                 set_OF(SIGN_OF(op_result * 2) ^ set_CF(SIGN_OF(op_result)))
  512.                                         OPCODE 2: // RCL
  513.                                                 R_M_OP(mem[rm_addr], += (regs8[FLAG_CF] << (scratch_uint - 1)) + , scratch2_uint >> (1 + TOP_BIT - scratch_uint));
  514.                                                 set_OF(SIGN_OF(op_result) ^ set_CF(scratch2_uint & 1 << (TOP_BIT - scratch_uint)))
  515.                                         OPCODE 3: // RCR
  516.                                                 R_M_OP(mem[rm_addr], += (regs8[FLAG_CF] << (TOP_BIT - scratch_uint)) + , scratch2_uint << (1 + TOP_BIT - scratch_uint));
  517.                                                 set_CF(scratch2_uint & 1 << (scratch_uint - 1));
  518.                                                 set_OF(SIGN_OF(op_result) ^ SIGN_OF(op_result * 2))
  519.                                         OPCODE 4: // SHL
  520.                                                 set_OF(SIGN_OF(op_result) ^ set_CF(SIGN_OF(op_dest << (scratch_uint - 1))))
  521.                                         OPCODE 5: // SHR
  522.                                                 set_OF(SIGN_OF(op_dest))
  523.                                         OPCODE 7: // SAR
  524.                                                 scratch_uint < TOP_BIT || set_CF(scratch2_uint);
  525.                                                 set_OF(0);
  526.                                                 R_M_OP(mem[rm_addr], +=, scratch2_uint *= ~(((1 << TOP_BIT) - 1) >> scratch_uint));
  527.                                 }
  528.                         OPCODE 13: // LOOPxx|JCZX
  529.                                 scratch_uint = !!--regs16[REG_CX];
  530.  
  531.                                 switch(i_reg4bit)
  532.                                 {
  533.                                         OPCODE_CHAIN 0: // LOOPNZ
  534.                                                 scratch_uint &= !regs8[FLAG_ZF]
  535.                                         OPCODE 1: // LOOPZ
  536.                                                 scratch_uint &= regs8[FLAG_ZF]
  537.                                         OPCODE 3: // JCXXZ
  538.                                                 scratch_uint = !++regs16[REG_CX];
  539.                                 }
  540.                                 reg_ip += scratch_uint*(char)i_data0
  541.                         OPCODE 14: // JMP | CALL short/near
  542.                                 reg_ip += 3 - i_d;
  543.                                 if (!i_w)
  544.                                 {
  545.                                         if (i_d) // JMP far
  546.                                                 reg_ip = 0,
  547.                                                 regs16[REG_CS] = i_data2;
  548.                                         else // CALL
  549.                                                 R_M_PUSH(reg_ip);
  550.                                 }
  551.                                 reg_ip += i_d && i_w ? (char)i_data0 : i_data0
  552.                         OPCODE 15: // TEST reg, r/m
  553.                                 MEM_OP(op_from_addr, &, op_to_addr)
  554.                         OPCODE 16: // XCHG AX, regs16
  555.                                 i_w = 1;
  556.                                 op_to_addr = REGS_BASE;
  557.                                 op_from_addr = GET_REG_ADDR(i_reg4bit);
  558.                         OPCODE_CHAIN 24: // NOP|XCHG reg, r/m
  559.                                 if (op_to_addr != op_from_addr)
  560.                                         OP(^=),
  561.                                         MEM_OP(op_from_addr, ^=, op_to_addr),
  562.                                         OP(^=)
  563.                         OPCODE 17: // MOVSx (extra=0)|STOSx (extra=1)|LODSx (extra=2)
  564.                                 scratch2_uint = seg_override_en ? seg_override : REG_DS;
  565.  
  566.                                 for (scratch_uint = rep_override_en ? regs16[REG_CX] : 1; scratch_uint; scratch_uint--)
  567.                                 {
  568.                                         MEM_OP(extra < 2 ? SEGREG(REG_ES, REG_DI,) : REGS_BASE, =, extra & 1 ? REGS_BASE : SEGREG(scratch2_uint, REG_SI,)),
  569.                                         extra & 1 || INDEX_INC(REG_SI),
  570.                                         extra & 2 || INDEX_INC(REG_DI);
  571.                                 }
  572.  
  573.                                 if (rep_override_en)
  574.                                         regs16[REG_CX] = 0
  575.                         OPCODE 18: // CMPSx (extra=0)|SCASx (extra=1)
  576.                                 scratch2_uint = seg_override_en ? seg_override : REG_DS;
  577.  
  578.                                 if ((scratch_uint = rep_override_en ? regs16[REG_CX] : 1))
  579.                                 {
  580.                                         for (; scratch_uint; rep_override_en || scratch_uint--)
  581.                                         {
  582.                                                 MEM_OP(extra ? REGS_BASE : SEGREG(scratch2_uint, REG_SI,), -, SEGREG(REG_ES, REG_DI,)),
  583.                                                 extra || INDEX_INC(REG_SI),
  584.                                                 INDEX_INC(REG_DI), rep_override_en && !(--regs16[REG_CX] && (!op_result == rep_mode)) && (scratch_uint = 0);
  585.                                         }
  586.  
  587.                                         set_flags_type = FLAGS_UPDATE_SZP | FLAGS_UPDATE_AO_ARITH; // Funge to set SZP/AO flags
  588.                                         set_CF(op_result > op_dest);
  589.                                 }
  590.                         OPCODE 19: // RET|RETF|IRET
  591.                                 i_d = i_w;
  592.                                 R_M_POP(reg_ip);
  593.                                 if (extra) // IRET|RETF|RETF imm16
  594.                                         R_M_POP(regs16[REG_CS]);
  595.                                 if (extra & 2) // IRET
  596.                                         set_flags(R_M_POP(scratch_uint));
  597.                                 else if (!i_d) // RET|RETF imm16
  598.                                         regs16[REG_SP] += i_data0
  599.                         OPCODE 20: // MOV r/m, immed
  600.                                 R_M_OP(mem[op_from_addr], =, i_data2)
  601.                         OPCODE 21: // IN AL/AX, DX/imm8
  602.                                 io_ports[0x20] = 0; // PIC EOI
  603.                                 io_ports[0x42] = --io_ports[0x40]; // PIT channel 0/2 read placeholder
  604.                                 io_ports[0x3DA] ^= 9; // CGA refresh
  605.                                 scratch_uint = extra ? regs16[REG_DX] : (unsigned char)i_data0;
  606.                                 scratch_uint == 0x60 && (io_ports[0x64] = 0); // Scancode read flag
  607.                                 scratch_uint == 0x3D5 && (io_ports[0x3D4] >> 1 == 7) && (io_ports[0x3D5] = ((mem[0x49E]*80 + mem[0x49D] + CAST(short)mem[0x4AD]) & (io_ports[0x3D4] & 1 ? 0xFF : 0xFF00)) >> (io_ports[0x3D4] & 1 ? 0 : 8)); // CRT cursor position
  608.                                 R_M_OP(regs8[REG_AL], =, io_ports[scratch_uint]);
  609.                         OPCODE 22: // OUT DX/imm8, AL/AX
  610.                                 scratch_uint = extra ? regs16[REG_DX] : (unsigned char)i_data0;
  611.                                 R_M_OP(io_ports[scratch_uint], =, regs8[REG_AL]);
  612.                                 scratch_uint == 0x61 && (io_hi_lo = 0, spkr_en |= regs8[REG_AL] & 3); // Speaker control
  613.                                 (scratch_uint == 0x40 || scratch_uint == 0x42) && (io_ports[0x43] & 6) && (mem[0x469 + scratch_uint - (io_hi_lo ^= 1)] = regs8[REG_AL]); // PIT rate programming
  614. #ifndef NO_GRAPHICS
  615.                                 scratch_uint == 0x43 && (io_hi_lo = 0, regs8[REG_AL] >> 6 == 2) && (SDL_PauseAudio((regs8[REG_AL] & 0xF7) != 0xB6), 0); // Speaker enable
  616. #endif
  617.                                 scratch_uint == 0x3D5 && (io_ports[0x3D4] >> 1 == 6) && (mem[0x4AD + !(io_ports[0x3D4] & 1)] = regs8[REG_AL]); // CRT video RAM start offset
  618.                                 scratch_uint == 0x3D5 && (io_ports[0x3D4] >> 1 == 7) && (scratch2_uint = ((mem[0x49E]*80 + mem[0x49D] + CAST(short)mem[0x4AD]) & (io_ports[0x3D4] & 1 ? 0xFF00 : 0xFF)) + (regs8[REG_AL] << (io_ports[0x3D4] & 1 ? 0 : 8)) - CAST(short)mem[0x4AD], mem[0x49D] = scratch2_uint % 80, mem[0x49E] = scratch2_uint / 80); // CRT cursor position
  619.                                 scratch_uint == 0x3B5 && io_ports[0x3B4] == 1 && (GRAPHICS_X = regs8[REG_AL] * 16); // Hercules resolution reprogramming. Defaults are set in the BIOS
  620.                                 scratch_uint == 0x3B5 && io_ports[0x3B4] == 6 && (GRAPHICS_Y = regs8[REG_AL] * 4);
  621.                         OPCODE 23: // REPxx
  622.                                 rep_override_en = 2;
  623.                                 rep_mode = i_w;
  624.                                 seg_override_en && seg_override_en++
  625.                         OPCODE 25: // PUSH reg
  626.                                 R_M_PUSH(regs16[extra])
  627.                         OPCODE 26: // POP reg
  628.                                 R_M_POP(regs16[extra])
  629.                         OPCODE 27: // xS: segment overrides
  630.                                 seg_override_en = 2;
  631.                                 seg_override = extra;
  632.                                 rep_override_en && rep_override_en++
  633.                         OPCODE 28: // DAA/DAS
  634.                                 i_w = 0;
  635.                                 extra ? DAA_DAS(-=, >=, 0xFF, 0x99) : DAA_DAS(+=, <, 0xF0, 0x90) // extra = 0 for DAA, 1 for DAS
  636.                         OPCODE 29: // AAA/AAS
  637.                                 op_result = AAA_AAS(extra - 1)
  638.                         OPCODE 30: // CBW
  639.                                 regs8[REG_AH] = -SIGN_OF(regs8[REG_AL])
  640.                         OPCODE 31: // CWD
  641.                                 regs16[REG_DX] = -SIGN_OF(regs16[REG_AX])
  642.                         OPCODE 32: // CALL FAR imm16:imm16
  643.                                 R_M_PUSH(regs16[REG_CS]);
  644.                                 R_M_PUSH(reg_ip + 5);
  645.                                 regs16[REG_CS] = i_data2;
  646.                                 reg_ip = i_data0
  647.                         OPCODE 33: // PUSHF
  648.                                 make_flags();
  649.                                 R_M_PUSH(scratch_uint)
  650.                         OPCODE 34: // POPF
  651.                                 set_flags(R_M_POP(scratch_uint))
  652.                         OPCODE 35: // SAHF
  653.                                 make_flags();
  654.                                 set_flags((scratch_uint & 0xFF00) + regs8[REG_AH])
  655.                         OPCODE 36: // LAHF
  656.                                 make_flags(),
  657.                                 regs8[REG_AH] = scratch_uint
  658.                         OPCODE 37: // LES|LDS reg, r/m
  659.                                 i_w = i_d = 1;
  660.                                 DECODE_RM_REG;
  661.                                 OP(=);
  662.                                 MEM_OP(REGS_BASE + extra, =, rm_addr + 2)
  663.                         OPCODE 38: // INT 3
  664.                                 ++reg_ip;
  665.                                 pc_interrupt(3)
  666.                         OPCODE 39: // INT imm8
  667.                                 reg_ip += 2;
  668.                                 pc_interrupt(i_data0)
  669.                         OPCODE 40: // INTO
  670.                                 ++reg_ip;
  671.                                 regs8[FLAG_OF] && pc_interrupt(4)
  672.                         OPCODE 41: // AAM
  673.                                 if (i_data0 &= 0xFF)
  674.                                         regs8[REG_AH] = regs8[REG_AL] / i_data0,
  675.                                         op_result = regs8[REG_AL] %= i_data0;
  676.                                 else // Divide by zero
  677.                                         pc_interrupt(0)
  678.                         OPCODE 42: // AAD
  679.                                 i_w = 0;
  680.                                 regs16[REG_AX] = op_result = 0xFF & regs8[REG_AL] + i_data0 * regs8[REG_AH]
  681.                         OPCODE 43: // SALC
  682.                                 regs8[REG_AL] = -regs8[FLAG_CF]
  683.                         OPCODE 44: // XLAT
  684.                                 regs8[REG_AL] = mem[SEGREG(seg_override_en ? seg_override : REG_DS, REG_BX, regs8[REG_AL] +)]
  685.                         OPCODE 45: // CMC
  686.                                 regs8[FLAG_CF] ^= 1
  687.                         OPCODE 46: // CLC|STC|CLI|STI|CLD|STD
  688.                                 regs8[extra / 2] = extra & 1
  689.                         OPCODE 47: // TEST AL/AX, immed
  690.                                 R_M_OP(regs8[REG_AL], &, i_data0)
  691.                         OPCODE 48: // Emulator-specific 0F xx opcodes
  692.                                 switch ((char)i_data0)
  693.                                 {
  694.                                         OPCODE_CHAIN 0: // PUTCHAR_AL
  695.                                                 write(1, regs8, 1);
  696.                         // printf("%c", regs8[0]);
  697.                                         OPCODE 1: // GET_RTC
  698.                                                 time(&clock_buf);
  699.                                                 ftime(&ms_clock);
  700.                                                 memcpy(mem + SEGREG(REG_ES, REG_BX,), localtime(&clock_buf), sizeof(struct tm));
  701.                                                 CAST(short)mem[SEGREG(REG_ES, REG_BX, 36+)] = ms_clock.millitm;
  702.                                         OPCODE 2: // DISK_READ
  703.                                         OPCODE_CHAIN 3: // DISK_WRITE
  704.                                                 regs8[REG_AL] = ~lseek(disk[regs8[REG_DL]], CAST(unsigned)regs16[REG_BP] << 9, 0)
  705.                                                         ? ((char)i_data0 == 3 ? (int(*)())write : (int(*)())read)(disk[regs8[REG_DL]], mem + SEGREG(REG_ES, REG_BX,), regs16[REG_AX])
  706.                                                         : 0;
  707.                                 }
  708.                 }
  709.  
  710.                 // Increment instruction pointer by computed instruction length. Tables in the BIOS binary
  711.                 // help us here.
  712.                 reg_ip += (i_mod*(i_mod != 3) + 2*(!i_mod && i_rm == 6))*i_mod_size + bios_table_lookup[TABLE_BASE_INST_SIZE][raw_opcode_id] + bios_table_lookup[TABLE_I_W_SIZE][raw_opcode_id]*(i_w + 1);
  713.  
  714.                 // If instruction needs to update SF, ZF and PF, set them as appropriate
  715.                 if (set_flags_type & FLAGS_UPDATE_SZP)
  716.                 {
  717.                         regs8[FLAG_SF] = SIGN_OF(op_result);
  718.                         regs8[FLAG_ZF] = !op_result;
  719.                         regs8[FLAG_PF] = bios_table_lookup[TABLE_PARITY_FLAG][(unsigned char)op_result];
  720.  
  721.                         // If instruction is an arithmetic or logic operation, also set AF/OF/CF as appropriate.
  722.                         if (set_flags_type & FLAGS_UPDATE_AO_ARITH)
  723.                                 set_AF_OF_arith();
  724.                         if (set_flags_type & FLAGS_UPDATE_OC_LOGIC)
  725.                                 set_CF(0), set_OF(0);
  726.                 }
  727.  
  728.                 // Poll timer/keyboard every KEYBOARD_TIMER_UPDATE_DELAY instructions
  729.                 if (!(++inst_counter % KEYBOARD_TIMER_UPDATE_DELAY))
  730.                         int8_asap = 1;
  731.  
  732. #ifndef NO_GRAPHICS
  733.                 // Update the video graphics display every GRAPHICS_UPDATE_DELAY instructions
  734.                 if (!(inst_counter % GRAPHICS_UPDATE_DELAY))
  735.                 {
  736.                         // Video card in graphics mode?
  737.                         if (io_ports[0x3B8] & 2)
  738.                         {
  739.                                 // If we don't already have an SDL window open, set it up and compute color and video memory translation tables
  740.                                 if (!sdl_screen)
  741.                                 {
  742.                                         for (int i = 0; i < 16; i++)
  743.                                                 pixel_colors[i] = mem[0x4AC] ? // CGA?
  744.                                                         cga_colors[(i & 12) >> 2] + (cga_colors[i & 3] << 16) // CGA -> RGB332
  745.                                                         : 0xFF*(((i & 1) << 24) + ((i & 2) << 15) + ((i & 4) << 6) + ((i & 8) >> 3)); // Hercules -> RGB332
  746.  
  747.                                         for (int i = 0; i < GRAPHICS_X * GRAPHICS_Y / 4; i++)
  748.                                                 vid_addr_lookup[i] = i / GRAPHICS_X * (GRAPHICS_X / 8) + (i / 2) % (GRAPHICS_X / 8) + 0x2000*(mem[0x4AC] ? (2 * i / GRAPHICS_X) % 2 : (4 * i / GRAPHICS_X) % 4);
  749.  
  750.                                         SDL_Init(SDL_INIT_VIDEO);
  751.                                         sdl_screen = SDL_SetVideoMode(GRAPHICS_X, GRAPHICS_Y, 8, 0);
  752.                                         SDL_EnableUNICODE(1);
  753.                                         SDL_EnableKeyRepeat(500, 30);
  754.                                 }
  755.  
  756.                                 // Refresh SDL display from emulated graphics card video RAM
  757.                                 vid_mem_base = mem + 0xB0000 + 0x8000*(mem[0x4AC] ? 1 : io_ports[0x3B8] >> 7); // B800:0 for CGA/Hercules bank 2, B000:0 for Hercules bank 1
  758.                                 for (int i = 0; i < GRAPHICS_X * GRAPHICS_Y / 4; i++)
  759.                                         ((unsigned *)sdl_screen->pixels)[i] = pixel_colors[15 & (vid_mem_base[vid_addr_lookup[i]] >> 4*!(i & 1))];
  760.  
  761.                                 SDL_Flip(sdl_screen);
  762.                         }
  763.                         else if (sdl_screen) // Application has gone back to text mode, so close the SDL window
  764.                         {
  765.                                 SDL_QuitSubSystem(SDL_INIT_VIDEO);
  766.                                 sdl_screen = 0;
  767.                         }
  768.                         SDL_PumpEvents();
  769.                 }
  770. #endif
  771.  
  772.                 // Application has set trap flag, so fire INT 1
  773.                 if (trap_flag)
  774.                         pc_interrupt(1);
  775.  
  776.                 trap_flag = regs8[FLAG_TF];
  777.  
  778.                 // If a timer tick is pending, interrupts are enabled, and no overrides/REP are active,
  779.                 // then process the tick and check for new keystrokes
  780.                 if (int8_asap && !seg_override_en && !rep_override_en && regs8[FLAG_IF] && !regs8[FLAG_TF])
  781.                         pc_interrupt(0xA), int8_asap = 0, SDL_KEYBOARD_DRIVER;
  782.         }
  783.  
  784. #ifndef NO_GRAPHICS
  785.         SDL_Quit();
  786. #endif
  787.         return 0;
  788. }
  789.