unit load_store;

interface

uses fpu_types;

const
  _NONE_ = 0;   (* st0_ptr etc not needed *)

const
  _REG0_ = 1;   (* Will be storing st(0) *)

const
  _PUSH_ = 3;   (* Need to check for space to push onto stack *)

const
  _null_ = 4;   (* Function illegal or not implemented *)


const
  type_table: array[0..31] of u_char = (
    _PUSH_, _PUSH_, _PUSH_, _PUSH_,
    _null_, _null_, _null_, _null_,
    _REG0_, _REG0_, _REG0_, _REG0_,
    _REG0_, _REG0_, _REG0_, _REG0_,
    _NONE_, _null_, _NONE_, _PUSH_,
    _NONE_, _PUSH_, _null_, _PUSH_,
    _NONE_, _null_, _NONE_, _REG0_,
    _NONE_, _REG0_, _NONE_, _REG0_
    );

const
  data_sizes_16: array[0..31] of u_char = (
    4, 4, 8, 2, 0, 0, 0, 0,
    4, 4, 8, 2, 4, 4, 8, 2,
    14, 0, 94, 10, 2, 10, 0, 8,
    14, 0, 94, 10, 2, 10, 2, 8);

const
  data_sizes_32: array[0..31] of u_char = (
    4, 4, 8, 2, 0, 0, 0, 0,
    4, 4, 8, 2, 4, 4, 8, 2,
    28, 0, 108, 10, 2, 10, 0, 8,
    28, 0, 108, 10, 2, 10, 2, 8);

function FPU_load_store (type_: u_char; addr_modes: fpu_addr_modes;
  data_address: pointer): intg;

{$include fpu_const.pas}

implementation

uses fpu_system, wmfpuemu_Glue, errors, Service_fpu, fpu_tags, reg_ld_str;

