Add a clean copy of the 8086tiny emulator.
authorDavid Given <dg@cowlark.com>
Sun, 3 Jun 2018 10:13:26 +0000 (12:13 +0200)
committerDavid Given <dg@cowlark.com>
Sun, 3 Jun 2018 10:13:26 +0000 (12:13 +0200)
18 files changed:
plat/pc86/emu/8086tiny.c [new file with mode: 0644]
plat/pc86/emu/Makefile [new file with mode: 0644]
plat/pc86/emu/README.md [new file with mode: 0644]
plat/pc86/emu/bios [new file with mode: 0644]
plat/pc86/emu/bios_source/bios.asm [new file with mode: 0644]
plat/pc86/emu/docs/8086tiny.css [new file with mode: 0644]
plat/pc86/emu/docs/doc.html [new file with mode: 0644]
plat/pc86/emu/docs/images/button_active.gif [new file with mode: 0644]
plat/pc86/emu/docs/images/button_default.gif [new file with mode: 0644]
plat/pc86/emu/docs/images/logo.gif [new file with mode: 0644]
plat/pc86/emu/docs/images/templatemo_bg.gif [new file with mode: 0644]
plat/pc86/emu/docs/images/templatemo_content_bg.gif [new file with mode: 0644]
plat/pc86/emu/docs/images/templatemo_footer_bg.gif [new file with mode: 0644]
plat/pc86/emu/docs/images/templatemo_header_bg.gif [new file with mode: 0644]
plat/pc86/emu/docs/images/templatemo_side_bg.gif [new file with mode: 0644]
plat/pc86/emu/docs/images/templatemo_top_bg.gif [new file with mode: 0644]
plat/pc86/emu/license.txt [new file with mode: 0644]
plat/pc86/emu/runme [new file with mode: 0755]

