--- /dev/null
+// 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;
+}
--- /dev/null
+; 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