function FPU_load_store (type_: u_char; addr_modes: fpu_addr_modes;
  data_address: pointer): intg;
  var
    loaded_data: FPU_REG;
    st0_ptr: PFPU_REG;
    st0_tag: u_char;  (* This is just to stop a gcc warning. *)
    loaded_tag: u_char;
  begin
    st0_tag := TAG_Empty;  (* This is just to stop a gcc warning. *)

    st0_ptr := 0;    (* Initialized just to stop compiler warnings. *)

    if (addr_modes.default_mode and PROTECTED_) <> 0 then
      begin
      if (addr_modes.default_mode = SEG32) then
        begin
        if (I387.soft.alimit < data_sizes_32[type_]) then
          begin
          math_abort(nil, 0);
          exit;
          end;
        end
      else
        if (addr_modes.default_mode = PM16) then
          begin
          if (I387.soft.alimit < data_sizes_16[type_]) then
            begin
            math_abort(nil, 0);
            exit;
            end;
          end;
{$ifdef PARANOID}
      else
	EXCEPTION(EX_INTERNAL|$140);
{$endif}(* PARANOID *)
      end;

    case (type_table[type_]) of
      _NONE_:
        begin
        end;
      _REG0_:
        begin
        st0_ptr := st(0);       (* Some of these instructions pop after
         storing *)
        st0_tag := FPU_gettag0();
        end;
      _PUSH_:
        begin
        if (FPU_gettagi(-1) <> TAG_Empty) then
          begin
          FPU_stack_overflow();
          Result := 0;
          exit;
          end;
        Dec(I387.soft.ftop);
        st0_ptr := st(0);
        end;
      _null_:
        begin
        FPU_illegal();
        Result := 0;
        exit;
        end;
      end;

    case (type_) of
      000:       (* fld m32real *)
        begin
        clear_C1();
        loaded_tag := FPU_load_single(data_address, @loaded_data);
        if ((loaded_tag = TAG_Special) and (isNaN(@loaded_data) <> 0) and
          (real_1op_NaN(@loaded_data) < 0)) then
          begin
          Inc(I387.soft.ftop);
          exit;
          end;
        FPU_copy_to_reg0(@loaded_data, loaded_tag);
        end;
      001:      (* fild m32int *)
        begin
        clear_C1();
        loaded_tag := FPU_load_int32(ps32(data_address), @loaded_data);
        FPU_copy_to_reg0(@loaded_data, loaded_tag);
        end;
      002:      (* fld m64real *)
        begin
        clear_C1();
        loaded_tag := FPU_load_double(data_address, @loaded_data);
        if ((loaded_tag = TAG_Special) and (isNaN(@loaded_data) <> 0) and
          (real_1op_NaN(@loaded_data) < 0)) then
          begin
          Inc(I387.soft.ftop);
          exit;
          end;
        FPU_copy_to_reg0(@loaded_data, loaded_tag);
        end;
      003:      (* fild m16int *)
        begin
        clear_C1();
        loaded_tag := FPU_load_int16(ps16(data_address), @loaded_data);
        FPU_copy_to_reg0(@loaded_data, loaded_tag);
        end;
      8:      (* fst m32real *)
        begin
        clear_C1();
        FPU_store_single(st0_ptr, st0_tag, psingle(data_address));
        end;
      9:      (* fist m32int *)
        begin
        clear_C1();
        FPU_store_int32(st0_ptr, st0_tag, ps32(data_address));
        end;
      10:     (* fst m64real *)
        begin
        clear_C1();
        FPU_store_double(st0_ptr, st0_tag, pdouble(data_address));
        end;
      11:     (* fist m16int *)
        begin
        clear_C1();
        FPU_store_int16(st0_ptr, st0_tag, ps16(data_address));
        end;
      12:     (* fstp m32real *)
        begin
        clear_C1();
        if (FPU_store_single(st0_ptr, st0_tag, psingle(data_address))) <> 0 then
          pop_0();  (* pop only if the number was actually stored
         (see the 80486 manual p16-28) *)
        end;
      13:     (* fistp m32int *)
        begin
        clear_C1();
        if (FPU_store_int32(st0_ptr, st0_tag, ps32(data_address))) <> 0 then
          pop_0();  (* pop only if the number was actually stored
         (see the 80486 manual p16-28) *)
        end;
      14:     (* fstp m64real *)
        begin
        clear_C1();
        if (FPU_store_double(st0_ptr, st0_tag, pdouble(data_address)) <> 0) then
          pop_0();  (* pop only if the number was actually stored
         (see the 80486 manual p16-28) *)
        end;
      15:     (* fistp m16int *)
        begin
        clear_C1();
        if (FPU_store_int16(st0_ptr, st0_tag, ps16(data_address)) <> 0) then
          pop_0();  (* pop only if the number was actually stored
         (see the 80486 manual p16-28) *)
        end;
      16:     (* fldenv  m14/28byte *)
        begin
        fldenv(addr_modes, pu_char(data_address));
      (* Ensure that the values just loaded are not changed by
   fix-up operations. *)
        Result := 1;
        exit;
        end;
      18:     (* frstor m94/108byte *)
        begin
        frstor(addr_modes, pu_char(data_address));
      (* Ensure that the values just loaded are not changed by
   fix-up operations. *)
        Result := 1;
        exit;
        end;
      19:     (* fbld m80dec *)
        begin
        clear_C1();
        loaded_tag := FPU_load_bcd(pu_char(data_address));
        FPU_settag0(loaded_tag);
        end;
      20:     (* fldcw *)
        begin
        //RE_ENTRANT_CHECK_OFF;
        FPU_verify_area(VERIFY_READ, data_address, 2);
        I387.soft.cwd := FPU_get_user(pu16(data_address), sizeof(u16));
        //RE_ENTRANT_CHECK_ON;
        if ((I387.soft.swd <> 0) and ((not I387.soft.cwd) <> 0) and
          (CW_Exceptions <> 0)) then
          I387.soft.swd := I387.soft.swd or (SW_Summary or SW_Backward)
        else
          I387.soft.swd := I387.soft.swd and not (SW_Summary or SW_Backward);
{$ifdef PECULIAR_486}
      control_word :=      control_word or $40;  (* An 80486 appears to always set this bit *)
{$endif}(* PECULIAR_486 *)
        Result := 1;
        exit;
        end;
      21:      (* fld m80real *)
        begin
        clear_C1();
        loaded_tag := FPU_load_extended(pextended(data_address)^, 0);
        FPU_settag0(loaded_tag);
        end;
      23:      (* fild m64int *)
        begin
        clear_C1();
        loaded_tag := FPU_load_int64(ps64(data_address));
        FPU_settag0(loaded_tag);
        end;
      24:     (* fstenv  m14/28byte *)
        begin
        fstenv(addr_modes, pu_char(data_address));
        Result := 1;
        exit;
        end;
      26:      (* fsave *)
        begin
        fsave(addr_modes, pu_char(data_address));
        Result := 1;
        exit;
        end;
      27:      (* fbstp m80dec *)
        begin
        clear_C1();
        if (FPU_store_bcd(st0_ptr, st0_tag, pu_char(data_address)^) <> 0) then
          pop_0();  (* pop only if the number was actually stored
         (see the 80486 manual p16-28) *)
        end;
      28:      (* fstcw m16int *)
        begin
        //RE_ENTRANT_CHECK_OFF;
        FPU_verify_area(VERIFY_WRITE, data_address, 2);
        FPU_put_user(I387.soft.cwd, pu16(data_address), sizeof(u16));
        //RE_ENTRANT_CHECK_ON;
        Result := 1;
        exit;
        end;
      29:      (* fstp m80real *)
        begin
        clear_C1();
        if (FPU_store_extended(st0_ptr, st0_tag, pextended(data_address))) <> 0 then
          pop_0();  (* pop only if the number was actually stored
         (see the 80486 manual p16-28) *)
        end;
      30:      (* fstsw m2byte *)
        begin
        //RE_ENTRANT_CHECK_OFF;
        FPU_verify_area(VERIFY_WRITE, data_address, 2);
        FPU_put_user(status_word(), pu16(data_address), sizeof(u16));
        //RE_ENTRANT_CHECK_ON;
        Result := 1;
        end;
      31:      (* fistp m64int *)
        begin
        clear_C1();
        if (FPU_store_int64(st0_ptr, st0_tag, ps64(data_address)^)) <> 0 then
          pop_0();  (* pop only if the number was actually stored
         (see the 80486 manual p16-28) *)
        end;
      end;
    Result := 0;
  end;

end.
