home *** CD-ROM | disk | FTP | other *** search
/ Il CD di internet / CD.iso / SOURCE / KERNEL-S / V1.0 / LINUX-1.0 / LINUX-1 / linux / drivers / FPU-emu / get_address.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-02-16  |  8.0 KB  |  317 lines

  1. /*---------------------------------------------------------------------------+
  2.  |  get_address.c                                                            |
  3.  |                                                                           |
  4.  | Get the effective address from an FPU instruction.                        |
  5.  |                                                                           |
  6.  | Copyright (C) 1992,1993,1994                                              |
  7.  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
  8.  |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
  9.  |                                                                           |
  10.  |                                                                           |
  11.  +---------------------------------------------------------------------------*/
  12.  
  13. /*---------------------------------------------------------------------------+
  14.  | Note:                                                                     |
  15.  |    The file contains code which accesses user memory.                     |
  16.  |    Emulator static data may change when user memory is accessed, due to   |
  17.  |    other processes using the emulator while swapping is in progress.      |
  18.  +---------------------------------------------------------------------------*/
  19.  
  20.  
  21. #include <linux/stddef.h>
  22.  
  23. #include <asm/segment.h>
  24.  
  25. #include "fpu_system.h"
  26. #include "exception.h"
  27. #include "fpu_emu.h"
  28.  
  29. static int reg_offset[] = {
  30.     offsetof(struct info,___eax),
  31.     offsetof(struct info,___ecx),
  32.     offsetof(struct info,___edx),
  33.     offsetof(struct info,___ebx),
  34.     offsetof(struct info,___esp),
  35.     offsetof(struct info,___ebp),
  36.     offsetof(struct info,___esi),
  37.     offsetof(struct info,___edi)
  38. };
  39.  
  40. #define REG_(x) (*(long *)(reg_offset[(x)]+(char *) FPU_info))
  41.  
  42. static int reg_offset_vm86[] = {
  43.     offsetof(struct info,___cs),
  44.     offsetof(struct info,___vm86_ds),
  45.     offsetof(struct info,___vm86_es),
  46.     offsetof(struct info,___vm86_fs),
  47.     offsetof(struct info,___vm86_gs),
  48.     offsetof(struct info,___ss)
  49.       };
  50.  
  51. #define VM86_REG_(x) (*(unsigned short *) \
  52.               (reg_offset_vm86[((unsigned)x)]+(char *) FPU_info))
  53.  
  54.  
  55. /* Decode the SIB byte. This function assumes mod != 0 */
  56. static void *sib(int mod, unsigned long *fpu_eip)
  57. {
  58.   unsigned char ss,index,base;
  59.   long offset;
  60.  
  61.   RE_ENTRANT_CHECK_OFF;
  62.   FPU_code_verify_area(1);
  63.   base = get_fs_byte((char *) (*fpu_eip));   /* The SIB byte */
  64.   RE_ENTRANT_CHECK_ON;
  65.   (*fpu_eip)++;
  66.   ss = base >> 6;
  67.   index = (base >> 3) & 7;
  68.   base &= 7;
  69.  
  70.   if ((mod == 0) && (base == 5))
  71.     offset = 0;              /* No base register */
  72.   else
  73.     offset = REG_(base);
  74.  
  75.   if (index == 4)
  76.     {
  77.       /* No index register */
  78.       /* A non-zero ss is illegal */
  79.       if ( ss )
  80.     EXCEPTION(EX_Invalid);
  81.     }
  82.   else
  83.     {
  84.       offset += (REG_(index)) << ss;
  85.     }
  86.  
  87.   if (mod == 1)
  88.     {
  89.       /* 8 bit signed displacement */
  90.       RE_ENTRANT_CHECK_OFF;
  91.       FPU_code_verify_area(1);
  92.       offset += (signed char) get_fs_byte((char *) (*fpu_eip));
  93.       RE_ENTRANT_CHECK_ON;
  94.       (*fpu_eip)++;
  95.     }
  96.   else if (mod == 2 || base == 5) /* The second condition also has mod==0 */
  97.     {
  98.       /* 32 bit displacment */
  99.       RE_ENTRANT_CHECK_OFF;
  100.       FPU_code_verify_area(4);
  101.       offset += (signed) get_fs_long((unsigned long *) (*fpu_eip));
  102.       RE_ENTRANT_CHECK_ON;
  103.       (*fpu_eip) += 4;
  104.     }
  105.  
  106.   return (void *) offset;
  107. }
  108.  
  109.  
  110. static unsigned long vm86_segment(unsigned char segment)
  111.   segment--;
  112. #ifdef PARANOID
  113.   if ( segment > PREFIX_SS_ )
  114.     {
  115.       EXCEPTION(EX_INTERNAL|0x130);
  116.       math_abort(FPU_info,SIGSEGV);
  117.     }
  118. #endif PARANOID
  119.   return (unsigned long)VM86_REG_(segment) << 4;
  120. }
  121.  
  122.  
  123. /*
  124.        MOD R/M byte:  MOD == 3 has a special use for the FPU
  125.                       SIB byte used iff R/M = 100b
  126.  
  127.        7   6   5   4   3   2   1   0
  128.        .....   .........   .........
  129.         MOD    OPCODE(2)     R/M
  130.  
  131.  
  132.        SIB byte
  133.  
  134.        7   6   5   4   3   2   1   0
  135.        .....   .........   .........
  136.         SS      INDEX        BASE
  137.  
  138. */
  139.  
  140. void get_address(unsigned char FPU_modrm, unsigned long *fpu_eip,
  141.          fpu_addr_modes addr_modes)
  142. {
  143.   unsigned char mod;
  144.   long *cpu_reg_ptr;
  145.   int offset = 0;     /* Initialized just to stop compiler warnings. */
  146.  
  147. #ifndef PECULIAR_486
  148.   /* This is a reasonable place to do this */
  149.   FPU_data_selector = FPU_DS;
  150. #endif PECULIAR_486
  151.  
  152.   /* Memory accessed via the cs selector is write protected
  153.      in 32 bit protected mode. */
  154. #define FPU_WRITE_BIT 0x10
  155.   if ( !addr_modes.vm86 && (FPU_modrm & FPU_WRITE_BIT)
  156.       && (addr_modes.override.segment == PREFIX_CS_) )
  157.     {
  158.       math_abort(FPU_info,SIGSEGV);
  159.     }
  160.  
  161.   mod = (FPU_modrm >> 6) & 3;
  162.  
  163.   if (FPU_rm == 4 && mod != 3)
  164.     {
  165.       FPU_data_address = sib(mod, fpu_eip);
  166.       return;
  167.     }
  168.  
  169.   cpu_reg_ptr = & REG_(FPU_rm);
  170.   switch (mod)
  171.     {
  172.     case 0:
  173.       if (FPU_rm == 5)
  174.     {
  175.       /* Special case: disp32 */
  176.       RE_ENTRANT_CHECK_OFF;
  177.       FPU_code_verify_area(4);
  178.       offset = get_fs_long((unsigned long *) (*fpu_eip));
  179.       (*fpu_eip) += 4;
  180.       RE_ENTRANT_CHECK_ON;
  181.       FPU_data_address = (void *) offset;
  182.       return;
  183.     }
  184.       else
  185.     {
  186.       FPU_data_address = (void *)*cpu_reg_ptr;  /* Just return the contents
  187.                            of the cpu register */
  188.       return;
  189.     }
  190.     case 1:
  191.       /* 8 bit signed displacement */
  192.       RE_ENTRANT_CHECK_OFF;
  193.       FPU_code_verify_area(1);
  194.       offset = (signed char) get_fs_byte((char *) (*fpu_eip));
  195.       RE_ENTRANT_CHECK_ON;
  196.       (*fpu_eip)++;
  197.       break;
  198.     case 2:
  199.       /* 32 bit displacement */
  200.       RE_ENTRANT_CHECK_OFF;
  201.       FPU_code_verify_area(4);
  202.       offset = (signed) get_fs_long((unsigned long *) (*fpu_eip));
  203.       (*fpu_eip) += 4;
  204.       RE_ENTRANT_CHECK_ON;
  205.       break;
  206.     case 3:
  207.       /* Not legal for the FPU */
  208.       EXCEPTION(EX_Invalid);
  209.     }
  210.  
  211.   if ( addr_modes.vm86 )
  212.     {
  213.       offset += vm86_segment(addr_modes.override.segment);
  214.     }
  215.  
  216.   FPU_data_address = offset + (char *)*cpu_reg_ptr;
  217. }
  218.  
  219.  
  220. void get_address_16(unsigned char FPU_modrm, unsigned long *fpu_eip,
  221.               fpu_addr_modes addr_modes)
  222. {
  223.   unsigned char mod;
  224.   int offset = 0;     /* Default used for mod == 0 */
  225.  
  226. #ifndef PECULIAR_486
  227.   /* This is a reasonable place to do this */
  228.   FPU_data_selector = FPU_DS;
  229. #endif PECULIAR_486
  230.  
  231.   /* Memory accessed via the cs selector is write protected
  232.      in 32 bit protected mode. */
  233. #define FPU_WRITE_BIT 0x10
  234.   if ( !addr_modes.vm86 && (FPU_modrm & FPU_WRITE_BIT)
  235.       && (addr_modes.override.segment == PREFIX_CS_) )
  236.     {
  237.       math_abort(FPU_info,SIGSEGV);
  238.     }
  239.  
  240.   mod = (FPU_modrm >> 6) & 3;
  241.  
  242.   switch (mod)
  243.     {
  244.     case 0:
  245.       if (FPU_rm == 6)
  246.     {
  247.       /* Special case: disp16 */
  248.       RE_ENTRANT_CHECK_OFF;
  249.       FPU_code_verify_area(2);
  250.       offset = (unsigned short)get_fs_word((unsigned short *) (*fpu_eip));
  251.       (*fpu_eip) += 2;
  252.       RE_ENTRANT_CHECK_ON;
  253.       goto add_segment;
  254.     }
  255.       break;
  256.     case 1:
  257.       /* 8 bit signed displacement */
  258.       RE_ENTRANT_CHECK_OFF;
  259.       FPU_code_verify_area(1);
  260.       offset = (signed char) get_fs_byte((signed char *) (*fpu_eip));
  261.       RE_ENTRANT_CHECK_ON;
  262.       (*fpu_eip)++;
  263.       break;
  264.     case 2:
  265.       /* 16 bit displacement */
  266.       RE_ENTRANT_CHECK_OFF;
  267.       FPU_code_verify_area(2);
  268.       offset = (unsigned) get_fs_word((unsigned short *) (*fpu_eip));
  269.       (*fpu_eip) += 2;
  270.       RE_ENTRANT_CHECK_ON;
  271.       break;
  272.     case 3:
  273.       /* Not legal for the FPU */
  274.       EXCEPTION(EX_Invalid);
  275.       break;
  276.     }
  277.   switch ( FPU_rm )
  278.     {
  279.     case 0:
  280.       offset += FPU_info->___ebx + FPU_info->___esi;
  281.       break;
  282.     case 1:
  283.       offset += FPU_info->___ebx + FPU_info->___edi;
  284.       break;
  285.     case 2:
  286.       offset += FPU_info->___ebp + FPU_info->___esi;
  287.       break;
  288.     case 3:
  289.       offset += FPU_info->___ebp + FPU_info->___edi;
  290.       break;
  291.     case 4:
  292.       offset += FPU_info->___esi;
  293.       break;
  294.     case 5:
  295.       offset += FPU_info->___edi;
  296.       break;
  297.     case 6:
  298.       offset += FPU_info->___ebp;
  299.       break;
  300.     case 7:
  301.       offset += FPU_info->___ebx;
  302.       break;
  303.     }
  304.  
  305.  add_segment:
  306.   offset &= 0xffff;
  307.  
  308.   if ( addr_modes.vm86 )
  309.     {
  310.       offset += vm86_segment(addr_modes.override.segment);
  311.     }
  312.  
  313.   FPU_data_address = (void *)offset ;
  314. }
  315.  
  316.