Subversion Repositories Kolibri OS

Rev

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
}