| /* | |
| * Dos/PC Emulator | |
| * Copyright (C) 1991 Jim Hudgens | |
| * | |
| * | |
| * The file is part of GDE. | |
| * | |
| * GDE is free software; you can redistribute it and/or modify | |
| * it under the terms of the GNU General Public License as published by | |
| * the Free Software Foundation; either version 1, or (at your option) | |
| * any later version. | |
| * | |
| * GDE is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| * GNU General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU General Public License | |
| * along with GDE; see the file COPYING. If not, write to | |
| * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | |
| * | |
| */ | |
| #include "altairz80_defs.h" | |
| #include "i86.h" | |
| extern uint32 GetBYTEExtended(register uint32 Addr); | |
| extern void PutBYTEExtended(register uint32 Addr, const register uint32 Value); | |
| /* $Log: i86_prim_ops.c,v $ | |
| * Revision 0.9 2003-01-10 23:33:10 jce | |
| * fixed some more warnings under gcc -Wall | |
| * | |
| * Revision 0.8 1992/04/11 21:58:13 hudgens | |
| * fixed some code causing warnings under gcc -Wall | |
| * | |
| * Revision 0.7 1991/07/30 02:04:34 hudgens | |
| * added copyright. | |
| * | |
| * Revision 0.6 1991/07/21 16:50:37 hudgens | |
| * fixed all flags in the bit shift and rotate instructions so that they | |
| * finally agree. | |
| * Also fixed the flags associated with IMUL and MUL instructions. | |
| * | |
| * Revision 0.5 1991/07/21 01:28:16 hudgens | |
| * added support for aad and aam primitives. | |
| * | |
| * Revision 0.4 1991/07/20 22:26:25 hudgens | |
| * fixed problem with sign extension in subroutine mem_access_word. | |
| * | |
| * Revision 0.3 1991/07/17 03:48:22 hudgens | |
| * fixed bugs having to do with the setting of flags in the | |
| * shift and rotate operations. Also, fixed sign extension problem | |
| * with push_word and pop_word. | |
| * | |
| * Revision 0.2 1991/04/01 02:36:00 hudgens | |
| * Fixed several nasty bugs dealing with flag setting in the subroutines | |
| * sub_byte, sub_word, sbb_byte, sbb_word, and test_word. The results | |
| * now agree with the PC on both of the testaopb and testaopw tests. | |
| * | |
| * Revision 0.1 1991/03/30 21:13:37 hudgens | |
| * Initial checkin. | |
| * | |
| * | |
| */ | |
| /* [JCE] Stop gcc -Wall complaining */ | |
| extern void i86_intr_raise(PC_ENV *m, uint8 intrnum); | |
| /* the following table was generated using the following | |
| code (running on an IBM AT, Turbo C++ 2.0), for all values of i | |
| between 0 and 255. AL is loaded with i's value, and then the | |
| operation "and al,al" sets the parity flag. The flags are pushed | |
| onto the stack, and then popped back into AX. Then AX is | |
| returned. So the value of each table entry represents the | |
| parity of its index into the table. This results in a somewhat | |
| faster mechanism for parity calculations than the straightforward | |
| method. | |
| andflags(i,res) int *res; { | |
| int flags; | |
| _AX = i; asm and al,al; asm pushf; *res = _AX; | |
| asm pop ax; flags = _AX; return flags; | |
| } | |
| */ | |
| uint8 parity_tab[] = { | |
| /*0*/ 1, /*1*/ 0, /*2*/ 0, /*3*/ 1, | |
| /*4*/ 0, /*5*/ 1, /*6*/ 1, /*7*/ 0, | |
| /*8*/ 0, /*9*/ 1, /*a*/ 1, /*b*/ 0, | |
| /*c*/ 1, /*d*/ 0, /*e*/ 0, /*f*/ 1, | |
| /*10*/ 0, /*11*/ 1, /*12*/ 1, /*13*/ 0, | |
| /*14*/ 1, /*15*/ 0, /*16*/ 0, /*17*/ 1, | |
| /*18*/ 1, /*19*/ 0, /*1a*/ 0, /*1b*/ 1, | |
| /*1c*/ 0, /*1d*/ 1, /*1e*/ 1, /*1f*/ 0, | |
| /*20*/ 0, /*21*/ 1, /*22*/ 1, /*23*/ 0, | |
| /*24*/ 1, /*25*/ 0, /*26*/ 0, /*27*/ 1, | |
| /*28*/ 1, /*29*/ 0, /*2a*/ 0, /*2b*/ 1, | |
| /*2c*/ 0, /*2d*/ 1, /*2e*/ 1, /*2f*/ 0, | |
| /*30*/ 1, /*31*/ 0, /*32*/ 0, /*33*/ 1, | |
| /*34*/ 0, /*35*/ 1, /*36*/ 1, /*37*/ 0, | |
| /*38*/ 0, /*39*/ 1, /*3a*/ 1, /*3b*/ 0, | |
| /*3c*/ 1, /*3d*/ 0, /*3e*/ 0, /*3f*/ 1, | |
| /*40*/ 0, /*41*/ 1, /*42*/ 1, /*43*/ 0, | |
| /*44*/ 1, /*45*/ 0, /*46*/ 0, /*47*/ 1, | |
| /*48*/ 1, /*49*/ 0, /*4a*/ 0, /*4b*/ 1, | |
| /*4c*/ 0, /*4d*/ 1, /*4e*/ 1, /*4f*/ 0, | |
| /*50*/ 1, /*51*/ 0, /*52*/ 0, /*53*/ 1, | |
| /*54*/ 0, /*55*/ 1, /*56*/ 1, /*57*/ 0, | |
| /*58*/ 0, /*59*/ 1, /*5a*/ 1, /*5b*/ 0, | |
| /*5c*/ 1, /*5d*/ 0, /*5e*/ 0, /*5f*/ 1, | |
| /*60*/ 1, /*61*/ 0, /*62*/ 0, /*63*/ 1, | |
| /*64*/ 0, /*65*/ 1, /*66*/ 1, /*67*/ 0, | |
| /*68*/ 0, /*69*/ 1, /*6a*/ 1, /*6b*/ 0, | |
| /*6c*/ 1, /*6d*/ 0, /*6e*/ 0, /*6f*/ 1, | |
| /*70*/ 0, /*71*/ 1, /*72*/ 1, /*73*/ 0, | |
| /*74*/ 1, /*75*/ 0, /*76*/ 0, /*77*/ 1, | |
| /*78*/ 1, /*79*/ 0, /*7a*/ 0, /*7b*/ 1, | |
| /*7c*/ 0, /*7d*/ 1, /*7e*/ 1, /*7f*/ 0, | |
| /*80*/ 0, /*81*/ 1, /*82*/ 1, /*83*/ 0, | |
| /*84*/ 1, /*85*/ 0, /*86*/ 0, /*87*/ 1, | |
| /*88*/ 1, /*89*/ 0, /*8a*/ 0, /*8b*/ 1, | |
| /*8c*/ 0, /*8d*/ 1, /*8e*/ 1, /*8f*/ 0, | |
| /*90*/ 1, /*91*/ 0, /*92*/ 0, /*93*/ 1, | |
| /*94*/ 0, /*95*/ 1, /*96*/ 1, /*97*/ 0, | |
| /*98*/ 0, /*99*/ 1, /*9a*/ 1, /*9b*/ 0, | |
| /*9c*/ 1, /*9d*/ 0, /*9e*/ 0, /*9f*/ 1, | |
| /*a0*/ 1, /*a1*/ 0, /*a2*/ 0, /*a3*/ 1, | |
| /*a4*/ 0, /*a5*/ 1, /*a6*/ 1, /*a7*/ 0, | |
| /*a8*/ 0, /*a9*/ 1, /*aa*/ 1, /*ab*/ 0, | |
| /*ac*/ 1, /*ad*/ 0, /*ae*/ 0, /*af*/ 1, | |
| /*b0*/ 0, /*b1*/ 1, /*b2*/ 1, /*b3*/ 0, | |
| /*b4*/ 1, /*b5*/ 0, /*b6*/ 0, /*b7*/ 1, | |
| /*b8*/ 1, /*b9*/ 0, /*ba*/ 0, /*bb*/ 1, | |
| /*bc*/ 0, /*bd*/ 1, /*be*/ 1, /*bf*/ 0, | |
| /*c0*/ 1, /*c1*/ 0, /*c2*/ 0, /*c3*/ 1, | |
| /*c4*/ 0, /*c5*/ 1, /*c6*/ 1, /*c7*/ 0, | |
| /*c8*/ 0, /*c9*/ 1, /*ca*/ 1, /*cb*/ 0, | |
| /*cc*/ 1, /*cd*/ 0, /*ce*/ 0, /*cf*/ 1, | |
| /*d0*/ 0, /*d1*/ 1, /*d2*/ 1, /*d3*/ 0, | |
| /*d4*/ 1, /*d5*/ 0, /*d6*/ 0, /*d7*/ 1, | |
| /*d8*/ 1, /*d9*/ 0, /*da*/ 0, /*db*/ 1, | |
| /*dc*/ 0, /*dd*/ 1, /*de*/ 1, /*df*/ 0, | |
| /*e0*/ 0, /*e1*/ 1, /*e2*/ 1, /*e3*/ 0, | |
| /*e4*/ 1, /*e5*/ 0, /*e6*/ 0, /*e7*/ 1, | |
| /*e8*/ 1, /*e9*/ 0, /*ea*/ 0, /*eb*/ 1, | |
| /*ec*/ 0, /*ed*/ 1, /*ee*/ 1, /*ef*/ 0, | |
| /*f0*/ 1, /*f1*/ 0, /*f2*/ 0, /*f3*/ 1, | |
| /*f4*/ 0, /*f5*/ 1, /*f6*/ 1, /*f7*/ 0, | |
| /*f8*/ 0, /*f9*/ 1, /*fa*/ 1, /*fb*/ 0, | |
| /*fc*/ 1, /*fd*/ 0, /*fe*/ 0, /*ff*/ 1, | |
| }; | |
| uint8 xor_0x3_tab[] = { 0, 1, 1, 0 }; | |
| /* CARRY CHAIN CALCULATION. | |
| This represents a somewhat expensive calculation which is | |
| apparently required to emulate the setting of the OF and | |
| AF flag. The latter is not so important, but the former is. | |
| The overflow flag is the XOR of the top two bits of the | |
| carry chain for an addition (similar for subtraction). | |
| Since we do not want to simulate the addition in a bitwise | |
| manner, we try to calculate the carry chain given the | |
| two operands and the result. | |
| So, given the following table, which represents the | |
| addition of two bits, we can derive a formula for | |
| the carry chain. | |
| a b cin r cout | |
| 0 0 0 0 0 | |
| 0 0 1 1 0 | |
| 0 1 0 1 0 | |
| 0 1 1 0 1 | |
| 1 0 0 1 0 | |
| 1 0 1 0 1 | |
| 1 1 0 0 1 | |
| 1 1 1 1 1 | |
| Construction of table for cout: | |
| ab | |
| r \ 00 01 11 10 | |
| |------------------ | |
| 0 | 0 1 1 1 | |
| 1 | 0 0 1 0 | |
| By inspection, one gets: cc = ab + r'(a + b) | |
| That represents alot of operations, but NO CHOICE.... | |
| BORROW CHAIN CALCULATION. | |
| The following table represents the | |
| subtraction of two bits, from which we can derive a formula for | |
| the borrow chain. | |
| a b bin r bout | |
| 0 0 0 0 0 | |
| 0 0 1 1 1 | |
| 0 1 0 1 1 | |
| 0 1 1 0 1 | |
| 1 0 0 1 0 | |
| 1 0 1 0 0 | |
| 1 1 0 0 0 | |
| 1 1 1 1 1 | |
| Construction of table for cout: | |
| ab | |
| r \ 00 01 11 10 | |
| |------------------ | |
| 0 | 0 1 0 0 | |
| 1 | 1 1 1 0 | |
| By inspection, one gets: bc = a'b + r(a' + b) | |
| */ | |
| uint8 aad_word(PC_ENV *m, uint16 d) | |
| { | |
| uint16 l; | |
| uint8 hb,lb; | |
| hb = (d>>8)&0xff; | |
| lb = (d&0xff); | |
| l = lb + 10 * hb; | |
| CONDITIONAL_SET_FLAG(l & 0x80, m, F_SF); | |
| CONDITIONAL_SET_FLAG(l == 0, m, F_ZF); | |
| CONDITIONAL_SET_FLAG(parity_tab[l & 0xff], m, F_PF); | |
| return (uint8) l; | |
| } | |
| uint16 aam_word(PC_ENV *m, uint8 d) | |
| { | |
| uint16 h,l; | |
| h = d / 10; | |
| l = d % 10; | |
| l |= (h<<8); | |
| CONDITIONAL_SET_FLAG(l & 0x80, m, F_SF); | |
| CONDITIONAL_SET_FLAG(l == 0, m, F_ZF); | |
| CONDITIONAL_SET_FLAG(parity_tab[l & 0xff], m, F_PF); | |
| return l; | |
| } | |
| uint8 adc_byte(PC_ENV *m, uint8 d, uint8 s) | |
| { | |
| register uint16 res; /* all operands in native machine order */ | |
| register uint16 cc; | |
| if (ACCESS_FLAG(m,F_CF) ) | |
| res = 1 + d + s; | |
| else | |
| res = d + s; | |
| CONDITIONAL_SET_FLAG(res & 0x100, m, F_CF); | |
| CONDITIONAL_SET_FLAG((res&0xff)==0, m, F_ZF); | |
| CONDITIONAL_SET_FLAG(res & 0x80, m, F_SF); | |
| CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); | |
| /* calculate the carry chain SEE NOTE AT TOP.*/ | |
| cc = (s & d) | ((~res) & (s | d)); | |
| CONDITIONAL_SET_FLAG(xor_0x3_tab[(cc>>6)&0x3], m, F_OF); | |
| CONDITIONAL_SET_FLAG(cc&0x8, m, F_AF); | |
| return (uint8) res; | |
| } | |
| uint16 adc_word(PC_ENV *m, uint16 d, uint16 s) | |
| { | |
| register uint32 res; /* all operands in native machine order */ | |
| register uint32 cc; | |
| if (ACCESS_FLAG(m,F_CF) ) | |
| res = 1 + d + s; | |
| else | |
| res = d + s; | |
| /* set the carry flag to be bit 8 */ | |
| CONDITIONAL_SET_FLAG(res & 0x10000, m, F_CF); | |
| CONDITIONAL_SET_FLAG((res&0xffff)==0, m, F_ZF); | |
| CONDITIONAL_SET_FLAG(res & 0x8000, m, F_SF); | |
| CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); | |
| /* calculate the carry chain SEE NOTE AT TOP.*/ | |
| cc = (s & d) | ((~res) & (s | d)); | |
| CONDITIONAL_SET_FLAG(xor_0x3_tab[(cc>>14)&0x3], m, F_OF); | |
| CONDITIONAL_SET_FLAG(cc&0x8, m, F_AF); | |
| return res; | |
| } | |
| /* Given flags=f, and bytes d (dest) and s (source) | |
| perform the add and set the flags and the result back to | |
| *d. USE NATIVE MACHINE ORDER... | |
| */ | |
| uint8 add_byte(PC_ENV *m, uint8 d, uint8 s) | |
| { | |
| register uint16 res; /* all operands in native machine order */ | |
| register uint16 cc; | |
| res = d + s; | |
| /* set the carry flag to be bit 8 */ | |
| CONDITIONAL_SET_FLAG(res & 0x100, m, F_CF); | |
| CONDITIONAL_SET_FLAG((res&0xff)==0, m, F_ZF); | |
| CONDITIONAL_SET_FLAG(res & 0x80, m, F_SF); | |
| CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); | |
| /* calculate the carry chain SEE NOTE AT TOP.*/ | |
| cc = (s & d) | ((~res) & (s | d)); | |
| CONDITIONAL_SET_FLAG(xor_0x3_tab[(cc>>6)&0x3], m, F_OF); | |
| CONDITIONAL_SET_FLAG(cc&0x8, m, F_AF); | |
| return (uint8) res; | |
| } | |
| /* Given flags=f, and bytes d (dest) and s (source) | |
| perform the add and set the flags and the result back to | |
| *d. USE NATIVE MACHINE ORDER... | |
| */ | |
| uint16 add_word(PC_ENV *m, uint16 d, uint16 s) | |
| { | |
| register uint32 res; /* all operands in native machine order */ | |
| register uint32 cc; | |
| res = d + s; | |
| /* set the carry flag to be bit 8 */ | |
| CONDITIONAL_SET_FLAG(res & 0x10000, m, F_CF); | |
| CONDITIONAL_SET_FLAG((res&0xffff)==0, m, F_ZF); | |
| CONDITIONAL_SET_FLAG(res & 0x8000, m, F_SF); | |
| CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); | |
| /* calculate the carry chain SEE NOTE AT TOP.*/ | |
| cc = (s & d) | ((~res) & (s | d)); | |
| CONDITIONAL_SET_FLAG(xor_0x3_tab[(cc>>14)&0x3], m, F_OF); | |
| CONDITIONAL_SET_FLAG(cc&0x8, m, F_AF); | |
| return res; | |
| } | |
| /* | |
| Flags m->R_FLG, dest *d, source *s, do a bitwise and of the | |
| source and destination, and then store back to the | |
| destination. Size=byte. | |
| */ | |
| uint8 and_byte(PC_ENV *m, uint8 d, uint8 s) | |
| { | |
| register uint8 res; /* all operands in native machine order */ | |
| res = d & s; | |
| /* set the flags */ | |
| CLEAR_FLAG(m, F_OF); | |
| CLEAR_FLAG(m, F_CF); | |
| CONDITIONAL_SET_FLAG(res&0x80, m, F_SF); | |
| CONDITIONAL_SET_FLAG(res==0, m, F_ZF); | |
| CONDITIONAL_SET_FLAG(parity_tab[res], m, F_PF); | |
| return res; | |
| } | |
| /* | |
| Flags m->R_FLG, dest *d, source *s, do a bitwise and of the | |
| source and destination, and then store back to the | |
| destination. Size=byte. | |
| */ | |
| uint16 and_word(PC_ENV *m, uint16 d, uint16 s) | |
| { | |
| register uint16 res; /* all operands in native machine order */ | |
| res = d & s; | |
| /* set the flags */ | |
| CLEAR_FLAG(m, F_OF); | |
| CLEAR_FLAG(m, F_CF); | |
| CONDITIONAL_SET_FLAG(res&0x8000, m, F_SF); | |
| CONDITIONAL_SET_FLAG(res==0, m, F_ZF); | |
| CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); | |
| return res; | |
| } | |
| uint8 cmp_byte(PC_ENV *m, uint8 d, uint8 s) | |
| { | |
| register uint32 res; /* all operands in native machine order */ | |
| register uint32 bc; | |
| res = d - s; | |
| CLEAR_FLAG(m, F_CF); | |
| CONDITIONAL_SET_FLAG(res&0x80, m, F_SF); | |
| CONDITIONAL_SET_FLAG((res&0xff)==0, m, F_ZF); | |
| CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); | |
| /* calculate the borrow chain. See note at top */ | |
| bc= (res&(~d|s))|(~d&s); | |
| CONDITIONAL_SET_FLAG(bc&0x80,m, F_CF); | |
| CONDITIONAL_SET_FLAG(xor_0x3_tab[(bc>>6)&0x3], m, F_OF); | |
| CONDITIONAL_SET_FLAG(bc&0x8, m, F_AF); | |
| return d; /* long story why this is needed. Look at opcode | |
| 0x80 in ops.c, for an idea why this is necessary.*/ | |
| } | |
| uint16 cmp_word(PC_ENV *m, uint16 d, uint16 s) | |
| { | |
| register uint32 res; /* all operands in native machine order */ | |
| register uint32 bc; | |
| res = d - s; | |
| CONDITIONAL_SET_FLAG(res&0x8000, m, F_SF); | |
| CONDITIONAL_SET_FLAG((res&0xffff)==0, m, F_ZF); | |
| CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); | |
| /* calculate the borrow chain. See note at top */ | |
| bc= (res&(~d|s))|(~d&s); | |
| CONDITIONAL_SET_FLAG(bc&0x8000,m, F_CF); | |
| CONDITIONAL_SET_FLAG(xor_0x3_tab[(bc>>14)&0x3], m, F_OF); | |
| CONDITIONAL_SET_FLAG(bc&0x8, m, F_AF); | |
| return d; | |
| } | |
| uint8 dec_byte(PC_ENV *m, uint8 d) | |
| { | |
| register uint32 res; /* all operands in native machine order */ | |
| register uint32 bc; | |
| res = d - 1; | |
| CONDITIONAL_SET_FLAG(res&0x80, m, F_SF); | |
| CONDITIONAL_SET_FLAG((res&0xff)==0, m, F_ZF); | |
| CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); | |
| /* calculate the borrow chain. See note at top */ | |
| /* based on sub_byte, uses s==1. */ | |
| bc= (res&(~d|1))|(~d&1); | |
| /* carry flag unchanged */ | |
| CONDITIONAL_SET_FLAG(xor_0x3_tab[(bc>>6)&0x3], m, F_OF); | |
| CONDITIONAL_SET_FLAG(bc&0x8, m, F_AF); | |
| return res; | |
| } | |
| uint16 dec_word(PC_ENV *m, uint16 d) | |
| { | |
| register uint32 res; /* all operands in native machine order */ | |
| register uint32 bc; | |
| res = d - 1; | |
| CONDITIONAL_SET_FLAG(res&0x8000, m, F_SF); | |
| CONDITIONAL_SET_FLAG((res&0xffff)==0, m, F_ZF); | |
| CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); | |
| /* calculate the borrow chain. See note at top */ | |
| /* based on the sub_byte routine, with s==1 */ | |
| bc= (res&(~d|1))|(~d&1); | |
| /* carry flag unchanged */ | |
| CONDITIONAL_SET_FLAG(xor_0x3_tab[(bc>>14)&0x3], m, F_OF); | |
| CONDITIONAL_SET_FLAG(bc&0x8, m, F_AF); | |
| return res; | |
| } | |
| /* Given flags=f, and byte d (dest) | |
| perform the inc and set the flags and the result back to | |
| d. USE NATIVE MACHINE ORDER... | |
| */ | |
| uint8 inc_byte(PC_ENV *m, uint8 d) | |
| { | |
| register uint32 res; /* all operands in native machine order */ | |
| register uint32 cc; | |
| res = d + 1; | |
| CONDITIONAL_SET_FLAG((res&0xff)==0, m, F_ZF); | |
| CONDITIONAL_SET_FLAG(res & 0x80, m, F_SF); | |
| CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); | |
| /* calculate the carry chain SEE NOTE AT TOP.*/ | |
| cc = ((1 & d) | (~res)) & (1 | d); | |
| CONDITIONAL_SET_FLAG(xor_0x3_tab[(cc>>6)&0x3], m, F_OF); | |
| CONDITIONAL_SET_FLAG(cc&0x8, m, F_AF); | |
| return res; | |
| } | |
| /* Given flags=f, and byte d (dest) | |
| perform the inc and set the flags and the result back to | |
| *d. USE NATIVE MACHINE ORDER... | |
| */ | |
| uint16 inc_word(PC_ENV *m, uint16 d) | |
| { | |
| register uint32 res; /* all operands in native machine order */ | |
| register uint32 cc; | |
| res = d + 1; | |
| CONDITIONAL_SET_FLAG((res&0xffff)==0, m, F_ZF); | |
| CONDITIONAL_SET_FLAG(res & 0x8000, m, F_SF); | |
| CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); | |
| /* calculate the carry chain SEE NOTE AT TOP.*/ | |
| cc = (1 & d) | ((~res) & (1 | d)); | |
| CONDITIONAL_SET_FLAG(xor_0x3_tab[(cc>>14)&0x3], m, F_OF); | |
| CONDITIONAL_SET_FLAG(cc&0x8, m, F_AF); | |
| return res ; | |
| } | |
| uint8 or_byte(PC_ENV *m, uint8 d, uint8 s) | |
| { | |
| register uint8 res; /* all operands in native machine order */ | |
| res = d | s; | |
| CLEAR_FLAG(m, F_OF); | |
| CLEAR_FLAG(m, F_CF); | |
| CONDITIONAL_SET_FLAG(res&0x80, m, F_SF); | |
| CONDITIONAL_SET_FLAG(res==0, m, F_ZF); | |
| CONDITIONAL_SET_FLAG(parity_tab[res], m, F_PF); | |
| return res; | |
| } | |
| uint16 or_word(PC_ENV *m, uint16 d, uint16 s) | |
| { | |
| register uint16 res; /* all operands in native machine order */ | |
| res = d | s; | |
| /* set the carry flag to be bit 8 */ | |
| CLEAR_FLAG(m, F_OF); | |
| CLEAR_FLAG(m, F_CF); | |
| CONDITIONAL_SET_FLAG(res&0x8000, m, F_SF); | |
| CONDITIONAL_SET_FLAG(res==0, m, F_ZF); | |
| CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); | |
| return res; | |
| } | |
| uint8 neg_byte(PC_ENV *m, uint8 s) | |
| { | |
| register uint8 res; | |
| register uint8 bc; | |
| CONDITIONAL_SET_FLAG(s!=0, m, F_CF); | |
| res = -s; | |
| CONDITIONAL_SET_FLAG((res&0xff)==0, m, F_ZF); | |
| CONDITIONAL_SET_FLAG(res & 0x80, m, F_SF); | |
| CONDITIONAL_SET_FLAG(parity_tab[res], m, F_PF); | |
| /* calculate the borrow chain --- modified such that d=0. | |
| substitutiing d=0 into bc= res&(~d|s)|(~d&s); | |
| (the one used for sub) and simplifying, since ~d=0xff..., | |
| ~d|s == 0xffff..., and res&0xfff... == res. Similarly | |
| ~d&s == s. So the simplified result is:*/ | |
| bc= res|s; | |
| CONDITIONAL_SET_FLAG(xor_0x3_tab[(bc>>6)&0x3], m, F_OF); | |
| CONDITIONAL_SET_FLAG(bc&0x8, m, F_AF); | |
| return res; | |
| } | |
| uint16 neg_word(PC_ENV *m, uint16 s) | |
| { | |
| register uint16 res; | |
| register uint16 bc; | |
| CONDITIONAL_SET_FLAG(s!=0, m, F_CF); | |
| res = -s; | |
| CONDITIONAL_SET_FLAG((res&0xffff)==0, m, F_ZF); | |
| CONDITIONAL_SET_FLAG(res & 0x8000, m, F_SF); | |
| CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); | |
| /* calculate the borrow chain --- modified such that d=0. | |
| substitutiing d=0 into bc= res&(~d|s)|(~d&s); | |
| (the one used for sub) and simplifying, since ~d=0xff..., | |
| ~d|s == 0xffff..., and res&0xfff... == res. Similarly | |
| ~d&s == s. So the simplified result is:*/ | |
| bc= res|s; | |
| CONDITIONAL_SET_FLAG(xor_0x3_tab[(bc>>6)&0x3], m, F_OF); | |
| CONDITIONAL_SET_FLAG(bc&0x8, m, F_AF); | |
| return res; | |
| } | |
| uint8 not_byte(PC_ENV *m, uint8 s) | |
| { | |
| return ~s; | |
| } | |
| uint16 not_word(PC_ENV *m, uint16 s) | |
| { | |
| return ~s; | |
| } | |
| /* access stuff from absolute location in memory. | |
| no segment registers are involved. | |
| */ | |
| uint16 mem_access_word(PC_ENV *m, int addr) | |
| { | |
| /* Load in two steps. Native byte order independent */ | |
| return GetBYTEExtended(addr) | (GetBYTEExtended(addr + 1) << 8); | |
| } | |
| /* given the register_set r, and memory descriptor m, | |
| and word w, push w onto the stack. | |
| w ASSUMED IN NATIVE MACHINE ORDER. Doesn't matter in this case??? | |
| */ | |
| void push_word(PC_ENV *m, uint16 w) | |
| { | |
| m->R_SP --; | |
| PutBYTEExtended((m->R_SS << 4) + m->R_SP, w >> 8); | |
| m->R_SP --; | |
| PutBYTEExtended((m->R_SS << 4) + m->R_SP, w & 0xff); | |
| } | |
| /* given the memory descriptor m, | |
| and word w, pop word from the stack. | |
| */ | |
| uint16 pop_word(PC_ENV *m) | |
| { | |
| register uint16 res; | |
| res = GetBYTEExtended((m->R_SS << 4) + m->R_SP); | |
| m->R_SP++; | |
| res |= GetBYTEExtended((m->R_SS << 4) + m->R_SP) << 8; | |
| m->R_SP++; | |
| return res; | |
| } | |
| /***************************************************************** | |
| BEGIN region consisting of bit shifts and rotates, | |
| much of which may be wrong. Large hirsute factor. | |
| *****************************************************************/ | |
| uint8 rcl_byte(PC_ENV *m, uint8 d, uint8 s) | |
| { | |
| register uint32 res, cnt, mask,cf; | |
| /* s is the rotate distance. It varies from 0 - 8. */ | |
| /* have | |
| CF B_7 B_6 B_5 B_4 B_3 B_2 B_1 B_0 | |
| want to rotate through the carry by "s" bits. We could | |
| loop, but that's inefficient. So the width is 9, | |
| and we split into three parts: | |
| The new carry flag (was B_n) | |
| the stuff in B_n-1 .. B_0 | |
| the stuff in B_7 .. B_n+1 | |
| The new rotate is done mod 9, and given this, | |
| for a rotation of n bits (mod 9) the new carry flag is | |
| then located n bits from the MSB. The low part is | |
| then shifted up cnt bits, and the high part is or'd | |
| in. Using CAPS for new values, and lowercase for the | |
| original values, this can be expressed as: | |
| IF n > 0 | |
| 1) CF <- b_(8-n) | |
| 2) B_(7) .. B_(n) <- b_(8-(n+1)) .. b_0 | |
| 3) B_(n-1) <- cf | |
| 4) B_(n-2) .. B_0 <- b_7 .. b_(8-(n-1)) | |
| I think this is correct. | |
| */ | |
| res = d; | |
| /* [JCE] Extra brackets to stop gcc -Wall moaning */ | |
| if ((cnt = s % 9)) /* not a typo, do nada if cnt==0 */ | |
| { | |
| /* extract the new CARRY FLAG. */ | |
| /* CF <- b_(8-n) */ | |
| cf = (d >> (8-cnt)) & 0x1; | |
| /* get the low stuff which rotated | |
| into the range B_7 .. B_cnt */ | |
| /* B_(7) .. B_(n) <- b_(8-(n+1)) .. b_0 */ | |
| /* note that the right hand side done by the mask */ | |
| res = (d << cnt) & 0xff; | |
| /* now the high stuff which rotated around | |
| into the positions B_cnt-2 .. B_0 */ | |
| /* B_(n-2) .. B_0 <- b_7 .. b_(8-(n-1)) */ | |
| /* shift it downward, 7-(n-2) = 9-n positions. | |
| and mask off the result before or'ing in. | |
| */ | |
| mask = (1<<(cnt-1)) - 1; | |
| res |= (d >> (9-cnt)) & mask; | |
| /* if the carry flag was set, or it in. */ | |
| if (ACCESS_FLAG(m,F_CF)) /* carry flag is set */ | |
| { | |
| /* B_(n-1) <- cf */ | |
| res |= 1 << (cnt-1); | |
| } | |
| /* set the new carry flag, based on the variable "cf" */ | |
| CONDITIONAL_SET_FLAG(cf, m, F_CF); | |
| /* OVERFLOW is set *IFF* cnt==1, then it is the | |
| xor of CF and the most significant bit. Blecck. */ | |
| /* parenthesized this expression since it appears to | |
| be causing OF to be missed. */ | |
| CONDITIONAL_SET_FLAG(cnt==1&&xor_0x3_tab[cf+((res>>6)&0x2)], m, F_OF); | |
| } | |
| return res & 0xff; | |
| } | |
| uint16 rcl_word(PC_ENV *m, uint16 d, uint16 s) | |
| { | |
| register uint32 res, cnt, mask,cf; | |
| /* see analysis above. */ | |
| /* width here is 16 bits + carry bit */ | |
| res = d; | |
| /* [JCE] Extra brackets to stop gcc -Wall moaning */ | |
| if ((cnt = s % 17)) /* not a typo, do nada if cnt==0 */ | |
| { | |
| /* extract the new CARRY FLAG. */ | |
| /* CF <- b_(16-n) */ | |
| cf = (d >> (16-cnt)) & 0x1; | |
| /* get the low stuff which rotated | |
| into the range B_15 .. B_cnt */ | |
| /* B_(15) .. B_(n) <- b_(16-(n+1)) .. b_0 */ | |
| /* note that the right hand side done by the mask */ | |
| res = (d << cnt) & 0xffff; | |
| /* now the high stuff which rotated around | |
| into the positions B_cnt-2 .. B_0 */ | |
| /* B_(n-2) .. B_0 <- b_15 .. b_(16-(n-1)) */ | |
| /* shift it downward, 15-(n-2) = 17-n positions. | |
| and mask off the result before or'ing in. | |
| */ | |
| mask = (1<<(cnt-1)) - 1; | |
| res |= (d >> (17-cnt)) & mask; | |
| /* if the carry flag was set, or it in. */ | |
| if (ACCESS_FLAG(m, F_CF)) /* carry flag is set */ | |
| { | |
| /* B_(n-1) <- cf */ | |
| res |= 1 << (cnt-1); | |
| } | |
| /* set the new carry flag, based on the variable "cf" */ | |
| CONDITIONAL_SET_FLAG(cf, m, F_CF); | |
| /* OVERFLOW is set *IFF* cnt==1, then it is the | |
| xor of CF and the most significant bit. Blecck. | |
| Note that we're forming a 2 bit word here to index | |
| into the table. The expression cf+(res>>14)&0x2 | |
| represents the two bit word b_15 CF. | |
| */ | |
| /* parenthesized following expression... */ | |
| CONDITIONAL_SET_FLAG(cnt==1&&xor_0x3_tab[cf+((res>>14)&0x2)], m, F_OF); | |
| } | |
| return res & 0xffff; | |
| } | |
| uint8 rcr_byte(PC_ENV *m, uint8 d, uint8 s) | |
| { | |
| uint8 res, cnt; | |
| uint8 mask, cf, ocf = 0; | |
| /* rotate right through carry */ | |
| /* | |
| s is the rotate distance. It varies from 0 - 8. | |
| d is the byte object rotated. | |
| have | |
| CF B_7 B_6 B_5 B_4 B_3 B_2 B_1 B_0 | |
| The new rotate is done mod 9, and given this, | |
| for a rotation of n bits (mod 9) the new carry flag is | |
| then located n bits from the LSB. The low part is | |
| then shifted up cnt bits, and the high part is or'd | |
| in. Using CAPS for new values, and lowercase for the | |
| original values, this can be expressed as: | |
| IF n > 0 | |
| 1) CF <- b_(n-1) | |
| 2) B_(8-(n+1)) .. B_(0) <- b_(7) .. b_(n) | |
| 3) B_(8-n) <- cf | |
| 4) B_(7) .. B_(8-(n-1)) <- b_(n-2) .. b_(0) | |
| I think this is correct. | |
| */ | |
| res = d; | |
| /* [JCE] Extra brackets to stop gcc -Wall moaning */ | |
| if ((cnt = s % 9)) /* not a typo, do nada if cnt==0 */ | |
| { | |
| /* extract the new CARRY FLAG. */ | |
| /* CF <- b_(n-1) */ | |
| if (cnt == 1) | |
| { | |
| cf = d & 0x1; | |
| /* note hackery here. Access_flag(..) evaluates to either | |
| 0 if flag not set | |
| non-zero if flag is set. | |
| doing access_flag(..) != 0 casts that into either | |
| 0..1 in any representation of the flags register | |
| (i.e. packed bit array or unpacked.) | |
| */ | |
| ocf = ACCESS_FLAG(m,F_CF) != 0; | |
| } | |
| else | |
| cf = (d >> (cnt-1)) & 0x1; | |
| /* B_(8-(n+1)) .. B_(0) <- b_(7) .. b_n */ | |
| /* note that the right hand side done by the mask | |
| This is effectively done by shifting the | |
| object to the right. The result must be masked, | |
| in case the object came in and was treated | |
| as a negative number. Needed???*/ | |
| mask = (1<<(8-cnt))-1; | |
| res = (d >> cnt) & mask; | |
| /* now the high stuff which rotated around | |
| into the positions B_cnt-2 .. B_0 */ | |
| /* B_(7) .. B_(8-(n-1)) <- b_(n-2) .. b_(0) */ | |
| /* shift it downward, 7-(n-2) = 9-n positions. | |
| and mask off the result before or'ing in. | |
| */ | |
| res |= (d << (9-cnt)); | |
| /* if the carry flag was set, or it in. */ | |
| if (ACCESS_FLAG(m,F_CF)) /* carry flag is set */ | |
| { | |
| /* B_(8-n) <- cf */ | |
| res |= 1 << (8 - cnt); | |
| } | |
| /* set the new carry flag, based on the variable "cf" */ | |
| CONDITIONAL_SET_FLAG(cf, m, F_CF); | |
| /* OVERFLOW is set *IFF* cnt==1, then it is the | |
| xor of CF and the most significant bit. Blecck. */ | |
| /* parenthesized... */ | |
| if (cnt == 1) | |
| { /* [JCE] Explicit braces to stop gcc -Wall moaning */ | |
| CONDITIONAL_SET_FLAG(xor_0x3_tab[ocf+((d>>6)&0x2)], m, F_OF); | |
| } | |
| } | |
| return res; | |
| } | |
| uint16 rcr_word(PC_ENV *m, uint16 d, uint16 s) | |
| { | |
| uint16 res, cnt; | |
| uint16 mask, cf, ocf = 0; | |
| /* rotate right through carry */ | |
| /* | |
| s is the rotate distance. It varies from 0 - 8. | |
| d is the byte object rotated. | |
| have | |
| CF B_15 ... B_0 | |
| The new rotate is done mod 17, and given this, | |
| for a rotation of n bits (mod 17) the new carry flag is | |
| then located n bits from the LSB. The low part is | |
| then shifted up cnt bits, and the high part is or'd | |
| in. Using CAPS for new values, and lowercase for the | |
| original values, this can be expressed as: | |
| IF n > 0 | |
| 1) CF <- b_(n-1) | |
| 2) B_(16-(n+1)) .. B_(0) <- b_(15) .. b_(n) | |
| 3) B_(16-n) <- cf | |
| 4) B_(15) .. B_(16-(n-1)) <- b_(n-2) .. b_(0) | |
| I think this is correct. | |
| */ | |
| res = d; | |
| /* [JCE] Extra brackets to stop gcc -Wall moaning */ | |
| if ((cnt = s % 17)) /* not a typo, do nada if cnt==0 */ | |
| { | |
| /* extract the new CARRY FLAG. */ | |
| /* CF <- b_(n-1) */ | |
| if (cnt==1) | |
| { | |
| cf = d & 0x1; | |
| /* see note above on teh byte version */ | |
| ocf = ACCESS_FLAG(m,F_CF) != 0; | |
| } | |
| else | |
| cf = (d >> (cnt-1)) & 0x1; | |
| /* B_(16-(n+1)) .. B_(0) <- b_(15) .. b_n */ | |
| /* note that the right hand side done by the mask | |
| This is effectively done by shifting the | |
| object to the right. The result must be masked, | |
| in case the object came in and was treated | |
| as a negative number. Needed???*/ | |
| mask = (1<<(16-cnt))-1; | |
| res = (d >> cnt) & mask; | |
| /* now the high stuff which rotated around | |
| into the positions B_cnt-2 .. B_0 */ | |
| /* B_(15) .. B_(16-(n-1)) <- b_(n-2) .. b_(0) */ | |
| /* shift it downward, 15-(n-2) = 17-n positions. | |
| and mask off the result before or'ing in. | |
| */ | |
| res |= (d << (17-cnt)); | |
| /* if the carry flag was set, or it in. */ | |
| if (ACCESS_FLAG(m,F_CF)) /* carry flag is set */ | |
| { | |
| /* B_(16-n) <- cf */ | |
| res |= 1 << (16 - cnt); | |
| } | |
| /* set the new carry flag, based on the variable "cf" */ | |
| CONDITIONAL_SET_FLAG(cf, m, F_CF); | |
| /* OVERFLOW is set *IFF* cnt==1, then it is the | |
| xor of CF and the most significant bit. Blecck. */ | |
| if (cnt==1) | |
| { /* [JCE] Explicit braces to stop gcc -Wall moaning */ | |
| CONDITIONAL_SET_FLAG(xor_0x3_tab[ocf+((d>>14)&0x2)], m, F_OF); | |
| } | |
| } | |
| return res; | |
| } | |
| uint8 rol_byte(PC_ENV *m, uint8 d, uint8 s) | |
| { | |
| register uint32 res, cnt, mask; | |
| /* rotate left */ | |
| /* | |
| s is the rotate distance. It varies from 0 - 8. | |
| d is the byte object rotated. | |
| have | |
| CF B_7 ... B_0 | |
| The new rotate is done mod 8. | |
| Much simpler than the "rcl" or "rcr" operations. | |
| IF n > 0 | |
| 1) B_(7) .. B_(n) <- b_(8-(n+1)) .. b_(0) | |
| 2) B_(n-1) .. B_(0) <- b_(7) .. b_(8-n) | |
| I think this is correct. | |
| */ | |
| res =d; | |
| /* [JCE] Extra brackets to stop gcc -Wall moaning */ | |
| if ((cnt = s % 8)) /* not a typo, do nada if cnt==0 */ | |
| { | |
| /* B_(7) .. B_(n) <- b_(8-(n+1)) .. b_(0) */ | |
| res = (d << cnt); | |
| /* B_(n-1) .. B_(0) <- b_(7) .. b_(8-n) */ | |
| mask = (1 << cnt) - 1; | |
| res |= (d >> (8-cnt)) & mask; | |
| /* set the new carry flag, Note that it is the low order | |
| bit of the result!!! */ | |
| CONDITIONAL_SET_FLAG(res&0x1, m, F_CF); | |
| /* OVERFLOW is set *IFF* cnt==1, then it is the | |
| xor of CF and the most significant bit. Blecck. */ | |
| CONDITIONAL_SET_FLAG(cnt==1&&xor_0x3_tab[(res&0x1)+((res>>6)&0x2)], m, F_OF); | |
| } | |
| return res&0xff; | |
| } | |
| uint16 rol_word(PC_ENV *m, uint16 d, uint16 s) | |
| { | |
| register uint32 res, cnt, mask; | |
| /* rotate left */ | |
| /* | |
| s is the rotate distance. It varies from 0 - 8. | |
| d is the byte object rotated. | |
| have | |
| CF B_15 ... B_0 | |
| The new rotate is done mod 8. | |
| Much simpler than the "rcl" or "rcr" operations. | |
| IF n > 0 | |
| 1) B_(15) .. B_(n) <- b_(16-(n+1)) .. b_(0) | |
| 2) B_(n-1) .. B_(0) <- b_(16) .. b_(16-n) | |
| I think this is correct. | |
| */ | |
| res = d; | |
| /* [JCE] Extra brackets to stop gcc -Wall moaning */ | |
| if ((cnt = s % 16)) /* not a typo, do nada if cnt==0 */ | |
| { | |
| /* B_(16) .. B_(n) <- b_(16-(n+1)) .. b_(0) */ | |
| res = (d << cnt); | |
| /* B_(n-1) .. B_(0) <- b_(15) .. b_(16-n) */ | |
| mask = (1 << cnt) - 1; | |
| res |= (d >> (16-cnt)) & mask; | |
| /* set the new carry flag, Note that it is the low order | |
| bit of the result!!! */ | |
| CONDITIONAL_SET_FLAG(res&0x1, m, F_CF); | |
| /* OVERFLOW is set *IFF* cnt==1, then it is the | |
| xor of CF and the most significant bit. Blecck. */ | |
| CONDITIONAL_SET_FLAG(cnt==1&&xor_0x3_tab[(res&0x1)+((res>>14)&0x2)], m, F_OF); | |
| } | |
| return res&0xffff; | |
| } | |
| uint8 ror_byte(PC_ENV *m, uint8 d, uint8 s) | |
| { | |
| register uint32 res, cnt, mask; | |
| /* rotate right */ | |
| /* | |
| s is the rotate distance. It varies from 0 - 8. | |
| d is the byte object rotated. | |
| have | |
| B_7 ... B_0 | |
| The rotate is done mod 8. | |
| IF n > 0 | |
| 1) B_(8-(n+1)) .. B_(0) <- b_(7) .. b_(n) | |
| 2) B_(7) .. B_(8-n) <- b_(n-1) .. b_(0) | |
| */ | |
| res = d; | |
| /* [JCE] Extra brackets to stop gcc -Wall moaning */ | |
| if ((cnt = s % 8)) /* not a typo, do nada if cnt==0 */ | |
| { | |
| /* B_(7) .. B_(8-n) <- b_(n-1) .. b_(0)*/ | |
| res = (d << (8-cnt)); | |
| /* B_(8-(n+1)) .. B_(0) <- b_(7) .. b_(n) */ | |
| mask = (1 << (8-cnt)) - 1; | |
| res |= (d >> (cnt)) & mask; | |
| /* set the new carry flag, Note that it is the low order | |
| bit of the result!!! */ | |
| CONDITIONAL_SET_FLAG(res&0x80, m, F_CF); | |
| /* OVERFLOW is set *IFF* cnt==1, then it is the | |
| xor of the two most significant bits. Blecck. */ | |
| CONDITIONAL_SET_FLAG(cnt==1&& xor_0x3_tab[(res>>6)&0x3], m, F_OF); | |
| } | |
| return res&0xff; | |
| } | |
| uint16 ror_word(PC_ENV *m, uint16 d, uint16 s) | |
| { | |
| register uint32 res, cnt, mask; | |
| /* rotate right */ | |
| /* | |
| s is the rotate distance. It varies from 0 - 8. | |
| d is the byte object rotated. | |
| have | |
| B_15 ... B_0 | |
| The rotate is done mod 16. | |
| IF n > 0 | |
| 1) B_(16-(n+1)) .. B_(0) <- b_(15) .. b_(n) | |
| 2) B_(15) .. B_(16-n) <- b_(n-1) .. b_(0) | |
| I think this is correct. | |
| */ | |
| res =d ; | |
| /* [JCE] Extra brackets to stop gcc -Wall moaning */ | |
| if ((cnt = s % 16)) /* not a typo, do nada if cnt==0 */ | |
| { | |
| /* B_(15) .. B_(16-n) <- b_(n-1) .. b_(0)*/ | |
| res = (d << (16-cnt)); | |
| /* B_(16-(n+1)) .. B_(0) <- b_(15) .. b_(n) */ | |
| mask = (1 << (16-cnt)) - 1; | |
| res |= (d >> (cnt)) & mask; | |
| /* set the new carry flag, Note that it is the low order | |
| bit of the result!!! */ | |
| CONDITIONAL_SET_FLAG(res&0x8000, m, F_CF); | |
| /* OVERFLOW is set *IFF* cnt==1, then it is the | |
| xor of CF and the most significant bit. Blecck. */ | |
| CONDITIONAL_SET_FLAG(cnt==1 && xor_0x3_tab[(res>>14)&0x3], m, F_OF); | |
| } | |
| return res & 0xffff; | |
| } | |
| uint8 shl_byte(PC_ENV *m, uint8 d, uint8 s) | |
| { | |
| uint32 cnt,res,cf; | |
| if (s < 8) | |
| { | |
| cnt = s % 8; | |
| /* last bit shifted out goes into carry flag */ | |
| if (cnt>0) | |
| { | |
| res = d << cnt; | |
| cf = d & (1<<(8-cnt)); | |
| CONDITIONAL_SET_FLAG(cf, m, F_CF); | |
| CONDITIONAL_SET_FLAG((res&0xff)==0, m, F_ZF); | |
| CONDITIONAL_SET_FLAG(res & 0x80, m, F_SF); | |
| CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); | |
| } | |
| else | |
| { | |
| res = (uint8)d; | |
| } | |
| if (cnt == 1) | |
| { | |
| /* Needs simplification. */ | |
| CONDITIONAL_SET_FLAG( | |
| (((res&0x80)==0x80) ^ | |
| (ACCESS_FLAG(m,F_CF) != 0)) , | |
| /* was (m->R_FLG&F_CF)==F_CF)), */ | |
| m, F_OF); | |
| } | |
| else | |
| { | |
| CLEAR_FLAG(m,F_OF); | |
| } | |
| } | |
| else | |
| { | |
| res = 0; | |
| /* CLEAR_FLAG(m,F_CF);*/ | |
| CONDITIONAL_SET_FLAG((s == 8) && (d & 1), m, F_CF); /* Peter Schorn bug fix */ | |
| CLEAR_FLAG(m,F_OF); | |
| CLEAR_FLAG(m,F_SF); | |
| CLEAR_FLAG(m,F_PF); | |
| SET_FLAG(m,F_ZF); | |
| } | |
| return res & 0xff; | |
| } | |
| uint16 shl_word(PC_ENV *m, uint16 d, uint16 s) | |
| { | |
| uint32 cnt,res,cf; | |
| if (s < 16) | |
| { | |
| cnt = s % 16; | |
| if (cnt > 0) | |
| { | |
| res = d << cnt; | |
| /* last bit shifted out goes into carry flag */ | |
| cf = d & (1<<(16-cnt)); | |
| CONDITIONAL_SET_FLAG(cf, m, F_CF); | |
| CONDITIONAL_SET_FLAG((res&0xffff)==0, m, F_ZF); | |
| CONDITIONAL_SET_FLAG(res & 0x8000, m, F_SF); | |
| CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); | |
| } | |
| else | |
| { | |
| res = (uint16)d; | |
| } | |
| if (cnt == 1) | |
| { | |
| /* Needs simplification. */ | |
| CONDITIONAL_SET_FLAG( | |
| (((res&0x8000)==0x8000) ^ | |
| (ACCESS_FLAG(m,F_CF) != 0)), | |
| /*((m&F_CF)==F_CF)),*/ | |
| m, F_OF); | |
| } | |
| else | |
| { | |
| CLEAR_FLAG(m,F_OF); | |
| } | |
| } | |
| else | |
| { | |
| res = 0; | |
| /* CLEAR_FLAG(m,F_CF);*/ | |
| CONDITIONAL_SET_FLAG((s == 16) && (d & 1), m, F_CF); /* Peter Schorn bug fix */ | |
| CLEAR_FLAG(m,F_OF); | |
| SET_FLAG(m,F_ZF); | |
| CLEAR_FLAG(m,F_SF); | |
| CLEAR_FLAG(m,F_PF); | |
| } | |
| return res & 0xffff; | |
| } | |
| uint8 shr_byte(PC_ENV *m, uint8 d, uint8 s) | |
| { | |
| uint32 cnt,res,cf,mask; | |
| if (s < 8) | |
| { | |
| cnt = s % 8; | |
| if (cnt > 0) | |
| { | |
| mask = (1<<(8-cnt))-1; | |
| cf = d & (1<<(cnt-1)); | |
| res = (d >> cnt) & mask; | |
| CONDITIONAL_SET_FLAG(cf, m, F_CF); | |
| CONDITIONAL_SET_FLAG((res&0xff)==0, m, F_ZF); | |
| CONDITIONAL_SET_FLAG(res & 0x80, m, F_SF); | |
| CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); | |
| } | |
| else | |
| { | |
| res = (uint8) d; | |
| } | |
| if (cnt == 1) | |
| { | |
| CONDITIONAL_SET_FLAG(xor_0x3_tab[(res>>6)&0x3], m, F_OF); | |
| } | |
| else | |
| { | |
| CLEAR_FLAG(m,F_OF); | |
| } | |
| } | |
| else | |
| { | |
| res = 0; | |
| /* CLEAR_FLAG(m,F_CF);*/ | |
| CONDITIONAL_SET_FLAG((s == 8) && (d & 0x80), m, F_CF); /* Peter Schorn bug fix */ | |
| CLEAR_FLAG(m,F_OF); | |
| SET_FLAG(m,F_ZF); | |
| CLEAR_FLAG(m,F_SF); | |
| CLEAR_FLAG(m,F_PF); | |
| } | |
| return res & 0xff; | |
| } | |
| uint16 shr_word(PC_ENV *m, uint16 d, uint16 s) | |
| { | |
| uint32 cnt,res,cf,mask; | |
| if (s < 16) | |
| { | |
| cnt = s % 16; | |
| if (cnt > 0) | |
| { | |
| mask = (1<<(16-cnt))-1; | |
| cf = d & (1<<(cnt-1)); | |
| res = (d >> cnt) & mask; | |
| CONDITIONAL_SET_FLAG(cf, m, F_CF); | |
| CONDITIONAL_SET_FLAG((res&0xffff)==0, m, F_ZF); | |
| CONDITIONAL_SET_FLAG(res & 0x8000, m, F_SF); | |
| CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); | |
| } | |
| else | |
| { | |
| res = d; | |
| } | |
| if (cnt == 1) | |
| { | |
| CONDITIONAL_SET_FLAG(xor_0x3_tab[(res>>14)&0x3], m, F_OF); | |
| } | |
| else | |
| { | |
| CLEAR_FLAG(m,F_OF); | |
| } | |
| } | |
| else | |
| { | |
| res = 0; | |
| /* CLEAR_FLAG(m,F_CF);*/ | |
| CONDITIONAL_SET_FLAG((s == 16) && (d & 0x8000), m, F_CF); /* Peter Schorn bug fix */ | |
| CLEAR_FLAG(m,F_OF); | |
| SET_FLAG(m,F_ZF); | |
| CLEAR_FLAG(m,F_SF); | |
| CLEAR_FLAG(m,F_PF); | |
| } | |
| return res & 0xffff; | |
| } | |
| /* XXXX ??? flags may be wrong??? */ | |
| uint8 sar_byte(PC_ENV *m, uint8 d, uint8 s) | |
| { | |
| uint32 cnt,res,cf,mask,sf; | |
| res = d; | |
| sf = d & 0x80; | |
| cnt = s % 8; | |
| if (cnt > 0 && cnt < 8) | |
| { | |
| mask = (1<<(8-cnt))-1; | |
| cf = d & (1<<(cnt-1)); | |
| res = (d >> cnt) & mask; | |
| CONDITIONAL_SET_FLAG(cf, m, F_CF); | |
| if (sf) | |
| { | |
| res |= ~mask; | |
| } | |
| CONDITIONAL_SET_FLAG((res&0xff)==0, m, F_ZF); | |
| CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); | |
| CONDITIONAL_SET_FLAG(res & 0x80, m, F_SF); | |
| } | |
| else if (cnt >= 8) | |
| { | |
| if (sf) | |
| { | |
| res = 0xff; | |
| SET_FLAG(m,F_CF); | |
| CLEAR_FLAG(m,F_ZF); | |
| SET_FLAG(m, F_SF); | |
| SET_FLAG(m, F_PF); | |
| } | |
| else | |
| { | |
| res = 0; | |
| CLEAR_FLAG(m,F_CF); | |
| SET_FLAG(m,F_ZF); | |
| CLEAR_FLAG(m, F_SF); | |
| CLEAR_FLAG(m, F_PF); | |
| } | |
| } | |
| return res&0xff; | |
| } | |
| uint16 sar_word(PC_ENV *m, uint16 d, uint16 s) | |
| { | |
| uint32 cnt, res, cf, mask, sf; | |
| sf = d & 0x8000; | |
| cnt = s % 16; | |
| res = d; | |
| if (cnt > 0 && cnt < 16) | |
| { | |
| mask = (1<<(16-cnt))-1; | |
| cf = d & (1<<(cnt-1)); | |
| res = (d >> cnt) & mask; | |
| CONDITIONAL_SET_FLAG(cf, m, F_CF); | |
| if (sf) | |
| { | |
| res |= ~mask; | |
| } | |
| CONDITIONAL_SET_FLAG((res&0xffff)==0, m, F_ZF); | |
| CONDITIONAL_SET_FLAG(res & 0x8000, m, F_SF); | |
| CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); | |
| } | |
| else if (cnt >= 16) | |
| { | |
| if (sf) | |
| { | |
| res = 0xffff; | |
| SET_FLAG(m,F_CF); | |
| CLEAR_FLAG(m,F_ZF); | |
| SET_FLAG(m, F_SF); | |
| SET_FLAG(m, F_PF); | |
| } | |
| else | |
| { | |
| res = 0; | |
| CLEAR_FLAG(m,F_CF); | |
| SET_FLAG(m,F_ZF); | |
| CLEAR_FLAG(m, F_SF); | |
| CLEAR_FLAG(m, F_PF); | |
| } | |
| } | |
| return res & 0xffff; | |
| } | |
| uint8 sbb_byte(PC_ENV *m, uint8 d, uint8 s) | |
| { | |
| register uint32 res; /* all operands in native machine order */ | |
| register uint32 bc; | |
| if (ACCESS_FLAG(m,F_CF) ) | |
| res = d - s - 1; | |
| else | |
| res = d - s; | |
| CONDITIONAL_SET_FLAG(res&0x80, m, F_SF); | |
| CONDITIONAL_SET_FLAG((res&0xff)==0, m, F_ZF); | |
| CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); | |
| /* calculate the borrow chain. See note at top */ | |
| bc= (res&(~d|s))|(~d&s); | |
| CONDITIONAL_SET_FLAG(bc&0x80,m, F_CF); | |
| CONDITIONAL_SET_FLAG(xor_0x3_tab[(bc>>6)&0x3], m, F_OF); | |
| CONDITIONAL_SET_FLAG(bc&0x8, m, F_AF); | |
| return res & 0xff; | |
| } | |
| uint16 sbb_word(PC_ENV *m, uint16 d, uint16 s) | |
| { | |
| register uint32 res; /* all operands in native machine order */ | |
| register uint32 bc; | |
| if (ACCESS_FLAG(m,F_CF)) | |
| res = d - s - 1; | |
| else | |
| res = d - s; | |
| CONDITIONAL_SET_FLAG(res&0x8000, m, F_SF); | |
| CONDITIONAL_SET_FLAG((res&0xffff)==0, m, F_ZF); | |
| CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); | |
| /* calculate the borrow chain. See note at top */ | |
| bc= (res&(~d|s))|(~d&s); | |
| CONDITIONAL_SET_FLAG(bc&0x8000,m, F_CF); | |
| CONDITIONAL_SET_FLAG(xor_0x3_tab[(bc>>14)&0x3], m, F_OF); | |
| CONDITIONAL_SET_FLAG(bc&0x8, m, F_AF); | |
| return res & 0xffff; | |
| } | |
| uint8 sub_byte(PC_ENV *m, uint8 d, uint8 s) | |
| { | |
| register uint32 res; /* all operands in native machine order */ | |
| register uint32 bc; | |
| res = d - s; | |
| CONDITIONAL_SET_FLAG(res&0x80, m, F_SF); | |
| CONDITIONAL_SET_FLAG((res&0xff)==0, m, F_ZF); | |
| CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); | |
| /* calculate the borrow chain. See note at top */ | |
| bc= (res&(~d|s))|(~d&s); | |
| CONDITIONAL_SET_FLAG(bc&0x80,m, F_CF); | |
| CONDITIONAL_SET_FLAG(xor_0x3_tab[(bc>>6)&0x3], m, F_OF); | |
| CONDITIONAL_SET_FLAG(bc&0x8, m, F_AF); | |
| return res & 0xff; | |
| } | |
| uint16 sub_word(PC_ENV *m, uint16 d, uint16 s) | |
| { | |
| register uint32 res; /* all operands in native machine order */ | |
| register uint32 bc; | |
| res = d - s; | |
| CONDITIONAL_SET_FLAG(res&0x8000, m, F_SF); | |
| CONDITIONAL_SET_FLAG((res&0xffff)==0, m, F_ZF); | |
| CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); | |
| /* calculate the borrow chain. See note at top */ | |
| bc= (res&(~d|s))|(~d&s); | |
| CONDITIONAL_SET_FLAG(bc&0x8000,m, F_CF); | |
| CONDITIONAL_SET_FLAG(xor_0x3_tab[(bc>>14)&0x3], m, F_OF); | |
| CONDITIONAL_SET_FLAG(bc&0x8, m, F_AF); | |
| return res & 0xffff; | |
| } | |
| void test_byte(PC_ENV *m, uint8 d, uint8 s) | |
| { | |
| register uint32 res; /* all operands in native machine order */ | |
| res = d & s; | |
| CLEAR_FLAG(m, F_OF); | |
| CONDITIONAL_SET_FLAG(res&0x80, m, F_SF); | |
| CONDITIONAL_SET_FLAG(res==0, m, F_ZF); | |
| CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); | |
| /* AF == dont care*/ | |
| CLEAR_FLAG(m, F_CF); | |
| } | |
| void test_word(PC_ENV *m, uint16 d, uint16 s) | |
| { | |
| register uint32 res; /* all operands in native machine order */ | |
| res = d & s; | |
| CLEAR_FLAG(m, F_OF); | |
| CONDITIONAL_SET_FLAG(res&0x8000, m, F_SF); | |
| CONDITIONAL_SET_FLAG(res==0, m, F_ZF); | |
| CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); | |
| /* AF == dont care*/ | |
| CLEAR_FLAG(m, F_CF); | |
| } | |
| uint8 xor_byte(PC_ENV *m, uint8 d, uint8 s) | |
| { | |
| register uint8 res; /* all operands in native machine order */ | |
| res = d ^ s; | |
| CLEAR_FLAG(m, F_OF); | |
| CONDITIONAL_SET_FLAG(res&0x80, m, F_SF); | |
| CONDITIONAL_SET_FLAG(res==0, m, F_ZF); | |
| CONDITIONAL_SET_FLAG(parity_tab[res], m, F_PF); | |
| CLEAR_FLAG(m, F_CF); | |
| return res; | |
| } | |
| uint16 xor_word(PC_ENV *m, uint16 d, uint16 s) | |
| { | |
| register uint16 res; /* all operands in native machine order */ | |
| res = d ^ s; | |
| /* set the carry flag to be bit 8 */ | |
| CLEAR_FLAG(m, F_OF); | |
| CONDITIONAL_SET_FLAG(res&0x8000, m, F_SF); | |
| CONDITIONAL_SET_FLAG(res==0, m, F_ZF); | |
| CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); | |
| CLEAR_FLAG(m, F_CF); | |
| return res; | |
| } | |
| void imul_byte(PC_ENV *m, uint8 s) | |
| { | |
| int16 res = (int8)m->R_AL * (int8)s; | |
| m->R_AX = res; | |
| /* Undef --- Can't hurt */ | |
| CONDITIONAL_SET_FLAG(res&0x8000,m,F_SF); | |
| CONDITIONAL_SET_FLAG(res==0,m,F_ZF); | |
| if (m->R_AH == 0 || m->R_AH == 0xff) | |
| { | |
| CLEAR_FLAG(m, F_CF); | |
| CLEAR_FLAG(m, F_OF); | |
| } | |
| else | |
| { | |
| SET_FLAG(m, F_CF); | |
| SET_FLAG(m, F_OF); | |
| } | |
| } | |
| void imul_word(PC_ENV *m, uint16 s) | |
| { | |
| int32 res = (int16)m->R_AX * (int16)s; | |
| m->R_AX = res & 0xffff; | |
| m->R_DX = (res >> 16) & 0xffff; | |
| /* Undef --- Can't hurt */ | |
| CONDITIONAL_SET_FLAG(res&0x80000000,m,F_SF); | |
| CONDITIONAL_SET_FLAG(res==0,m,F_ZF); | |
| if (m->R_DX == 0 || m->R_DX == 0xffff) | |
| { | |
| CLEAR_FLAG(m, F_CF); | |
| CLEAR_FLAG(m, F_OF); | |
| } | |
| else | |
| { | |
| SET_FLAG(m, F_CF); | |
| SET_FLAG(m, F_OF); | |
| } | |
| } | |
| void mul_byte(PC_ENV *m, uint8 s) | |
| { | |
| uint16 res = m->R_AL * s; | |
| m->R_AX = res; | |
| /* Undef --- Can't hurt */ | |
| CLEAR_FLAG(m,F_SF); | |
| CONDITIONAL_SET_FLAG(res==0,m,F_ZF); | |
| if (m->R_AH == 0) | |
| { | |
| CLEAR_FLAG(m, F_CF); | |
| CLEAR_FLAG(m, F_OF); | |
| } | |
| else | |
| { | |
| SET_FLAG(m, F_CF); | |
| SET_FLAG(m, F_OF); | |
| } | |
| } | |
| void mul_word(PC_ENV *m, uint16 s) | |
| { | |
| uint32 res = m->R_AX * s; | |
| /* Undef --- Can't hurt */ | |
| CLEAR_FLAG(m,F_SF); | |
| CONDITIONAL_SET_FLAG(res==0,m,F_ZF); | |
| m->R_AX = res & 0xffff; | |
| m->R_DX = (res >> 16) & 0xffff; | |
| if (m->R_DX == 0) | |
| { | |
| CLEAR_FLAG(m, F_CF); | |
| CLEAR_FLAG(m, F_OF); | |
| } | |
| else | |
| { | |
| SET_FLAG(m, F_CF); | |
| SET_FLAG(m, F_OF); | |
| } | |
| } | |
| void idiv_byte(PC_ENV *m, uint8 s) | |
| { | |
| int32 dvd,div,mod; | |
| dvd = (int16)m->R_AX; | |
| if (s == 0) | |
| { | |
| i86_intr_raise(m,0); | |
| return; | |
| } | |
| div = dvd / (int8)s; | |
| mod = dvd % (int8)s; | |
| if (abs(div) > 0x7f) | |
| { | |
| i86_intr_raise(m,0); | |
| return; | |
| } | |
| /* Undef --- Can't hurt */ | |
| CONDITIONAL_SET_FLAG(div&0x80,m,F_SF); | |
| CONDITIONAL_SET_FLAG(div==0,m,F_ZF); | |
| m->R_AL = (int8)div; | |
| m->R_AH = (int8)mod; | |
| } | |
| void idiv_word(PC_ENV *m, uint16 s) | |
| { | |
| int32 dvd,dvs,div,mod; | |
| dvd = m->R_DX; | |
| dvd = (dvd << 16) | m->R_AX; | |
| if (s == 0) | |
| { | |
| i86_intr_raise(m,0); | |
| return; | |
| } | |
| dvs = (int16)s; | |
| div = dvd / dvs; | |
| mod = dvd % dvs; | |
| if (abs(div) > 0x7fff) | |
| { | |
| i86_intr_raise(m,0); | |
| return; | |
| } | |
| /* Undef --- Can't hurt */ | |
| CONDITIONAL_SET_FLAG(div&0x8000,m,F_SF); | |
| CONDITIONAL_SET_FLAG(div==0,m,F_ZF); | |
| /* debug_printf(m, "\n%d/%d=%d,%d\n",dvd,dvs,div,mod); */ | |
| m->R_AX = div; | |
| m->R_DX = mod; | |
| } | |
| void div_byte(PC_ENV *m, uint8 s) | |
| { | |
| uint32 dvd,dvs,div,mod; | |
| dvs = s; | |
| dvd = m->R_AX; | |
| if (s == 0) | |
| { | |
| i86_intr_raise(m,0); | |
| return; | |
| } | |
| div = dvd / dvs; | |
| mod = dvd % dvs; | |
| if (abs(div) > 0xff) | |
| { | |
| i86_intr_raise(m,0); | |
| return; | |
| } | |
| /* Undef --- Can't hurt */ | |
| CLEAR_FLAG(m,F_SF); | |
| CONDITIONAL_SET_FLAG(div==0,m,F_ZF); | |
| m->R_AL = (uint8)div; | |
| m->R_AH = (uint8)mod; | |
| } | |
| void div_word(PC_ENV *m, uint16 s) | |
| { | |
| uint32 dvd,dvs,div,mod; | |
| dvd = m->R_DX; | |
| dvd = (dvd << 16) | m->R_AX; | |
| dvs = s; | |
| if (dvs == 0) | |
| { | |
| i86_intr_raise(m,0); | |
| return; | |
| } | |
| div = dvd / dvs; | |
| mod = dvd % dvs; | |
| /* printf("dvd=%x dvs=%x -> div=%x mod=%x\n",dvd, dvs,div, mod);*/ | |
| if (abs(div) > 0xffff) | |
| { | |
| i86_intr_raise(m,0); | |
| return; | |
| } | |
| /* Undef --- Can't hurt */ | |
| CLEAR_FLAG(m,F_SF); | |
| CONDITIONAL_SET_FLAG(div==0,m,F_ZF); | |
| m->R_AX = div; | |
| m->R_DX = mod; | |
| } |