Rev 5203 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
5203 | clevermous | 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 |
||
9 | //#include |
||
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 | |||
8523 | maxcodehac | 27 | /* |
5203 | clevermous | 28 | #include |
8523 | maxcodehac | 29 | */ |
5203 | clevermous | 30 | #ifndef _WIN32 |
31 | #include |
||
32 | #include |
||
33 | #endif |
||
34 | |||
35 | #ifndef NO_GRAPHICS |
||
8523 | maxcodehac | 36 | #include |
5203 | clevermous | 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 | |||
8523 | maxcodehac | 279 | #include |
280 | #define kbhit con_kbhit |
||
281 | #define getch con_getch |
||
5203 | clevermous | 282 | |
283 | // Emulator entry point |
||
284 | int main(int argc, char **argv) |
||
285 | { |
||
8523 | maxcodehac | 286 | load_console(); |
287 | con_set_title("8086tiny"); |
||
5203 | clevermous | 288 | |
289 | //freopen("OUT", "w" ,stdout); |
||
290 | #ifndef NO_GRAPHICS |
||
291 | // Initialise SDL |
||
292 | SDL_Init(SDL_INIT_AUDIO); |
||
293 | sdl_audio.callback = audio_callback; |
||
294 | #ifdef _WIN32 |
||
295 | sdl_audio.samples = 512; |
||
296 | #endif |
||
297 | SDL_OpenAudio(&sdl_audio, &sdl_audio_obt); |
||
298 | #endif |
||
299 | |||
300 | // regs16 and reg8 point to F000:0, the start of memory-mapped registers. CS is initialised to F000 |
||
301 | regs16 = (unsigned short *)(regs8 = mem + REGS_BASE); |
||
302 | regs16[REG_CS] = 0xF000; |
||
303 | |||
304 | // Trap flag off |
||
305 | regs8[FLAG_TF] = 0; |
||
306 | |||
307 | // Set DL equal to the boot device: 0 for the FD, or 0x80 for the HD. Normally, boot from the FD. |
||
308 | // But, if the HD image file is prefixed with @, then boot from the HD |
||
309 | regs8[REG_DL] = ((argc > 3) && (*argv[3] == '@')) ? argv[3]++, 0x80 : 0; |
||
310 | |||
311 | // Open BIOS (file id disk[2]), floppy disk image (disk[1]), and hard disk image (disk[0]) if specified |
||
312 | for (file_index = 3; file_index;) |
||
313 | disk[--file_index] = *++argv ? open(*argv, 32898) : 0; |
||
314 | |||
315 | // Set CX:AX equal to the hard disk image size, if present |
||
316 | CAST(unsigned)regs16[REG_AX] = *disk ? lseek(*disk, 0, 2) >> 9 : 0; |
||
317 | |||
318 | // Load BIOS image into F000:0100, and set IP to 0100 |
||
319 | read(disk[2], regs8 + (reg_ip = 0x100), 0xFF00); |
||
320 | |||
321 | // Load instruction decoding helper table |
||
322 | for (int i = 0; i < 20; i++) |
||
323 | for (int j = 0; j < 256; j++) |
||
324 | bios_table_lookup[i][j] = regs8[regs16[0x81 + i] + j]; |
||
325 | |||
326 | // Instruction execution loop. Terminates if CS:IP = 0:0 |
||
327 | for (; opcode_stream = mem + 16 * regs16[REG_CS] + reg_ip, opcode_stream != mem;) |
||
328 | { |
||
329 | // Set up variables to prepare for decoding an opcode |
||
330 | set_opcode(*opcode_stream); |
||
331 | |||
332 | // Extract i_w and i_d fields from instruction |
||
333 | i_w = (i_reg4bit = raw_opcode_id & 7) & 1; |
||
334 | i_d = i_reg4bit / 2 & 1; |
||
335 | |||
336 | // Extract instruction data fields |
||
337 | i_data0 = CAST(short)opcode_stream[1]; |
||
338 | i_data1 = CAST(short)opcode_stream[2]; |
||
339 | i_data2 = CAST(short)opcode_stream[3]; |
||
340 | |||
341 | // seg_override_en and rep_override_en contain number of instructions to hold segment override and REP prefix respectively |
||
342 | if (seg_override_en) |
||
343 | seg_override_en--; |
||
344 | if (rep_override_en) |
||
345 | rep_override_en--; |
||
346 | |||
347 | // i_mod_size > 0 indicates that opcode uses i_mod/i_rm/i_reg, so decode them |
||
348 | if (i_mod_size) |
||
349 | { |
||
350 | i_mod = (i_data0 & 0xFF) >> 6; |
||
351 | i_rm = i_data0 & 7; |
||
352 | i_reg = i_data0 / 8 & 7; |
||
353 | |||
354 | if ((!i_mod && i_rm == 6) || (i_mod == 2)) |
||
355 | i_data2 = CAST(short)opcode_stream[4]; |
||
356 | else if (i_mod != 1) |
||
357 | i_data2 = i_data1; |
||
358 | else // If i_mod is 1, operand is (usually) 8 bits rather than 16 bits |
||
359 | i_data1 = (char)i_data1; |
||
360 | |||
361 | DECODE_RM_REG; |
||
362 | } |
||
363 | |||
364 | // Instruction execution unit |
||
365 | switch (xlat_opcode_id) |
||
366 | { |
||
367 | OPCODE_CHAIN 0: // Conditional jump (JAE, JNAE, etc.) |
||
368 | // i_w is the invert flag, e.g. i_w == 1 means JNAE, whereas i_w == 0 means JAE |
||
369 | scratch_uchar = raw_opcode_id / 2 & 7; |
||
370 | 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]])) |
||
371 | OPCODE 1: // MOV reg, imm |
||
372 | i_w = !!(raw_opcode_id & 8); |
||
373 | R_M_OP(mem[GET_REG_ADDR(i_reg4bit)], =, i_data0) |
||
374 | OPCODE 3: // PUSH regs16 |
||
375 | R_M_PUSH(regs16[i_reg4bit]) |
||
376 | OPCODE 4: // POP regs16 |
||
377 | R_M_POP(regs16[i_reg4bit]) |
||
378 | OPCODE 2: // INC|DEC regs16 |
||
379 | i_w = 1; |
||
380 | i_d = 0; |
||
381 | i_reg = i_reg4bit; |
||
382 | DECODE_RM_REG; |
||
383 | i_reg = extra |
||
384 | OPCODE_CHAIN 5: // INC|DEC|JMP|CALL|PUSH |
||
385 | if (i_reg < 2) // INC|DEC |
||
386 | MEM_OP(op_from_addr, += 1 - 2 * i_reg +, REGS_BASE + 2 * REG_ZERO), |
||
387 | op_source = 1, |
||
388 | set_AF_OF_arith(), |
||
389 | set_OF(op_dest + 1 - i_reg == 1 << (TOP_BIT - 1)), |
||
390 | (xlat_opcode_id == 5) && (set_opcode(0x10), 0); // Decode like ADC |
||
391 | else if (i_reg != 6) // JMP|CALL |
||
392 | i_reg - 3 || R_M_PUSH(regs16[REG_CS]), // CALL (far) |
||
393 | i_reg & 2 && R_M_PUSH(reg_ip + 2 + i_mod*(i_mod != 3) + 2*(!i_mod && i_rm == 6)), // CALL (near or far) |
||
394 | i_reg & 1 && (regs16[REG_CS] = CAST(short)mem[op_from_addr + 2]), // JMP|CALL (far) |
||
395 | R_M_OP(reg_ip, =, mem[op_from_addr]), |
||
396 | set_opcode(0x9A); // Decode like CALL |
||
397 | else // PUSH |
||
398 | R_M_PUSH(mem[rm_addr]) |
||
399 | OPCODE 6: // TEST r/m, imm16 / NOT|NEG|MUL|IMUL|DIV|IDIV reg |
||
400 | op_to_addr = op_from_addr; |
||
401 | |||
402 | switch (i_reg) |
||
403 | { |
||
404 | OPCODE_CHAIN 0: // TEST |
||
405 | set_opcode(0x20); // Decode like AND |
||
406 | reg_ip += i_w + 1; |
||
407 | R_M_OP(mem[op_to_addr], &, i_data2) |
||
408 | OPCODE 2: // NOT |
||
409 | OP(=~) |
||
410 | OPCODE 3: // NEG |
||
411 | OP(=-); |
||
412 | op_dest = 0; |
||
413 | set_opcode(0x28); // Decode like SUB |
||
414 | set_CF(op_result > op_dest) |
||
415 | OPCODE 4: // MUL |
||
416 | i_w ? MUL_MACRO(unsigned short, regs16) : MUL_MACRO(unsigned char, regs8) |
||
417 | OPCODE 5: // IMUL |
||
418 | i_w ? MUL_MACRO(short, regs16) : MUL_MACRO(char, regs8) |
||
419 | OPCODE 6: // DIV |
||
420 | i_w ? DIV_MACRO(unsigned short, unsigned, regs16) : DIV_MACRO(unsigned char, unsigned short, regs8) |
||
421 | OPCODE 7: // IDIV |
||
422 | i_w ? DIV_MACRO(short, int, regs16) : DIV_MACRO(char, short, regs8); |
||
423 | } |
||
424 | OPCODE 7: // ADD|OR|ADC|SBB|AND|SUB|XOR|CMP AL/AX, immed |
||
425 | rm_addr = REGS_BASE; |
||
426 | i_data2 = i_data0; |
||
427 | i_mod = 3; |
||
428 | i_reg = extra; |
||
429 | reg_ip--; |
||
430 | OPCODE_CHAIN 8: // ADD|OR|ADC|SBB|AND|SUB|XOR|CMP reg, immed |
||
431 | op_to_addr = rm_addr; |
||
432 | regs16[REG_SCRATCH] = (i_d |= !i_w) ? (char)i_data2 : i_data2; |
||
433 | op_from_addr = REGS_BASE + 2 * REG_SCRATCH; |
||
434 | reg_ip += !i_d + 1; |
||
435 | set_opcode(0x08 * (extra = i_reg)); |
||
436 | OPCODE_CHAIN 9: // ADD|OR|ADC|SBB|AND|SUB|XOR|CMP|MOV reg, r/m |
||
437 | switch (extra) |
||
438 | { |
||
439 | OPCODE_CHAIN 0: // ADD |
||
440 | OP(+=), |
||
441 | set_CF(op_result < op_dest) |
||
442 | OPCODE 1: // OR |
||
443 | OP(|=) |
||
444 | OPCODE 2: // ADC |
||
445 | ADC_SBB_MACRO(+) |
||
446 | OPCODE 3: // SBB |
||
447 | ADC_SBB_MACRO(-) |
||
448 | OPCODE 4: // AND |
||
449 | OP(&=) |
||
450 | OPCODE 5: // SUB |
||
451 | OP(-=), |
||
452 | set_CF(op_result > op_dest) |
||
453 | OPCODE 6: // XOR |
||
454 | OP(^=) |
||
455 | OPCODE 7: // CMP |
||
456 | OP(-), |
||
457 | set_CF(op_result > op_dest) |
||
458 | OPCODE 8: // MOV |
||
459 | OP(=); |
||
460 | } |
||
461 | OPCODE 10: // MOV sreg, r/m | POP r/m | LEA reg, r/m |
||
462 | if (!i_w) // MOV |
||
463 | i_w = 1, |
||
464 | i_reg += 8, |
||
465 | DECODE_RM_REG, |
||
466 | OP(=); |
||
467 | else if (!i_d) // LEA |
||
468 | seg_override_en = 1, |
||
469 | seg_override = REG_ZERO, |
||
470 | DECODE_RM_REG, |
||
471 | R_M_OP(mem[op_from_addr], =, rm_addr); |
||
472 | else // POP |
||
473 | R_M_POP(mem[rm_addr]) |
||
474 | OPCODE 11: // MOV AL/AX, [loc] |
||
475 | i_mod = i_reg = 0; |
||
476 | i_rm = 6; |
||
477 | i_data1 = i_data0; |
||
478 | DECODE_RM_REG; |
||
479 | MEM_OP(op_from_addr, =, op_to_addr) |
||
480 | OPCODE 12: // ROL|ROR|RCL|RCR|SHL|SHR|???|SAR reg/mem, 1/CL/imm (80186) |
||
481 | scratch2_uint = SIGN_OF(mem[rm_addr]), |
||
482 | scratch_uint = extra ? // xxx reg/mem, imm |
||
483 | ++reg_ip, |
||
484 | (char)i_data1 |
||
485 | : // xxx reg/mem, CL |
||
486 | i_d |
||
487 | ? 31 & regs8[REG_CL] |
||
488 | : // xxx reg/mem, 1 |
||
489 | 1; |
||
490 | if (scratch_uint) |
||
491 | { |
||
492 | if (i_reg < 4) // Rotate operations |
||
493 | scratch_uint %= i_reg / 2 + TOP_BIT, |
||
494 | R_M_OP(scratch2_uint, =, mem[rm_addr]); |
||
495 | if (i_reg & 1) // Rotate/shift right operations |
||
496 | R_M_OP(mem[rm_addr], >>=, scratch_uint); |
||
497 | else // Rotate/shift left operations |
||
498 | R_M_OP(mem[rm_addr], <<=, scratch_uint); |
||
499 | if (i_reg > 3) // Shift operations |
||
500 | set_opcode(0x10); // Decode like ADC |
||
501 | if (i_reg > 4) // SHR or SAR |
||
502 | set_CF(op_dest >> (scratch_uint - 1) & 1); |
||
503 | } |
||
504 | |||
505 | switch (i_reg) |
||
506 | { |
||
507 | OPCODE_CHAIN 0: // ROL |
||
508 | R_M_OP(mem[rm_addr], += , scratch2_uint >> (TOP_BIT - scratch_uint)); |
||
509 | set_OF(SIGN_OF(op_result) ^ set_CF(op_result & 1)) |
||
510 | OPCODE 1: // ROR |
||
511 | scratch2_uint &= (1 << scratch_uint) - 1, |
||
512 | R_M_OP(mem[rm_addr], += , scratch2_uint << (TOP_BIT - scratch_uint)); |
||
513 | set_OF(SIGN_OF(op_result * 2) ^ set_CF(SIGN_OF(op_result))) |
||
514 | OPCODE 2: // RCL |
||
515 | R_M_OP(mem[rm_addr], += (regs8[FLAG_CF] << (scratch_uint - 1)) + , scratch2_uint >> (1 + TOP_BIT - scratch_uint)); |
||
516 | set_OF(SIGN_OF(op_result) ^ set_CF(scratch2_uint & 1 << (TOP_BIT - scratch_uint))) |
||
517 | OPCODE 3: // RCR |
||
518 | R_M_OP(mem[rm_addr], += (regs8[FLAG_CF] << (TOP_BIT - scratch_uint)) + , scratch2_uint << (1 + TOP_BIT - scratch_uint)); |
||
519 | set_CF(scratch2_uint & 1 << (scratch_uint - 1)); |
||
520 | set_OF(SIGN_OF(op_result) ^ SIGN_OF(op_result * 2)) |
||
521 | OPCODE 4: // SHL |
||
522 | set_OF(SIGN_OF(op_result) ^ set_CF(SIGN_OF(op_dest << (scratch_uint - 1)))) |
||
523 | OPCODE 5: // SHR |
||
524 | set_OF(SIGN_OF(op_dest)) |
||
525 | OPCODE 7: // SAR |
||
526 | scratch_uint < TOP_BIT || set_CF(scratch2_uint); |
||
527 | set_OF(0); |
||
528 | R_M_OP(mem[rm_addr], +=, scratch2_uint *= ~(((1 << TOP_BIT) - 1) >> scratch_uint)); |
||
529 | } |
||
530 | OPCODE 13: // LOOPxx|JCZX |
||
531 | scratch_uint = !!--regs16[REG_CX]; |
||
532 | |||
533 | switch(i_reg4bit) |
||
534 | { |
||
535 | OPCODE_CHAIN 0: // LOOPNZ |
||
536 | scratch_uint &= !regs8[FLAG_ZF] |
||
537 | OPCODE 1: // LOOPZ |
||
538 | scratch_uint &= regs8[FLAG_ZF] |
||
539 | OPCODE 3: // JCXXZ |
||
540 | scratch_uint = !++regs16[REG_CX]; |
||
541 | } |
||
542 | reg_ip += scratch_uint*(char)i_data0 |
||
543 | OPCODE 14: // JMP | CALL short/near |
||
544 | reg_ip += 3 - i_d; |
||
545 | if (!i_w) |
||
546 | { |
||
547 | if (i_d) // JMP far |
||
548 | reg_ip = 0, |
||
549 | regs16[REG_CS] = i_data2; |
||
550 | else // CALL |
||
551 | R_M_PUSH(reg_ip); |
||
552 | } |
||
553 | reg_ip += i_d && i_w ? (char)i_data0 : i_data0 |
||
554 | OPCODE 15: // TEST reg, r/m |
||
555 | MEM_OP(op_from_addr, &, op_to_addr) |
||
556 | OPCODE 16: // XCHG AX, regs16 |
||
557 | i_w = 1; |
||
558 | op_to_addr = REGS_BASE; |
||
559 | op_from_addr = GET_REG_ADDR(i_reg4bit); |
||
560 | OPCODE_CHAIN 24: // NOP|XCHG reg, r/m |
||
561 | if (op_to_addr != op_from_addr) |
||
562 | OP(^=), |
||
563 | MEM_OP(op_from_addr, ^=, op_to_addr), |
||
564 | OP(^=) |
||
565 | OPCODE 17: // MOVSx (extra=0)|STOSx (extra=1)|LODSx (extra=2) |
||
566 | scratch2_uint = seg_override_en ? seg_override : REG_DS; |
||
567 | |||
568 | for (scratch_uint = rep_override_en ? regs16[REG_CX] : 1; scratch_uint; scratch_uint--) |
||
569 | { |
||
570 | MEM_OP(extra < 2 ? SEGREG(REG_ES, REG_DI,) : REGS_BASE, =, extra & 1 ? REGS_BASE : SEGREG(scratch2_uint, REG_SI,)), |
||
571 | extra & 1 || INDEX_INC(REG_SI), |
||
572 | extra & 2 || INDEX_INC(REG_DI); |
||
573 | } |
||
574 | |||
575 | if (rep_override_en) |
||
576 | regs16[REG_CX] = 0 |
||
577 | OPCODE 18: // CMPSx (extra=0)|SCASx (extra=1) |
||
578 | scratch2_uint = seg_override_en ? seg_override : REG_DS; |
||
579 | |||
580 | if ((scratch_uint = rep_override_en ? regs16[REG_CX] : 1)) |
||
581 | { |
||
582 | for (; scratch_uint; rep_override_en || scratch_uint--) |
||
583 | { |
||
584 | MEM_OP(extra ? REGS_BASE : SEGREG(scratch2_uint, REG_SI,), -, SEGREG(REG_ES, REG_DI,)), |
||
585 | extra || INDEX_INC(REG_SI), |
||
586 | INDEX_INC(REG_DI), rep_override_en && !(--regs16[REG_CX] && (!op_result == rep_mode)) && (scratch_uint = 0); |
||
587 | } |
||
588 | |||
589 | set_flags_type = FLAGS_UPDATE_SZP | FLAGS_UPDATE_AO_ARITH; // Funge to set SZP/AO flags |
||
590 | set_CF(op_result > op_dest); |
||
591 | } |
||
592 | OPCODE 19: // RET|RETF|IRET |
||
593 | i_d = i_w; |
||
594 | R_M_POP(reg_ip); |
||
595 | if (extra) // IRET|RETF|RETF imm16 |
||
596 | R_M_POP(regs16[REG_CS]); |
||
597 | if (extra & 2) // IRET |
||
598 | set_flags(R_M_POP(scratch_uint)); |
||
599 | else if (!i_d) // RET|RETF imm16 |
||
600 | regs16[REG_SP] += i_data0 |
||
601 | OPCODE 20: // MOV r/m, immed |
||
602 | R_M_OP(mem[op_from_addr], =, i_data2) |
||
603 | OPCODE 21: // IN AL/AX, DX/imm8 |
||
604 | io_ports[0x20] = 0; // PIC EOI |
||
605 | io_ports[0x42] = --io_ports[0x40]; // PIT channel 0/2 read placeholder |
||
606 | io_ports[0x3DA] ^= 9; // CGA refresh |
||
607 | scratch_uint = extra ? regs16[REG_DX] : (unsigned char)i_data0; |
||
608 | scratch_uint == 0x60 && (io_ports[0x64] = 0); // Scancode read flag |
||
609 | 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 |
||
610 | R_M_OP(regs8[REG_AL], =, io_ports[scratch_uint]); |
||
611 | OPCODE 22: // OUT DX/imm8, AL/AX |
||
612 | scratch_uint = extra ? regs16[REG_DX] : (unsigned char)i_data0; |
||
613 | R_M_OP(io_ports[scratch_uint], =, regs8[REG_AL]); |
||
614 | scratch_uint == 0x61 && (io_hi_lo = 0, spkr_en |= regs8[REG_AL] & 3); // Speaker control |
||
615 | (scratch_uint == 0x40 || scratch_uint == 0x42) && (io_ports[0x43] & 6) && (mem[0x469 + scratch_uint - (io_hi_lo ^= 1)] = regs8[REG_AL]); // PIT rate programming |
||
616 | #ifndef NO_GRAPHICS |
||
617 | scratch_uint == 0x43 && (io_hi_lo = 0, regs8[REG_AL] >> 6 == 2) && (SDL_PauseAudio((regs8[REG_AL] & 0xF7) != 0xB6), 0); // Speaker enable |
||
618 | #endif |
||
619 | scratch_uint == 0x3D5 && (io_ports[0x3D4] >> 1 == 6) && (mem[0x4AD + !(io_ports[0x3D4] & 1)] = regs8[REG_AL]); // CRT video RAM start offset |
||
620 | 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 |
||
621 | scratch_uint == 0x3B5 && io_ports[0x3B4] == 1 && (GRAPHICS_X = regs8[REG_AL] * 16); // Hercules resolution reprogramming. Defaults are set in the BIOS |
||
622 | scratch_uint == 0x3B5 && io_ports[0x3B4] == 6 && (GRAPHICS_Y = regs8[REG_AL] * 4); |
||
623 | OPCODE 23: // REPxx |
||
624 | rep_override_en = 2; |
||
625 | rep_mode = i_w; |
||
626 | seg_override_en && seg_override_en++ |
||
627 | OPCODE 25: // PUSH reg |
||
628 | R_M_PUSH(regs16[extra]) |
||
629 | OPCODE 26: // POP reg |
||
630 | R_M_POP(regs16[extra]) |
||
631 | OPCODE 27: // xS: segment overrides |
||
632 | seg_override_en = 2; |
||
633 | seg_override = extra; |
||
634 | rep_override_en && rep_override_en++ |
||
635 | OPCODE 28: // DAA/DAS |
||
636 | i_w = 0; |
||
637 | extra ? DAA_DAS(-=, >=, 0xFF, 0x99) : DAA_DAS(+=, <, 0xF0, 0x90) // extra = 0 for DAA, 1 for DAS |
||
638 | OPCODE 29: // AAA/AAS |
||
639 | op_result = AAA_AAS(extra - 1) |
||
640 | OPCODE 30: // CBW |
||
641 | regs8[REG_AH] = -SIGN_OF(regs8[REG_AL]) |
||
642 | OPCODE 31: // CWD |
||
643 | regs16[REG_DX] = -SIGN_OF(regs16[REG_AX]) |
||
644 | OPCODE 32: // CALL FAR imm16:imm16 |
||
645 | R_M_PUSH(regs16[REG_CS]); |
||
646 | R_M_PUSH(reg_ip + 5); |
||
647 | regs16[REG_CS] = i_data2; |
||
648 | reg_ip = i_data0 |
||
649 | OPCODE 33: // PUSHF |
||
650 | make_flags(); |
||
651 | R_M_PUSH(scratch_uint) |
||
652 | OPCODE 34: // POPF |
||
653 | set_flags(R_M_POP(scratch_uint)) |
||
654 | OPCODE 35: // SAHF |
||
655 | make_flags(); |
||
656 | set_flags((scratch_uint & 0xFF00) + regs8[REG_AH]) |
||
657 | OPCODE 36: // LAHF |
||
658 | make_flags(), |
||
659 | regs8[REG_AH] = scratch_uint |
||
660 | OPCODE 37: // LES|LDS reg, r/m |
||
661 | i_w = i_d = 1; |
||
662 | DECODE_RM_REG; |
||
663 | OP(=); |
||
664 | MEM_OP(REGS_BASE + extra, =, rm_addr + 2) |
||
665 | OPCODE 38: // INT 3 |
||
666 | ++reg_ip; |
||
667 | pc_interrupt(3) |
||
668 | OPCODE 39: // INT imm8 |
||
669 | reg_ip += 2; |
||
670 | pc_interrupt(i_data0) |
||
671 | OPCODE 40: // INTO |
||
672 | ++reg_ip; |
||
673 | regs8[FLAG_OF] && pc_interrupt(4) |
||
674 | OPCODE 41: // AAM |
||
675 | if (i_data0 &= 0xFF) |
||
676 | regs8[REG_AH] = regs8[REG_AL] / i_data0, |
||
677 | op_result = regs8[REG_AL] %= i_data0; |
||
678 | else // Divide by zero |
||
679 | pc_interrupt(0) |
||
680 | OPCODE 42: // AAD |
||
681 | i_w = 0; |
||
682 | regs16[REG_AX] = op_result = 0xFF & regs8[REG_AL] + i_data0 * regs8[REG_AH] |
||
683 | OPCODE 43: // SALC |
||
684 | regs8[REG_AL] = -regs8[FLAG_CF] |
||
685 | OPCODE 44: // XLAT |
||
686 | regs8[REG_AL] = mem[SEGREG(seg_override_en ? seg_override : REG_DS, REG_BX, regs8[REG_AL] +)] |
||
687 | OPCODE 45: // CMC |
||
688 | regs8[FLAG_CF] ^= 1 |
||
689 | OPCODE 46: // CLC|STC|CLI|STI|CLD|STD |
||
690 | regs8[extra / 2] = extra & 1 |
||
691 | OPCODE 47: // TEST AL/AX, immed |
||
692 | R_M_OP(regs8[REG_AL], &, i_data0) |
||
693 | OPCODE 48: // Emulator-specific 0F xx opcodes |
||
694 | switch ((char)i_data0) |
||
695 | { |
||
696 | OPCODE_CHAIN 0: // PUTCHAR_AL |
||
697 | write(1, regs8, 1); |
||
8523 | maxcodehac | 698 | // printf("%c", regs8[0]); |
5203 | clevermous | 699 | OPCODE 1: // GET_RTC |
700 | time(&clock_buf); |
||
701 | ftime(&ms_clock); |
||
702 | memcpy(mem + SEGREG(REG_ES, REG_BX,), localtime(&clock_buf), sizeof(struct tm)); |
||
703 | CAST(short)mem[SEGREG(REG_ES, REG_BX, 36+)] = ms_clock.millitm; |
||
704 | OPCODE 2: // DISK_READ |
||
705 | OPCODE_CHAIN 3: // DISK_WRITE |
||
706 | regs8[REG_AL] = ~lseek(disk[regs8[REG_DL]], CAST(unsigned)regs16[REG_BP] << 9, 0) |
||
707 | ? ((char)i_data0 == 3 ? (int(*)())write : (int(*)())read)(disk[regs8[REG_DL]], mem + SEGREG(REG_ES, REG_BX,), regs16[REG_AX]) |
||
708 | : 0; |
||
709 | } |
||
710 | } |
||
711 | |||
712 | // Increment instruction pointer by computed instruction length. Tables in the BIOS binary |
||
713 | // help us here. |
||
714 | 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); |
||
715 | |||
716 | // If instruction needs to update SF, ZF and PF, set them as appropriate |
||
717 | if (set_flags_type & FLAGS_UPDATE_SZP) |
||
718 | { |
||
719 | regs8[FLAG_SF] = SIGN_OF(op_result); |
||
720 | regs8[FLAG_ZF] = !op_result; |
||
721 | regs8[FLAG_PF] = bios_table_lookup[TABLE_PARITY_FLAG][(unsigned char)op_result]; |
||
722 | |||
723 | // If instruction is an arithmetic or logic operation, also set AF/OF/CF as appropriate. |
||
724 | if (set_flags_type & FLAGS_UPDATE_AO_ARITH) |
||
725 | set_AF_OF_arith(); |
||
726 | if (set_flags_type & FLAGS_UPDATE_OC_LOGIC) |
||
727 | set_CF(0), set_OF(0); |
||
728 | } |
||
729 | |||
730 | // Poll timer/keyboard every KEYBOARD_TIMER_UPDATE_DELAY instructions |
||
731 | if (!(++inst_counter % KEYBOARD_TIMER_UPDATE_DELAY)) |
||
732 | int8_asap = 1; |
||
733 | |||
734 | #ifndef NO_GRAPHICS |
||
735 | // Update the video graphics display every GRAPHICS_UPDATE_DELAY instructions |
||
736 | if (!(inst_counter % GRAPHICS_UPDATE_DELAY)) |
||
737 | { |
||
738 | // Video card in graphics mode? |
||
739 | if (io_ports[0x3B8] & 2) |
||
740 | { |
||
741 | // If we don't already have an SDL window open, set it up and compute color and video memory translation tables |
||
742 | if (!sdl_screen) |
||
743 | { |
||
744 | for (int i = 0; i < 16; i++) |
||
745 | pixel_colors[i] = mem[0x4AC] ? // CGA? |
||
746 | cga_colors[(i & 12) >> 2] + (cga_colors[i & 3] << 16) // CGA -> RGB332 |
||
747 | : 0xFF*(((i & 1) << 24) + ((i & 2) << 15) + ((i & 4) << 6) + ((i & 8) >> 3)); // Hercules -> RGB332 |
||
748 | |||
749 | for (int i = 0; i < GRAPHICS_X * GRAPHICS_Y / 4; i++) |
||
750 | 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); |
||
751 | |||
752 | SDL_Init(SDL_INIT_VIDEO); |
||
753 | sdl_screen = SDL_SetVideoMode(GRAPHICS_X, GRAPHICS_Y, 8, 0); |
||
754 | SDL_EnableUNICODE(1); |
||
755 | SDL_EnableKeyRepeat(500, 30); |
||
756 | } |
||
757 | |||
758 | // Refresh SDL display from emulated graphics card video RAM |
||
759 | 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 |
||
760 | for (int i = 0; i < GRAPHICS_X * GRAPHICS_Y / 4; i++) |
||
761 | ((unsigned *)sdl_screen->pixels)[i] = pixel_colors[15 & (vid_mem_base[vid_addr_lookup[i]] >> 4*!(i & 1))]; |
||
762 | |||
763 | SDL_Flip(sdl_screen); |
||
764 | } |
||
765 | else if (sdl_screen) // Application has gone back to text mode, so close the SDL window |
||
766 | { |
||
767 | SDL_QuitSubSystem(SDL_INIT_VIDEO); |
||
768 | sdl_screen = 0; |
||
769 | } |
||
770 | SDL_PumpEvents(); |
||
771 | } |
||
772 | #endif |
||
773 | |||
774 | // Application has set trap flag, so fire INT 1 |
||
775 | if (trap_flag) |
||
776 | pc_interrupt(1); |
||
777 | |||
778 | trap_flag = regs8[FLAG_TF]; |
||
779 | |||
780 | // If a timer tick is pending, interrupts are enabled, and no overrides/REP are active, |
||
781 | // then process the tick and check for new keystrokes |
||
782 | if (int8_asap && !seg_override_en && !rep_override_en && regs8[FLAG_IF] && !regs8[FLAG_TF]) |
||
783 | pc_interrupt(0xA), int8_asap = 0, SDL_KEYBOARD_DRIVER; |
||
784 | } |
||
785 | |||
786 | #ifndef NO_GRAPHICS |
||
787 | SDL_Quit(); |
||
788 | #endif |
||
789 | return 0; |
||
790 | }>>><>><>><>><>>><>,>><>>><>>><>><>><>><>><>><>><>><>=,><=,>>>><>>>>>><>><>>><>> |