diff --git a/plat/pc86/emu/8086tiny.c b/plat/pc86/emu/8086tiny.c
new file mode 100644 (file)
index 0000000..0d3e880
--- /dev/null
@@ -0,0 +1,760 @@
+// 8086tiny: a tiny, highly functional, highly portable PC emulator/VM
+// Copyright 2013-14, Adrian Cable (adrian.cable@gmail.com) - http://www.megalith.co.uk/8086tiny
+//
+// Revision 1.25
+//
+// This work is licensed under the MIT License. See included LICENSE.TXT.
+
+#include <time.h>
+#include <sys/timeb.h>
+#include <memory.h>
+
+#ifndef _WIN32
+#include <unistd.h>
+#include <fcntl.h>
+#endif
+
+#ifndef NO_GRAPHICS
+#include "SDL.h"
+#endif
+
+// Emulator system constants
+#define IO_PORT_COUNT 0x10000
+#define RAM_SIZE 0x10FFF0
+#define REGS_BASE 0xF0000
+#define VIDEO_RAM_SIZE 0x10000
+
+// Graphics/timer/keyboard update delays (explained later)
+#ifndef GRAPHICS_UPDATE_DELAY
+#define GRAPHICS_UPDATE_DELAY 360000
+#endif
+#define KEYBOARD_TIMER_UPDATE_DELAY 20000
+
+// 16-bit register decodes
+#define REG_AX 0
+#define REG_CX 1
+#define REG_DX 2
+#define REG_BX 3
+#define REG_SP 4
+#define REG_BP 5
+#define REG_SI 6
+#define REG_DI 7
+
+#define REG_ES 8
+#define REG_CS 9
+#define REG_SS 10
+#define REG_DS 11
+
+#define REG_ZERO 12
+#define REG_SCRATCH 13
+
+// 8-bit register decodes
+#define REG_AL 0
+#define REG_AH 1
+#define REG_CL 2
+#define REG_CH 3
+#define REG_DL 4
+#define REG_DH 5
+#define REG_BL 6
+#define REG_BH 7
+
+// FLAGS register decodes
+#define FLAG_CF 40
+#define FLAG_PF 41
+#define FLAG_AF 42
+#define FLAG_ZF 43
+#define FLAG_SF 44
+#define FLAG_TF 45
+#define FLAG_IF 46
+#define FLAG_DF 47
+#define FLAG_OF 48
+
+// Lookup tables in the BIOS binary
+#define TABLE_XLAT_OPCODE 8
+#define TABLE_XLAT_SUBFUNCTION 9
+#define TABLE_STD_FLAGS 10
+#define TABLE_PARITY_FLAG 11
+#define TABLE_BASE_INST_SIZE 12
+#define TABLE_I_W_SIZE 13
+#define TABLE_I_MOD_SIZE 14
+#define TABLE_COND_JUMP_DECODE_A 15
+#define TABLE_COND_JUMP_DECODE_B 16
+#define TABLE_COND_JUMP_DECODE_C 17
+#define TABLE_COND_JUMP_DECODE_D 18
+#define TABLE_FLAGS_BITFIELDS 19
+
+// Bitfields for TABLE_STD_FLAGS values
+#define FLAGS_UPDATE_SZP 1
+#define FLAGS_UPDATE_AO_ARITH 2
+#define FLAGS_UPDATE_OC_LOGIC 4
+
+// Helper macros
+
+// Decode mod, r_m and reg fields in instruction
+#define DECODE_RM_REG scratch2_uint = 4 * !i_mod, \
+                                         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), \
+                                         op_from_addr = GET_REG_ADDR(i_reg), \
+                                         i_d && (scratch_uint = op_from_addr, op_from_addr = rm_addr, op_to_addr = scratch_uint)
+
+// Return memory-mapped register location (offset into mem array) for register #reg_id
+#define GET_REG_ADDR(reg_id) (REGS_BASE + (i_w ? 2 * reg_id : 2 * reg_id + reg_id / 4 & 7))
+
+// Returns number of top bit in operand (i.e. 8 for 8-bit operands, 16 for 16-bit operands)
+#define TOP_BIT 8*(i_w + 1)
+
+// Opcode execution unit helpers
+#define OPCODE ;break; case
+#define OPCODE_CHAIN ; case
+
+// [I]MUL/[I]DIV/DAA/DAS/ADC/SBB helpers
+#define MUL_MACRO(op_data_type,out_regs) (set_opcode(0x10), \
+                                                                                 out_regs[i_w + 1] = (op_result = CAST(op_data_type)mem[rm_addr] * (op_data_type)*out_regs) >> 16, \
+                                                                                 regs16[REG_AX] = op_result, \
+                                                                                 set_OF(set_CF(op_result - (op_data_type)op_result)))
+#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)
+#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))), \
+                                                                 set_CF((((mask & 1 ? scratch2_uint : regs8[REG_AL]) & mask) > min) || regs8[FLAG_CF]) && (op_result = regs8[REG_AL] op1 0x60)
+#define ADC_SBB_MACRO(a) OP(a##= regs8[FLAG_CF] +), \
+                                                set_CF(regs8[FLAG_CF] && (op_result == op_dest) || (a op_result < a(int)op_dest)), \
+                                                set_AF_OF_arith()
+
+// Execute arithmetic/logic operations in emulator memory/registers
+#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) \
+                                                                : (op_dest = dest, op_result = dest op (op_source = CAST(unsigned char)src)))
+#define MEM_OP(dest,op,src) R_M_OP(mem[dest],op,mem[src])
+#define OP(op) MEM_OP(op_to_addr,op,op_from_addr)
+
+// Increment or decrement a register #reg_id (usually SI or DI), depending on direction flag and operand size (given by i_w)
+#define INDEX_INC(reg_id) (regs16[reg_id] -= (2 * regs8[FLAG_DF] - 1)*(i_w + 1))
+
+// Helpers for stack operations
+#define R_M_PUSH(a) (i_w = 1, R_M_OP(mem[SEGREG(REG_SS, REG_SP, --)], =, a))
+#define R_M_POP(a) (i_w = 1, regs16[REG_SP] += 2, R_M_OP(a, =, mem[SEGREG(REG_SS, REG_SP, -2+)]))
+
+// Convert segment:offset to linear address in emulator memory space
+#define SEGREG(reg_seg,reg_ofs,op) 16 * regs16[reg_seg] + (unsigned short)(op regs16[reg_ofs])
+
+// Returns sign bit of an 8-bit or 16-bit operand
+#define SIGN_OF(a) (1 & (i_w ? CAST(short)a : a) >> (TOP_BIT - 1))
+
+// Reinterpretation cast
+#define CAST(a) *(a*)&
+
+// Keyboard driver for console. This may need changing for UNIX/non-UNIX platforms
+#ifdef _WIN32
+#define KEYBOARD_DRIVER kbhit() && (mem[0x4A6] = getch(), pc_interrupt(7))
+#else
+#define KEYBOARD_DRIVER read(0, mem + 0x4A6, 1) && (int8_asap = (mem[0x4A6] == 0x1B), pc_interrupt(7))
+#endif
+
+// Keyboard driver for SDL
+#ifdef NO_GRAPHICS
+#define SDL_KEYBOARD_DRIVER KEYBOARD_DRIVER
+#else
+#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)
+#endif
+
+// Global variable definitions
+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];
+unsigned short *regs16, reg_ip, seg_override, file_index, wave_counter;
+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;
+int op_result, disk[3], scratch_int;
+time_t clock_buf;
+struct timeb ms_clock;
+
+#ifndef NO_GRAPHICS
+SDL_AudioSpec sdl_audio = {44100, AUDIO_U8, 1, 0, 128};
+SDL_Surface *sdl_screen;
+SDL_Event sdl_event;
+unsigned short vid_addr_lookup[VIDEO_RAM_SIZE], cga_colors[4] = {0 /* Black */, 0x1F1F /* Cyan */, 0xE3E3 /* Magenta */, 0xFFFF /* White */};
+#endif
+
+// Helper functions
+
+// Set carry flag
+char set_CF(int new_CF)
+{
+       return regs8[FLAG_CF] = !!new_CF;
+}
+
+// Set auxiliary flag
+char set_AF(int new_AF)
+{
+       return regs8[FLAG_AF] = !!new_AF;
+}
+
+// Set overflow flag
+char set_OF(int new_OF)
+{
+       return regs8[FLAG_OF] = !!new_OF;
+}
+
+// Set auxiliary and overflow flag after arithmetic operations
+char set_AF_OF_arith()
+{
+       set_AF((op_source ^= op_dest ^ op_result) & 0x10);
+       if (op_result == op_dest)
+               return set_OF(0);
+       else
+               return set_OF(1 & (regs8[FLAG_CF] ^ op_source >> (TOP_BIT - 1)));
+}
+
+// Assemble and return emulated CPU FLAGS register in scratch_uint
+void make_flags()
+{
+       scratch_uint = 0xF002; // 8086 has reserved and unused flags set to 1
+       for (int i = 9; i--;)
+               scratch_uint += regs8[FLAG_CF + i] << bios_table_lookup[TABLE_FLAGS_BITFIELDS][i];
+}
+
+// Set emulated CPU FLAGS register from regs8[FLAG_xx] values
+void set_flags(int new_flags)
+{
+       for (int i = 9; i--;)
+               regs8[FLAG_CF + i] = !!(1 << bios_table_lookup[TABLE_FLAGS_BITFIELDS][i] & new_flags);
+}
+
+// Convert raw opcode to translated opcode index. This condenses a large number of different encodings of similar
+// instructions into a much smaller number of distinct functions, which we then execute
+void set_opcode(unsigned char opcode)
+{
+       xlat_opcode_id = bios_table_lookup[TABLE_XLAT_OPCODE][raw_opcode_id = opcode];
+       extra = bios_table_lookup[TABLE_XLAT_SUBFUNCTION][opcode];
+       i_mod_size = bios_table_lookup[TABLE_I_MOD_SIZE][opcode];
+       set_flags_type = bios_table_lookup[TABLE_STD_FLAGS][opcode];
+}
+
+// Execute INT #interrupt_num on the emulated machine
+char pc_interrupt(unsigned char interrupt_num)
+{
+       set_opcode(0xCD); // Decode like INT
+
+       make_flags();
+       R_M_PUSH(scratch_uint);
+       R_M_PUSH(regs16[REG_CS]);
+       R_M_PUSH(reg_ip);
+       MEM_OP(REGS_BASE + 2 * REG_CS, =, 4 * interrupt_num + 2);
+       R_M_OP(reg_ip, =, mem[4 * interrupt_num]);
+
+       return regs8[FLAG_TF] = regs8[FLAG_IF] = 0;
+}
+
+// AAA and AAS instructions - which_operation is +1 for AAA, and -1 for AAS
+int AAA_AAS(char which_operation)
+{
+       return (regs16[REG_AX] += 262 * which_operation*set_AF(set_CF(((regs8[REG_AL] & 0x0F) > 9) || regs8[FLAG_AF])), regs8[REG_AL] &= 0x0F);
+}
+
+#ifndef NO_GRAPHICS
+void audio_callback(void *data, unsigned char *stream, int len)
+{
+       for (int i = 0; i < len; i++)
+               stream[i] = (spkr_en == 3) && CAST(unsigned short)mem[0x4AA] ? -((54 * wave_counter++ / CAST(unsigned short)mem[0x4AA]) & 1) : sdl_audio.silence;
+
+       spkr_en = io_ports[0x61] & 3;
+}
+#endif
+
+// Emulator entry point
+int main(int argc, char **argv)
+{
+#ifndef NO_GRAPHICS
+       // Initialise SDL
+       SDL_Init(SDL_INIT_AUDIO);
+       sdl_audio.callback = audio_callback;
+#ifdef _WIN32
+       sdl_audio.samples = 512;
+#endif
+       SDL_OpenAudio(&sdl_audio, 0);
+#endif
+
+       // regs16 and reg8 point to F000:0, the start of memory-mapped registers. CS is initialised to F000
+       regs16 = (unsigned short *)(regs8 = mem + REGS_BASE);
+       regs16[REG_CS] = 0xF000;
+
+       // Trap flag off
+       regs8[FLAG_TF] = 0;
+
+       // Set DL equal to the boot device: 0 for the FD, or 0x80 for the HD. Normally, boot from the FD.
+       // But, if the HD image file is prefixed with @, then boot from the HD
+       regs8[REG_DL] = ((argc > 3) && (*argv[3] == '@')) ? argv[3]++, 0x80 : 0;
+
+       // Open BIOS (file id disk[2]), floppy disk image (disk[1]), and hard disk image (disk[0]) if specified
+       for (file_index = 3; file_index;)
+               disk[--file_index] = *++argv ? open(*argv, 32898) : 0;
+
+       // Set CX:AX equal to the hard disk image size, if present
+       CAST(unsigned)regs16[REG_AX] = *disk ? lseek(*disk, 0, 2) >> 9 : 0;
+
+       // Load BIOS image into F000:0100, and set IP to 0100
+       read(disk[2], regs8 + (reg_ip = 0x100), 0xFF00);
+
+       // Load instruction decoding helper table
+       for (int i = 0; i < 20; i++)
+               for (int j = 0; j < 256; j++)
+                       bios_table_lookup[i][j] = regs8[regs16[0x81 + i] + j];
+
+       // Instruction execution loop. Terminates if CS:IP = 0:0
+       for (; opcode_stream = mem + 16 * regs16[REG_CS] + reg_ip, opcode_stream != mem;)
+       {
+               // Set up variables to prepare for decoding an opcode
+               set_opcode(*opcode_stream);
+
+               // Extract i_w and i_d fields from instruction
+               i_w = (i_reg4bit = raw_opcode_id & 7) & 1;
+               i_d = i_reg4bit / 2 & 1;
+
+               // Extract instruction data fields
+               i_data0 = CAST(short)opcode_stream[1];
+               i_data1 = CAST(short)opcode_stream[2];
+               i_data2 = CAST(short)opcode_stream[3];
+
+               // seg_override_en and rep_override_en contain number of instructions to hold segment override and REP prefix respectively
+               if (seg_override_en)
+                       seg_override_en--;
+               if (rep_override_en)
+                       rep_override_en--;
+
+               // i_mod_size > 0 indicates that opcode uses i_mod/i_rm/i_reg, so decode them
+               if (i_mod_size)
+               {
+                       i_mod = (i_data0 & 0xFF) >> 6;
+                       i_rm = i_data0 & 7;
+                       i_reg = i_data0 / 8 & 7;
+
+                       if ((!i_mod && i_rm == 6) || (i_mod == 2))
+                               i_data2 = CAST(short)opcode_stream[4];
+                       else if (i_mod != 1)
+                               i_data2 = i_data1;
+                       else // If i_mod is 1, operand is (usually) 8 bits rather than 16 bits
+                               i_data1 = (char)i_data1;
+
+                       DECODE_RM_REG;
+               }
+
+               // Instruction execution unit
+               switch (xlat_opcode_id)
+               {
+                       OPCODE_CHAIN 0: // Conditional jump (JAE, JNAE, etc.)
+                               // i_w is the invert flag, e.g. i_w == 1 means JNAE, whereas i_w == 0 means JAE 
+                               scratch_uchar = raw_opcode_id / 2 & 7;
+                               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]]))
+                       OPCODE 1: // MOV reg, imm
+                               i_w = !!(raw_opcode_id & 8);
+                               R_M_OP(mem[GET_REG_ADDR(i_reg4bit)], =, i_data0)
+                       OPCODE 3: // PUSH regs16
+                               R_M_PUSH(regs16[i_reg4bit])
+                       OPCODE 4: // POP regs16
+                               R_M_POP(regs16[i_reg4bit])
+                       OPCODE 2: // INC|DEC regs16
+                               i_w = 1;
+                               i_d = 0;
+                               i_reg = i_reg4bit;
+                               DECODE_RM_REG;
+                               i_reg = extra
+                       OPCODE_CHAIN 5: // INC|DEC|JMP|CALL|PUSH
+                               if (i_reg < 2) // INC|DEC
+                                       MEM_OP(op_from_addr, += 1 - 2 * i_reg +, REGS_BASE + 2 * REG_ZERO),
+                                       op_source = 1,
+                                       set_AF_OF_arith(),
+                                       set_OF(op_dest + 1 - i_reg == 1 << (TOP_BIT - 1)),
+                                       (xlat_opcode_id == 5) && (set_opcode(0x10), 0); // Decode like ADC
+                               else if (i_reg != 6) // JMP|CALL
+                                       i_reg - 3 || R_M_PUSH(regs16[REG_CS]), // CALL (far)
+                                       i_reg & 2 && R_M_PUSH(reg_ip + 2 + i_mod*(i_mod != 3) + 2*(!i_mod && i_rm == 6)), // CALL (near or far)
+                                       i_reg & 1 && (regs16[REG_CS] = CAST(short)mem[op_from_addr + 2]), // JMP|CALL (far)
+                                       R_M_OP(reg_ip, =, mem[op_from_addr]),
+                                       set_opcode(0x9A); // Decode like CALL
+                               else // PUSH
+                                       R_M_PUSH(mem[rm_addr])
+                       OPCODE 6: // TEST r/m, imm16 / NOT|NEG|MUL|IMUL|DIV|IDIV reg
+                               op_to_addr = op_from_addr;
+
+                               switch (i_reg)
+                               {
+                                       OPCODE_CHAIN 0: // TEST
+                                               set_opcode(0x20); // Decode like AND
+                                               reg_ip += i_w + 1;
+                                               R_M_OP(mem[op_to_addr], &, i_data2)
+                                       OPCODE 2: // NOT
+                                               OP(=~)
+                                       OPCODE 3: // NEG
+                                               OP(=-);
+                                               op_dest = 0;
+                                               set_opcode(0x28); // Decode like SUB
+                                               set_CF(op_result > op_dest)
+                                       OPCODE 4: // MUL
+                                               i_w ? MUL_MACRO(unsigned short, regs16) : MUL_MACRO(unsigned char, regs8)
+                                       OPCODE 5: // IMUL
+                                               i_w ? MUL_MACRO(short, regs16) : MUL_MACRO(char, regs8)
+                                       OPCODE 6: // DIV
+                                               i_w ? DIV_MACRO(unsigned short, unsigned, regs16) : DIV_MACRO(unsigned char, unsigned short, regs8)
+                                       OPCODE 7: // IDIV
+                                               i_w ? DIV_MACRO(short, int, regs16) : DIV_MACRO(char, short, regs8);
+                               }
+                       OPCODE 7: // ADD|OR|ADC|SBB|AND|SUB|XOR|CMP AL/AX, immed
+                               rm_addr = REGS_BASE;
+                               i_data2 = i_data0;
+                               i_mod = 3;
+                               i_reg = extra;
+                               reg_ip--;
+                       OPCODE_CHAIN 8: // ADD|OR|ADC|SBB|AND|SUB|XOR|CMP reg, immed
+                               op_to_addr = rm_addr;
+                               regs16[REG_SCRATCH] = (i_d |= !i_w) ? (char)i_data2 : i_data2;
+                               op_from_addr = REGS_BASE + 2 * REG_SCRATCH;
+                               reg_ip += !i_d + 1;
+                               set_opcode(0x08 * (extra = i_reg));
+                       OPCODE_CHAIN 9: // ADD|OR|ADC|SBB|AND|SUB|XOR|CMP|MOV reg, r/m
+                               switch (extra)
+                               {
+                                       OPCODE_CHAIN 0: // ADD
+                                               OP(+=),
+                                               set_CF(op_result < op_dest)
+                                       OPCODE 1: // OR
+                                               OP(|=)
+                                       OPCODE 2: // ADC
+                                               ADC_SBB_MACRO(+)
+                                       OPCODE 3: // SBB
+                                               ADC_SBB_MACRO(-)
+                                       OPCODE 4: // AND
+                                               OP(&=)
+                                       OPCODE 5: // SUB
+                                               OP(-=),
+                                               set_CF(op_result > op_dest)
+                                       OPCODE 6: // XOR
+                                               OP(^=)
+                                       OPCODE 7: // CMP
+                                               OP(-),
+                                               set_CF(op_result > op_dest)
+                                       OPCODE 8: // MOV
+                                               OP(=);
+                               }
+                       OPCODE 10: // MOV sreg, r/m | POP r/m | LEA reg, r/m
+                               if (!i_w) // MOV
+                                       i_w = 1,
+                                       i_reg += 8,
+                                       DECODE_RM_REG,
+                                       OP(=);
+                               else if (!i_d) // LEA
+                                       seg_override_en = 1,
+                                       seg_override = REG_ZERO,
+                                       DECODE_RM_REG,
+                                       R_M_OP(mem[op_from_addr], =, rm_addr);
+                               else // POP
+                                       R_M_POP(mem[rm_addr])
+                       OPCODE 11: // MOV AL/AX, [loc]
+                               i_mod = i_reg = 0;
+                               i_rm = 6;
+                               i_data1 = i_data0;
+                               DECODE_RM_REG;
+                               MEM_OP(op_from_addr, =, op_to_addr)
+                       OPCODE 12: // ROL|ROR|RCL|RCR|SHL|SHR|???|SAR reg/mem, 1/CL/imm (80186)
+                               scratch2_uint = SIGN_OF(mem[rm_addr]),
+                               scratch_uint = extra ? // xxx reg/mem, imm
+                                       ++reg_ip,
+                                       (char)i_data1
+                               : // xxx reg/mem, CL
+                                       i_d
+                                               ? 31 & regs8[REG_CL]
+                               : // xxx reg/mem, 1
+                                       1;
+                               if (scratch_uint)
+                               {
+                                       if (i_reg < 4) // Rotate operations
+                                               scratch_uint %= i_reg / 2 + TOP_BIT,
+                                               R_M_OP(scratch2_uint, =, mem[rm_addr]);
+                                       if (i_reg & 1) // Rotate/shift right operations
+                                               R_M_OP(mem[rm_addr], >>=, scratch_uint);
+                                       else // Rotate/shift left operations
+                                               R_M_OP(mem[rm_addr], <<=, scratch_uint);
+                                       if (i_reg > 3) // Shift operations
+                                               set_opcode(0x10); // Decode like ADC
+                                       if (i_reg > 4) // SHR or SAR
+                                               set_CF(op_dest >> (scratch_uint - 1) & 1);
+                               }
+
+                               switch (i_reg)
+                               {
+                                       OPCODE_CHAIN 0: // ROL
+                                               R_M_OP(mem[rm_addr], += , scratch2_uint >> (TOP_BIT - scratch_uint));
+                                               set_OF(SIGN_OF(op_result) ^ set_CF(op_result & 1))
+                                       OPCODE 1: // ROR
+                                               scratch2_uint &= (1 << scratch_uint) - 1,
+                                               R_M_OP(mem[rm_addr], += , scratch2_uint << (TOP_BIT - scratch_uint));
+                                               set_OF(SIGN_OF(op_result * 2) ^ set_CF(SIGN_OF(op_result)))
+                                       OPCODE 2: // RCL
+                                               R_M_OP(mem[rm_addr], += (regs8[FLAG_CF] << (scratch_uint - 1)) + , scratch2_uint >> (1 + TOP_BIT - scratch_uint));
+                                               set_OF(SIGN_OF(op_result) ^ set_CF(scratch2_uint & 1 << (TOP_BIT - scratch_uint)))
+                                       OPCODE 3: // RCR
+                                               R_M_OP(mem[rm_addr], += (regs8[FLAG_CF] << (TOP_BIT - scratch_uint)) + , scratch2_uint << (1 + TOP_BIT - scratch_uint));
+                                               set_CF(scratch2_uint & 1 << (scratch_uint - 1));
+                                               set_OF(SIGN_OF(op_result) ^ SIGN_OF(op_result * 2))
+                                       OPCODE 4: // SHL
+                                               set_OF(SIGN_OF(op_result) ^ set_CF(SIGN_OF(op_dest << (scratch_uint - 1))))
+                                       OPCODE 5: // SHR
+                                               set_OF(SIGN_OF(op_dest))
+                                       OPCODE 7: // SAR
+                                               scratch_uint < TOP_BIT || set_CF(scratch2_uint);
+                                               set_OF(0);
+                                               R_M_OP(mem[rm_addr], +=, scratch2_uint *= ~(((1 << TOP_BIT) - 1) >> scratch_uint));
+                               }
+                       OPCODE 13: // LOOPxx|JCZX
+                               scratch_uint = !!--regs16[REG_CX];
+
+                               switch(i_reg4bit)
+                               {
+                                       OPCODE_CHAIN 0: // LOOPNZ
+                                               scratch_uint &= !regs8[FLAG_ZF]
+                                       OPCODE 1: // LOOPZ
+                                               scratch_uint &= regs8[FLAG_ZF]
+                                       OPCODE 3: // JCXXZ
+                                               scratch_uint = !++regs16[REG_CX];
+                               }
+                               reg_ip += scratch_uint*(char)i_data0
+                       OPCODE 14: // JMP | CALL short/near
+                               reg_ip += 3 - i_d;
+                               if (!i_w)
+                               {
+                                       if (i_d) // JMP far
+                                               reg_ip = 0,
+                                               regs16[REG_CS] = i_data2;
+                                       else // CALL
+                                               R_M_PUSH(reg_ip);
+                               }
+                               reg_ip += i_d && i_w ? (char)i_data0 : i_data0
+                       OPCODE 15: // TEST reg, r/m
+                               MEM_OP(op_from_addr, &, op_to_addr)
+                       OPCODE 16: // XCHG AX, regs16
+                               i_w = 1;
+                               op_to_addr = REGS_BASE;
+                               op_from_addr = GET_REG_ADDR(i_reg4bit);
+                       OPCODE_CHAIN 24: // NOP|XCHG reg, r/m
+                               if (op_to_addr != op_from_addr)
+                                       OP(^=),
+                                       MEM_OP(op_from_addr, ^=, op_to_addr),
+                                       OP(^=)
+                       OPCODE 17: // MOVSx (extra=0)|STOSx (extra=1)|LODSx (extra=2)
+                               scratch2_uint = seg_override_en ? seg_override : REG_DS;
+
+                               for (scratch_uint = rep_override_en ? regs16[REG_CX] : 1; scratch_uint; scratch_uint--)
+                               {
+                                       MEM_OP(extra < 2 ? SEGREG(REG_ES, REG_DI,) : REGS_BASE, =, extra & 1 ? REGS_BASE : SEGREG(scratch2_uint, REG_SI,)),
+                                       extra & 1 || INDEX_INC(REG_SI),
+                                       extra & 2 || INDEX_INC(REG_DI);
+                               }
+
+                               if (rep_override_en)
+                                       regs16[REG_CX] = 0
+                       OPCODE 18: // CMPSx (extra=0)|SCASx (extra=1)
+                               scratch2_uint = seg_override_en ? seg_override : REG_DS;
+
+                               if ((scratch_uint = rep_override_en ? regs16[REG_CX] : 1))
+                               {
+                                       for (; scratch_uint; rep_override_en || scratch_uint--)
+                                       {
+                                               MEM_OP(extra ? REGS_BASE : SEGREG(scratch2_uint, REG_SI,), -, SEGREG(REG_ES, REG_DI,)),
+                                               extra || INDEX_INC(REG_SI),
+                                               INDEX_INC(REG_DI), rep_override_en && !(--regs16[REG_CX] && (!op_result == rep_mode)) && (scratch_uint = 0);
+                                       }
+
+                                       set_flags_type = FLAGS_UPDATE_SZP | FLAGS_UPDATE_AO_ARITH; // Funge to set SZP/AO flags
+                                       set_CF(op_result > op_dest);
+                               }
+                       OPCODE 19: // RET|RETF|IRET
+                               i_d = i_w;
+                               R_M_POP(reg_ip);
+                               if (extra) // IRET|RETF|RETF imm16
+                                       R_M_POP(regs16[REG_CS]);
+                               if (extra & 2) // IRET
+                                       set_flags(R_M_POP(scratch_uint));
+                               else if (!i_d) // RET|RETF imm16
+                                       regs16[REG_SP] += i_data0
+                       OPCODE 20: // MOV r/m, immed
+                               R_M_OP(mem[op_from_addr], =, i_data2)
+                       OPCODE 21: // IN AL/AX, DX/imm8
+                               io_ports[0x20] = 0; // PIC EOI
+                               io_ports[0x42] = --io_ports[0x40]; // PIT channel 0/2 read placeholder
+                               io_ports[0x3DA] ^= 9; // CGA refresh
+                               scratch_uint = extra ? regs16[REG_DX] : (unsigned char)i_data0;
+                               scratch_uint == 0x60 && (io_ports[0x64] = 0); // Scancode read flag
+                               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
+                               R_M_OP(regs8[REG_AL], =, io_ports[scratch_uint]);
+                       OPCODE 22: // OUT DX/imm8, AL/AX
+                               scratch_uint = extra ? regs16[REG_DX] : (unsigned char)i_data0;
+                               R_M_OP(io_ports[scratch_uint], =, regs8[REG_AL]);
+                               scratch_uint == 0x61 && (io_hi_lo = 0, spkr_en |= regs8[REG_AL] & 3); // Speaker control
+                               (scratch_uint == 0x40 || scratch_uint == 0x42) && (io_ports[0x43] & 6) && (mem[0x469 + scratch_uint - (io_hi_lo ^= 1)] = regs8[REG_AL]); // PIT rate programming
+#ifndef NO_GRAPHICS
+                               scratch_uint == 0x43 && (io_hi_lo = 0, regs8[REG_AL] >> 6 == 2) && (SDL_PauseAudio((regs8[REG_AL] & 0xF7) != 0xB6), 0); // Speaker enable
+#endif
+                               scratch_uint == 0x3D5 && (io_ports[0x3D4] >> 1 == 6) && (mem[0x4AD + !(io_ports[0x3D4] & 1)] = regs8[REG_AL]); // CRT video RAM start offset
+                               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
+                               scratch_uint == 0x3B5 && io_ports[0x3B4] == 1 && (GRAPHICS_X = regs8[REG_AL] * 16); // Hercules resolution reprogramming. Defaults are set in the BIOS
+                               scratch_uint == 0x3B5 && io_ports[0x3B4] == 6 && (GRAPHICS_Y = regs8[REG_AL] * 4);
+                       OPCODE 23: // REPxx
+                               rep_override_en = 2;
+                               rep_mode = i_w;
+                               seg_override_en && seg_override_en++
+                       OPCODE 25: // PUSH reg
+                               R_M_PUSH(regs16[extra])
+                       OPCODE 26: // POP reg
+                               R_M_POP(regs16[extra])
+                       OPCODE 27: // xS: segment overrides
+                               seg_override_en = 2;
+                               seg_override = extra;
+                               rep_override_en && rep_override_en++
+                       OPCODE 28: // DAA/DAS
+                               i_w = 0;
+                               extra ? DAA_DAS(-=, >=, 0xFF, 0x99) : DAA_DAS(+=, <, 0xF0, 0x90) // extra = 0 for DAA, 1 for DAS
+                       OPCODE 29: // AAA/AAS
+                               op_result = AAA_AAS(extra - 1)
+                       OPCODE 30: // CBW
+                               regs8[REG_AH] = -SIGN_OF(regs8[REG_AL])
+                       OPCODE 31: // CWD
+                               regs16[REG_DX] = -SIGN_OF(regs16[REG_AX])
+                       OPCODE 32: // CALL FAR imm16:imm16
+                               R_M_PUSH(regs16[REG_CS]);
+                               R_M_PUSH(reg_ip + 5);
+                               regs16[REG_CS] = i_data2;
+                               reg_ip = i_data0
+                       OPCODE 33: // PUSHF
+                               make_flags();
+                               R_M_PUSH(scratch_uint)
+                       OPCODE 34: // POPF
+                               set_flags(R_M_POP(scratch_uint))
+                       OPCODE 35: // SAHF
+                               make_flags();
+                               set_flags((scratch_uint & 0xFF00) + regs8[REG_AH])
+                       OPCODE 36: // LAHF
+                               make_flags(),
+                               regs8[REG_AH] = scratch_uint
+                       OPCODE 37: // LES|LDS reg, r/m
+                               i_w = i_d = 1;
+                               DECODE_RM_REG;
+                               OP(=);
+                               MEM_OP(REGS_BASE + extra, =, rm_addr + 2)
+                       OPCODE 38: // INT 3
+                               ++reg_ip;
+                               pc_interrupt(3)
+                       OPCODE 39: // INT imm8
+                               reg_ip += 2;
+                               pc_interrupt(i_data0)
+                       OPCODE 40: // INTO
+                               ++reg_ip;
+                               regs8[FLAG_OF] && pc_interrupt(4)
+                       OPCODE 41: // AAM
+                               if (i_data0 &= 0xFF)
+                                       regs8[REG_AH] = regs8[REG_AL] / i_data0,
+                                       op_result = regs8[REG_AL] %= i_data0;
+                               else // Divide by zero
+                                       pc_interrupt(0)
+                       OPCODE 42: // AAD
+                               i_w = 0;
+                               regs16[REG_AX] = op_result = 0xFF & regs8[REG_AL] + i_data0 * regs8[REG_AH]
+                       OPCODE 43: // SALC
+                               regs8[REG_AL] = -regs8[FLAG_CF]
+                       OPCODE 44: // XLAT
+                               regs8[REG_AL] = mem[SEGREG(seg_override_en ? seg_override : REG_DS, REG_BX, regs8[REG_AL] +)]
+                       OPCODE 45: // CMC
+                               regs8[FLAG_CF] ^= 1
+                       OPCODE 46: // CLC|STC|CLI|STI|CLD|STD
+                               regs8[extra / 2] = extra & 1
+                       OPCODE 47: // TEST AL/AX, immed
+                               R_M_OP(regs8[REG_AL], &, i_data0)
+                       OPCODE 48: // Emulator-specific 0F xx opcodes
+                               switch ((char)i_data0)
+                               {
+                                       OPCODE_CHAIN 0: // PUTCHAR_AL
+                                               write(1, regs8, 1)
+                                       OPCODE 1: // GET_RTC
+                                               time(&clock_buf);
+                                               ftime(&ms_clock);
+                                               memcpy(mem + SEGREG(REG_ES, REG_BX,), localtime(&clock_buf), sizeof(struct tm));
+                                               CAST(short)mem[SEGREG(REG_ES, REG_BX, 36+)] = ms_clock.millitm;
+                                       OPCODE 2: // DISK_READ
+                                       OPCODE_CHAIN 3: // DISK_WRITE
+                                               regs8[REG_AL] = ~lseek(disk[regs8[REG_DL]], CAST(unsigned)regs16[REG_BP] << 9, 0)
+                                                       ? ((char)i_data0 == 3 ? (int(*)())write : (int(*)())read)(disk[regs8[REG_DL]], mem + SEGREG(REG_ES, REG_BX,), regs16[REG_AX])
+                                                       : 0;
+                               }
+               }
+
+               // Increment instruction pointer by computed instruction length. Tables in the BIOS binary
+               // help us here.
+               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);
+
+               // If instruction needs to update SF, ZF and PF, set them as appropriate
+               if (set_flags_type & FLAGS_UPDATE_SZP)
+               {
+                       regs8[FLAG_SF] = SIGN_OF(op_result);
+                       regs8[FLAG_ZF] = !op_result;
+                       regs8[FLAG_PF] = bios_table_lookup[TABLE_PARITY_FLAG][(unsigned char)op_result];
+
+                       // If instruction is an arithmetic or logic operation, also set AF/OF/CF as appropriate.
+                       if (set_flags_type & FLAGS_UPDATE_AO_ARITH)
+                               set_AF_OF_arith();
+                       if (set_flags_type & FLAGS_UPDATE_OC_LOGIC)
+                               set_CF(0), set_OF(0);
+               }
+
+               // Poll timer/keyboard every KEYBOARD_TIMER_UPDATE_DELAY instructions
+               if (!(++inst_counter % KEYBOARD_TIMER_UPDATE_DELAY))
+                       int8_asap = 1;
+
+#ifndef NO_GRAPHICS
+               // Update the video graphics display every GRAPHICS_UPDATE_DELAY instructions
+               if (!(inst_counter % GRAPHICS_UPDATE_DELAY))
+               {
+                       // Video card in graphics mode?
+                       if (io_ports[0x3B8] & 2)
+                       {
+                               // If we don't already have an SDL window open, set it up and compute color and video memory translation tables
+                               if (!sdl_screen)
+                               {
+                                       for (int i = 0; i < 16; i++)
+                                               pixel_colors[i] = mem[0x4AC] ? // CGA?
+                                                       cga_colors[(i & 12) >> 2] + (cga_colors[i & 3] << 16) // CGA -> RGB332
+                                                       : 0xFF*(((i & 1) << 24) + ((i & 2) << 15) + ((i & 4) << 6) + ((i & 8) >> 3)); // Hercules -> RGB332
+
+                                       for (int i = 0; i < GRAPHICS_X * GRAPHICS_Y / 4; i++)
+                                               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);
+
+                                       SDL_Init(SDL_INIT_VIDEO);
+                                       sdl_screen = SDL_SetVideoMode(GRAPHICS_X, GRAPHICS_Y, 8, 0);
+                                       SDL_EnableUNICODE(1);
+                                       SDL_EnableKeyRepeat(500, 30);
+                               }
+
+                               // Refresh SDL display from emulated graphics card video RAM
+                               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
+                               for (int i = 0; i < GRAPHICS_X * GRAPHICS_Y / 4; i++)
+                                       ((unsigned *)sdl_screen->pixels)[i] = pixel_colors[15 & (vid_mem_base[vid_addr_lookup[i]] >> 4*!(i & 1))];
+
+                               SDL_Flip(sdl_screen);
+                       }
+                       else if (sdl_screen) // Application has gone back to text mode, so close the SDL window
+                       {
+                               SDL_QuitSubSystem(SDL_INIT_VIDEO);
+                               sdl_screen = 0;
+                       }
+                       SDL_PumpEvents();
+               }
+#endif
+
+               // Application has set trap flag, so fire INT 1
+               if (trap_flag)
+                       pc_interrupt(1);
+
+               trap_flag = regs8[FLAG_TF];
+
+               // If a timer tick is pending, interrupts are enabled, and no overrides/REP are active,
+               // then process the tick and check for new keystrokes
+               if (int8_asap && !seg_override_en && !rep_override_en && regs8[FLAG_IF] && !regs8[FLAG_TF])
+                       pc_interrupt(0xA), int8_asap = 0, SDL_KEYBOARD_DRIVER;
+       }
+
+#ifndef NO_GRAPHICS
+       SDL_Quit();
+#endif
+       return 0;
+}
diff --git a/plat/pc86/emu/Makefile b/plat/pc86/emu/Makefile
new file mode 100644 (file)
index 0000000..e976817
--- /dev/null
@@ -0,0 +1,28 @@
+# 8086tiny: a tiny, highly functional, highly portable PC emulator/VM
+# Copyright 2013-14, Adrian Cable (adrian.cable@gmail.com) - http://www.megalith.co.uk/8086tiny
+#
+# This work is licensed under the MIT License. See included LICENSE.TXT.
+
+# 8086tiny builds with graphics and sound support
+# 8086tiny_slowcpu improves graphics performance on slow platforms (e.g. Raspberry Pi)
+# no_graphics compiles without SDL graphics/sound
+
+OPTS_ALL=-O3 -fsigned-char -std=c99
+OPTS_SDL=`sdl-config --cflags --libs`
+OPTS_NOGFX=-DNO_GRAPHICS
+OPTS_SLOWCPU=-DGRAPHICS_UPDATE_DELAY=25000
+
+8086tiny: 8086tiny.c
+       ${CC} 8086tiny.c ${OPTS_SDL} ${OPTS_ALL} -o 8086tiny
+       strip 8086tiny
+
+8086tiny_slowcpu: 8086tiny.c
+       ${CC} 8086tiny.c ${OPTS_SDL} ${OPTS_ALL} ${OPTS_SLOWCPU} -o 8086tiny
+       strip 8086tiny
+
+no_graphics: 8086tiny.c
+       ${CC} 8086tiny.c ${OPTS_NOGFX} ${OPTS_ALL} -o 8086tiny
+       strip 8086tiny
+
+clean:
+       rm 8086tiny
diff --git a/plat/pc86/emu/README.md b/plat/pc86/emu/README.md
new file mode 100644 (file)
index 0000000..87bd012
--- /dev/null
@@ -0,0 +1,10 @@
+8086tiny
+========
+
+8086tiny is a completely free (MIT License) open source PC XT-compatible emulator/virtual machine written in C. It is, we believe, the smallest of its kind (the fully-commented source is under 25K). Despite its size, 8086tiny provides a highly accurate 8086 CPU emulation, together with support for PC peripherals including XT-style keyboard, floppy/hard disk, clock, audio, and Hercules/CGA graphics. 8086tiny is powerful enough to run software like AutoCAD, Windows 3.0, and legacy PC games: the 8086tiny distribution includes Alley Cat, the author's favorite PC game of all time.
+
+8086tiny is highly portable and runs on practically any little endian machine, from simple 32-bit MCUs upwards. 8086tiny has successfully been deployed on 32-bit/64-bit Intel machines (Windows, Mac OS X and Linux), Nexus 4/ARM (Android), iPad 3 and iPhone 5S (iOS), and Raspberry Pi (Linux).
+
+The philosophy of 8086tiny is to keep the code base as small as possible, and through the open source license encourage individual developers to tune and extend it as per their specific requirements, adding support, for example, for more complex instruction sets (e.g. Pentium) or peripherals (e.g. mouse). Forking this repository is highly encouraged!
+
+Any questions, comments or suggestions are very welcome in our forum at 8086tiny.freeforums.net.
diff --git a/plat/pc86/emu/bios b/plat/pc86/emu/bios
new file mode 100644 (file)
index 0000000..2d2ffe3
Binary files /dev/null and b/plat/pc86/emu/bios differ
diff --git a/plat/pc86/emu/bios_source/bios.asm b/plat/pc86/emu/bios_source/bios.asm
new file mode 100644 (file)
index 0000000..93eca85
--- /dev/null
@@ -0,0 +1,3851 @@
+; BIOS source for 8086tiny IBM PC emulator (revision 1.21 and above). Compiles with NASM.\r
+; Copyright 2013-14, Adrian Cable (adrian.cable@gmail.com) - http://www.megalith.co.uk/8086tiny\r
+;\r
+; Revision 1.61\r
+;\r
+; This work is licensed under the MIT License. See included LICENSE.TXT.\r
+\r
+       cpu     8086\r
+\r
+; Here we define macros for some custom instructions that help the emulator talk with the outside\r
+; world. They are described in detail in the hint.html file, which forms part of the emulator\r
+; distribution.\r
+\r
+%macro extended_putchar_al 0\r
+       db      0x0f, 0x00\r
+%endmacro\r
+\r
+%macro extended_get_rtc 0\r
+       db      0x0f, 0x01\r
+%endmacro\r
+\r
+%macro extended_read_disk 0\r
+       db      0x0f, 0x02\r
+%endmacro\r
+\r
+%macro extended_write_disk 0\r
+       db      0x0f, 0x03\r
+%endmacro\r
+\r
+org    100h                            ; BIOS loads at offset 0x0100\r
+\r
+main:\r
+\r
+       jmp     bios_entry\r
+\r
+; Here go pointers to the different data tables used for instruction decoding\r
+\r
+       dw      rm_mode12_reg1  ; Table 0: R/M mode 1/2 "register 1" lookup\r
+       dw      rm_mode012_reg2 ; Table 1: R/M mode 1/2 "register 2" lookup\r
+       dw      rm_mode12_disp  ; Table 2: R/M mode 1/2 "DISP multiplier" lookup\r
+       dw      rm_mode12_dfseg ; Table 3: R/M mode 1/2 "default segment" lookup\r
+       dw      rm_mode0_reg1   ; Table 4: R/M mode 0 "register 1" lookup\r
+       dw      rm_mode012_reg2 ; Table 5: R/M mode 0 "register 2" lookup\r
+       dw      rm_mode0_disp   ; Table 6: R/M mode 0 "DISP multiplier" lookup\r
+       dw      rm_mode0_dfseg  ; Table 7: R/M mode 0 "default segment" lookup\r
+       dw      xlat_ids        ; Table 8: Translation of raw opcode index ("Raw ID") to function number ("Xlat'd ID")\r
+       dw      ex_data         ; Table 9: Translation of Raw ID to Extra Data\r
+       dw      std_flags       ; Table 10: How each Raw ID sets the flags (bit 1 = sets SZP, bit 2 = sets AF/OF for arithmetic, bit 3 = sets OF/CF for logic)\r
+       dw      parity          ; Table 11: Parity flag loop-up table (256 entries)\r
+       dw      base_size       ; Table 12: Translation of Raw ID to base instruction size (bytes)\r
+       dw      i_w_adder       ; Table 13: Translation of Raw ID to i_w size adder yes/no\r
+       dw      i_mod_adder     ; Table 14: Translation of Raw ID to i_mod size adder yes/no\r
+       dw      jxx_dec_a       ; Table 15: Jxx decode table A\r
+       dw      jxx_dec_b       ; Table 16: Jxx decode table B\r
+       dw      jxx_dec_c       ; Table 17: Jxx decode table C\r
+       dw      jxx_dec_d       ; Table 18: Jxx decode table D\r
+       dw      flags_mult      ; Table 19: FLAGS multipliers\r
+\r
+; These values (BIOS ID string, BIOS date and so forth) go at the very top of memory\r
+\r
+biosstr        db      '8086tiny BIOS Revision 1.61!', 0, 0            ; Why not?\r
+mem_top        db      0xea, 0, 0x01, 0, 0xf0, '03/08/14', 0, 0xfe, 0\r
+\r
+bios_entry:\r
+\r
+       ; Set up initial stack to F000:F000\r
+\r
+       mov     sp, 0xf000\r
+       mov     ss, sp\r
+\r
+       push    cs\r
+       pop     es\r
+\r
+       push    ax\r
+\r
+       ; The emulator requires a few control registers in memory to always be zero for correct\r
+       ; instruction decoding (in particular, register look-up operations). These are the\r
+       ; emulator's zero segment (ZS) and always-zero flag (XF). Because the emulated memory\r
+       ; space is uninitialised, we need to be sure these values are zero before doing anything\r
+       ; else. The instructions we need to use to set them must not rely on look-up operations.\r
+       ; So e.g. MOV to memory is out but string operations are fine.\r
+\r
+       cld\r
+\r
+       xor     ax, ax\r
+       mov     di, 24\r
+       stosw                   ; Set ZS = 0\r
+       mov     di, 49\r
+       stosb                   ; Set XF = 0\r
+\r
+       ; Now we can do whatever we want! DL starts off being the boot disk.\r
+\r
+       mov     [cs:boot_device], dl\r
+\r
+       ; Set up Hercules graphics support. We start with the adapter in text mode\r
+\r
+       push    dx\r
+\r
+       mov     dx, 0x3b8\r
+       mov     al, 0\r
+       out     dx, al          ; Set Hercules support to text mode\r
+\r
+       mov     dx, 0x3b4\r
+       mov     al, 1           ; Hercules CRTC "horizontal displayed" register select\r
+       out     dx, al\r
+       mov     dx, 0x3b5\r
+       mov     al, 0x2d        ; 0x2D = 45 (* 16) = 720 pixels wide (GRAPHICS_X)\r
+       out     dx, al\r
+       mov     dx, 0x3b4\r
+       mov     al, 6           ; Hercules CRTC "vertical displayed" register select\r
+       out     dx, al\r
+       mov     dx, 0x3b5\r
+       mov     al, 0x57        ; 0x57 = 87 (* 4) = 348 pixels high (GRAPHICS_Y)\r
+       out     dx, al\r
+\r
+       pop     dx\r
+\r
+       pop     ax\r
+\r
+       ; Check cold boot/warm boot. We initialise disk parameters on cold boot only\r
+\r
+       cmp     byte [cs:boot_state], 0 ; Cold boot?\r
+       jne     boot\r
+\r
+       mov     byte [cs:boot_state], 1 ; Set flag so next boot will be warm boot\r
+\r
+       ; First, set up the disk subsystem. Only do this on the very first startup, when\r
+       ; the emulator sets up the CX/AX registers with disk information.\r
+\r
+       ; Compute the cylinder/head/sector count for the HD disk image, if present.\r
+       ; Total number of sectors is in CX:AX, or 0 if there is no HD image. First,\r
+       ; we put it in DX:CX.\r
+\r
+       mov     dx, cx\r
+       mov     cx, ax\r
+\r
+       mov     [cs:hd_secs_hi], dx\r
+       mov     [cs:hd_secs_lo], cx\r
+\r
+       cmp     cx, 0\r
+       je      maybe_no_hd\r
+\r
+       mov     word [cs:num_disks], 2\r
+       jmp     calc_hd\r
+\r
+maybe_no_hd:\r
+\r
+       cmp     dx, 0\r
+       je      no_hd\r
+\r
+       mov     word [cs:num_disks], 2\r
+       jmp     calc_hd\r
+\r
+no_hd:\r
+\r
+       mov     word [cs:num_disks], 1\r
+\r
+calc_hd:\r
+\r
+       mov     ax, cx\r
+       mov     word [cs:hd_max_track], 1\r
+       mov     word [cs:hd_max_head], 1\r
+\r
+       cmp     dx, 0           ; More than 63 total sectors? If so, we have more than 1 track.\r
+       ja      sect_overflow\r
+       cmp     ax, 63\r
+       ja      sect_overflow\r
+\r
+       mov     [cs:hd_max_sector], ax\r
+       jmp     calc_heads\r
+\r
+sect_overflow:\r
+\r
+       mov     cx, 63          ; Calculate number of tracks\r
+       div     cx\r
+       mov     [cs:hd_max_track], ax\r
+       mov     word [cs:hd_max_sector], 63\r
+\r
+calc_heads:\r
+\r
+       mov     dx, 0           ; More than 1024 tracks? If so, we have more than 1 head.\r
+       mov     ax, [cs:hd_max_track]\r
+       cmp     ax, 1024\r
+       ja      track_overflow\r
+       \r
+       jmp     calc_end\r
+\r
+track_overflow:\r
+\r
+       mov     cx, 1024\r
+       div     cx\r
+       mov     [cs:hd_max_head], ax\r
+       mov     word [cs:hd_max_track], 1024\r
+\r
+calc_end:\r
+\r
+       ; Convert number of tracks into maximum track (0-based) and then store in INT 41\r
+       ; HD parameter table\r
+\r
+       mov     ax, [cs:hd_max_head]\r
+       mov     [cs:int41_max_heads], al\r
+       mov     ax, [cs:hd_max_track]\r
+       mov     [cs:int41_max_cyls], ax\r
+       mov     ax, [cs:hd_max_sector]\r
+       mov     [cs:int41_max_sect], al\r
+\r
+       dec     word [cs:hd_max_track]\r
+       dec     word [cs:hd_max_head]\r
+       \r
+; Main BIOS entry point. Zero the flags, and set up registers.\r
+\r
+boot:  mov     ax, 0\r
+       push    ax\r
+       popf\r
+\r
+       push    cs\r
+       push    cs\r
+       pop     ds\r
+       pop     ss\r
+       mov     sp, 0xf000\r
+       \r
+; Set up the IVT. First we zero out the table\r
+\r
+       cld\r
+\r
+       xor     ax, ax\r
+       mov     es, ax\r
+       xor     di, di\r
+       mov     cx, 512\r
+       rep     stosw\r
+\r
+; Then we load in the pointers to our interrupt handlers\r
+\r
+       mov     di, 0\r
+       mov     si, int_table\r
+       mov     cx, [itbl_size]\r
+       rep     movsb\r
+\r
+; Set pointer to INT 41 table for hard disk\r
+\r
+       mov     cx, int41\r
+       mov     word [es:4*0x41], cx\r
+       mov     cx, 0xf000\r
+       mov     word [es:4*0x41 + 2], cx\r
+\r
+; Set up last 16 bytes of memory, including boot jump, BIOS date, machine ID byte\r
+\r
+       mov     ax, 0xffff\r
+       mov     es, ax\r
+       mov     di, 0\r
+       mov     si, mem_top\r
+       mov     cx, 16\r
+       rep     movsb\r
+\r
+; Set up the BIOS data area\r
+\r
+       mov     ax, 0x40\r
+       mov     es, ax\r
+       mov     di, 0\r
+       mov     si, bios_data\r
+       mov     cx, 0x100\r
+       rep     movsb\r
+\r
+; Clear video memory\r
+\r
+       mov     ax, 0xb800\r
+       mov     es, ax\r
+       mov     di, 0\r
+       mov     cx, 80*25\r
+       mov     ax, 0x0700\r
+       rep     stosw\r
+\r
+; Clear video memory shadow buffer\r
+\r
+       mov     ax, 0xc800\r
+       mov     es, ax\r
+       mov     di, 0\r
+       mov     cx, 80*25\r
+       mov     ax, 0x0700\r
+       rep     stosw\r
+\r
+; Set up some I/O ports, between 0 and FFF. Most of them we set to 0xFF, to indicate no device present\r
+\r
+       mov     dx, 0x61\r
+       mov     al, 0\r
+       out     dx, al          ; Make sure the speaker is off\r
+\r
+       mov     dx, 0x60\r
+       out     dx, al          ; No scancode\r
+\r
+       mov     dx, 0x64\r
+       out     dx, al          ; No key waiting\r
+\r
+       mov     dx, 0\r
+       mov     al, 0xFF\r
+\r
+next_out:\r
+\r
+       inc     dx\r
+\r
+       cmp     dx, 0x40        ; We deal with the PIT channel 0 later\r
+       je      next_out\r
+       cmp     dx, 0x42        ; We deal with the PIT channel 2 later\r
+       je      next_out\r
+       cmp     dx, 0x3B8       ; We deal with the Hercules port later, too\r
+       je      next_out\r
+       cmp     dx, 0x60        ; Keyboard scancode\r
+       je      next_out\r
+       cmp     dx, 0x61        ; Sound output\r
+       je      next_out\r
+       cmp     dx, 0x64        ; Keyboard status\r
+       je      next_out\r
+\r
+       out     dx, al\r
+\r
+       cmp     dx, 0xFFF\r
+       jl      next_out\r
+\r
+       mov     al, 0\r
+\r
+       mov     dx, 0x3DA       ; CGA refresh port\r
+       out     dx, al\r
+\r
+       mov     dx, 0x3BA       ; Hercules detection port\r
+       out     dx, al\r
+\r
+       mov     dx, 0x3B8       ; Hercules video mode port\r
+       out     dx, al\r
+\r
+       mov     dx, 0x3BC       ; LPT1\r
+       out     dx, al\r
+\r
+       mov     dx, 0x62        ; PPI - needed for memory parity checks\r
+       out     dx, al\r
+\r
+; Get initial RTC value\r
+\r
+       push    cs\r
+       pop     es\r
+       mov     bx, timetable\r
+       extended_get_rtc\r
+       mov     ax, [es:tm_msec]\r
+       mov     [cs:last_int8_msec], ax\r
+\r
+; Read boot sector from FDD, and load it into 0:7C00\r
+\r
+       mov     ax, 0\r
+       mov     es, ax\r
+\r
+       mov     ax, 0x0201\r
+       mov     dh, 0\r
+       mov     dl, [cs:boot_device]\r
+       mov     cx, 1\r
+       mov     bx, 0x7c00\r
+       int     13h\r
+\r
+; Jump to boot sector\r
+\r
+       jmp     0:0x7c00\r
+\r
+; ************************* INT 7h handler - keyboard driver (8086tiny internal)\r
+\r
+int7:  ; Whenever the user presses a key, INT 7 is called by the emulator.\r
+       ; ASCII character of the keystroke is at 0040:this_keystroke\r
+\r
+       push    ds\r
+       push    es\r
+       push    ax\r
+       push    bx\r
+       push    bp\r
+\r
+       push    cs\r
+       pop     ds\r
+\r
+       mov     bx, 0x40        ; Set segment to BIOS data area segment (0x40)\r
+       mov     es, bx\r
+\r
+       ; Retrieve the keystroke\r
+\r
+       mov     ax, [es:this_keystroke-bios_data]\r
+       mov     byte [es:this_keystroke+1-bios_data], 0\r
+\r
+  real_key:\r
+\r
+       mov     byte [cs:last_key_sdl], 0\r
+\r
+       test    ah, 4 ; This key doesn't come from SDL\r
+       jz      check_linux_bksp\r
+\r
+       mov     byte [es:keyflags1-bios_data], 0\r
+       mov     byte [es:keyflags2-bios_data], 0\r
+\r
+       mov     byte [cs:last_key_sdl], 1 ; Key down from SDL\r
+\r
+       test    ah, 0x40 ; Key up\r
+       jz      sdl_check_specials\r
+\r
+       mov     byte [cs:last_key_sdl], 2 ; Key up from SDL\r
+\r
+  sdl_check_specials:\r
+\r
+       mov     bx, ax\r
+       and     bh, 7 ; If key is between 52F and 534 (Shift/Ctrl/Alt), ignore the key state flags\r
+       cmp     bx, 0x52f\r
+       je      sdl_just_press_shift\r
+       cmp     bx, 0x530\r
+       je      sdl_just_press_shift\r
+       cmp     bx, 0x533\r
+       je      sdl_just_press_alt\r
+       cmp     bx, 0x534\r
+       je      sdl_just_press_alt\r
+       cmp     bx, 0x531\r
+       je      sdl_just_press_ctrl\r
+       cmp     bx, 0x532\r
+       je      sdl_just_press_ctrl\r
+       jmp     sdl_check_alt\r
+\r
+  sdl_just_press_shift:\r
+\r
+       mov     al, 0x36 ; Shift\r
+       and     ah, 0x40 ; Key up?\r
+       add     al, ah\r
+       add     al, ah\r
+       call    io_key_available\r
+       jmp     i2_dne\r
+\r
+  sdl_just_press_alt:\r
+\r
+       mov     al, 0x38 ; Alt\r
+       and     ah, 0x40 ; Key up?\r
+       add     al, ah\r
+       add     al, ah\r
+       call    io_key_available\r
+       jmp     i2_dne\r
+\r
+  sdl_just_press_ctrl:\r
+\r
+       mov     al, 0x1d ; Ctrl\r
+       and     ah, 0x40 ; Key up?\r
+       add     al, ah\r
+       add     al, ah\r
+       call    io_key_available\r
+       jmp     i2_dne\r
+\r
+  sdl_check_alt:\r
+\r
+       test    ah, 8 ; Alt+something?\r
+       jz      sdl_no_alt\r
+       add     byte [es:keyflags1-bios_data], 8\r
+       add     byte [es:keyflags2-bios_data], 2\r
+\r
+  sdl_no_alt:\r
+\r
+       test    ah, 0x20 ; Ctrl+something?\r
+       jz      sdl_no_ctrl\r
+       add     byte [es:keyflags1-bios_data], 4\r
+\r
+  sdl_no_ctrl:\r
+\r
+       test    ah, 0x10 ; Shift+something?\r
+       jz      sdl_no_mods\r
+       add     byte [es:keyflags1-bios_data], 1\r
+\r
+  sdl_no_mods:\r
+\r
+       and     ah, 1 ; We have processed all SDL modifiers, so remove them\r
+\r
+       ;cmp    ax, 160 ; Alt+Space?\r
+       ;jne    next_sdl_alt_keys\r
+       ;mov    al, ' '\r
+       ;mov    byte [es:this_keystroke-bios_data], al\r
+\r
+  check_sdl_f_keys:\r
+\r
+       cmp     ax, 0x125\r
+       ja      i2_dne ; Unknown key\r
+\r
+       cmp     ax, 0x11a\r
+       jb      check_sdl_pgup_pgdn_keys\r
+\r
+       sub     ax, 0xdf ; F1 - F10\r
+       cmp     ax, 0x45\r
+       jb      check_sdl_f_keys2\r
+       add     ax, 0x12 ; F11 - F12\r
+\r
+  check_sdl_f_keys2:\r
+\r
+       mov     bh, al\r
+       mov     al, 0\r
+       jmp     sdl_scancode_xlat_done\r
+\r
+  check_sdl_pgup_pgdn_keys:\r
+\r
+       cmp     ax, 0x116\r
+       jb      check_sdl_cursor_keys\r
+       cmp     ax, 0x119\r
+       ja      check_sdl_cursor_keys\r
+\r
+       sub     ax, 0x116\r
+       mov     bx, pgup_pgdn_xlt\r
+       cs      xlat\r
+\r
+       mov     bh, al\r
+       mov     al, 0\r
+       jmp     sdl_scancode_xlat_done\r
+\r
+  check_sdl_cursor_keys:\r
+\r
+       cmp     ax, 0x111 ; SDL cursor keys\r
+       jb      sdl_process_key ; No special handling for other keys yet\r
+       \r
+       sub     ax, 0x111\r
+       mov     bx, unix_cursor_xlt\r
+       xlat    ; Convert SDL cursor keys to scancode\r
+\r
+       mov     bh, al\r
+       mov     al, 0\r
+       mov     byte [es:this_keystroke-bios_data], 0\r
+       jmp     sdl_scancode_xlat_done\r
+\r
+  sdl_process_key:\r
+\r
+       cmp     ax, 0x100\r
+       jae     i2_dne ; Unsupported key\r
+       cmp     al, 0x7f ; SDL 0x7F backspace? Convert to 0x08\r
+       jne     sdl_process_key2\r
+       mov     al, 8\r
+\r
+  sdl_process_key2:\r
+\r
+       push    ax\r
+       mov     bx, a2scan_tbl ; ASCII to scancode table\r
+       xlat\r
+       mov     bh, al\r
+       pop     ax ; Scancode in BH, keycode in AL\r
+\r
+  sdl_scancode_xlat_done:\r
+\r
+       add     bh, 0x80 ; Key up scancode\r
+       cmp     byte [cs:last_key_sdl], 2 ; Key up?\r
+       je      sdl_not_in_buf\r
+\r
+       sub     bh, 0x80 ; Key down scancode\r
+\r
+  sdl_key_down:\r
+\r
+       mov     [es:this_keystroke-bios_data], al\r
+               \r
+  sdl_not_in_buf:\r
+\r
+       mov     al, bh\r
+       call    io_key_available\r
+       jmp     i2_dne  \r
+\r
+  check_linux_bksp:\r
+\r
+       cmp     al, 0 ; Null keystroke - ignore\r
+       je      i2_dne\r
+\r
+       cmp     al, 0x7f ; Linux code for backspace - change to 8\r
+       jne     after_check_bksp\r
+\r
+       mov     al, 8\r
+       mov     byte [es:this_keystroke-bios_data], 8\r
+\r
+  after_check_bksp:\r
+\r
+       cmp     byte [es:next_key_fn-bios_data], 1 ; If previous keypress was Ctrl+F (signifying this key is is Fxx), skip checks for Ctrl+A (Alt+xx) and Ctrl+F (Fxx)\r
+       je      i2_n\r
+\r
+       cmp     al, 0x01 ; Ctrl+A pressed - this is the sequence for "next key is Alt+"\r
+       jne     i2_not_alt\r
+\r
+       mov     byte [es:keyflags1-bios_data], 8 ; Alt flag down\r
+       mov     byte [es:keyflags2-bios_data], 2 ; Alt flag down\r
+       mov     al, 0x38 ; Simulated Alt by Ctrl+A prefix?\r
+       call    io_key_available\r
+\r
+       mov     byte [es:next_key_alt-bios_data], 1\r
+       jmp     i2_dne\r
+\r
+  i2_not_alt:\r
+\r
+       cmp     al, 0x06 ; Ctrl+F pressed - this is the sequence for "next key is Fxx"\r
+       jne     i2_not_fn\r
+\r
+       mov     byte [es:next_key_fn-bios_data], 1\r
+       jmp     i2_dne\r
+\r
+  i2_not_fn:\r
+\r
+       cmp     byte [es:notranslate_flg-bios_data], 1 ; If no translation mode is on, just pass through the scan code. ASCII key is zero.\r
+       mov     byte [es:notranslate_flg-bios_data], 0\r
+       jne     need_to_translate\r
+\r
+       mov     byte [es:this_keystroke-bios_data], 0\r
+       jmp     after_translate\r
+\r
+  need_to_translate:\r
+\r
+       cmp     al, 0xe0 ; Some OSes return scan codes after 0xE0 for things like cursor moves. So, if we find it, set a flag saying the next code received should not be translated.\r
+       mov     byte [es:notranslate_flg-bios_data], 1\r
+       je      i2_dne  ; Don't add the 0xE0 to the keyboard buffer\r
+\r
+       mov     byte [es:notranslate_flg-bios_data], 0\r
+\r
+       cmp     al, 0x1b ; ESC key pressed. Either this a "real" escape, or it is UNIX cursor keys. In either case, we do nothing now, except set a flag\r
+       jne     i2_escnext\r
+\r
+       ; If the last key pressed was ESC, then we need to stuff it\r
+       cmp     byte [es:escape_flag-bios_data], 1\r
+       jne     i2_sf\r
+\r
+       ; Stuff an ESC character\r
+       \r
+       mov     byte [es:this_keystroke-bios_data], 0x1b\r
+\r
+       mov     al, 0x01\r
+       call    keypress_release\r
+\r
+  i2_sf:\r
+\r
+       mov     byte [es:escape_flag-bios_data], 1\r
+       jmp     i2_dne\r
+\r
+  i2_escnext:\r
+\r
+       ; Check if the last key was an escape character\r
+       cmp     byte [es:escape_flag-bios_data], 1\r
+       jne     i2_noesc\r
+\r
+       ; It is, so check if this key is a [ control character\r
+       cmp     al, '[' ; [ key pressed\r
+       je      i2_esc\r
+\r
+       ; It isn't, so stuff an ESC character plus this key\r
+       \r
+       mov     byte [es:this_keystroke-bios_data], 0x1b\r
+\r
+       mov     al, 0x01\r
+       call    keypress_release\r
+\r
+       ; Now actually process this key\r
+       mov     byte [es:escape_flag-bios_data], 0\r
+       mov     al, [es:this_keystroke-bios_data]\r
+       jmp     i2_noesc\r
+\r
+  i2_esc:\r
+\r
+       ; Last + this characters are ESC ] - do nothing now, but set escape flag\r
+       mov     byte [es:escape_flag-bios_data], 2\r
+       jmp     i2_dne\r
+\r
+  i2_noesc:\r
+\r
+       cmp     byte [es:escape_flag-bios_data], 2\r
+       jne     i2_regular_key\r
+\r
+       ; No shifts or Alt for cursor keys\r
+       mov     byte [es:keyflags1-bios_data], 0\r
+       mov     byte [es:keyflags2-bios_data], 0\r
+\r
+       ; Last + this characters are ESC ] xxx - cursor key, so translate and stuff it\r
+       sub     al, 'A'\r
+       mov     bx, unix_cursor_xlt\r
+       xlat\r
+\r
+       mov     byte [es:this_keystroke-bios_data], 0\r
+       jmp     after_translate\r
+       \r
+  i2_regular_key:\r
+\r
+       mov     byte [es:notranslate_flg-bios_data], 0\r
+\r
+       mov     bx, a2shift_tbl ; ASCII to shift code table\r
+       xlat\r
+\r
+       ; Now, BL is 1 if shift is down, 0 otherwise. If shift is down, put a shift down scan code\r
+       ; in port 0x60. Then call int 9. Otherwise, put a shift up scan code in, and call int 9.\r
+\r
+       push    ax\r
+\r
+       ; Put shift flags in BIOS, 0040:0017. Add 8 to shift flags if Alt is down.\r
+       mov     ah, [es:next_key_alt-bios_data]\r
+       cpu     186\r
+       shl     ah, 3\r
+       cpu     8086\r
+       add     al, ah\r
+\r
+       cmp     byte [es:this_keystroke-bios_data], 0x1A ; Ctrl+A to Ctrl+Z? Then add Ctrl to BIOS key flags\r
+       ja      i2_no_ctrl\r
+       cmp     byte [es:this_keystroke-bios_data], 0\r
+       je      i2_no_ctrl\r
+       cmp     byte [es:this_keystroke-bios_data], 0xD ; CR\r
+       je      i2_no_ctrl\r
+       cmp     byte [es:this_keystroke-bios_data], 0xA ; LF\r
+       je      i2_no_ctrl\r
+       cmp     byte [es:this_keystroke-bios_data], 0x8 ; Backspace\r
+       je      i2_no_ctrl\r
+       cmp     byte [es:this_keystroke-bios_data], 0x9 ; Tab\r
+       je      i2_no_ctrl\r
+       add     al, 4 ; Ctrl in key flags\r
+\r
+       push    ax\r
+       mov     al, 0x1d ; Ctrl key down\r
+       call    io_key_available\r
+       pop     ax\r
+\r
+  i2_no_ctrl:\r
+\r
+       mov     [es:keyflags1-bios_data], al\r
+\r
+       cpu     186\r
+       shr     ah, 2\r
+       cpu     8086\r
+       mov     [es:keyflags2-bios_data], ah\r
+\r
+       pop     ax\r
+\r
+       test    al, 1 ; Shift down?\r
+       jz      i2_n\r
+\r
+       mov     al, 0x36 ; Right shift down\r
+       call    io_key_available\r
+\r
+  i2_n:\r
+\r
+       mov     al, [es:this_keystroke-bios_data]\r
+\r
+       mov     bx, a2scan_tbl ; ASCII to scan code table\r
+       xlat\r
+\r
+       cmp     byte [es:next_key_fn-bios_data], 1      ; Fxx?\r
+       jne     after_translate\r
+\r
+       cmp     byte [es:this_keystroke-bios_data], 1 ; Ctrl+F then Ctrl+A outputs code for Ctrl+A\r
+       je      after_translate\r
+\r
+       cmp     byte [es:this_keystroke-bios_data], 6 ; Ctrl+F then Ctrl+F outputs code for Ctrl+F  \r
+       je      after_translate\r
+       \r
+       mov     byte [es:this_keystroke-bios_data], 0   ; Fxx key, so zero out ASCII code\r
+       add     al, 0x39\r
+\r
+  after_translate:\r
+\r
+       mov     byte [es:escape_flag-bios_data], 0\r
+       mov     byte [es:escape_flag_last-bios_data], 0\r
+\r
+       ; If the key is actually an Alt+ key we use an ASCII code of 0 instead of the real value.\r
+\r
+       cmp     byte [es:next_key_alt-bios_data], 1\r
+       jne     skip_ascii_zero\r
+\r
+       mov     byte [es:this_keystroke-bios_data], 0\r
+\r
+  skip_ascii_zero:\r
+\r
+       ; Output key down/key up event (scancode in AL) to keyboard port\r
+       call    keypress_release\r
+\r
+       ; If scan code is not 0xE0, then also send right shift up if necessary\r
+       cmp     al, 0xe0\r
+       je      i2_dne\r
+\r
+       test    byte [es:keyflags1-bios_data], 1\r
+       jz      check_ctrl\r
+\r
+       mov     al, 0xb6 ; Right shift up\r
+       call    io_key_available\r
+\r
+  check_ctrl:\r
+\r
+       test    byte [es:keyflags1-bios_data], 4\r
+       jz      check_alt\r
+\r
+       mov     al, 0x9d ; Right Ctrl up\r
+       call    io_key_available\r
+\r
+  check_alt:\r
+\r
+       mov     al, byte [es:next_key_alt-bios_data]\r
+       mov     byte [es:next_key_alt-bios_data], 0\r
+       mov     byte [es:next_key_fn-bios_data], 0\r
+\r
+       cmp     al, 1\r
+       je      endalt\r
+\r
+       jmp     i2_dne\r
+\r
+  endalt:\r
+\r
+       mov     al, 0xb8 ; Left Alt up\r
+       call    io_key_available\r
+\r
+  i2_dne:\r
+\r
+       pop     bp\r
+       pop     bx\r
+       pop     ax\r
+       pop     es\r
+       pop     ds\r
+       iret\r
+\r
+; ************************* INT 9h handler - keyboard (PC BIOS standard)\r
+\r
+int9:\r
+\r
+       push    es\r
+       push    ax\r
+       push    bx\r
+       push    bp\r
+\r
+       in      al, 0x60\r
+\r
+       cmp     al, 0x80 ; Key up?\r
+       jae     no_add_buf\r
+       cmp     al, 0x36 ; Shift?\r
+       je      no_add_buf\r
+       cmp     al, 0x38 ; Alt?\r
+       je      no_add_buf\r
+       cmp     al, 0x1d ; Ctrl?\r
+       je      no_add_buf\r
+\r
+       mov     bx, 0x40\r
+       mov     es, bx\r
+\r
+       mov     bh, al\r
+       mov     al, [es:this_keystroke-bios_data]\r
+\r
+       ; Tail of the BIOS keyboard buffer goes in BP. This is where we add new keystrokes\r
+\r
+       mov     bp, [es:kbbuf_tail-bios_data]\r
+       mov     byte [es:bp], al ; ASCII code\r
+       mov     byte [es:bp+1], bh ; Scan code\r
+\r
+       ; ESC keystroke is in the buffer now\r
+       add     word [es:kbbuf_tail-bios_data], 2\r
+       call    kb_adjust_buf ; Wrap the tail around the head if the buffer gets too large\r
+\r
+  no_add_buf:\r
+\r
+       mov     al, 1\r
+       out     0x64, al\r
+\r
+       pop     bp\r
+       pop     bx\r
+       pop     ax\r
+       pop     es\r
+\r
+       iret\r
+\r
+; ************************* INT Ah handler - timer (8086tiny internal)\r
+\r
+inta:\r
+       ; 8086tiny called interrupt 0xA frequently, at a rate dependent on the speed of your computer.\r
+       ; This interrupt handler scales down the call rate and calls INT 8 at 18.2 times per second,\r
+       ; as per a real PC.\r
+\r
+       ; See if there is an ESC waiting from a previous INT 7h. If so, put it in the keyboard buffer\r
+       ; (because by now - 1/18.2 secs on - we know it can't be part of an escape key sequence).\r
+       ; Also handle CGA refresh register. Also release any keys that are still marked as down.\r
+\r
+       push    ax\r
+       push    bx\r
+       push    dx\r
+       push    bp\r
+       push    es\r
+\r
+       push    cx\r
+       push    di\r
+       push    ds\r
+       push    si\r
+\r
+       call    vmem_driver_entry       ; CGA text mode driver - documented later\r
+\r
+       ; Increment 32-bit BIOS timer tick counter, once every 18.2 ms\r
+\r
+       push    cs\r
+       pop     es\r
+       mov     bx, timetable\r
+       extended_get_rtc\r
+       \r
+       mov     ax, [cs:tm_msec]\r
+       sub     ax, [cs:last_int8_msec]\r
+\r
+  make_ctr_positive:\r
+\r
+       cmp     ax, 0\r
+       jge     no_add_1000\r
+\r
+       add     ax, 1000\r
+       jmp     make_ctr_positive\r
+\r
+  no_add_1000:\r
+\r
+       mov     bx, 0x40\r
+       mov     es, bx\r
+\r
+       mov     dx, 0\r
+       mov     bx, 1193\r
+       mul     bx\r
+\r
+       mov     bx, [es:timer0_freq-bios_data]\r
+\r
+       cmp     bx, 0 ; 0 actually means FFFF\r
+       jne     no_adjust_10000\r
+\r
+       mov     bx, 0xffff\r
+\r
+  no_adjust_10000:\r
+\r
+       div     bx ; AX now contains number of timer ticks since last int 8 (DX is remainder)\r
+\r
+       cmp     ax, 0\r
+       je      i8_end\r
+\r
+       add     word [es:0x6C], ax\r
+       adc     word [es:0x6E], 0\r
+\r
+inta_call_int8:\r
+\r
+       push    ax      ; Workaround for CPM-86 - INT 1C destroys AX!!\r
+       int     8\r
+       pop     ax\r
+\r
+       dec     ax\r
+       cmp     ax, 0\r
+       jne     inta_call_int8\r
+\r
+       mov     ax, [cs:tm_msec]\r
+       mov     [cs:last_int8_msec], ax\r
+\r
+skip_timer_increment:\r
+\r
+       ; If last key was from SDL, don't simulate key up events (SDL will do it for us)\r
+       cmp     byte [cs:last_key_sdl], 0\r
+       jne     i8_end\r
+\r
+       ; See if we have any keys down. If so, release them\r
+       cmp     byte [es:key_now_down-bios_data], 0\r
+       je      i8_no_key_down\r
+\r
+       mov     al, [es:key_now_down-bios_data]\r
+       mov     byte [es:key_now_down-bios_data], 0\r
+       add     al, 0x80\r
+       call    io_key_available\r
+\r
+  i8_no_key_down:\r
+\r
+       ; See if we have a waiting ESC flag\r
+       cmp     byte [es:escape_flag-bios_data], 1\r
+       jne     i8_end\r
+       \r
+       ; Did we have one last two cycles as well?\r
+       cmp     byte [es:escape_flag_last-bios_data], 1\r
+       je      i8_stuff_esc\r
+\r
+       inc     byte [es:escape_flag_last-bios_data]\r
+       jmp     i8_end\r
+\r
+i8_stuff_esc:\r
+\r
+       ; Yes, clear the ESC flag and put it in the keyboard buffer\r
+       mov     byte [es:escape_flag-bios_data], 0\r
+       mov     byte [es:escape_flag_last-bios_data], 0\r
+\r
+       ; mov   bp, [es:kbbuf_tail-bios_data]\r
+       ; mov   byte [es:bp], 0x1b ; ESC ASCII code\r
+       ; mov   byte [es:bp+1], 0x01 ; ESC scan code\r
+\r
+       ; ESC keystroke is in the buffer now\r
+       ; add   word [es:kbbuf_tail-bios_data], 2\r
+       ; call  kb_adjust_buf ; Wrap the tail around the head if the buffer gets too large\r
+\r
+       mov     byte [es:this_keystroke-bios_data], 0x1b\r
+\r
+       ; Push out ESC keypress/release\r
+       mov     al, 0x01\r
+       call    keypress_release\r
+\r
+i8_end:        \r
+\r
+       ; A Hercules graphics adapter flips bit 7 of I/O port 3BA on refresh\r
+       mov     dx, 0x3BA\r
+       in      al, dx\r
+       xor     al, 0x80\r
+       out     dx, al\r
+\r
+       pop     si\r
+       pop     ds\r
+       pop     di\r
+       pop     cx\r
+       \r
+       pop     es\r
+       pop     bp\r
+       pop     dx\r
+       pop     bx\r
+       pop     ax\r
+\r
+       iret\r
+\r
+; ************************* INT 8h handler - timer\r
+\r
+int8:\r
+\r
+       int     0x1c\r
+       iret\r
+\r
+; ************************* INT 10h handler - video services\r
+\r
+int10:\r
+\r
+       cmp     ah, 0x00 ; Set video mode\r
+       je      int10_set_vm\r
+       cmp     ah, 0x01 ; Set cursor shape\r
+       je      int10_set_cshape\r
+       cmp     ah, 0x02 ; Set cursor position\r
+       je      int10_set_cursor\r
+       cmp     ah, 0x03 ; Get cursur position\r
+       je      int10_get_cursor\r
+       cmp     ah, 0x06 ; Scroll up window\r
+       je      int10_scrollup\r
+       cmp     ah, 0x07 ; Scroll down window\r
+       je      int10_scrolldown\r
+       cmp     ah, 0x08 ; Get character at cursor\r
+       je      int10_charatcur\r
+       cmp     ah, 0x09 ; Write char and attribute\r
+       je      int10_write_char_attrib\r
+       cmp     ah, 0x0e ; Write character at cursor position\r
+       je      int10_write_char\r
+       cmp     ah, 0x0f ; Get video mode\r
+       je      int10_get_vm\r
+       ; cmp   ah, 0x1a ; Feature check\r
+       ; je    int10_features\r
+\r
+       iret\r
+\r
+  int10_set_vm:\r
+\r
+       push    dx\r
+       push    cx\r
+       push    bx\r
+       push    es\r
+\r
+       cmp     al, 4 ; CGA mode 4\r
+       je      int10_switch_to_cga_gfx\r
+       cmp     al, 5\r
+       je      int10_switch_to_cga_gfx\r
+       cmp     al, 6\r
+       je      int10_switch_to_cga_gfx\r
+\r
+       push    ax\r
+\r
+       mov     dx, 0x3b8\r
+       mov     al, 0\r
+       out     dx, al\r
+\r
+       mov     dx, 0x3b4\r
+       mov     al, 1           ; Hercules CRTC "horizontal displayed" register select\r
+       out     dx, al\r
+       mov     dx, 0x3b5\r
+       mov     al, 0x2d        ; 0x2D = 45 (* 16) = 720 pixels wide (GRAPHICS_X)\r
+       out     dx, al\r
+       mov     dx, 0x3b4\r
+       mov     al, 6           ; Hercules CRTC "vertical displayed" register select\r
+       out     dx, al\r
+       mov     dx, 0x3b5\r
+       mov     al, 0x57        ; 0x57 = 87 (* 4) = 348 pixels high (GRAPHICS_Y)\r
+       out     dx, al\r
+\r
+       mov     dx, 0x40\r
+       mov     es, dx\r
+\r
+       mov     byte [es:0xac], 0 ; Tell emulator we are in Hercules mode\r
+\r
+       pop     ax\r
+\r
+       cmp     al, 7           ; If an app tries to set Hercules text mode 7, actually set mode 3 (we do not support mode 7's video memory buffer at B000:0)\r
+       je      int10_set_vm_3\r
+       cmp     al, 2           ; Same for text mode 2 (mono)\r
+       je      int10_set_vm_3\r
+\r
+       jmp     int10_set_vm_continue\r
+\r
+  int10_switch_to_cga_gfx:\r
+\r
+       ; Switch to CGA-like graphics mode (with Hercules CRTC set for 640 x 400)\r
+       \r
+       mov     dx, 0x40\r
+       mov     es, dx\r
+\r
+       mov     [es:0x49], al   ; Current video mode\r
+       mov     byte [es:0xac], 1 ; Tell emulator we are in CGA mode\r
+\r
+       mov     dx, 0x3b4\r
+       mov     al, 1           ; Hercules CRTC "horizontal displayed" register select\r
+       out     dx, al\r
+       mov     dx, 0x3b5\r
+       mov     al, 0x28        ; 0x28 = 40 (* 16) = 640 pixels wide (GRAPHICS_X)\r
+       out     dx, al\r
+       mov     dx, 0x3b4\r
+       mov     al, 6           ; Hercules CRTC "vertical displayed" register select\r
+       out     dx, al\r
+       mov     dx, 0x3b5\r
+       mov     al, 0x64        ; 0x64 = 100 (* 4) = 400 pixels high (GRAPHICS_Y)\r
+       out     dx, al\r
+\r
+       mov     dx, 0x3b8\r
+       mov     al, 0x8a\r
+       out     dx, al\r
+\r
+       mov     bh, 7   \r
+       call    clear_screen\r
+\r
+       mov     ax, 0x30\r
+       jmp     svmn_exit\r
+\r
+  int10_set_vm_3:\r
+\r
+       mov     al, 3\r
+\r
+  int10_set_vm_continue:\r
+\r
+       mov     bx, 0x40\r
+       mov     es, bx\r
+\r
+       mov     [es:vidmode-bios_data], al\r
+\r
+       mov     bh, 7           ; Black background, white foreground\r
+       call    clear_screen    ; ANSI clear screen\r
+\r
+       cmp     byte [es:vidmode-bios_data], 6\r
+       je      set6\r
+       mov     al, 0x30\r
+       jmp     svmn\r
+\r
+  set6:\r
+\r
+       mov     al, 0x3f\r
+\r
+  svmn:\r
+\r
+       ; Take Hercules adapter out of graphics mode when resetting video mode via int 10\r
+       push    ax\r
+       mov     dx, 0x3B8\r
+       mov     al, 0\r
+       out     dx, al\r
+       pop     ax\r
+\r
+  svmn_exit:\r
+\r
+       pop     es\r
+       pop     bx\r
+       pop     cx\r
+       pop     dx\r
+       iret\r
+\r
+  int10_set_cshape:\r
+\r
+       push    ds\r
+       push    ax\r
+       push    cx\r
+\r
+       mov     ax, 0x40\r
+       mov     ds, ax\r
+\r
+       mov     byte [cursor_visible-bios_data], 1      ; Show cursor\r
+\r
+       and     ch, 01100000b\r
+       cmp     ch, 00100000b\r
+       jne     cur_visible\r
+\r
+       mov     byte [cursor_visible-bios_data], 0      ; Hide cursor\r
+       call    ansi_hide_cursor\r
+       jmp     cur_done\r
+\r
+    cur_visible:\r
+\r
+       call    ansi_show_cursor\r
+\r
+    cur_done:\r
+\r
+       pop     cx\r
+       pop     ax\r
+       pop     ds\r
+       iret\r
+\r
+  int10_set_cursor:\r
+\r
+       push    ds\r
+       push    ax\r
+\r
+       mov     ax, 0x40\r
+       mov     ds, ax\r
+\r
+       mov     [curpos_y-bios_data], dh\r
+       mov     [crt_curpos_y-bios_data], dh\r
+       mov     [curpos_x-bios_data], dl\r
+       mov     [crt_curpos_x-bios_data], dl\r
+\r
+       cmp     dh, 24\r
+       jbe     skip_set_cur_row_max\r
+\r
+       ; If cursor is moved off the screen, then hide it\r
+       call    ansi_hide_cursor\r
+       jmp     skip_set_cur_ansi\r
+       \r
+    skip_set_cur_row_max:\r
+\r
+       cmp     dl, 79\r
+       jbe     skip_set_cur_col_max\r
+\r
+       ; If cursor is moved off the screen, then hide it\r
+       call    ansi_hide_cursor\r
+       jmp     skip_set_cur_ansi\r
+       \r
+    skip_set_cur_col_max:\r
+\r
+       mov     al, 0x1B        ; ANSI\r
+       extended_putchar_al\r
+       mov     al, '['         ; ANSI\r
+       extended_putchar_al\r
+       mov     al, dh          ; Row number\r
+       inc     al\r
+       call    puts_decimal_al\r
+       mov     al, ';'         ; ANSI\r
+       extended_putchar_al\r
+       mov     al, dl          ; Column number\r
+       inc     al\r
+       call    puts_decimal_al\r
+       mov     al, 'H'         ; Set cursor position command\r
+       extended_putchar_al\r
+\r
+       cmp     byte [cursor_visible-bios_data], 1\r
+       jne     skip_set_cur_ansi\r
+       call    ansi_show_cursor\r
+\r
+    skip_set_cur_ansi:\r
+\r
+       pop     ax\r
+       pop     ds\r
+       iret\r
+\r
+  int10_get_cursor:\r
+\r
+       push    es\r
+\r
+       mov     cx, 0x40\r
+       mov     es, cx\r
+\r
+       mov     cx, 0x0607\r
+       mov     dl, [es:curpos_x-bios_data]\r
+       mov     dh, [es:curpos_y-bios_data]\r
+\r
+       pop     es\r
+\r
+       iret\r
+\r
+  int10_scrollup:\r
+\r
+       push    bx\r
+       push    cx\r
+       push    bp\r
+       push    ax\r
+\r
+       mov     bp, bx          ; Convert from CGA to ANSI\r
+       mov     cl, 12\r
+       ror     bp, cl\r
+       and     bp, 7\r
+       mov     bl, byte [cs:bp+colour_table]\r
+       add     bl, 10\r
+\r
+       mov     al, 0x1B        ; Escape\r
+       extended_putchar_al\r
+       mov     al, '['         ; ANSI\r
+       extended_putchar_al\r
+       mov     al, bl          ; Background colour\r
+       call    puts_decimal_al\r
+       mov     al, 'm'         ; Set cursor position command\r
+       extended_putchar_al\r
+\r
+       pop     ax\r
+       pop     bp\r
+       pop     cx\r
+       pop     bx\r
+\r
+       cmp     al, 0 ; Clear window\r
+       jne     cls_partial\r
+\r
+       cmp     cx, 0 ; Start of screen\r
+       jne     cls_partial\r
+\r
+       cmp     dl, 0x4f ; Clearing columns 0-79\r
+       jb      cls_partial\r
+\r
+       cmp     dh, 0x18 ; Clearing rows 0-24 (or more)\r
+       jb      cls_partial\r
+\r
+       call    clear_screen\r
+       iret\r
+\r
+  cls_partial:\r
+\r
+       push    ax\r
+       push    bx\r
+\r
+       mov     bl, al          ; Number of rows to scroll are now in bl\r
+       cmp     bl, 0           ; Clear whole window?\r
+       jne     cls_partial_up_whole\r
+\r
+       mov     bl, 25          ; 25 rows\r
+\r
+  cls_partial_up_whole:\r
+\r
+       mov     al, 0x1B        ; Escape\r
+       extended_putchar_al\r
+       mov     al, '['         ; ANSI\r
+       extended_putchar_al\r
+\r
+       cmp     ch, 0           ; Start row 1? Maybe full screen\r
+       je      cls_maybe_fs\r
+       jmp     cls_not_fs\r
+\r
+    cls_maybe_fs:\r
+\r
+       cmp     dh, 24          ; End row 25? Full screen for sure\r
+       je      cls_fs\r
+\r
+    cls_not_fs:\r
+\r
+       mov     al, ch          ; Start row\r
+       inc     al\r
+       call    puts_decimal_al\r
+       mov     al, ';'         ; ANSI\r
+       extended_putchar_al\r
+       mov     al, dh          ; End row\r
+       inc     al\r
+       call    puts_decimal_al\r
+\r
+    cls_fs:\r
+\r
+       mov     al, 'r'         ; Set scrolling window\r
+       extended_putchar_al\r
+\r
+       mov     al, 0x1B        ; Escape\r
+       extended_putchar_al\r
+       mov     al, '['         ; ANSI\r
+       extended_putchar_al\r
+\r
+       cmp     bl, 1\r
+       jne     cls_fs_multiline\r
+\r
+       mov     al, 'M'\r
+       jmp     cs_fs_ml_out\r
+\r
+cls_fs_multiline:\r
+\r
+       mov     al, bl          ; Number of rows\r
+       call    puts_decimal_al\r
+       mov     al, 'S'         ; Scroll up\r
+\r
+cs_fs_ml_out:\r
+\r
+       extended_putchar_al\r
+\r
+       pop     bx\r
+       pop     ax\r
+\r
+       ; Update "actual" cursor position with expected value - different ANSI terminals do different things\r
+       ; to the cursor position when you scroll\r
+\r
+       push    ax\r
+       push    bx\r
+       push    dx\r
+       push    es\r
+\r
+       mov     ax, 0x40\r
+       mov     es, ax\r
+\r
+       mov     ah, 2\r
+       mov     bh, 0\r
+       mov     dh, [es:curpos_y-bios_data]\r
+       mov     dl, [es:curpos_x-bios_data]\r
+       int     10h\r
+\r
+       pop     es\r
+       pop     dx\r
+       pop     bx\r
+       pop     ax\r
+\r
+int10_scroll_up_vmem_update:\r
+\r
+       ; Now, we need to update video memory\r
+\r
+       push    bx\r
+       push    ax\r
+\r
+       push    ds\r
+       push    es\r
+       push    cx\r
+       push    dx\r
+       push    si\r
+       push    di\r
+\r
+       mov     byte [cs:vram_dirty], 1\r
+\r
+       push    bx\r
+\r
+       mov     bx, 0xb800\r
+       mov     es, bx\r
+       mov     ds, bx\r
+\r
+       pop     bx\r
+       mov     bl, al\r
+\r
+    cls_vmem_scroll_up_next_line:\r
+\r
+       cmp     bl, 0\r
+       je      cls_vmem_scroll_up_done\r
+\r
+    cls_vmem_scroll_up_one:\r
+\r
+       push    bx\r
+       push    dx\r
+\r
+       mov     ax, 0\r
+       mov     al, ch          ; Start row number is now in AX\r
+       mov     bx, 80\r
+       mul     bx\r
+       add     al, cl\r
+       adc     ah, 0           ; Character number is now in AX\r
+       mov     bx, 2\r
+       mul     bx              ; Memory location is now in AX\r
+\r
+       pop     dx\r
+       pop     bx\r
+\r
+       mov     di, ax\r
+       mov     si, ax\r
+       add     si, 2*80        ; In a moment we will copy CX words from DS:SI to ES:DI\r
+\r
+       mov     ax, 0\r
+       add     al, dl\r
+       adc     ah, 0\r
+       inc     ax\r
+       sub     al, cl\r
+       sbb     ah, 0           ; AX now contains the number of characters from the row to copy\r
+\r
+       cmp     ch, dh\r
+       jae     cls_vmem_scroll_up_one_done\r
+\r
+vmem_scroll_up_copy_next_row:\r
+\r
+       push    cx\r
+       mov     cx, ax          ; CX is now the length (in words) of the row to copy\r
+       cld\r
+       rep     movsw           ; Scroll the line up\r
+       pop     cx\r
+\r
+       inc     ch              ; Move onto the next row\r
+       jmp     cls_vmem_scroll_up_one\r
+\r
+    cls_vmem_scroll_up_one_done:\r
+\r
+       push    cx\r
+       mov     cx, ax          ; CX is now the length (in words) of the row to copy\r
+       mov     ah, bh          ; Attribute for new line\r
+       mov     al, 0           ; Write 0 to video memory for new characters\r
+       cld\r
+       rep     stosw\r
+       pop     cx\r
+\r
+       dec     bl              ; Scroll whole text block another line\r
+       jmp     cls_vmem_scroll_up_next_line    \r
+\r
+    cls_vmem_scroll_up_done:\r
+\r
+       ;mov    al, 0x1B        ; Escape\r
+       ;extended_putchar_al\r
+       ;mov    al, '['         ; ANSI\r
+       ;extended_putchar_al\r
+       ;mov    al, '0'         ; Reset attributes\r
+       ;extended_putchar_al\r
+       ;mov    al, 'm'\r
+       ;extended_putchar_al\r
+\r
+       pop     di\r
+       pop     si\r
+       pop     dx\r
+       pop     cx\r
+       pop     es\r
+       pop     ds\r
+\r
+       pop     ax\r
+       pop     bx\r
+\r
+       iret\r
+       \r
+  int10_scrolldown:\r
+\r
+       push    bx\r
+       push    cx\r
+       push    bp\r
+       push    ax\r
+\r
+       mov     bp, bx          ; Convert from CGA to ANSI\r
+       mov     cl, 12\r
+       ror     bp, cl\r
+       and     bp, 7\r
+       mov     bl, byte [cs:bp+colour_table]\r
+       add     bl, 10\r
+\r
+       mov     al, 0x1B        ; Escape\r
+       extended_putchar_al\r
+       mov     al, '['         ; ANSI\r
+       extended_putchar_al\r
+       mov     al, bl          ; Background colour\r
+       call    puts_decimal_al\r
+       mov     al, 'm'         ; Set cursor position command\r
+       extended_putchar_al\r
+\r
+       pop     ax\r
+       pop     bp\r
+       pop     cx\r
+       pop     bx\r
+\r
+       cmp     al, 0 ; Clear window\r
+       jne     cls_partial_down\r
+\r
+       cmp     cx, 0 ; Start of screen\r
+       jne     cls_partial_down\r
+\r
+       cmp     dl, 0x4f ; Clearing columns 0-79\r
+       jne     cls_partial_down\r
+\r
+       cmp     dh, 0x18 ; Clearing rows 0-24 (or more)\r
+       jl      cls_partial_down\r
+\r
+       call    clear_screen\r
+       iret\r
+\r
+  cls_partial_down:\r
+\r
+       push    ax\r
+       push    bx\r
+\r
+       mov     bx, 0\r
+       mov     bl, al          ; Number of rows to scroll are now in bl\r
+\r
+       cmp     bl, 0           ; Clear whole window?\r
+       jne     cls_partial_down_whole\r
+\r
+       mov     bl, 25          ; 25 rows\r
+\r
+  cls_partial_down_whole:\r
+\r
+       mov     al, 0x1B        ; Escape\r
+       extended_putchar_al\r
+       mov     al, '['         ; ANSI\r
+       extended_putchar_al\r
+\r
+       cmp     ch, 0           ; Start row 1? Maybe full screen\r
+       je      cls_maybe_fs_down\r
+       jmp     cls_not_fs_down\r
+\r
+    cls_maybe_fs_down:\r
+\r
+       cmp     dh, 24          ; End row 25? Full screen for sure\r
+       je      cls_fs_down\r
+\r
+    cls_not_fs_down:\r
+\r
+       mov     al, ch          ; Start row\r
+       inc     al\r
+       call    puts_decimal_al\r
+       mov     al, ';'         ; ANSI\r
+       extended_putchar_al\r
+       mov     al, dh          ; End row\r
+       inc     al\r
+       call    puts_decimal_al\r
+\r
+    cls_fs_down:\r
+\r
+       mov     al, 'r'         ; Set scrolling window\r
+       extended_putchar_al\r
+\r
+       mov     al, 0x1B        ; Escape\r
+       extended_putchar_al\r
+       mov     al, '['         ; ANSI\r
+       extended_putchar_al\r
+\r
+       cmp     bl, 1\r
+       jne     cls_fs_down_multiline\r
+\r
+       mov     al, 'D'\r
+       jmp     cs_fs_down_ml_out\r
+\r
+    cls_fs_down_multiline:\r
+\r
+       mov     al, bl          ; Number of rows\r
+       call    puts_decimal_al\r
+       mov     al, 'T'         ; Scroll down\r
+\r
+    cs_fs_down_ml_out:\r
+\r
+       extended_putchar_al\r
+\r
+       ; Update "actual" cursor position with expected value - different ANSI terminals do different things\r
+       ; to the cursor position when you scroll\r
+\r
+       pop     bx\r
+       pop     ax\r
+\r
+       push    ax\r
+       push    bx\r
+       push    dx\r
+       push    es\r
+\r
+       mov     ax, 0x40\r
+       mov     es, ax\r
+\r
+       mov     ah, 2\r
+       mov     bh, 0\r
+       mov     dh, [es:curpos_y-bios_data]\r
+       mov     dl, [es:curpos_x-bios_data]\r
+       int     10h\r
+\r
+       pop     es\r
+       pop     dx\r
+       pop     bx\r
+       pop     ax\r
+\r
+int10_scroll_down_vmem_update:\r
+\r
+       ; Now, we need to update video memory\r
+\r
+       push    ax\r
+       push    bx\r
+\r
+       push    ds\r
+       push    es\r
+       push    cx\r
+       push    dx\r
+       push    si\r
+       push    di\r
+\r
+       mov     byte [cs:vram_dirty], 1\r
+\r
+       push    bx\r
+\r
+       mov     bx, 0xb800\r
+       mov     es, bx\r
+       mov     ds, bx\r
+\r
+       pop     bx\r
+       mov     bl, al\r
+\r
+    cls_vmem_scroll_down_next_line:\r
+\r
+       cmp     bl, 0\r
+       je      cls_vmem_scroll_down_done\r
+\r
+    cls_vmem_scroll_down_one:\r
+\r
+       push    bx\r
+       push    dx\r
+\r
+       mov     ax, 0\r
+       mov     al, dh          ; End row number is now in AX\r
+       mov     bx, 80\r
+       mul     bx\r
+       add     al, cl\r
+       adc     ah, 0           ; Character number is now in AX\r
+       mov     bx, 2\r
+       mul     bx              ; Memory location (start of final row) is now in AX\r
+\r
+       pop     dx\r
+       pop     bx\r
+\r
+       mov     di, ax\r
+       mov     si, ax\r
+       sub     si, 2*80        ; In a moment we will copy CX words from DS:SI to ES:DI\r
+\r
+       mov     ax, 0\r
+       add     al, dl\r
+       adc     ah, 0\r
+       inc     ax\r
+       sub     al, cl\r
+       sbb     ah, 0           ; AX now contains the number of characters from the row to copy\r
+\r
+       cmp     ch, dh\r
+       jae     cls_vmem_scroll_down_one_done\r
+\r
+       push    cx\r
+       mov     cx, ax          ; CX is now the length (in words) of the row to copy\r
+       rep     movsw           ; Scroll the line down\r
+       pop     cx\r
+\r
+       dec     dh              ; Move onto the next row\r
+       jmp     cls_vmem_scroll_down_one\r
+\r
+    cls_vmem_scroll_down_one_done:\r
+\r
+       push    cx\r
+       mov     cx, ax          ; CX is now the length (in words) of the row to copy\r
+       mov     ah, bh          ; Attribute for new line\r
+       mov     al, 0           ; Write 0 to video memory for new characters\r
+       rep     stosw\r
+       pop     cx\r
+\r
+       dec     bl              ; Scroll whole text block another line\r
+       jmp     cls_vmem_scroll_down_next_line  \r
+\r
+    cls_vmem_scroll_down_done:\r
+\r
+       pop     di\r
+       pop     si\r
+       pop     dx\r
+       pop     cx\r
+       pop     es\r
+       pop     ds\r
+\r
+       ;mov    al, 0x1B        ; Escape\r
+       ;extended_putchar_al\r
+       ;mov    al, '['         ; ANSI\r
+       ;extended_putchar_al\r
+       ;mov    al, '0'         ; Reset attributes\r
+       ;extended_putchar_al\r
+       ;mov    al, 'm'\r
+       ;extended_putchar_al\r
+\r
+       pop     bx\r
+       pop     ax\r
+       iret\r
+\r
+  int10_charatcur:\r
+\r
+       ; This returns the character at the cursor. It is completely dysfunctional,\r
+       ; and only works at all if the character has previously been written following\r
+       ; an int 10/ah = 2 call to set the cursor position. Added just to support\r
+       ; GWBASIC.\r
+\r
+       push    ds\r
+       push    es\r
+       push    bx\r
+       push    dx\r
+\r
+       mov     bx, 0x40\r
+       mov     es, bx\r
+\r
+       mov     bx, 0xc000\r
+       mov     ds, bx\r
+\r
+       mov     bx, 160\r
+       mov     ax, 0\r
+       mov     al, [es:curpos_y-bios_data]\r
+       mul     bx\r
+\r
+       mov     bx, 0\r
+       mov     bl, [es:curpos_x-bios_data]\r
+       add     ax, bx\r
+       add     ax, bx\r
+       mov     bx, ax\r
+\r
+       mov     ah, 7\r
+       mov     al, [bx]\r
+\r
+       pop     dx\r
+       pop     bx\r
+       pop     es\r
+       pop     ds\r
+\r
+       iret\r
+\r
+  i10_unsup:\r
+\r
+       iret\r
+\r
+  int10_write_char:\r
+\r
+       ; First write the character to a buffer at C000:0. This is so that\r
+       ; we can later retrieve it using the get character at cursor function,\r
+       ; which GWBASIC uses.\r
+\r
+       push    ds\r
+       push    es\r
+       push    cx\r
+       push    dx\r
+       push    ax\r
+       push    bp\r
+       push    bx\r
+\r
+       push    ax\r
+\r
+       mov     cl, al\r
+       mov     ch, 7\r
+\r
+       mov     bx, 0x40\r
+       mov     es, bx\r
+\r
+       mov     bx, 0xc000\r
+       mov     ds, bx\r
+\r
+       mov     bx, 160\r
+       mov     ax, 0\r
+       mov     al, [es:curpos_y-bios_data]\r
+       mul     bx\r
+\r
+       mov     bx, 0\r
+       mov     bl, [es:curpos_x-bios_data]\r
+       shl     bx, 1\r
+       add     bx, ax\r
+\r
+       mov     [bx], cx\r
+       \r
+       pop     ax\r
+       push    ax\r
+\r
+       extended_putchar_al\r
+\r
+       jmp     int10_write_char_skip_lines\r
+\r
+  int10_write_char_attrib:\r
+\r
+       ; First write the character to a buffer at C000:0. This is so that\r
+       ; we can later retrieve it using the get character at cursor function,\r
+       ; which GWBASIC uses.\r
+\r
+       push    ds\r
+       push    es\r
+       push    cx\r
+       push    dx\r
+       push    ax\r
+       push    bp\r
+       push    bx\r
+\r
+       push    ax\r
+       push    cx\r
+\r
+       mov     cl, al\r
+       mov     ch, bl\r
+\r
+       mov     bx, 0x40\r
+       mov     es, bx\r
+\r
+       mov     bx, 0xc000\r
+       mov     ds, bx\r
+\r
+       mov     bx, 160\r
+       mov     ax, 0\r
+       mov     al, [es:curpos_y-bios_data]\r
+       mul     bx\r
+\r
+       mov     bx, 0\r
+       mov     bl, [es:curpos_x-bios_data]\r
+       shl     bx, 1\r
+       add     bx, ax\r
+\r
+       mov     [bx], cx\r
+\r
+       mov     bl, ch\r
+\r
+       mov     bh, bl\r
+       and     bl, 7           ; Foreground colour now in bl\r
+\r
+       mov     bp, bx          ; Convert from CGA to ANSI\r
+       and     bp, 0xff\r
+       mov     bl, byte [cs:bp+colour_table]\r
+\r
+       and     bh, 8           ; Bright attribute now in bh\r
+cpu    186\r
+       shr     bh, 3\r
+cpu    8086\r
+\r
+       mov     al, 0x1B        ; Escape\r
+       extended_putchar_al\r
+       mov     al, '['         ; ANSI\r
+       extended_putchar_al\r
+       mov     al, bh          ; Bright attribute\r
+       call    puts_decimal_al\r
+       mov     al, ';'\r
+       extended_putchar_al\r
+       mov     al, bl          ; Foreground colour\r
+       call    puts_decimal_al\r
+\r
+       mov     bl, ch\r
+\r
+       mov     bh, bl\r
+cpu    186\r
+       shr     bl, 4\r
+cpu    8086\r
+       and     bl, 7           ; Background colour now in bl\r
+\r
+       mov     bp, bx          ; Convert from CGA to ANSI\r
+       and     bp, 0xff\r
+       mov     bl, byte [cs:bp+colour_table]\r
+\r
+       add     bl, 10\r
+       ; rol   bh, 1\r
+       ; and   bh, 1           ; Bright attribute now in bh (not used right now)\r
+\r
+       mov     al, ';'\r
+       extended_putchar_al\r
+       mov     al, bl          ; Background colour\r
+       call    puts_decimal_al\r
+       mov     al, 'm'         ; Set cursor position command\r
+       extended_putchar_al\r
+       \r
+       pop     cx\r
+       pop     ax\r
+       push    ax\r
+\r
+    out_another_char:\r
+\r
+       extended_putchar_al\r
+       dec     cx\r
+       cmp     cx, 0\r
+       jne     out_another_char\r
+\r
+       mov     al, 0x1B        ; Escape\r
+       extended_putchar_al\r
+       mov     al, '['         ; ANSI\r
+       extended_putchar_al\r
+       mov     al, '0'         ; Reset attributes\r
+       extended_putchar_al\r
+       mov     al, 'm'\r
+       extended_putchar_al\r
+\r
+    int10_write_char_skip_lines:\r
+\r
+       pop     ax\r
+\r
+       push    es\r
+       pop     ds\r
+\r
+       cmp     al, 0x08\r
+       jne     int10_write_char_attrib_inc_x\r
+\r
+       dec     byte [curpos_x-bios_data]\r
+       dec     byte [crt_curpos_x-bios_data]\r
+       cmp     byte [curpos_x-bios_data], 0\r
+       jg      int10_write_char_attrib_done\r
+\r
+       mov     byte [curpos_x-bios_data], 0\r
+       mov     byte [crt_curpos_x-bios_data], 0\r
+       jmp     int10_write_char_attrib_done\r
+\r
+    int10_write_char_attrib_inc_x:\r
+\r
+       cmp     al, 0x0A        ; New line?\r
+       je      int10_write_char_attrib_newline\r
+\r
+       cmp     al, 0x0D        ; Carriage return?\r
+       jne     int10_write_char_attrib_not_cr\r
+\r
+       mov     byte [curpos_x-bios_data], 0\r
+       mov     byte [crt_curpos_x-bios_data], 0\r
+       jmp     int10_write_char_attrib_done\r
+\r
+    int10_write_char_attrib_not_cr:\r
+\r
+       inc     byte [curpos_x-bios_data]\r
+       inc     byte [crt_curpos_x-bios_data]\r
+       cmp     byte [curpos_x-bios_data], 80\r
+       jge     int10_write_char_attrib_newline\r
+       jmp     int10_write_char_attrib_done\r
+\r
+    int10_write_char_attrib_newline:\r
+\r
+       mov     byte [curpos_x-bios_data], 0\r
+       mov     byte [crt_curpos_x-bios_data], 0\r
+       inc     byte [curpos_y-bios_data]\r
+       inc     byte [crt_curpos_y-bios_data]\r
+\r
+       cmp     byte [curpos_y-bios_data], 25\r
+       jb      int10_write_char_attrib_done\r
+       mov     byte [curpos_y-bios_data], 24\r
+       mov     byte [crt_curpos_y-bios_data], 24\r
+\r
+       mov     bh, 7\r
+       mov     al, 1\r
+       mov     cx, 0\r
+       mov     dx, 0x184f\r
+\r
+       pushf\r
+       push    cs\r
+       call    int10_scroll_up_vmem_update\r
+\r
+    int10_write_char_attrib_done:\r
+\r
+       pop     bx\r
+       pop     bp\r
+       pop     ax\r
+       pop     dx\r
+       pop     cx\r
+       pop     es\r
+       pop     ds\r
+\r
+       iret\r
+\r
+  int10_get_vm:\r
+\r
+       push    es\r
+\r
+       mov     ax, 0x40\r
+       mov     es, ax\r
+\r
+       mov     ah, 80 ; Number of columns\r
+       mov     al, [es:vidmode-bios_data]\r
+       mov     bh, 0\r
+\r
+       pop     es\r
+\r
+       iret\r
+\r
+  int10_features:\r
+\r
+       ; Signify we have CGA display\r
+\r
+       ; mov   al, 0x1a\r
+       ; mov   bx, 0x0202\r
+       ; iret\r
+\r
+; ************************* INT 11h - get equipment list\r
+\r
+int11: \r
+       mov     ax, [cs:equip]\r
+       iret\r
+\r
+; ************************* INT 12h - return memory size\r
+\r
+int12: \r
+       mov     ax, 0x280 ; 640K conventional memory\r
+       iret\r
+\r
+; ************************* INT 13h handler - disk services\r
+\r
+int13:\r
+       cmp     ah, 0x00 ; Reset disk\r
+       je      int13_reset_disk\r
+       cmp     ah, 0x01 ; Get last status\r
+       je      int13_last_status\r
+\r
+       cmp     dl, 0x80 ; Hard disk being queried?\r
+       jne     i13_diskok\r
+\r
+       ; Now, need to check an HD is installed\r
+       cmp     word [cs:num_disks], 2\r
+       jge     i13_diskok\r
+\r
+       ; No HD, so return an error\r
+       mov     ah, 15 ; Report no such drive\r
+       jmp     reach_stack_stc\r
+\r
+  i13_diskok:\r
+\r
+       cmp     ah, 0x02 ; Read disk\r
+       je      int13_read_disk\r
+       cmp     ah, 0x03 ; Write disk\r
+       je      int13_write_disk\r
+       cmp     ah, 0x04 ; Verify disk\r
+       je      int13_verify\r
+       cmp     ah, 0x05 ; Format track - does nothing here\r
+       je      int13_format\r
+       cmp     ah, 0x08 ; Get drive parameters (hard disk)\r
+       je      int13_getparams\r
+       cmp     ah, 0x0c ; Seek (hard disk)\r
+       je      int13_seek\r
+       cmp     ah, 0x10 ; Check if drive ready (hard disk)\r
+       je      int13_hdready\r
+       cmp     ah, 0x15 ; Get disk type\r
+       je      int13_getdisktype\r
+       cmp     ah, 0x16 ; Detect disk change\r
+       je      int13_diskchange\r
+\r
+       mov     ah, 1 ; Invalid function\r
+       jmp     reach_stack_stc\r
+\r
+       iret\r
+\r
+  int13_reset_disk:\r
+\r
+       jmp     reach_stack_clc\r
+\r
+  int13_last_status:\r
+\r
+       mov     ah, [cs:disk_laststatus]\r
+       je      ls_no_error\r
+\r
+       stc\r
+       iret\r
+\r
+    ls_no_error:\r
+\r
+       clc\r
+       iret\r
+\r
+  int13_read_disk:\r
+\r
+       push    dx\r
+\r
+       cmp     dl, 0 ; Floppy 0\r
+       je      i_flop_rd\r
+       cmp     dl, 0x80 ; HD\r
+       je      i_hd_rd\r
+\r
+       pop     dx\r
+       mov     ah, 1\r
+       jmp     reach_stack_stc\r
+\r
+    i_flop_rd:\r
+\r
+       push    si\r
+       push    bp\r
+\r
+       cmp     cl, [cs:int1e_spt]\r
+       ja      rd_error\r
+\r
+       pop     bp\r
+       pop     si\r
+\r
+       mov     dl, 1           ; Floppy disk file handle is stored at j[1] in emulator\r
+       jmp     i_rd\r
+\r
+    i_hd_rd:\r
+\r
+       mov     dl, 0           ; Hard disk file handle is stored at j[0] in emulator\r
+\r
+    i_rd: \r
+\r
+       push    si\r
+       push    bp\r
+\r
+       ; Convert head/cylinder/sector number to byte offset in disk image\r
+\r
+       call    chs_to_abs\r
+\r
+       ; Now, SI:BP contains the absolute sector offset of the block. We then multiply by 512 to get the offset into the disk image\r
+\r
+       mov     ah, 0\r
+       cpu     186\r
+       shl     ax, 9\r
+       extended_read_disk\r
+       shr     ax, 9\r
+       cpu     8086\r
+       mov     ah, 0x02        ; Put read code back\r
+\r
+       cmp     al, 0\r
+       je      rd_error\r
+\r
+       ; Read was successful. Now, check if we have read the boot sector. If so, we want to update\r
+       ; our internal table of sectors/track to match the disk format\r
+\r
+       cmp     dx, 1           ; FDD?\r
+       jne     rd_noerror\r
+       cmp     cx, 1           ; First sector?\r
+       jne     rd_noerror\r
+\r
+       push    ax\r
+\r
+       mov     al, [es:bx+24]  ; Number of SPT in floppy disk BPB\r
+\r
+       ; cmp   al, 0           ; If disk is unformatted, do not update the table\r
+       ; jne   rd_update_spt\r
+       cmp     al, 9           ; 9 SPT, i.e. 720K disk, so update the table\r
+       je      rd_update_spt\r
+       cmp     al, 18\r
+       je      rd_update_spt   ; 18 SPT, i.e. 1.44MB disk, so update the table\r
+\r
+       pop     ax\r
+\r
+       jmp     rd_noerror\r
+\r
+    rd_update_spt:\r
+\r
+       mov     [cs:int1e_spt], al\r
+       pop     ax\r
+\r
+    rd_noerror:\r
+\r
+       clc\r
+       mov     ah, 0 ; No error\r
+       jmp     rd_finish\r
+\r
+    rd_error:\r
+\r
+       stc\r
+       mov     ah, 4 ; Sector not found\r
+\r
+    rd_finish:\r
+\r
+       pop     bp\r
+       pop     si\r
+       pop     dx\r
+\r
+       mov     [cs:disk_laststatus], ah\r
+       jmp     reach_stack_carry\r
+\r
+  int13_write_disk:\r
+\r
+       push    dx\r
+\r
+       cmp     dl, 0 ; Floppy 0\r
+       je      i_flop_wr\r
+       cmp     dl, 0x80 ; HD\r
+       je      i_hd_wr\r
+\r
+       pop     dx\r
+       mov     ah, 1\r
+       jmp     reach_stack_stc\r
+\r
+    i_flop_wr:\r
+\r
+       mov     dl, 1           ; Floppy disk file handle is stored at j[1] in emulator\r
+       jmp     i_wr\r
+\r
+    i_hd_wr:\r
+\r
+       mov     dl, 0           ; Hard disk file handle is stored at j[0] in emulator\r
+\r
+    i_wr:\r
+\r
+       push    si\r
+       push    bp\r
+       push    cx\r
+       push    di\r
+\r
+       ; Convert head/cylinder/sector number to byte offset in disk image\r
+\r
+       call    chs_to_abs\r
+\r
+       ; Signal an error if we are trying to write beyond the end of the disk\r
+       \r
+       cmp     dl, 0 ; Hard disk?\r
+       jne     wr_fine ; No - no need for disk sector valid check - NOTE: original submission was JNAE which caused write problems on floppy disk\r
+\r
+       ; First, we add the number of sectors we are trying to write from the absolute\r
+       ; sector number returned by chs_to_abs. We need to have at least this many\r
+       ; sectors on the disk, otherwise return a sector not found error.\r
+\r
+       mov     cx, bp\r
+       mov     di, si\r
+\r
+       mov     ah, 0\r
+       add     cx, ax\r
+       adc     di, 0\r
+\r
+       cmp     di, [cs:hd_secs_hi]\r
+       ja      wr_error\r
+       jb      wr_fine\r
+       cmp     cx, [cs:hd_secs_lo]\r
+       ja      wr_error\r
+\r
+wr_fine:\r
+\r
+       mov     ah, 0\r
+       cpu     186\r
+       shl     ax, 9\r
+       extended_write_disk\r
+       shr     ax, 9\r
+       cpu     8086\r
+       mov     ah, 0x03        ; Put write code back\r
+\r
+       cmp     al, 0\r
+       je      wr_error\r
+\r
+       clc\r
+       mov     ah, 0 ; No error\r
+       jmp     wr_finish\r
+\r
+    wr_error:\r
+\r
+       stc\r
+       mov     ah, 4 ; Sector not found\r
+\r
+    wr_finish:\r
+\r
+       pop     di\r
+       pop     cx\r
+       pop     bp\r
+       pop     si\r
+       pop     dx\r
+\r
+       mov     [cs:disk_laststatus], ah\r
+       jmp     reach_stack_carry\r
+\r
+  int13_verify:\r
+\r
+       mov     ah, 0\r
+       jmp     reach_stack_clc\r
+\r
+  int13_getparams:\r
+\r
+       cmp     dl, 0\r
+       je      i_gp_fl\r
+       cmp     dl, 0x80\r
+       je      i_gp_hd\r
+\r
+       mov     ah, 0x01\r
+       mov     [cs:disk_laststatus], ah\r
+       jmp     reach_stack_stc\r
+\r
+    i_gp_fl:\r
+\r
+       push    cs\r
+       pop     es\r
+       mov     di, int1e       ; ES:DI now points to floppy parameters table (INT 1E)\r
+\r
+       mov     ax, 0\r
+       mov     bx, 4\r
+       mov     ch, 0x4f\r
+       mov     cl, [cs:int1e_spt]\r
+       mov     dx, 0x0101\r
+\r
+       mov     byte [cs:disk_laststatus], 0\r
+       jmp     reach_stack_clc\r
+\r
+    i_gp_hd:\r
+\r
+       mov     ax, 0\r
+       mov     bx, 0\r
+       mov     dl, 1\r
+       mov     dh, [cs:hd_max_head]\r
+       mov     cx, [cs:hd_max_track]\r
+       ror     ch, 1\r
+       ror     ch, 1\r
+       add     ch, [cs:hd_max_sector]\r
+       xchg    ch, cl\r
+\r
+       mov     byte [cs:disk_laststatus], 0\r
+       jmp     reach_stack_clc\r
+\r
+  int13_seek:\r
+\r
+       mov     ah, 0\r
+       jmp     reach_stack_clc\r
+\r
+  int13_hdready:\r
+\r
+       cmp     byte [cs:num_disks], 2  ; HD present?\r
+       jne     int13_hdready_nohd\r
+       cmp     dl, 0x80                ; Checking first HD?\r
+       jne     int13_hdready_nohd\r
+\r
+       mov     ah, 0\r
+       jmp     reach_stack_clc\r
+\r
+    int13_hdready_nohd:\r
+\r
+       jmp     reach_stack_stc\r
+\r
+  int13_format:\r
+\r
+       mov     ah, 0\r
+       jmp     reach_stack_clc\r
+\r
+  int13_getdisktype:\r
+\r
+       cmp     dl, 0 ; Floppy\r
+       je      gdt_flop\r
+       cmp     dl, 0x80 ; HD\r
+       je      gdt_hd\r
+\r
+       mov     ah, 15 ; Report no such drive\r
+       mov     [cs:disk_laststatus], ah\r
+       jmp     reach_stack_stc\r
+\r
+    gdt_flop:\r
+\r
+       mov     ah, 1\r
+       jmp     reach_stack_clc\r
+\r
+    gdt_hd:\r
+\r
+       mov     ah, 3\r
+       mov     cx, [cs:hd_secs_hi]\r
+       mov     dx, [cs:hd_secs_lo]\r
+       jmp     reach_stack_clc\r
+\r
+  int13_diskchange:\r
+\r
+       mov     ah, 0 ; Disk not changed\r
+       jmp     reach_stack_clc\r
+\r
+; ************************* INT 14h - serial port functions\r
+\r
+int14:\r
+       cmp     ah, 0\r
+       je      int14_init\r
+\r
+       jmp     reach_stack_stc\r
+\r
+  int14_init:\r
+\r
+       mov     ax, 0\r
+       jmp     reach_stack_stc\r
+\r
+; ************************* INT 15h - get system configuration\r
+\r
+int15: ; Here we do not support any of the functions, and just return\r
+       ; a function not supported code - like the original IBM PC/XT does.\r
+\r
+       ; cmp   ah, 0xc0\r
+       ; je    int15_sysconfig\r
+       ; cmp   ah, 0x41\r
+       ; je    int15_waitevent\r
+       ; cmp   ah, 0x4f\r
+       ; je    int15_intercept\r
+       ; cmp   ah, 0x88\r
+       ; je    int15_getextmem\r
+\r
+; Otherwise, function not supported\r
+\r
+       mov     ah, 0x86\r
+\r
+       jmp     reach_stack_stc\r
+\r
+;  int15_sysconfig: ; Return address of system configuration table in ROM\r
+;\r
+;      mov     bx, 0xf000\r
+;      mov     es, bx\r
+;      mov     bx, rom_config\r
+;      mov     ah, 0\r
+;\r
+;      jmp     reach_stack_clc\r
+;\r
+;  int15_waitevent: ; Events not supported\r
+;\r
+;      mov     ah, 0x86\r
+;\r
+;      jmp     reach_stack_stc\r
+;\r
+;  int15_intercept: ; Keyboard intercept\r
+;\r
+;      jmp     reach_stack_stc\r
+;\r
+;  int15_getextmem: ; Extended memory not supported\r
+;\r
+;      mov     ah,0x86\r
+;\r
+;      jmp     reach_stack_stc\r
+\r
+; ************************* INT 16h handler - keyboard\r
+\r
+int16:\r
+       cmp     ah, 0x00 ; Get keystroke (remove from buffer)\r
+       je      kb_getkey\r
+       cmp     ah, 0x01 ; Check for keystroke (do not remove from buffer)\r
+       je      kb_checkkey\r
+       cmp     ah, 0x02 ; Check shift flags\r
+       je      kb_shiftflags\r
+       cmp     ah, 0x12 ; Check shift flags\r
+       je      kb_extshiftflags\r
+\r
+       iret\r
+\r
+  kb_getkey:\r
+       \r
+       push    es\r
+       push    bx\r
+       push    cx\r
+       push    dx\r
+\r
+       mov     bx, 0x40\r
+       mov     es, bx\r
+\r
+    kb_gkblock:\r
+\r
+       cli\r
+\r
+       mov     cx, [es:kbbuf_tail-bios_data]\r
+       mov     bx, [es:kbbuf_head-bios_data]\r
+       mov     dx, [es:bx]\r
+\r
+       sti\r
+\r
+       ; Wait until there is a key in the buffer\r
+       cmp     cx, bx\r
+       je      kb_gkblock\r
+\r
+       add     word [es:kbbuf_head-bios_data], 2\r
+       call    kb_adjust_buf\r
+\r
+       mov     ah, dh\r
+       mov     al, dl\r
+\r
+       pop     dx\r
+       pop     cx\r
+       pop     bx\r
+       pop     es      \r
+\r
+       iret\r
+\r
+  kb_checkkey:\r
+\r
+       push    es\r
+       push    bx\r
+       push    cx\r
+       push    dx\r
+\r
+       mov     bx, 0x40\r
+       mov     es, bx\r
+\r
+       mov     cx, [es:kbbuf_tail-bios_data]\r
+       mov     bx, [es:kbbuf_head-bios_data]\r
+       mov     dx, [es:bx]\r
+\r
+       sti\r
+\r
+       ; Check if there is a key in the buffer. ZF is set if there is none.\r
+       cmp     cx, bx\r
+\r
+       mov     ah, dh\r
+       mov     al, dl\r
+\r
+       pop     dx\r
+       pop     cx\r
+       pop     bx\r
+       pop     es\r
+\r
+       retf    2       ; NEED TO FIX THIS!!\r
+\r
+    kb_shiftflags:\r
+\r
+       push    es\r
+       push    bx\r
+\r
+       mov     bx, 0x40\r
+       mov     es, bx\r
+\r
+       mov     al, [es:keyflags1-bios_data]\r
+\r
+       pop     bx\r
+       pop     es\r
+\r
+       iret\r
+\r
+    kb_extshiftflags:\r
+\r
+       push    es\r
+       push    bx\r
+\r
+       mov     bx, 0x40\r
+       mov     es, bx\r
+\r
+       mov     al, [es:keyflags1-bios_data]\r
+       mov     ah, al\r
+\r
+       pop     bx\r
+       pop     es\r
+\r
+       iret\r
+\r
+; ************************* INT 17h handler - printer\r
+\r
+int17:\r
+       cmp     ah, 0x01\r
+       je      int17_initprint ; Initialise printer\r
+\r
+       jmp     reach_stack_stc\r
+\r
+  int17_initprint:\r
+\r
+       mov     ah, 1 ; No printer\r
+       jmp     reach_stack_stc\r
+\r
+; ************************* INT 19h = reboot\r
+\r
+int19:\r
+       jmp     boot\r
+\r
+; ************************* INT 1Ah - clock\r
+\r
+int1a:\r
+       cmp     ah, 0\r
+       je      int1a_getsystime ; Get ticks since midnight (used for RTC time)\r
+       cmp     ah, 2\r
+       je      int1a_gettime ; Get RTC time (not actually used by DOS)\r
+       cmp     ah, 4\r
+       je      int1a_getdate ; Get RTC date\r
+       cmp     ah, 0x0f\r
+       je      int1a_init    ; Initialise RTC\r
+\r
+       iret\r
+\r
+  int1a_getsystime:\r
+\r
+       push    ax\r
+       push    bx\r
+       push    ds\r
+       push    es\r
+\r
+       push    cs\r
+       push    cs\r
+       pop     ds\r
+       pop     es\r
+\r
+       mov     bx, timetable\r
+\r
+       extended_get_rtc\r
+\r
+       mov     ax, 182  ; Clock ticks in 10 seconds\r
+       mul     word [tm_msec]\r
+       mov     bx, 10000\r
+       div     bx ; AX now contains clock ticks in milliseconds counter\r
+       mov     [tm_msec], ax\r
+\r
+       mov     ax, 182  ; Clock ticks in 10 seconds\r
+       mul     word [tm_sec]\r
+       mov     bx, 10\r
+       mov     dx, 0\r
+       div     bx ; AX now contains clock ticks in seconds counter\r
+       mov     [tm_sec], ax\r
+\r
+       mov     ax, 1092 ; Clock ticks in a minute\r
+       mul     word [tm_min] ; AX now contains clock ticks in minutes counter\r
+       mov     [tm_min], ax\r
+       \r
+       mov     ax, 65520 ; Clock ticks in an hour\r
+       mul     word [tm_hour] ; DX:AX now contains clock ticks in hours counter\r
+\r
+       add     ax, [tm_msec] ; Add milliseconds in to AX\r
+       adc     dx, 0 ; Carry into DX if necessary\r
+       add     ax, [tm_sec] ; Add seconds in to AX\r
+       adc     dx, 0 ; Carry into DX if necessary\r
+       add     ax, [tm_min] ; Add minutes in to AX\r
+       adc     dx, 0 ; Carry into DX if necessary\r
+\r
+       push    dx\r
+       push    ax\r
+       pop     dx\r
+       pop     cx\r
+\r
+       pop     es\r
+       pop     ds\r
+       pop     bx\r
+       pop     ax\r
+\r
+       mov     al, 0\r
+       iret\r
+\r
+  int1a_gettime:\r
+\r
+       ; Return the system time in BCD format. DOS doesn't use this, but we need to return\r
+       ; something or the system thinks there is no RTC.\r
+\r
+       push    ds\r
+       push    es\r
+       push    ax\r
+       push    bx\r
+\r
+       push    cs\r
+       push    cs\r
+       pop     ds\r
+       pop     es\r
+\r
+       mov     bx, timetable\r
+\r
+       extended_get_rtc\r
+\r
+       mov     ax, 0\r
+       mov     cx, [tm_hour]\r
+       call    hex_to_bcd\r
+       mov     bh, al          ; Hour in BCD is in BH\r
+\r
+       mov     ax, 0\r
+       mov     cx, [tm_min]\r
+       call    hex_to_bcd\r
+       mov     bl, al          ; Minute in BCD is in BL\r
+\r
+       mov     ax, 0\r
+       mov     cx, [tm_sec]\r
+       call    hex_to_bcd\r
+       mov     dh, al          ; Second in BCD is in DH\r
+\r
+       mov     dl, 0           ; Daylight saving flag = 0 always\r
+\r
+       mov     cx, bx          ; Hour:minute now in CH:CL\r
+\r
+       pop     bx\r
+       pop     ax\r
+       pop     es\r
+       pop     ds\r
+\r
+       jmp     reach_stack_clc\r
+\r
+  int1a_getdate:\r
+\r
+       ; Return the system date in BCD format.\r
+\r
+       push    ds\r
+       push    es\r
+       push    bx\r
+       push    ax\r
+\r
+       push    cs\r
+       push    cs\r
+       pop     ds\r
+       pop     es\r
+\r
+       mov     bx, timetable\r
+\r
+       extended_get_rtc\r
+\r
+       mov     ax, 0x1900\r
+       mov     cx, [tm_year]\r
+       call    hex_to_bcd\r
+       mov     cx, ax\r
+       push    cx\r
+\r
+       mov     ax, 1\r
+       mov     cx, [tm_mon]\r
+       call    hex_to_bcd\r
+       mov     dh, al\r
+\r
+       mov     ax, 0\r
+       mov     cx, [tm_mday]\r
+       call    hex_to_bcd\r
+       mov     dl, al\r
+\r
+       pop     cx\r
+       pop     ax\r
+       pop     bx\r
+       pop     es\r
+       pop     ds\r
+\r
+       jmp     reach_stack_clc\r
+\r
+  int1a_init:\r
+\r
+       jmp     reach_stack_clc\r
+\r
+; ************************* INT 1Ch - the other timer interrupt\r
+\r
+int1c:\r
+\r
+       iret\r
+\r
+; ************************* INT 1Eh - diskette parameter table\r
+\r
+int1e:\r
+\r
+               db 0xdf ; Step rate 2ms, head unload time 240ms\r
+               db 0x02 ; Head load time 4 ms, non-DMA mode 0\r
+               db 0x25 ; Byte delay until motor turned off\r
+               db 0x02 ; 512 bytes per sector\r
+int1e_spt      db 18   ; 18 sectors per track (1.44MB)\r
+               db 0x1B ; Gap between sectors for 3.5" floppy\r
+               db 0xFF ; Data length (ignored)\r
+               db 0x54 ; Gap length when formatting\r
+               db 0xF6 ; Format filler byte\r
+               db 0x0F ; Head settle time (1 ms)\r
+               db 0x08 ; Motor start time in 1/8 seconds\r
+\r
+; ************************* INT 41h - hard disk parameter table\r
+\r
+int41:\r
+\r
+int41_max_cyls dw 0\r
+int41_max_heads        db 0\r
+               dw 0\r
+               dw 0\r
+               db 0\r
+               db 11000000b\r
+               db 0\r
+               db 0\r
+               db 0\r
+               dw 0\r
+int41_max_sect db 0\r
+               db 0\r
+\r
+; ************************* ROM configuration table\r
+\r
+rom_config     dw 16           ; 16 bytes following\r
+               db 0xfe         ; Model\r
+               db 'A'          ; Submodel\r
+               db 'C'          ; BIOS revision\r
+               db 0b00100000   ; Feature 1\r
+               db 0b00000000   ; Feature 2\r
+               db 0b00000000   ; Feature 3\r
+               db 0b00000000   ; Feature 4\r
+               db 0b00000000   ; Feature 5\r
+               db 0, 0, 0, 0, 0, 0\r
+\r
+; Internal state variables\r
+\r
+num_disks      dw 0    ; Number of disks present\r
+hd_secs_hi     dw 0    ; Total sectors on HD (high word)\r
+hd_secs_lo     dw 0    ; Total sectors on HD (low word)\r
+hd_max_sector  dw 0    ; Max sector number on HD\r
+hd_max_track   dw 0    ; Max track number on HD\r
+hd_max_head    dw 0    ; Max head number on HD\r
+drive_tracks_temp dw 0\r
+drive_sectors_temp dw 0\r
+drive_heads_temp  dw 0\r
+drive_num_temp    dw 0\r
+boot_state     db 0\r
+cga_refresh_reg        db 0\r
+\r
+; Default interrupt handlers\r
+\r
+int0:\r
+int1:\r
+int2:\r
+int3:\r
+int4:\r
+int5:\r
+int6:\r
+intb:\r
+intc:\r
+intd:\r
+inte:\r
+intf:\r
+int18:\r
+int1b:\r
+int1d:\r
+\r
+iret\r
+\r
+; ************ Function call library ************\r
+\r
+; Hex to BCD routine. Input is AX in hex (can be 0), and adds CX in hex to it, forming a BCD output in AX.\r
+\r
+hex_to_bcd:\r
+\r
+       push    bx\r
+\r
+       jcxz    h2bfin\r
+\r
+  h2bloop:\r
+\r
+       inc     ax\r
+\r
+       ; First process the low nibble of AL\r
+       mov     bh, al\r
+       and     bh, 0x0f\r
+       cmp     bh, 0x0a\r
+       jne     c1\r
+       add     ax, 0x0006\r
+\r
+       ; Then the high nibble of AL\r
+  c1:\r
+       mov     bh, al\r
+       and     bh, 0xf0\r
+       cmp     bh, 0xa0\r
+       jne     c2\r
+       add     ax, 0x0060\r
+\r
+       ; Then the low nibble of AH\r
+  c2:  \r
+       mov     bh, ah\r
+       and     bh, 0x0f\r
+       cmp     bh, 0x0a\r
+       jne     c3\r
+       add     ax, 0x0600\r
+\r
+  c3:  \r
+       loop    h2bloop\r
+  h2bfin:\r
+       pop     bx\r
+       ret\r
+\r
+; Takes a number in AL (from 0 to 99), and outputs the value in decimal using extended_putchar_al.\r
+\r
+puts_decimal_al:\r
+\r
+       push    ax\r
+       \r
+       aam\r
+       add     ax, 0x3030      ; '00'\r
+       \r
+       cmp     ah, 0x30\r
+       je      pda_2nd         ; First digit is zero, so print only 2nd digit\r
+\r
+       xchg    ah, al          ; First digit is now in AL\r
+       extended_putchar_al     ; Print first digit\r
+       xchg    ah, al          ; Second digit is now in AL\r
+\r
+  pda_2nd:\r
+\r
+       extended_putchar_al     ; Print second digit\r
+\r
+       pop     ax\r
+       ret\r
+\r
+; Keyboard adjust buffer head and tail. If either head or the tail are at the end of the buffer, reset them\r
+; back to the start, since it is a circular buffer.\r
+\r
+kb_adjust_buf:\r
+\r
+       push    ax\r
+       push    bx\r
+\r
+       ; Check to see if the head is at the end of the buffer (or beyond). If so, bring it back\r
+       ; to the start\r
+\r
+       mov     ax, [es:kbbuf_end_ptr-bios_data]\r
+       cmp     [es:kbbuf_head-bios_data], ax\r
+       jnge    kb_adjust_tail\r
+\r
+       mov     bx, [es:kbbuf_start_ptr-bios_data]\r
+       mov     [es:kbbuf_head-bios_data], bx   \r
+\r
+  kb_adjust_tail:\r
+\r
+       ; Check to see if the tail is at the end of the buffer (or beyond). If so, bring it back\r
+       ; to the start\r
+\r
+       mov     ax, [es:kbbuf_end_ptr-bios_data]\r
+       cmp     [es:kbbuf_tail-bios_data], ax\r
+       jnge    kb_adjust_done\r
+\r
+       mov     bx, [es:kbbuf_start_ptr-bios_data]\r
+       mov     [es:kbbuf_tail-bios_data], bx   \r
+\r
+  kb_adjust_done:\r
+\r
+       pop     bx\r
+       pop     ax\r
+       ret\r
+\r
+; Convert CHS disk position (in CH, CL and DH) to absolute sector number in BP:SI\r
+; Floppy disks have 512 bytes per sector, 9/18 sectors per track, 2 heads. DH is head number (1 or 0), CH bits 5..0 is\r
+; sector number, CL7..6 + CH7..0 is 10-bit cylinder/track number. Hard disks have 512 bytes per sector, but a variable\r
+; number of tracks and heads.\r
+\r
+chs_to_abs:\r
+\r
+       push    ax      \r
+       push    bx\r
+       push    cx\r
+       push    dx\r
+\r
+       mov     [cs:drive_num_temp], dl\r
+\r
+       ; First, we extract the track number from CH and CL.\r
+\r
+       push    cx\r
+       mov     bh, cl\r
+       mov     cl, 6\r
+       shr     bh, cl\r
+       mov     bl, ch\r
+\r
+       ; Multiply track number (now in BX) by the number of heads\r
+\r
+       cmp     byte [cs:drive_num_temp], 1 ; Floppy disk?\r
+\r
+       push    dx\r
+\r
+       mov     dx, 0\r
+       xchg    ax, bx\r
+\r
+       jne     chs_hd\r
+\r
+       shl     ax, 1 ; Multiply by 2 (number of heads on FD)\r
+       push    ax\r
+       xor     ax, ax\r
+       mov     al, [cs:int1e_spt]\r
+       mov     [cs:drive_sectors_temp], ax ; Retrieve sectors per track from INT 1E table\r
+       pop     ax\r
+\r
+       jmp     chs_continue\r
+\r
+chs_hd:\r
+\r
+       mov     bp, [cs:hd_max_head]\r
+       inc     bp\r
+       mov     [cs:drive_heads_temp], bp\r
+\r
+       mul     word [cs:drive_heads_temp] ; HD, so multiply by computed head count\r
+\r
+       mov     bp, [cs:hd_max_sector] ; We previously calculated maximum HD track, so number of tracks is 1 more\r
+       mov     [cs:drive_sectors_temp], bp\r
+\r
+chs_continue:\r
+\r
+       xchg    ax, bx\r
+\r
+       pop     dx\r
+\r
+       xchg    dh, dl\r
+       mov     dh, 0\r
+       add     bx, dx\r
+\r
+       mov     ax, [cs:drive_sectors_temp]\r
+       mul     bx\r
+\r
+       ; Now we extract the sector number (from 1 to 63) - for some reason they start from 1\r
+\r
+       pop     cx\r
+       mov     ch, 0\r
+       and     cl, 0x3F\r
+       dec     cl\r
+\r
+       add     ax, cx\r
+       adc     dx, 0\r
+       mov     bp, ax\r
+       mov     si, dx\r
+\r
+       ; Now, SI:BP contains offset into disk image file (FD or HD)\r
+\r
+       pop     dx\r
+       pop     cx\r
+       pop     bx\r
+       pop     ax\r
+       ret\r
+\r
+; Clear screen using ANSI codes. Also clear video memory with attribute in BH\r
+\r
+clear_screen:\r
+\r
+       push    ax\r
+\r
+       mov     al, 0x1B        ; Escape\r
+       extended_putchar_al\r
+       mov     al, '['         ; ANSI\r
+       extended_putchar_al\r
+       mov     al, 'r'         ; Set scrolling window\r
+       extended_putchar_al\r
+\r
+       mov     al, 0x1B        ; Escape\r
+       extended_putchar_al\r
+       mov     al, '['         ; ANSI\r
+       extended_putchar_al\r
+       mov     al, '0'         ; Reset attributes\r
+       extended_putchar_al\r
+       mov     al, 'm'         ; Reset attributes\r
+       extended_putchar_al\r
+\r
+       push    bx\r
+       push    cx\r
+       push    bp\r
+       push    ax\r
+       push    es\r
+\r
+       mov     bp, bx          ; Convert from CGA to ANSI\r
+       mov     cl, 12\r
+       ror     bp, cl\r
+       and     bp, 7\r
+       mov     bl, byte [cs:bp+colour_table]\r
+       add     bl, 10\r
+\r
+       mov     al, 0x1B        ; Escape\r
+       extended_putchar_al\r
+       mov     al, '['         ; ANSI\r
+       extended_putchar_al\r
+       mov     al, bl          ; Background colour\r
+       call    puts_decimal_al\r
+       mov     al, 'm'         ; Set cursor position command\r
+       extended_putchar_al\r
+\r
+       mov     ax, 0x40\r
+       mov     es, ax\r
+       mov     byte [es:curpos_x-bios_data], 0\r
+       mov     byte [es:crt_curpos_x-bios_data], 0\r
+       mov     byte [es:curpos_y-bios_data], 0\r
+       mov     byte [es:crt_curpos_y-bios_data], 0\r
+\r
+       pop     es\r
+       pop     ax\r
+       pop     bp\r
+       pop     cx\r
+       pop     bx\r
+\r
+       mov     al, 0x1B        ; Escape\r
+       extended_putchar_al\r
+       mov     al, '['         ; ANSI\r
+       extended_putchar_al\r
+       mov     al, '2'         ; Clear screen\r
+       extended_putchar_al\r
+       mov     al, 'J'\r
+       extended_putchar_al\r
+\r
+       mov     al, 0x1B        ; Escape\r
+       extended_putchar_al\r
+       mov     al, '['         ; ANSI\r
+       extended_putchar_al\r
+       mov     al, '1'         ; Cursor row 1\r
+       extended_putchar_al\r
+       mov     al, ';'\r
+       extended_putchar_al\r
+       mov     al, '1'         ; Cursor column 1\r
+       extended_putchar_al\r
+       mov     al, 'H'         ; Set cursor\r
+       extended_putchar_al\r
+\r
+       push    es\r
+       push    di\r
+       push    cx\r
+\r
+       cld\r
+       mov     ax, 0xb800\r
+       mov     es, ax\r
+       mov     di, 0\r
+       mov     al, 0\r
+       mov     ah, bh\r
+       mov     cx, 80*25\r
+       rep     stosw\r
+\r
+       cld\r
+       mov     di, 0xc800\r
+       mov     es, di\r
+       mov     di, 0\r
+       mov     cx, 80*25\r
+       rep     stosw\r
+\r
+       cld\r
+       mov     di, 0xc000\r
+       mov     es, di\r
+       mov     di, 0\r
+       mov     cx, 80*25\r
+       rep     stosw\r
+\r
+       pop     cx\r
+       pop     di\r
+       pop     es\r
+\r
+       pop     ax\r
+\r
+       mov     byte [cs:vram_dirty], 1\r
+\r
+       ret\r
+\r
+; Pushes a key press, followed by a key release, event to I/O port 0x60 and calls\r
+; INT 9.\r
+\r
+keypress_release:\r
+\r
+       push    ax\r
+\r
+       cmp     byte [es:key_now_down-bios_data], 0\r
+       je      kpr_no_prev_release\r
+\r
+       mov     al, [es:key_now_down-bios_data]\r
+       add     al, 0x80\r
+       call    io_key_available\r
+\r
+       pop     ax\r
+       push    ax\r
+\r
+  kpr_no_prev_release:\r
+\r
+       mov     [es:key_now_down-bios_data], al\r
+       call    io_key_available\r
+\r
+       pop     ax\r
+\r
+       ret\r
+\r
+; Sets key available flag on I/O port 0x64, outputs key scan code in AL to I/O port 0x60, and calls INT 9\r
+\r
+io_key_available:\r
+\r
+       push    ax\r
+       mov     al, 1\r
+       out     0x64, al\r
+       pop     ax\r
+\r
+       out     0x60, al\r
+       int     9\r
+       ret\r
+\r
+; Reaches up into the stack before the end of an interrupt handler, and sets the carry flag\r
+\r
+reach_stack_stc:\r
+\r
+       xchg    bp, sp\r
+       or      word [bp+4], 1\r
+       xchg    bp, sp\r
+       iret\r
+\r
+; Reaches up into the stack before the end of an interrupt handler, and clears the carry flag\r
+\r
+reach_stack_clc:\r
+\r
+       xchg    bp, sp\r
+       and     word [bp+4], 0xfffe\r
+       xchg    bp, sp\r
+       iret\r
+\r
+; Reaches up into the stack before the end of an interrupt handler, and returns with the current\r
+; setting of the carry flag\r
+\r
+reach_stack_carry:\r
+\r
+       jc      reach_stack_stc\r
+       jmp     reach_stack_clc\r
+\r
+; This is the VMEM driver, to support direct video memory access in 80x25 colour CGA mode.\r
+; It scans through CGA video memory at address B800:0, and if there is anything there (i.e.\r
+; applications are doing direct video memory writes), converts the buffer to a sequence of\r
+; ANSI terminal codes to render the screen output.\r
+;\r
+; Note: this destroys all registers. It is the responsibility of the caller to save/restore\r
+; them.\r
+\r
+vmem_driver_entry:\r
+       \r
+       cmp     byte [cs:in_update], 1\r
+       je      just_finish             ; If we are already in the middle of an update, skip. Needed for re-entrancy\r
+\r
+       inc     byte [cs:int8_ctr]\r
+       cmp     byte [cs:int8_ctr], 8   ; Only do this once every 8 timer ticks\r
+       jne     just_finish\r
+\r
+gmode_test:\r
+\r
+       mov     byte [cs:int8_ctr], 0   \r
+       mov     dx, 0x3b8               ; Do not update if in Hercules graphics mode\r
+       in      al, dx\r
+       test    al, 2\r
+       jz      vram_zero_check\r
+\r
+just_finish:\r
+\r
+       ret\r
+\r
+vram_zero_check:                       ; Check if video memory is blank - if so, do nothing\r
+       \r
+       mov     byte [cs:in_update], 1\r
+\r
+       sti\r
+\r
+       mov     bx, 0x40\r
+       mov     ds, bx\r
+\r
+       mov     di, [vmem_offset-bios_data] ; Adjust for CRTC video memory offset register\r
+       shl     di, 1\r
+       push    di\r
+\r
+       mov     bx, 0xb800\r
+       mov     es, bx\r
+       mov     cx, 0x7d0\r
+       mov     ax, 0x0700\r
+\r
+       cld\r
+       repz    scasw\r
+       pop     di\r
+       je      vmem_done       ; Nothing has been written to video RAM - no need to update\r
+\r
+       cmp     byte [cs:vram_dirty], 1 ; Cleared screen so always need to update\r
+       je      vram_update\r
+\r
+       mov     bx, 0xc800\r
+       mov     ds, bx\r
+       mov     si, 0\r
+       mov     cx, 0x7d0\r
+\r
+       cld\r
+       repz    cmpsw\r
+       jne     vram_update             ; Video RAM is changed - need to update\r
+\r
+       mov     bx, 0x40\r
+       mov     ds, bx\r
+       mov     bh, [crt_curpos_y-bios_data]\r
+       mov     bl, [crt_curpos_x-bios_data]\r
+       \r
+       cmp     bh, [cs:crt_curpos_y_last]\r
+       jne     restore_cursor ; Cursor position changed (but nothing else) so update just that\r
+       cmp     bl, [cs:crt_curpos_x_last]\r
+       jne     restore_cursor\r
+\r
+       jmp     vmem_done\r
+\r
+vram_update:\r
+\r
+       mov     bx, 0x40\r
+       mov     es, bx\r
+\r
+       push    cs\r
+       pop     ds\r
+\r
+       mov     byte [int_curpos_x], 0xff\r
+       mov     byte [int_curpos_y], 0xff\r
+\r
+       cmp     byte [es:cursor_visible-bios_data], 0\r
+       je      dont_hide_cursor\r
+\r
+       call    ansi_hide_cursor\r
+\r
+dont_hide_cursor:\r
+\r
+       mov     byte [last_attrib], 0xff\r
+\r
+       mov     bx, 0x40\r
+       mov     es, bx\r
+\r
+       mov     di, [es:vmem_offset-bios_data] ; Adjust for CRTC video memory offset register\r
+       shl     di, 1\r
+       sub     di, 2           ; Combined offset\r
+\r
+       mov     bx, 0xb800\r
+       mov     es, bx\r
+\r
+       ; Set up the initial cursor coordinates. Since the first thing we do is increment the cursor\r
+       ; position, this initial position is actually off the screen\r
+\r
+       mov     bp, -1          ; Row number\r
+       mov     si, 79          ; Column number\r
+\r
+disp_loop:\r
+\r
+       ; Advance to next column\r
+\r
+       add     di, 2\r
+       inc     si\r
+       cmp     si, 80\r
+       jne     cont\r
+\r
+       ; Column is 80, so set to 0 and advance a line\r
+\r
+loop_next_line:\r
+\r
+       mov     si, 0\r
+       inc     bp\r
+\r
+       ; Bottom of the screen reached already? If so, we're done\r
+\r
+       cmp     bp, 25\r
+       je      restore_attrib\r
+\r
+       ; See if this line has changed in video RAM\r
+\r
+       cmp     byte [cs:vram_dirty], 1\r
+       je      cont\r
+\r
+       push    si\r
+       push    di\r
+\r
+       mov     bx, 0xb800\r
+       mov     ds, bx\r
+       mov     bx, 0xc800\r
+       mov     es, bx\r
+       mov     si, di\r
+\r
+       push    es\r
+       mov     bx, 0x40\r
+       mov     es, bx\r
+       sub     di, [es:vmem_offset-bios_data] ; Adjust for CRTC video memory offset register\r
+       sub     di, [es:vmem_offset-bios_data]\r
+       pop     es\r
+\r
+       mov     cx, 80 ; One row's worth of characters\r
+\r
+       cld\r
+       repz    cmpsw\r
+       pop     di\r
+       pop     si\r
+\r
+       je      vmem_next_line ; This line is unchanged in video RAM, so do not update\r
+\r
+vmem_copy_buf:\r
+\r
+       ; Copy the changed line to our double buffer at C800:0\r
+\r
+       push    cx\r
+       push    si\r
+       push    di\r
+\r
+       push    es\r
+       mov     bx, 0x40\r
+       mov     es, bx\r
+       mov     si, di\r
+       sub     di, [es:vmem_offset-bios_data] ; Adjust for CRTC video memory offset register\r
+       sub     di, [es:vmem_offset-bios_data]\r
+       pop     es\r
+\r
+       mov     cx, 80 ; One row's worth of characters\r
+       cld\r
+       rep     movsw\r
+\r
+       pop     di\r
+       pop     si\r
+       pop     cx\r
+\r
+       ; We want to start the update at the first character which differs - so calculate its position.\r
+\r
+       mov     bx, 79\r
+       sub     bx, cx\r
+\r
+       add     di, bx\r
+       add     di, bx\r
+       add     si, bx\r
+\r
+       push    ds\r
+       pop     es      ; Set ES back to B800\r
+\r
+       jmp     cont\r
+\r
+vmem_next_line:\r
+\r
+       add     di, 160\r
+       jmp     loop_next_line ; Line is unchanged in video RAM\r
+\r
+cont:\r
+       push    cs\r
+       pop     ds\r
+\r
+       cmp     byte [es:di], 0         ; Ignore null characters in video memory\r
+       je      disp_loop\r
+\r
+       mov     ax, bp\r
+       mov     bx, si\r
+       mov     dh, al\r
+       mov     dl, bl\r
+\r
+       cmp     dh, [int_curpos_y]      ; Same row as the last time?\r
+       jne     ansi_set_cur_pos\r
+       push    dx\r
+       dec     dl\r
+       cmp     dl, [int_curpos_x]      ; One column to the right since the last time?\r
+       pop     dx\r
+       je      skip_set_cur_pos\r
+\r
+ansi_set_cur_pos:\r
+\r
+       mov     al, 0x1B        ; Escape\r
+       extended_putchar_al\r
+       mov     al, '['         ; ANSI\r
+       extended_putchar_al\r
+       mov     al, dh          ; Row number\r
+       inc     al\r
+       call    puts_decimal_al\r
+       mov     al, ';'         ; ANSI\r
+       extended_putchar_al\r
+       mov     al, dl          ; Column number\r
+       inc     al\r
+       call    puts_decimal_al\r
+       mov     al, 'H'         ; Set cursor position command\r
+       extended_putchar_al\r
+\r
+       mov     [int_curpos_y], dh\r
+\r
+skip_set_cur_pos:\r
+\r
+       mov     [int_curpos_x], dl\r
+\r
+       mov     dl, [es:di+1]\r
+       cmp     dl, [last_attrib]\r
+       je      skip_attrib\r
+\r
+       mov     [last_attrib], dl\r
+\r
+       mov     al, 0x1B        ; Escape\r
+       extended_putchar_al\r
+       mov     al, '['         ; ANSI\r
+       extended_putchar_al\r
+\r
+       mov     al, dl\r
+       and     al, 8           ; Bright attribute now in AL\r
+       cpu     186\r
+       shr     al, 3\r
+       cpu     8086\r
+\r
+       call    puts_decimal_al\r
+       mov     al, ';'\r
+       extended_putchar_al\r
+\r
+       push    dx\r
+\r
+       and     dl, 7           ; Foreground colour now in DL\r
+       mov     bx, colour_table\r
+       mov     al, dl\r
+       xlat\r
+\r
+       call    puts_decimal_al\r
+       mov     al, ';'\r
+       extended_putchar_al\r
+\r
+       pop     dx\r
+\r
+       cpu     186\r
+       shr     dl, 4\r
+       cpu     8086\r
+       and     dl, 7           ; Background colour now in DL\r
+\r
+       mov     al, dl\r
+       xlat\r
+\r
+       add     al, 10\r
+       call    puts_decimal_al\r
+       mov     al, 'm'         ; Set cursor attribute command\r
+       extended_putchar_al\r
+\r
+skip_attrib:\r
+\r
+       mov     al, [es:di]\r
+\r
+       cmp     al, 32          ; Non-printable ASCII? (< 32 decimal)\r
+       jae     just_show_it\r
+\r
+       mov     bx, low_ascii_conv\r
+       cs      xlat            ; Convert to printable representation (mostly spaces)\r
+\r
+just_show_it:\r
+\r
+       extended_putchar_al\r
+\r
+       jmp     disp_loop\r
+\r
+restore_attrib:\r
+\r
+       mov     al, 0x1B        ; Escape\r
+       extended_putchar_al\r
+       mov     al, '['         ; ANSI\r
+       extended_putchar_al\r
+       mov     al, '0'         ; Reset attributes\r
+       extended_putchar_al\r
+       mov     al, 'm'\r
+       extended_putchar_al\r
+\r
+restore_cursor:\r
+\r
+       ; On a real PC, the 6845 CRT cursor position registers take place over the BIOS\r
+       ; Data Area ones. So, if the cursor is not off the screen, set it to the CRT\r
+       ; position.\r
+\r
+       mov     bx, 0x40\r
+       mov     ds, bx\r
+\r
+       mov     bh, [crt_curpos_y-bios_data]\r
+       mov     bl, [crt_curpos_x-bios_data]\r
+       mov     [cs:crt_curpos_y_last], bh\r
+       mov     [cs:crt_curpos_x_last], bl\r
+               \r
+       cmp     bh, 24\r
+       ja      vmem_end_hidden_cursor\r
+       cmp     bl, 79\r
+       ja      vmem_end_hidden_cursor\r
+\r
+       mov     al, 0x1B        ; ANSI\r
+       extended_putchar_al\r
+       mov     al, '['         ; ANSI\r
+       extended_putchar_al\r
+       mov     al, bh          ; Row number\r
+       inc     al\r
+       call    puts_decimal_al\r
+       mov     al, ';'         ; ANSI\r
+       extended_putchar_al\r
+       mov     al, bl          ; Column number\r
+       inc     al\r
+       call    puts_decimal_al\r
+       mov     al, 'H'         ; Set cursor position command\r
+       extended_putchar_al\r
+\r
+restore_cursor_visible:\r
+\r
+       cmp     byte [cursor_visible-bios_data], 1\r
+       jne     vmem_end_hidden_cursor\r
+\r
+       call    ansi_show_cursor\r
+       jmp     vmem_done\r
+\r
+vmem_end_hidden_cursor:\r
+\r
+       call    ansi_hide_cursor\r
+\r
+vmem_done:\r
+\r
+       mov     byte [cs:vram_dirty], 0\r
+       mov     byte [cs:in_update], 0\r
+       ret\r
+\r
+; Show cursor using ANSI codes\r
+\r
+ansi_show_cursor:\r
+\r
+       mov     al, 0x1B\r
+       extended_putchar_al\r
+       mov     al, '['\r
+       extended_putchar_al\r
+       mov     al, '?'\r
+       extended_putchar_al\r
+       mov     al, '2'\r
+       extended_putchar_al\r
+       mov     al, '5'\r
+       extended_putchar_al\r
+       mov     al, 'h'\r
+       extended_putchar_al\r
+\r
+       ret\r
+\r
+; Hide cursor using ANSI codes\r
+\r
+ansi_hide_cursor:\r
+\r
+       mov     al, 0x1B\r
+       extended_putchar_al\r
+       mov     al, '['\r
+       extended_putchar_al\r
+       mov     al, '?'\r
+       extended_putchar_al\r
+       mov     al, '2'\r
+       extended_putchar_al\r
+       mov     al, '5'\r
+       extended_putchar_al\r
+       mov     al, 'l'\r
+       extended_putchar_al\r
+\r
+       ret\r
+\r
+; ****************************************************************************************\r
+; That's it for the code. Now, the data tables follow.\r
+; ****************************************************************************************\r
+\r
+; Standard PC-compatible BIOS data area - to copy to 40:0\r
+\r
+bios_data:\r
+\r
+com1addr       dw      0\r
+com2addr       dw      0\r
+com3addr       dw      0\r
+com4addr       dw      0\r
+lpt1addr       dw      0\r
+lpt2addr       dw      0\r
+lpt3addr       dw      0\r
+lpt4addr       dw      0\r
+equip          dw      0b0000000000100001\r
+;equip         dw      0b0000000100100001\r
+               db      0\r
+memsize                dw      0x280\r
+               db      0\r
+               db      0\r
+keyflags1      db      0\r
+keyflags2      db      0\r
+               db      0\r
+kbbuf_head     dw      kbbuf-bios_data\r
+kbbuf_tail     dw      kbbuf-bios_data\r
+kbbuf: times 32        db      'X'\r
+drivecal       db      0\r
+diskmotor      db      0\r
+motorshutoff   db      0x07\r
+disk_laststatus        db      0\r
+times 7                db      0\r
+vidmode                db      0x03\r
+vid_cols       dw      80\r
+page_size      dw      0x1000\r
+               dw      0\r
+curpos_x       db      0\r
+curpos_y       db      0\r
+times 7                dw      0\r
+cur_v_end      db      7\r
+cur_v_start    db      6\r
+disp_page      db      0\r
+crtport                dw      0x3d4\r
+               db      10\r
+               db      0\r
+times 5                db      0\r
+clk_dtimer     dd      0\r
+clk_rollover   db      0\r
+ctrl_break     db      0\r
+soft_rst_flg   dw      0x1234\r
+               db      0\r
+num_hd         db      0\r
+               db      0\r
+               db      0\r
+               dd      0\r
+               dd      0\r
+kbbuf_start_ptr        dw      0x001e\r
+kbbuf_end_ptr  dw      0x003e\r
+vid_rows       db      25         ; at 40:84\r
+               db      0\r
+               db      0\r
+vidmode_opt    db      0 ; 0x70\r
+               db      0 ; 0x89\r
+               db      0 ; 0x51\r
+               db      0 ; 0x0c\r
+               db      0\r
+               db      0\r
+               db      0\r
+               db      0\r
+               db      0\r
+               db      0\r
+               db      0\r
+               db      0\r
+               db      0\r
+               db      0\r
+               db      0\r
+kb_mode                db      0\r
+kb_led         db      0\r
+               db      0\r
+               db      0\r
+               db      0\r
+               db      0\r
+boot_device    db      0\r
+crt_curpos_x   db      0\r
+crt_curpos_y   db      0\r
+key_now_down   db      0\r
+next_key_fn    db      0\r
+cursor_visible db      1\r
+escape_flag_last       db      0\r
+next_key_alt   db      0\r
+escape_flag    db      0\r
+notranslate_flg        db      0\r
+this_keystroke db      0\r
+this_keystroke_ext             db      0\r
+timer0_freq    dw      0xffff ; PIT channel 0 (55ms)\r
+timer2_freq    dw      0      ; PIT channel 2\r
+cga_vmode      db      0\r
+vmem_offset    dw      0      ; Video RAM offset\r
+ending:                times (0xff-($-com1addr)) db    0\r
+\r
+; Keyboard scan code tables\r
+\r
+a2scan_tbl      db     0xFF, 0x1E, 0x30, 0x2E, 0x20, 0x12, 0x21, 0x22, 0x0E, 0x0F, 0x24, 0x25, 0x26, 0x1C, 0x31, 0x18, 0x19, 0x10, 0x13, 0x1F, 0x14, 0x16, 0x2F, 0x11, 0x2D, 0x15, 0x2C, 0x01, 0x00, 0x00, 0x00, 0x00, 0x39, 0x02, 0x28, 0x04, 0x05, 0x06, 0x08, 0x28, 0x0A, 0x0B, 0x09, 0x0D, 0x33, 0x0C, 0x34, 0x35, 0x0B, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x27, 0x27, 0x33, 0x0D, 0x34, 0x35, 0x03, 0x1E, 0x30, 0x2E, 0x20, 0x12, 0x21, 0x22, 0x23, 0x17, 0x24, 0x25, 0x26, 0x32, 0x31, 0x18, 0x19, 0x10, 0x13, 0x1F, 0x14, 0x16, 0x2F, 0x11, 0x2D, 0x15, 0x2C, 0x1A, 0x2B, 0x1B, 0x07, 0x0C, 0x29, 0x1E, 0x30, 0x2E, 0x20, 0x12, 0x21, 0x22, 0x23, 0x17, 0x24, 0x25, 0x26, 0x32, 0x31, 0x18, 0x19, 0x10, 0x13, 0x1F, 0x14, 0x16, 0x2F, 0x11, 0x2D, 0x15, 0x2C, 0x1A, 0x2B, 0x1B, 0x29, 0x0E\r
+a2shift_tbl     db     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0\r
+\r
+; Interrupt vector table - to copy to 0:0\r
+\r
+int_table      dw int0\r
+               dw 0xf000\r
+               dw int1\r
+               dw 0xf000\r
+               dw int2\r
+               dw 0xf000\r
+               dw int3\r
+               dw 0xf000\r
+               dw int4\r
+               dw 0xf000\r
+               dw int5\r
+               dw 0xf000\r
+               dw int6\r
+               dw 0xf000\r
+               dw int7\r
+               dw 0xf000\r
+               dw int8\r
+               dw 0xf000\r
+               dw int9\r
+               dw 0xf000\r
+               dw inta\r
+               dw 0xf000\r
+               dw intb\r
+               dw 0xf000\r
+               dw intc\r
+               dw 0xf000\r
+               dw intd\r
+               dw 0xf000\r
+               dw inte\r
+               dw 0xf000\r
+               dw intf\r
+               dw 0xf000\r
+               dw int10\r
+               dw 0xf000\r
+               dw int11\r
+               dw 0xf000\r
+               dw int12\r
+               dw 0xf000\r
+               dw int13\r
+               dw 0xf000\r
+               dw int14\r
+               dw 0xf000\r
+               dw int15\r
+               dw 0xf000\r
+               dw int16\r
+               dw 0xf000\r
+               dw int17\r
+               dw 0xf000\r
+               dw int18\r
+               dw 0xf000\r
+               dw int19\r
+               dw 0xf000\r
+               dw int1a\r
+               dw 0xf000\r
+               dw int1b\r
+               dw 0xf000\r
+               dw int1c\r
+               dw 0xf000\r
+               dw int1d\r
+               dw 0xf000\r
+               dw int1e\r
+\r
+itbl_size      dw $-int_table\r
+\r
+; Conversion from CGA video memory colours to ANSI colours\r
+\r
+colour_table   db      30, 34, 32, 36, 31, 35, 33, 37\r
+\r
+; Conversion from non-printable low ASCII to printable\r
+\r
+low_ascii_conv db      ' ', 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, '><|!|$', 250, '|^v><--^v'\r
+\r
+; Conversion from UNIX cursor keys/SDL keycodes to scancodes\r
+\r
+unix_cursor_xlt        db      0x48, 0x50, 0x4d, 0x4b\r
+\r
+; Conversion from SDL keycodes to Home/End/PgUp/PgDn scancodes\r
+\r
+pgup_pgdn_xlt  db      0x47, 0x4f, 0x49, 0x51\r
+\r
+; Internal variables for VMEM driver\r
+\r
+int8_ctr       db      0\r
+in_update      db      0\r
+vram_dirty     db      0\r
+last_attrib    db      0\r
+int_curpos_x   db      0\r
+int_curpos_y   db      0\r
+crt_curpos_x_last      db      0\r
+crt_curpos_y_last      db      0\r
+\r
+; INT 8 millisecond counter\r
+\r
+last_int8_msec dw      0\r
+last_key_sdl   db      0\r
+\r
+; Now follow the tables for instruction decode helping\r
+\r
+; R/M mode tables\r
+\r
+rm_mode0_reg1  db      3, 3, 5, 5, 6, 7, 12, 3\r
+rm_mode012_reg2        db      6, 7, 6, 7, 12, 12, 12, 12\r
+rm_mode0_disp  db      0, 0, 0, 0, 0, 0, 1, 0\r
+rm_mode0_dfseg db      11, 11, 10, 10, 11, 11, 11, 11\r
+\r
+rm_mode12_reg1 db      3, 3, 5, 5, 6, 7, 5, 3\r
+rm_mode12_disp db      1, 1, 1, 1, 1, 1, 1, 1\r
+rm_mode12_dfseg        db      11, 11, 10, 10, 11, 11, 10, 11\r
+\r
+; Opcode decode tables\r
+\r
+xlat_ids       db      9, 9, 9, 9, 7, 7, 25, 26, 9, 9, 9, 9, 7, 7, 25, 48, 9, 9, 9, 9, 7, 7, 25, 26, 9, 9, 9, 9, 7, 7, 25, 26, 9, 9, 9, 9, 7, 7, 27, 28, 9, 9, 9, 9, 7, 7, 27, 28, 9, 9, 9, 9, 7, 7, 27, 29, 9, 9, 9, 9, 7, 7, 27, 29, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 51, 54, 52, 52, 52, 52, 52, 52, 55, 55, 55, 55, 52, 52, 52, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 15, 15, 24, 24, 9, 9, 9, 9, 10, 10, 10, 10, 16, 16, 16, 16, 16, 16, 16, 16, 30, 31, 32, 53, 33, 34, 35, 36, 11, 11, 11, 11, 17, 17, 18, 18, 47, 47, 17, 17, 17, 17, 18, 18, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 12, 12, 19, 19, 37, 37, 20, 20, 49, 50, 19, 19, 38, 39, 40, 19, 12, 12, 12, 12, 41, 42, 43, 44, 53, 53, 53, 53, 53, 53, 53, 53, 13, 13, 13, 13, 21, 21, 22, 22, 14, 14, 14, 14, 21, 21, 22, 22, 53, 0, 23, 23, 53, 45, 6, 6, 46, 46, 46, 46, 46, 46, 5, 5\r
+ex_data        db      0, 0, 0, 0, 0, 0, 8, 8, 1, 1, 1, 1, 1, 1, 9, 36, 2, 2, 2, 2, 2, 2, 10, 10, 3, 3, 3, 3, 3, 3, 11, 11, 4, 4, 4, 4, 4, 4, 8, 0, 5, 5, 5, 5, 5, 5, 9, 1, 6, 6, 6, 6, 6, 6, 10, 2, 7, 7, 7, 7, 7, 7, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 21, 21, 21, 21, 21, 0, 0, 0, 0, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 12, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 16, 22, 0, 0, 0, 0, 1, 1, 0, 255, 48, 2, 0, 0, 0, 0, 255, 255, 40, 11, 3, 3, 3, 3, 3, 3, 3, 3, 43, 43, 43, 43, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 21, 0, 0, 2, 40, 21, 21, 80, 81, 92, 93, 94, 95, 0, 0\r
+std_flags      db      3, 3, 3, 3, 3, 3, 0, 0, 5, 5, 5, 5, 5, 5, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 5, 5, 5, 5, 5, 5, 0, 1, 3, 3, 3, 3, 3, 3, 0, 1, 5, 5, 5, 5, 5, 5, 0, 1, 3, 3, 3, 3, 3, 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\r
+base_size      db      2, 2, 2, 2, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 2, 2, 2, 2, 2, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 0, 0, 2, 2, 2, 2, 4, 1, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 2, 2\r
+i_w_adder      db      0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\r
+i_mod_adder    db      1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1\r
+\r
+flags_mult     db      0, 2, 4, 6, 7, 8, 9, 10, 11\r
+\r
+jxx_dec_a      db      48, 40, 43, 40, 44, 41, 49, 49\r
+jxx_dec_b      db      49, 49, 49, 43, 49, 49, 49, 43\r
+jxx_dec_c      db      49, 49, 49, 49, 49, 49, 44, 44\r
+jxx_dec_d      db      49, 49, 49, 49, 49, 49, 48, 48\r
+\r
+parity         db      1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1\r
+\r
+; This is the format of the 36-byte tm structure, returned by the emulator's RTC query call\r
+\r
+timetable:\r
+\r
+tm_sec         equ $\r
+tm_min         equ $+4\r
+tm_hour                equ $+8\r
+tm_mday                equ $+12\r
+tm_mon         equ $+16\r
+tm_year                equ $+20\r
+tm_wday                equ $+24\r
+tm_yday                equ $+28\r
+tm_dst         equ $+32\r
+tm_msec                equ $+36
\ No newline at end of file
diff --git a/plat/pc86/emu/docs/8086tiny.css b/plat/pc86/emu/docs/8086tiny.css
new file mode 100644 (file)
index 0000000..2c50188
--- /dev/null
@@ -0,0 +1,166 @@
+/*\r
+CSS Credit: http://www.templatemo.com/\r
+*/\r
+body {\r
+       margin:0;\r
+       padding:0;\r
+       line-height: 1.5em;\r
+       font-family: "Trebuchet MS", Verdana, Helvetica, Arial;\r
+       font-size: 12px;\r
+       color: #000000;\r
+       background: #999999 url(images/templatemo_bg.gif);\r
+}\r
+a:link, a:visited { color: #0066CC; text-decoration: none} \r
+a:active, a:hover { color: #008800; text-decoration: underline}\r
+\r
+#templatemo_container_wrapper {\r
+       background: url(images/templatemo_side_bg.gif) repeat-x;\r
+       padding-left: 2px;\r
+}\r
+#templatemo_container {\r
+       width: 809px;\r
+       margin: 0px auto;\r
+       background: url(images/templatemo_content_bg.gif);\r
+}\r
+#templatemo_top {\r
+       clear: left;\r
+       height: 25px;   /* 'padding-top' + 'height' must be equal to the 'background image height' */\r
+       padding-top: 18px;\r
+       padding-left: 30px;\r
+       color: #0000bf;\r
+       background: url(images/templatemo_top_bg.gif) no-repeat bottom;\r
+}\r
+#templatemo_header {\r
+       clear: left;\r
+       height: 113px;\r
+       text-align: center;\r
+       background: url(images/templatemo_header_bg.gif) no-repeat;\r
+}\r
+#inner_header {\r
+       height: 88px;\r
+       background: url(images/templatemo_header.jpg) no-repeat center center;\r
+}\r
+#templatemo_left_column {\r
+       clear: left;\r
+       float: left;\r
+       width: 540px;\r
+       padding-left: 20px;\r
+}\r
+#templatemo_right_column {\r
+       float: right;\r
+       width: 216px;\r
+       padding-right: 15px;\r
+}\r
+#templatemo_footer {\r
+       clear: both;\r
+       padding-top: 18px;\r
+       height: 37px;\r
+       text-align: center;\r
+       font-size: 11px;\r
+       background: url(images/templatemo_footer_bg.gif) no-repeat;\r
+       color: #666666;\r
+}\r
+#templatemo_footer a {\r
+       color: #666666;\r
+}\r
+#templatemo_site_title {\r
+       padding-top: 65px;\r
+       font-weight: bold;\r
+       font-size: 32px;\r
+       color: #FFFFFF;\r
+}\r
+.site_slogan_center {\r
+       float:left;\r
+       display:block;\r
+       width:50px;\r
+       height:25px;\r
+       text-indent:-999px;\r
+       cursor:default;\r
+}\r
+#templatemo_site_slogan {\r
+       padding-top: 14px;\r
+       font-weight: bold;\r
+       font-size: 13px;\r
+       color: #AAFFFF;\r
+}\r
+.templatemo_spacer {\r
+       clear: left;\r
+       height: 18px;\r
+}\r
+.templatemo_pic {\r
+       float: left;\r
+       margin-right: 10px;\r
+       margin-bottom: 10px;\r
+       border: 1px solid #000000;\r
+}\r
+.section_box {\r
+       margin: 10px;\r
+       padding: 10px;\r
+       border: 1px dashed #CCCCCC;\r
+       background: #F2F2F2;\r
+}\r
+.section_box2 {\r
+       clear: left;\r
+       margin-top: 10px;\r
+       background: #F6F6F6;\r
+       color: #000000;\r
+}\r
+.text_area {\r
+       padding: 10px;\r
+}\r
+.publish_date {\r
+       clear: both;\r
+       margin-top: 10px;\r
+       color: #999999;\r
+       font-size: 11px;\r
+       font-weight: bold;\r
+}\r
+.title {\r
+       padding-bottom: 12px;\r
+       font-size: 18px;\r
+       font-weight: bold;\r
+       color: #0066CC;\r
+}\r
+.subtitle {\r
+       padding-bottom: 6px;\r
+       font-size: 14px;\r
+       font-weight: bold;\r
+       color: #666666;\r
+}\r
+.post_title {\r
+       padding: 6px;\r
+       padding-left: 10px;\r
+       background: #DDEEFF;\r
+       font-size: 14px;\r
+       font-weight: bold;\r
+       color: #0066CC;\r
+}\r
+.templatemo_menu {\r
+       list-style-type: none;\r
+       margin: 10px;\r
+       margin-top: 0px;\r
+       padding: 0px;\r
+       width: 195px;\r
+}\r
+.templatemo_menu li a{\r
+       background: #F4F4F4 url(images/button_default.gif) no-repeat;\r
+       font-size: 13px;\r
+       font-weight: bold;\r
+       color: #0066CC;\r
+       display: block;\r
+       width: auto;\r
+       margin-bottom: 2px;\r
+       padding: 5px;\r
+       padding-left: 12px;\r
+       text-decoration: none;\r
+}\r
+* html .templatemo_menu li a{ \r
+       width: 190px;\r
+}\r
+.templatemo_menu li a:visited, .templatemo_menu li a:active{\r
+       color: #0066CC;\r
+}\r
+.templatemo_menu li a:hover{\r
+       background: #EEEEEE url(images/button_active.gif) no-repeat;\r
+       color: #FF3333;\r
+}
\ No newline at end of file
diff --git a/plat/pc86/emu/docs/doc.html b/plat/pc86/emu/docs/doc.html
new file mode 100644 (file)
index 0000000..de79c1b
--- /dev/null
@@ -0,0 +1,345 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\r
+<html xmlns="http://www.w3.org/1999/xhtml">\r
+<head>\r
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />\r
+<title>8086tiny: a tiny PC emulator/virtual machine - Documentation</title>\r
+<meta name="keywords" content="8086tiny free small portable 8086 pc emulator" />\r
+<meta name="description" content="8086tiny is a free, open source PC XT-compatible emulator/virtual machine written in C. It is, we believe, the smallest of its kind." />\r
+<link href="8086tiny.css" rel="stylesheet" type="text/css" />\r
+</head>\r
+<body>\r
+<!--\r
+This is a free CSS template provided by templatemo.com\r
+-->\r
+<div id="templatemo_container_wrapper">\r
+       <div class="templatemo_spacer"></div>\r
+<div id="templatemo_container">\r
+<div id="templatemo_top">\r
+<b>8086tiny</b> · Documentation\r
+</div>\r
+  <div id="templatemo_header">\r
+        <img src="images/logo.gif" width=780 align=middle>\r
+  </div>\r
+  \r
+<div id="templatemo_left_column">\r
+<div class="text_area" align="justify">\r
+\r
+<div class="title">Documentation</div>\r
+\r
+<p>8086tiny is a tiny, free, open source, portable Intel PC emulator/VM, powerful enough to run DOS, Windows 3.0, Excel, MS Flight Simulator, AutoCAD, Lotus 1-2-3, and similar applications. 8086tiny emulates a "late 80's era" PC XT-type machine with the following features:</p>\r
+\r
+<ul>\r
+<li>Intel 8086/186 CPU</li>\r
+<li>1MB RAM</li>\r
+<li>3.5" floppy disk controller (1.44MB/720KB)</li>\r
+<li>Fixed disk controller (supports a single hard drive up to 528MB)</li>\r
+<li>CGA/Hercules graphics card with 720x348 2-color and 320x200 4-color graphics (64KB video RAM), and CGA 80 x 25 16-color text mode support</li>\r
+<li>Accurate programmable interrupt timer (PIT)</li>\r
+<li>Keyboard controller with 83-key XT-style keyboard</li>\r
+<li>Real-time clock</li>\r
+<li>PC speaker</li>\r
+</ul>\r
+  \r
+<p>The emulator uses the SDL graphics library for portability, and compiles under a range of platforms (Windows, Mac OS X, Linux, Android, iOS, Raspberry Pi).</p>\r
+<p>While 8086tiny as supplied implements only the 8086 instruction set, it can be extended to more complex, modern instruction sets with relative ease.</p>\r
+\r
+  <div class="section_box2" align="justify">\r
+               <div class="post_title">Build Instructions</div>\r
+               <div class="text_area">\r
+            <p>\r
+              The 8086tiny distribution includes a Makefile that will compile unchanged under most UNIX platforms. The 8086tiny source also compiles unchanged under Microsoft Visual Studio C/C++.\r
+              </p>\r
+            <ul>\r
+              <li>Running <code>make</code> compiles the full 8086tiny distribution, which includes audio and CGA/Hercules graphics support via SDL.</li>\r
+              <li>To compile for slower platforms (e.g. Raspberry Pi), build with <code>make 8086tiny_slowcpu</code> to increase the graphics emulation frame rate.</li>\r
+              <li>If your platform lacks SDL and/or you do not need support for graphics-mode applications, you can compile without graphics and audio support by running <code>make no_graphics</code> to produce a smaller binary.</li>\r
+              </ul>\r
+               </div>\r
+  </div>\r
+\r
+  <div class="section_box2" align="justify">\r
+               <div class="post_title">Usage</div>\r
+               <div class="text_area">\r
+            <p><code>8086tiny bios-image-file floppy-image-file [@][harddisk-image-file]</code></p>\r
+\r
+            <p>If <code>harddisk-image-file</code> is prefixed with <code>@</code> then 8086tiny will boot from the hard disk image. Otherwise, 8086tiny will boot from the floppy disk image.</p>\r
+\r
+            <p>Under UNIXes, <b>the keyboard must be set to raw mode using <code>stty</code> for the emulator to run</b>. The distribution includes a script called <code>runme</code> which sets the keyboard mode appropriately and runs the emulator with floppy and/or hard disk images as appropriate:</p>\r
+            <code>\r
+              #!/bin/sh<br>\r
+              clear<br>\r
+              stty cbreak raw -echo min 0<br>\r
+              if [ -f hd.img ]<br>\r
+              then<br>\r
+              &nbsp;&nbsp;./8086tiny bios fd.img hd.img<br>\r
+              else<br>\r
+              &nbsp;&nbsp;./8086tiny bios fd.img<br>\r
+              fi<br>\r
+              stty cooked echo\r
+            </code>\r
+               </div>\r
+  </div>\r
+\r
+  <div class="section_box2" align="justify">\r
+               <div class="post_title">Building a Hard Disk Image</div>\r
+               <div class="text_area">\r
+            <p>\r
+              To create a hard disk image for use with the emulator, start by generating a flat file called, for example, <code>hd.img</code> of the required size (under 528MB), filled with zero bytes, made using <code>mkfile</code> or a similar tool.\r
+            </p>\r
+            <p>\r
+              Preparing the hard disk image for use with the emulator under DOS is done just like a real PC:\r
+              <ul>\r
+                <li>Boot the emulator, and use <code>FDISK</code> to partition the hard disk. When it's done FDISK will reboot the emulator.</li>\r
+                <li>Use <code>FORMAT C:</code> (or <code>FORMAT C: /S</code> to create a bootable disk) to format the disk image, and you are done.</li>\r
+                </ul>\r
+              <p>The resulting disk image is in the right format to be mounted on a real Windows PC using e.g. <code>OSFMount</code>, on a Mac using <code>hdiutil</code>, or on Linux using <code>mount</code>, providing an easy way to copy files and programs to and from the disk image. Or, you can install programs from within the emulator itself using regular floppy disk images (see "Floppy Drive Emulation" below).\r
+            </p>\r
+               </div>\r
+  </div>\r
+  \r
+    <div class="section_box2" align="justify">\r
+               <div class="post_title">Keyboard Support</div>\r
+               <div class="text_area">\r
+            <p>\r
+              The emulator simulates an XT-style keyboard controlled by an Intel 8042 chip on I/O port 0x60, generating interrupt 9 on each keypress. Because a real 8042 returns scan codes rather than the ASCII characters, for portability, the emulator BIOS does the reverse of a real PC BIOS and converts ASCII characters to scancodes, simulating press/release of the modifier keys (e.g. shift) as necessary to work like a "real" keyboard. The OS (DOS/Windows) then converts them back to ASCII characters and normally this process works seamlessly.\r
+            </p><p>\r
+            <p>\r
+              The scan code table in the BIOS maps your local keyboard layout onto scan codes matching a US-layout QWERTY keyboard. If you are using an international keyboard layout everything will work fine with no changes, provided "United States 83-key XT keyboard" or similar is selected if your OS (e.g. Windows 3.0) gives the option.\r
+            </p><p>\r
+              For console (text) applications, there are special key sequences to get Alt+<i>xxx</i>, F<i>xx</i> and some Ctrl+<i>xxx</i> keys, since these are not returned directly by the standard C I/O functions:\r
+              </p><ul>\r
+              <li>To send an Alt+<i>xxx</i> key combination, press Ctrl+A then the key, so for example to type Alt+F, press Ctrl+A then F.</li>\r
+              <li>To send an F<i>xx</i> key, press Ctrl+F then a number key. For example, to get the F4 key, press Ctrl+F then 4. To get F10, press Ctrl+F then 0.</li>\r
+              <li>To send Ctrl+A, press Ctrl+F then Ctrl+A. To send Ctrl+F, press Ctrl+F then Ctrl+F.</li>\r
+              <li>To send a Page Down key, press Ctrl+F then O. To send a Page Up key, press Ctrl+F then Q.</li>\r
+              </ul>\r
+            </p><p>For graphics (SDL) applications, all keys will work as per a "real" PC without needing the special sequences above.\r
+            </p><p>The keyboard is polled every <code>KEYBOARD_TIMER_UPDATE_DELAY</code> instructions. Decreasing this value will increase keyboard responsiveness, at the expense of emulated CPU speed, and vice versa. The default value of 20000 should be suitable for most platforms.</p>\r
+               </div>\r
+  </div>\r
+\r
+      <div class="section_box2" align="justify">\r
+               <div class="post_title">Floppy Drive Emulation</div>\r
+               <div class="text_area">\r
+            <p>\r
+              Emulates a 3.5" high-density floppy drive. Can read, write and format 1.44MB disks (18 sectors per track, 2 heads) and 720KB disks (9 sectors per track, 2 heads).\r
+            </p><p>\r
+              If you want to install your own software from a set of multiple floppy images (downloaded from e.g. <a href="http://www.vetusware.com">Vetusware</a>), the easiest way to "change disks" is to copy each disk image in turn over the floppy image file you specified on the command line, from a terminal other than the one running 8086tiny. Don't forget to put your original boot disk back at the end!\r
+            </p>\r
+               </div>\r
+  </div>\r
+\r
+    <div class="section_box2" align="justify">\r
+               <div class="post_title">Hard Drive Emulation</div>\r
+               <div class="text_area">\r
+            <p>\r
+              Supports up to 1023 cylinders, 63 sectors per track, 63 heads for disks up to 528MB.\r
+            </p><p>\r
+              Disk image format used is a subset of the standard "raw" format used by most disk image mount tools. In general, disk images prepared by the emulator will work with disk image tools and other emulators, but not the other way around.\r
+            </p><p>\r
+              The emulator uses an overly simplistic algorithm to derive a simulated cylinder/sector/head geometry from the disk image file's size. This algorithm often results in not all the space in the image file being available for disk partitions. For example, creating a 40,000,000 byte image file results in DOS FDISK seeing only 31.9MB as the volume size.\r
+            </p><p>\r
+              8086tiny will boot from a hard disk image if the HD image filename is prefixed with <code>@</code> on the command line. For example: <code>./8086tiny bios fd.img @hd.img</code>\r
+            </p>\r
+               </div>\r
+  </div>\r
+\r
+    <div class="section_box2" align="justify">\r
+               <div class="post_title">Text Mode Support</div>\r
+               <div class="text_area">\r
+            <p>\r
+              The emulator supports text output via the standard BIOS interrupt 0x10 interface, and also direct video memory access (one page, 4KB video RAM at segment B800) in 80 x 25 CGA 16-color text mode.\r
+            </p><p>\r
+              BIOS text output calls are converted to simple writes to <code>stdout</code>. Direct video memory accesses for the 80 x 25 CGA color text mode are converted to ANSI terminal escape sequences. If you are using a terminal which does not support ANSI (e.g. you are compiling the emulator with MS VC++ and running in a Windows console window) then PC applications that directly write to video memory in text mode may be unusable. Please make sure your terminal window is at least 80 x 25 characters in size.\r
+            </p><p>\r
+              Video memory in text mode is rendered to the terminal every <code>8 * KEYBOARD_TIMER_UPDATE_DELAY</code> instructions. Decreasing this value will increase the text update rate, at the expense of emulated CPU speed, and vice versa. The default value of 20000 should be suitable for most platforms.</p>\r
+            </p><p>\r
+              The regular PC character code page (437) includes various extended ASCII characters for things like line drawing. You might want to set the font in your terminal program to something that includes these (for example, on Mac OS X there is a suitable freeware font called Perfect DOS VGA 437). Otherwise, extended characters may render incorrectly (for example as question mark symbols).\r
+            </p><p>\r
+              Occasionally a DOS application on exit will leave the video hardware in an odd state which confuses the emulator, resulting in subsequent text output being invisible. If this happens, just use the DOS <code>CLS</code> command to clear the screen and all will be well again.\r
+            </p>\r
+               </div>\r
+  </div>\r
+\r
+    <div class="section_box2" align="justify">\r
+               <div class="post_title">Graphics Mode Support</div>\r
+               <div class="text_area">\r
+            <p>\r
+              Hercules 720x348 monochrome graphics mode and CGA 320x200 4-color graphics mode are emulated using SDL. Most CGA/Hercules features are supported via the normal I/O interface on ports 0x3D<i>x</i> and 0x3B<i>x</i> including video memory bank switching (segments B000/B800), which some games use for double-buffered graphics. Resolution reprogramming via the CRTC register is supported by 8086tiny 1.03 and later, as required by, for example, the ETEN Chinese System (which uses 640 x 408). The CGA 640x200 2-color mode is currently not supported.\r
+            </p><p>\r
+              When an application enters graphics mode, the emulator will open an SDL window (which will be closed when the application goes back to text mode). On UNIXes, SDL will automatically output graphics via X11 if the DISPLAY environment variable is set up.\r
+            </p><p>\r
+              The graphics display is updated every <code>GRAPHICS_UPDATE_DELAY</code> instructions. Decreasing this value will increase the graphics update rate, at the expense of emulated CPU speed, and vice versa. By default, <code>GRAPHICS_UPDATE_DELAY</code> is set to 360000 instructions, which gives good performance for faster platforms. On slower platforms like Raspberry Pi, a smaller value is suitable: building with <code>make 8086tiny_slowcpu</code> reduces <code>GRAPHICS_UPDATE_DELAY</code> to 50000 instructions.\r
+            </p><p>\r
+              Some applications (e.g. AutoCAD) support a PC configuration with a CGA card and a Hercules card, for simultaneous text and graphics output on different displays. The emulator simulates this configuration, too, using separate windows for the (terminal) text and (SDL) graphics displays.\r
+            </p><p>\r
+              If your application only requires text mode, you can compile 8086tiny without SDL by defining <code>NO_GRAPHICS</code>.\r
+            </p>\r
+               </div>\r
+  </div>\r
+\r
+      <div class="section_box2" align="justify">\r
+               <div class="post_title">Real-Time Clock Support</div>\r
+               <div class="text_area">\r
+            <p>\r
+              Reading the RTC (both time and date) is emulated via the standard BIOS clock interface, pulling the time/date from the host computer. Setting the time or date is not supported.\r
+            </p>\r
+               </div>\r
+  </div>\r
+\r
+      <div class="section_box2" align="justify">\r
+               <div class="post_title">Hardware Timer Support</div>\r
+               <div class="text_area">\r
+            <p>\r
+              Programmable interrupt timer channels 0 and 2 are emulated through the usual I/O port 0x40-0x43 interface. Only mode 3 (square wave generator) is currently supported, but this is what most applications use. Software that uses timers to control execution speed such as games should run at an accurate pace.\r
+            </p>\r
+               </div>\r
+  </div>\r
+\r
+        <div class="section_box2" align="justify">\r
+               <div class="post_title">PC Speaker Support</div>\r
+               <div class="text_area">\r
+            <p>\r
+              The PC speaker is emulated through the usual port 0x61 interface. The only PC speaker mode supported is via PIT channel 2, so you will hear most music but not non-musical sound effects.\r
+            </p>\r
+               </div>\r
+  </div>\r
+\r
+    <div class="section_box2" align="justify">\r
+               <div class="post_title">BIOS</div>\r
+               <div class="text_area">\r
+            <p></p>\r
+              Like a real PC, the emulator needs a BIOS to implement boot functionality and the standard interrupt interfaces. The 8086tiny BIOS was written from scratch using documentation in the public domain. It is around 6KB in size and assembles using NASM. Full source code and a pre-built binary are provided.\r
+            <p></p>\r
+              The BIOS binary comprises a code section and a data section. The code section implements the standard interrupt interfaces for video, disk, timer, clock and so on, much as a "real" PC BIOS does, and also a small timer-controlled video driver to convert video memory formatting into ANSI escape sequences when the emulator is in text mode.\r
+              The data section includes typical BIOS structures like a scan code table and the BIOS data area, but also a number of look-up tables to assist the emulator with instruction decoding. Full detail is provided in the "CPU, Memory and Register Emulation" section below.\r
+            </p>\r
+               </div>\r
+  </div>\r
+\r
+      <div class="section_box2" align="justify">\r
+               <div class="post_title">Memory Map and Register Emulation</div>\r
+               <div class="text_area">\r
+            <p>\r
+              The emulator simulates a hardware configuration with A20 address line wraparound disabled, making just over 1MB of RAM available to applications.\r
+            </p><p>\r
+              Memory map is largely as per a real PC, with interrupt vector table at 0:0, BIOS data area including keyboard buffer at <code>40:0</code>, CGA text video memory at <code>B800:0</code>, Hercules/CGA graphics memory at <code>B000</code>/<code>B800:0</code>, and BIOS at <code>F000:0100</code>. Unlike a real PC, in the emulator the CPU registers are memory-mapped (at <code>F000:0</code>), which enables considerable optimisation of the emulator's instruction execution unit by permitting the unification of memory and register operations, while remaining invisible to the running software.\r
+            </p>\r
+               </div>\r
+  </div>\r
+\r
+  <div class="section_box2" align="justify">\r
+               <div class="post_title">CPU, Memory and Register Emulation</div>\r
+               <div class="text_area">\r
+            <p>\r
+              CPU supports the full 8086 instruction set (plus some 80186 extensions), including undocumented instructions (e.g. <code>SALC</code>) and flag behaviors (e.g. <code>MUL</code>/<code>DIV</code>), opcode bugs which some applications rely on (e.g. <code>PUSH SP</code>), and the trap flag for debugger support.\r
+            </p><p>\r
+              The focus of 8086tiny is on minimizing code size without comproming emulation accuracy. Due to the complexities of the highly irregular Intel x86 instruction format, instruction decoding is assisted by a number of lookup tables which form part of the BIOS binary. For example, there are many different ways to encode a <code>MOV</code> instruction, depending on the types of the source and destination operands (immediate, register, or memory). There are sometimes even multiple ways to encode the same instruction (e.g. <code>MOV AX, [1234]</code> usually encodes as <code>A1 34 12</code> but can also encode as <code>8B 06 34 12</code>). To avoid having to implement similar functionality in the emulator multiple times for each instruction or encoding variant, look-up tables are used to map each instruction to an internal function and subfunction number.\r
+            </p><p>\r
+              As an example, we illustrate how the emulator executes the instruction <code>ADD AX, BX</code>, which encodes as hex <code>01 D8</code>.\r
+              </p>\r
+              <ul>\r
+                <li>The emulator begins by retrieving index <code>01</code> hex (the first byte of the instruction) from <code>TABLE_XLAT_OPCODE</code> and <code>TABLE_XLAT_SUBFUNCTION</code>, giving a <i>translated opcode ID</i> of decimal 9 (which corresponds to the Intel instruction template <code><i>arithmetic_function</i> reg, r/m</code>) and a <i>subfunction ID</i> of 0 (which corresponds to the <code>ADD</code> function), respectively.</li>\r
+                <li>The <code>OPCODE</code> chain in the source uses the <i>translated opcode ID</i> and <i>subfunction ID</i> to determine the operation to execute, in this case calling the <code>OP(+=)</code> macro followed by <code>set_CF()</code> to set the carry flag in accordance with the result of the addition.</li>\r
+                <li>Next, instruction length is computed. Because Intel x86 instructions are of arbitrary length (and, sometimes, multiple encodings of the same instruction can have different lengths), tables are used to determine the instruction length to move IP to the next instruction. The opcode index <code>01</code> hex is used as an index into <code>TABLE_BASE_INST_SIZE</code>, <code>TABLE_I_MOD_SIZE</code>, and <code>TABLE_I_W_SIZE</code> and these numbers are added to compute the total instruction length.</li>\r
+                <li>Finally, flags are set. The opcode index <code>01</code> hex is then used as an index into <code>TABLE_STD_FLAGS</code> to give a bitmask of 3, which is <code>FLAGS_UPDATE_SZP | FLAGS_UPDATE_AO_ARITH</code>.</li>\r
+                <ul><li><code>FLAGS_UPDATE_SZP</code> (1) signifies that this instruction sets the sign, zero and parity flags according to the operation's result in the standard way. Sign and zero flags are set directly from the result, and the parity flag is set by looking up the result in <code>TABLE_PARITY_FLAG</code>.</li>\r
+                <li><code>FLAGS_UPDATE_AO_ARITH</code> (2) signifies that this instruction sets the auxiliary and overflow flags as standard for arithmetic operations.</li>\r
+                <li>If <code>FLAGS_UPDATE_OC_LOGIC</i></code> (4) were set in the bitmask (it is not here), the overflow and carry flags would be set to 0, as standard for logic operations.</li></ul>\r
+              </ul>\r
+\r
+              <p>The CPU also implements some "special" two-byte opcodes to help the emulator talk with the outside world. These are:\r
+              </p><ul>\r
+                     <li><code>0F 00</code> (<code>PUTCHAR_AL</code>) - output character in register <code>AL</code> to terminal</li>\r
+                     <li><code>0F 01</code> (<code>GET_RTC</code>) - write real-time clock data (as returned by <code>localtime</code>) to memory location <code>ES:BX</code></li>\r
+                     <li><code>0F 02</code> (<code>READ_DISK</code>) - read <code>AX</code> bytes from disk at offset <code>512*(16*SI+BP)</code> into memory location ES:BX. Disk is specified in <code>DL</code> (0 = hard disk, 1 = floppy disk)</li>\r
+                     <li><code>0F 03</code> (<code>WRITE_DISK</code>) - write <code>AX</code> bytes at memory location <code>ES:BX</code> to disk at offset <code>512*(16*SI+BP)</code>. Disk is specified in <code>DL</code> as per <code>0F 02</code></li>\r
+                </ul>\r
+\r
+              <p>Emulator exit is triggered by a <code>JMP 0:0</code> instruction, to allow the user to easily quit the emulator without shutting down the terminal.</p>\r
+              <p>Extension of the instruction set supported by 8086tiny can be implemented by appropriate modification to the tables described above in the BIOS source, and addition of a corresponding new <code>OPCODE</code> block in the C source.</p>\r
+            </p>\r
+               </div>\r
+  </div>\r
+\r
+      <div class="section_box2" align="justify">\r
+               <div class="post_title">Supported Application Software</div>\r
+               <div class="text_area">\r
+            <p>\r
+              The emulator will run practically any software a real PC (of the spec listed at the top of this page) can.\r
+            </p><p>\r
+              The author has successfully tested a range of software on the emulator.\r
+              <ul>\r
+                <li>OSes/GUIs</li>\r
+                <ul><li>MS-DOS 6.22</li>\r
+                  <li>FreeDOS 0.82pl3</li>\r
+                  <li>Linux/ELKS 0.1.4</li>\r
+                  <li>Windows 3.0</li>\r
+                  <li>DESQview 2.8</li>\r
+                  <li>ETEN Chinese System</li></ul>\r
+                <li>Professional software</li>\r
+                <ul><li>Lotus 1-2-3 R2.4</li>\r
+                  <li>AsEasyAs 5.7</li>\r
+                  <li>Excel 2.1 for Windows</li>\r
+                  <li>AutoCAD 2.5</li>\r
+                  <li>WordStar 4</li></ul>\r
+                <li>Programming languages</li>\r
+                <ul><li>QBASIC</li>\r
+                  <li>GWBASIC</li>\r
+                  <li>Turbo C++</li></ul>\r
+                <li>Games</li>\r
+                <ul><li>Carrier Command</li>\r
+                  <li>Police Quest</li>\r
+                  <li>SimCity</li>\r
+                  <li>Alley Cat</li>\r
+                  <li>MS Flight Simulator 4</li>\r
+                  <li>Lots of freeware Windows games</li></ul>\r
+                <li>Diagnostic/benchmark software</li>\r
+                <ul><li>Manifest</li>\r
+                  <li>Microsoft MSD</li>\r
+                  <li>InfoSpot</li>\r
+                  <li>CheckIt</li></ul>\r
+                </ul>\r
+            </p>\r
+               </div>\r
+  </div>\r
+\r
+</div>\r
+</div>\r
+    \r
+       <div id="templatemo_right_column">\r
+           \r
+            <ul class="templatemo_menu">\r
+              <li><a href="http://www.megalith.co.uk/8086tiny/index.html">Home</a></li>\r
+              <li><a href="http://www.megalith.co.uk/8086tiny/download.html">Download</a></li>\r
+              <li><a href="http://www.megalith.co.uk/8086tiny/doc.html" style="background:#0066CC; color:#FFFFFF">Documentation</a></li>\r
+              <li><a href="http://8086tiny.freeforums.net">Blog/Forum</a></li>\r
+          </ul>\r
+          \r
+          <div class="section_box" align="justify">\r
+            <div class="subtitle">Author Contact</div>\r
+              <b>Adrian Cable</b><br />\r
+              <a href="&#109;&#97;&#105;&#x6c;&#x74;&#111;&#58;&#x61;&#100;&#114;&#x69;&#97;&#110;&#46;&#99;&#97;&#x62;&#x6c;&#x65;&#x40;&#x67;&#109;&#x61;&#105;&#x6c;&#x2e;&#x63;&#111;&#x6d;">&#x61;&#x64;&#x72;&#x69;&#x61;&#110;&#x2e;&#x63;&#97;&#98;&#x6c;&#x65;&#x40;&#103;&#x6d;&#x61;&#105;&#108;&#x2e;&#x63;&#x6f;&#109;</a><br />\r
+\r
+            <p>If 8086tiny brings you joy or profit, the author welcomes modest donations as a token of appreciation.</p>\r
+\r
+                         <center><form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top">\r
+                         <input type="hidden" name="cmd" value="_s-xclick">\r
+                         <input type="hidden" name="encrypted" value="-----BEGIN PKCS7-----MIIHLwYJKoZIhvcNAQcEoIIHIDCCBxwCAQExggEwMIIBLAIBADCBlDCBjjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtQYXlQYWwgSW5jLjETMBEGA1UECxQKbGl2ZV9jZXJ0czERMA8GA1UEAxQIbGl2ZV9hcGkxHDAaBgkqhkiG9w0BCQEWDXJlQHBheXBhbC5jb20CAQAwDQYJKoZIhvcNAQEBBQAEgYAWElO8FzxSwd1YS/aND7SDIRxBPxU3HBYiETwuclLx4YYoT2HgDJu3CytkTGDUTNHzgFmQ+KMm3Mb1zHlhLGC4PyEziiwvmRBnxXno3BsPMVKOhQhOeb4A0qSZzN2/DIwyUanqHeZqBkXa10d7G6kFvrkbZj5OS/pbGM0XvWPEKjELMAkGBSsOAwIaBQAwgawGCSqGSIb3DQEHATAUBggqhkiG9w0DBwQIlXXx20WdjIKAgYh66b5Dy3O+Q9pWJ4DXTSs/ShAQsNQZAbI3DMzRCskbD7qz6j+TOLATd2rnp3CpITejwkq597hVCuoFWNc9qcLzCvrC2DFalCVO/jy4YCmQ9mIps9TSaIX7rkPDuCB8g6hpHveAAlRMXfskNewMs6ZrqO1+Z+geEhqEM50FIZI0I0Q1f3rUw0VnoIIDhzCCA4MwggLsoAMCAQICAQAwDQYJKoZIhvcNAQEFBQAwgY4xCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLUGF5UGFsIEluYy4xEzARBgNVBAsUCmxpdmVfY2VydHMxETAPBgNVBAMUCGxpdmVfYXBpMRwwGgYJKoZIhvcNAQkBFg1yZUBwYXlwYWwuY29tMB4XDTA0MDIxMzEwMTMxNVoXDTM1MDIxMzEwMTMxNVowgY4xCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLUGF5UGFsIEluYy4xEzARBgNVBAsUCmxpdmVfY2VydHMxETAPBgNVBAMUCGxpdmVfYXBpMRwwGgYJKoZIhvcNAQkBFg1yZUBwYXlwYWwuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDBR07d/ETMS1ycjtkpkvjXZe9k+6CieLuLsPumsJ7QC1odNz3sJiCbs2wC0nLE0uLGaEtXynIgRqIddYCHx88pb5HTXv4SZeuv0Rqq4+axW9PLAAATU8w04qqjaSXgbGLP3NmohqM6bV9kZZwZLR/klDaQGo1u9uDb9lr4Yn+rBQIDAQABo4HuMIHrMB0GA1UdDgQWBBSWn3y7xm8XvVk/UtcKG+wQ1mSUazCBuwYDVR0jBIGzMIGwgBSWn3y7xm8XvVk/UtcKG+wQ1mSUa6GBlKSBkTCBjjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtQYXlQYWwgSW5jLjETMBEGA1UECxQKbGl2ZV9jZXJ0czERMA8GA1UEAxQIbGl2ZV9hcGkxHDAaBgkqhkiG9w0BCQEWDXJlQHBheXBhbC5jb22CAQAwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQCBXzpWmoBa5e9fo6ujionW1hUhPkOBakTr3YCDjbYfvJEiv/2P+IobhOGJr85+XHhN0v4gUkEDI8r2/rNk1m0GA8HKddvTjyGw/XqXa+LSTlDYkqI8OwR8GEYj4efEtcRpRYBxV8KxAW93YDWzFGvruKnnLbDAF6VR5w/cCMn5hzGCAZowggGWAgEBMIGUMIGOMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDU1vdW50YWluIFZpZXcxFDASBgNVBAoTC1BheVBhbCBJbmMuMRMwEQYDVQQLFApsaXZlX2NlcnRzMREwDwYDVQQDFAhsaXZlX2FwaTEcMBoGCSqGSIb3DQEJARYNcmVAcGF5cGFsLmNvbQIBADAJBgUrDgMCGgUAoF0wGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMTQwMTE0MDE1NDQyWjAjBgkqhkiG9w0BCQQxFgQUI+ZU8XN3EgMpFgUvYwzRoEQo0VwwDQYJKoZIhvcNAQEBBQAEgYBm8abQmL+NkFgnXkv/y9EwtDD8YXaFI2KnvpyzsFKGUfzxL5lkYSr4F/NHp749xFPJ37T1gOpRdNF0NsGCX26Wi8KHL8lvreRRsbYecyl7DEcN6TP9cNoKHGzt9+Qrzb33EXc3I1oeP1f5MJIFpqfP2Tqe7SWDMdSUsIHMgKJIZw==-----END PKCS7-----">\r
+                         <input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!">\r
+                         <img alt="" border="0" src="https://www.paypalobjects.com/en_US/i/scr/pixel.gif" width="1" height="1">\r
+                         </form></center>\r
+\r
+          </div>\r
+        </div>\r
+\r
+       <div id="templatemo_footer">\r
+    Copyright © 2013-2014 Adrian Cable · CSS template by <a href="http://www.templatemo.com" rel="nofollow" target="_blank">templatemo.com</a>\r
+    </div>\r
+        \r
+</div>\r
+<div class="templatemo_spacer"></div>\r
+</div>\r
+\r
+</body>\r
+</html>\r
diff --git a/plat/pc86/emu/docs/images/button_active.gif b/plat/pc86/emu/docs/images/button_active.gif
new file mode 100644 (file)
index 0000000..cad8e60
Binary files /dev/null and b/plat/pc86/emu/docs/images/button_active.gif differ
diff --git a/plat/pc86/emu/docs/images/button_default.gif b/plat/pc86/emu/docs/images/button_default.gif
new file mode 100644 (file)
index 0000000..35a0835
Binary files /dev/null and b/plat/pc86/emu/docs/images/button_default.gif differ
diff --git a/plat/pc86/emu/docs/images/logo.gif b/plat/pc86/emu/docs/images/logo.gif
new file mode 100644 (file)
index 0000000..a32bab7
Binary files /dev/null and b/plat/pc86/emu/docs/images/logo.gif differ
diff --git a/plat/pc86/emu/docs/images/templatemo_bg.gif b/plat/pc86/emu/docs/images/templatemo_bg.gif
new file mode 100644 (file)
index 0000000..f72e5d0
Binary files /dev/null and b/plat/pc86/emu/docs/images/templatemo_bg.gif differ
diff --git a/plat/pc86/emu/docs/images/templatemo_content_bg.gif b/plat/pc86/emu/docs/images/templatemo_content_bg.gif
new file mode 100644 (file)
index 0000000..0727be2
Binary files /dev/null and b/plat/pc86/emu/docs/images/templatemo_content_bg.gif differ
diff --git a/plat/pc86/emu/docs/images/templatemo_footer_bg.gif b/plat/pc86/emu/docs/images/templatemo_footer_bg.gif
new file mode 100644 (file)
index 0000000..7d11c1a
Binary files /dev/null and b/plat/pc86/emu/docs/images/templatemo_footer_bg.gif differ
diff --git a/plat/pc86/emu/docs/images/templatemo_header_bg.gif b/plat/pc86/emu/docs/images/templatemo_header_bg.gif
new file mode 100644 (file)
index 0000000..b9f8754
Binary files /dev/null and b/plat/pc86/emu/docs/images/templatemo_header_bg.gif differ
diff --git a/plat/pc86/emu/docs/images/templatemo_side_bg.gif b/plat/pc86/emu/docs/images/templatemo_side_bg.gif
new file mode 100644 (file)
index 0000000..bc85123
Binary files /dev/null and b/plat/pc86/emu/docs/images/templatemo_side_bg.gif differ
diff --git a/plat/pc86/emu/docs/images/templatemo_top_bg.gif b/plat/pc86/emu/docs/images/templatemo_top_bg.gif
new file mode 100644 (file)
index 0000000..f182925
Binary files /dev/null and b/plat/pc86/emu/docs/images/templatemo_top_bg.gif differ
diff --git a/plat/pc86/emu/license.txt b/plat/pc86/emu/license.txt
new file mode 100644 (file)
index 0000000..b99d26c
--- /dev/null
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2013-2014 Adrian Cable - http://www.megalith.co.uk/8086tiny
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/plat/pc86/emu/runme b/plat/pc86/emu/runme
new file mode 100755 (executable)
index 0000000..450aa08
--- /dev/null
@@ -0,0 +1,10 @@
+#!/bin/sh
+clear
+stty cbreak raw -echo min 0
+if [ -f hd.img ]
+then
+    ./8086tiny bios fd.img hd.img
+else
+    ./8086tiny bios fd.img
+fi
+stty cooked echo