{
    $Id$
    This file is part of the Free Pascal run time library.
    Copyright (c) 1993,97 by Carl-Eric Codere,
    member of the Free Pascal development team.

    See the file COPYING.FPC, included in this distribution,
    for details about the copyright.

    This program 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.

 **********************************************************************}
{****************************************************************************

      m68k.inc : Processor dependent implementation of system unit
                 For Motorola 680x0 Processor.
 
*****************************************************************************}

{****************************************************************************}
{     This include file contains as little assembler as possible, to make    }
{     porting to other systems easier.                                       }
{   Port to the Motorola 680x0 compiler by:                                  }
{                                                                            }
{     Carl-Eric Codere - port of non-system specific stuff.                  }
{                                                                            }
{  Some routines taken from the Atari freeware dlib source code, created by: }
{     Dale Schumacher                         399 Beacon Ave.                }
{     (alias: Dalnefre')                      St. Paul, MN  55104            }
{     dal@syntel.UUCP                         United States of America       }
{  Some routines taken from the freeware Atari Sozobon C compiler, created by:}
{      1988 by Sozobon, Limited.  Author: Johann Ruegg (freeware)             }
{  Thanks to all these people wherever they maybe today!                     }
{      BUGS in sqr and abs for return values. Only longint seems supported.  }
{                                                                            }
{  Still left to do:                                                         }
{     mod_rr routine to convert to pascal format.                            }
{                                                                            }
{   ALL routines in set.inc, system.inc and real2str.inc are system independant.}
{****************************************************************************}


    { Don't call the following routines directly. }
 Procedure Hlt;[public,alias: 'HALT_ERROR'];
 { called by code generator on run-time errors. }
 { on entry contains d0 = error code.           }
 var
  b:byte; { only byte is used... }
 begin
  asm
     move.b d0,b
  end;
     RunError(b);
 end;


   Procedure FillChar(var x; count: longint; value: byte);[alias: 'L_FILL_OBJECT'];
   begin
     asm
      move.l 8(a6), a0      { destination                   }
      move.l 12(a6), d1     { number of bytes to fill       }
      move.w 16(a6),d0      { fill data                     }
      cmpi.l #65535, d1     { check, if this is a word move }
      ble    @LMEMSET3      { use fast dbra mode            }
      bra @LMEMSET2
    @LMEMSET1:
      move.b d0,(a0)+
    @LMEMSET2:
      subq.l #1,d1
      cmp.b #-1,d1
      bne  @LMEMSET1
      bra @LMEMSET5        { finished slow mode , exit     }

    @LMEMSET4:             { fast loop mode section 68010+ }
      move.b d0,(a0)+
    @LMEMSET3:
      dbra d1,@LMEMSET4

    @LMEMSET5:
     end ['d0','d1','a0'];
   end;

   Procedure FillObject(var x; count: longint; value: byte);
   begin
     asm
      move.l 8(a6), a0      { destination                   }
      move.l 12(a6), d1     { number of bytes to fill       }
      move.w 16(a6),d0      { fill data                     }
      cmp.l  #65535, d1     { check, if this is a word move }
      ble    @LMEMSET3      { use fast dbra mode            }
      bra @LMEMSET2
    @LMEMSET1:
      move.b d0,(a0)+
    @LMEMSET2:
      subq.l #1,d1
      cmp.b #-1,d1
      bne  @LMEMSET1
      bra @LMEMSET5        { finished slow mode , exit     }

    @LMEMSET4:             { fast loop mode section 68010+ }
      move.b d0,(a0)+
    @LMEMSET3:
      dbra d1,@LMEMSET4

    @LMEMSET5:
     end ['d0','d1','a0'];
   end;

    procedure int_help_constructor;

      begin
         asm
          XDEF HELP_CONSTRUCTOR
            { Entry without preamble, since we need the ESP of the
              constructor }
            { Stack (relative to %ebp):
                12 Self
                8 VMT-Address
                4 main programm-Addr
                0 %ebp
            }
            { initialise self ? }
            move.l a5,d0
            tst.l  d0      { set flags }
            bne @LHC_4
            { get memory, but save register first }
            { temporary variable }
            subq.l #4,sp
            move.l sp,a5
            { Save Registers }
            movem.l d0-a7,-(sp)
            { Memory size }
            move.l 8(a6),a0
            move.l (a0),-(sp)
            { push method pointer }
            move.l a5,-(sp)
            jsr GETMEM
            { Restore all registers in the correct order }
            movem.l (sp)+,d0-a7
            { Memory size to a5 }
            move.l (a5),a5
            add.l  #4,sp
            { If no memory available : fail() }
            move.l a5,d0
            tst.l  d0         { set flags for a5 }
            beq @LHC_5
            { init self for the constructor }
            move.l a5,12(a6)
         @LHC_4:
            { is there a VMT address ? }
            move.l 8(a6),d0
            or.l d0,d0
            bne @LHC_7
            { In case the constructor doesn't do anything, the Zero-Flag }
            { can't be put, because this calls Fail() }
            add.l #1,d0
            rts
         @LHC_7:
            { set zero inside the object }
            { Save Registers }
            movem.l d0-a7,-(sp)
            move.w #0,-(sp)

            move.l 8(a6),a0
            move.l (a0),-(sp)
            move.l a5,-(sp)
            {                }
            jsr  FILLOBJECT
            { Restore all registers in the correct order }
            movem.l (sp)+,d0-a7
            { set the VMT address for the new created object }
            move.l 8(a6),d0
            move.l d0,(a5)
            or.l d0,d0
         @LHC_5:
            rts
         end;
      end;

    procedure help_fail;

      begin
         asm
         end;
      end;

    procedure int_help_destructor;

      begin
         asm
            { Stack (relative to %ebp):
                12 Self
                8 VMT-Address
                4 Main program-Addr
                0 %ebp
            }
            { temporary Variable }
          XDEF HELP_DESTRUCTOR
            subq.l #4,sp
            move.l sp,d6
            { Save Registers }
            movem.l d0-a7,-(sp)
            { Should the object be resolved ? }
            move.l 8(a6),d0
            or.l d0,d0
            beq @LHD_3
            { Yes, get size from SELF! }
            move.l 12(a6),a0
            { get VMT-pointer (from Self) to %ebx }
            move.l (a0),a1
            { And put size on the Stack }
            move.l (a1),-(sp)
            { SELF }
            { I think for precaution }
            { that we should clear the VMT here }
            clr.l (a0)
            { get address of local variable into  }
            { address register                    }
            move.l d6,a1
            move.l a0,(a1)
            move.l a1,-(sp)
            jsr FREEMEM
         @LHD_3:
            { Restore all registers in the correct order }
            movem.l (sp)+,d0-a7
            add.l #4,sp
            rts
         end;
      end;

  procedure new_class;assembler;

  asm
     XDEF NEW_CLASS
     { create class ? }
     move.l 8(a6), d0
     tst.l  d0
     { check for nil... }
     beq    @NEW_CLASS1

     { a5 contains vmt }
     move.l a5,-(sp)
     { call newinstance (class method!) }
     jsr 16(a5)
     { new instance returns a pointer to the new created }
     { instance in d0                                    }
     { load a5  and insert self                          }
     move.l d0,8(a6)
     move.l d0,a5
     bra    @end
  @NEW_CLASS1:
     move.l a5,8(a6)
  @end:
  end;



  procedure dispose_class;assembler;

  asm
    XDEF DISPOSE_CLASS
     { destroy class ? }
     move.l 8(a6),d0
     { save self }
     move.l a5,8(a6)
     tst.l  d0
     beq    @DISPOSE_CLASS
     { no inherited call }
     move.l (a5),d0
     { push self }
     move.l a5,-(sp)
     { call freeinstance }
     move.l d0,a0
     jsr    20(a0)
  @DISPOSE_CLASS:
     { load self }
     move.l 8(a6),a5
  end;

  { checks for a correct vmt pointer }
  procedure co;assembler;
   { Entry code: On Stack --> pointer to check }
  asm
   XDEF CHECK_OBJECT
     { save important registers }
     movem.l  a0/d0,-(sp)

     { check if pointer is nil.... before trying to access it }
     move.l   8(sp),a0
     { z flag set by move }
     beq      @co_re      
   
     move.l   (a0),d0
     add.l    4(a0),d0
     bne      @co_re
     { restore registers }
     movem.l  (sp)+,a0/d0
     bra      @end
@co_re:
     { restore registers }
     movem.l  (sp)+,a0/d0
     move.b   #210,d0
     jsr      HALT_ERROR
@end:
  end;



    function get_addr(BP : longint) : longint;
      begin
         asm
            move.l BP,a0
            cmp.l #0,a0
            beq @Lnul_address
            move.l 4(a0),a0
         @Lnul_address:
            move.l a0,@RESULT
         end ['a0'];
      end;

    function get_next_frame(bp : longint) : longint;

      begin
         asm
            move.l bp,a0
            cmp.l  #0,a0
            beq    @Lnul_frame
            move.l (a0),a0
         @Lnul_frame:
            move.l a0,@RESULT
         end ['a0'];
      end;

    procedure runerror(w : word);

      function get_addr : longint;

        begin
           asm
              move.l (a6),a0
              move.l 4(a0),a0
              move.l a0,@RESULT
           end ['a0'];
        end;

      function get_error_bp : longint;

        begin
           asm
              { get base pointer of error }
              move.l (a6),d0
              move.l d0,@RESULT
           end ['d0'];
        end;

      begin
         errorcode:=w;
         exitcode:=w;
         erroraddr:=pointer(get_addr);
         DoError:=True;
         ErrorBase:=get_error_bp;
         halt(errorcode);
      end;

    procedure io1(addr : longint);[public,alias: 'IOCHECK'];

      var
         l : longint;

      begin
         { Since IOCHECK is called directly and only later the optimiser }
         { Maybe also save global registers  }
         asm
            movem.l d0-a7,-(sp)
         end;
         l:=ioresult;
         if l<>0 then
           begin
              writeln('IO-Error ',l,' at ',addr);
              halt(byte(l));
           end;
         asm
            { the register are put back in the correct order }
            movem.l (sp)+,d0-a7
         end;
      end;

    procedure re_overflow;[public,alias: 'RE_OVERFLOW'];

      var
         addr : longint;

      begin
         { Overflow was shortly before the return address }
         asm
            move.l 4(a6),d0
            move.l d0,addr
         end;
         writeln('Overflow at ',addr);
         RunError(215);
      end;

    { Copies Strings }
    procedure strcopy(dstr,sstr : pointer;len : longint);[public,alias: 'STRCOPY'];

      begin
         asm
            move.l 12(a6),a0
            move.l 16(a6),a1     { Load pointer to strings }
            move.l 8(a6),d1      { Get length              }

            move.b (a0)+,d0     { Get source length }
            cmp.b  d1,d0
            ble    @LM4
            move.b d1,d0        { If longer than maximum size of target, cut
                                  source length }
         @LM4:
            andi.l #$ff,d0     { zero extend d0-byte }
            move.l d0,d1       { save length to copy }
            move.b d0,(a1)+    { save new length     }
            bra  @LMSTRCOPY55
         @LMSTRCOPY56:
            move.b (a0)+,(a1)+
         @LMSTRCOPY55:
            dbra  d1,@LMSTRCOPY56
         end;
      end;

    { Concatenate Strings }
    procedure strconcat(s1,s2 : pointer);[public,alias: 'STRCONCAT'];

      begin
         asm
          move.b  #255,d0
          move.l  s1,a0
          move.l  s1,a1
          sub.b   (a0),d0              {  copyl:= 255 -length(s1)    }
          move.b  (a1),d6
          cmp.b   d0,d6                { if copyl > length(s2) then  }
          ble     @Lcontinue
          move.b  (a1),d0              {  copyl:=length(s2)          }
    @Lcontinue:
          move.b  (a0),d6
          and.l   #$ff,d6
          lea     1(a0,d6),a0          { s1[length(s1)+1]            }
          add.l   #1,a1                { s2[1]                       }
          move.b  d0,d6
    @Loop:
          move.b  (a1)+,(a0)+          { s1[i] := s2[i];             }
          dbra    d6,@Loop
          move.l  s1,a0
          add.b   d0,(a0)              { change to new string length }
         end ['d0','d1','a0','a1','d6'];
      end;

    { Compares strings }
    procedure strcmp(dstr,sstr : pointer);[public,alias: 'STRCMP'];

      begin
         asm
            move.l 12(a6),a0
            move.l 8(a6),a1     { Get address of both strings }

            move.b (a0)+,d0     { Get length of first string  }
            move.b (a1)+,d6     { Get length of 2nd string    }

            move.b d0,d1
            cmp.b  d6,d1        { Get shortest string length }
            ble    @LSTRCONCAT1
            move.b  d6,d1

         @LSTRCONCAT1:
            tst.b   d1          { Both strings have a length of zero, exit }
            beq    @LSTRCONCAT2
            andi.l  #$ff,d6

            or.l    d1,d1      { Make sure to set Zerfo flag to 0          }
            bra    @LSTRCONCAT4
         @LSTRCONCAT5:
            { Workaroung for GAS v.134 bug }
            {  old: cmp.b (a1)+,(a0)+      }
            cmpm.b  (a1)+,(a0)+
         @LSTRCONCAT4:
            dbne    d6,@LSTRCONCAT5   { Repeat until not equal }
            bne     @LSTRCONCAT3
          @LSTRCONCAT2:
            cmp.b   d6,d0   { Compare length - set flag if equal length strings }
         @LSTRCONCAT3:
         end;
      end;


  Function strpas(p: pchar): string;
  { only 255 first characters are actually copied. }
   var
    counter : byte;
    str: string;
  Begin
     counter := 0;
     str := '';
     while (ord(p[counter]) <> 0) and (counter < 255) do
     begin
        counter:=counter+1;
        str[counter] := char(p[counter-1]);
     end;
     str[0] := char(counter);
     strpas := str;
  end;

  function strlen(p : pchar) : longint;
  var
     counter : longint;
  Begin
       counter := 0;
       repeat
          counter:=counter+1;
       until ord(p[counter]) = 0;
       strlen := counter;
  end;


   procedure move(var source;var dest;count : longint);
   { base pointer+8 = source                  }
   { base pointer+12 = destination            }
   { base pointer+16 = number of bytes to move}
   begin
     asm
      clr.l      d0
      move.l   16(a6),d0   {  number of bytes }
    @LMOVE0:
      move.l   12(a6),a1   {  destination          }
      move.l   8(a6),a0      {  source               }

      cmpi.l #65535, d0     { check, if this is a word move }
      ble    @LMEMSET00     { use fast dbra mode 68010+     }

      cmp.l      a0,a1         {  check copy direction }
      bls      @LMOVE4
      add.l      d0,a0         { move pointers to end  }
      add.l      d0,a1
      bra     @LMOVE2
    @LMOVE1:
      move.b   -(a0),-(a1)   {  (s < d) copy loop }
    @LMOVE2:
      subq.l    #1,d0
      cmpi.l    #-1,d0
      bne       @LMOVE1
      bra       @LMOVE5
    @LMOVE3:
      move.b  (a0)+,(a1)+  { (s >= d) copy loop }
    @LMOVE4:
      subq.l    #1,d0
      cmpi.l    #-1,d0
      bne       @LMOVE3
      bra       @LMOVE5

    @LMEMSET00:            { use fast loop mode 68010+ }
      cmp.l      a0,a1         {  check copy direction }
      bls      @LMOVE04
      add.l      d0,a0         { move pointers to end  }
      add.l      d0,a1
      bra     @LMOVE02
    @LMOVE01:
      move.b   -(a0),-(a1)   {  (s < d) copy loop }
    @LMOVE02:
      dbra      d0,@LMOVE01
      bra       @LMOVE5
    @LMOVE03:
      move.b  (a0)+,(a1)+  { (s >= d) copy loop }
    @LMOVE04:
      dbra      d0,@LMOVE03
    { end fast loop mode }
    @LMOVE5:
    end ['d0','a0','a1'];
   end;


    procedure fillword(var x;count : longint;value : word);

      begin
     asm
      move.l 8(a6), a0      { destination             }
      move.l 12(a6), d1     { number of bytes to fill }
      move.w 16(a6),d0      { fill data               }
      bra @LMEMSET21
    @LMEMSET11:
      move.w d0,(a0)+
    @LMEMSET21:
      subq.l #1,d1
      cmp.b #-1,d1
      bne  @LMEMSET11
     end ['d0','d1','a0'];
   end;

{$ifndef ordintern}
    {!!!!!! not very fast, but easy. }
    function ord(b : boolean) : byte;
      begin
         ord:=byte(b);
      end;
{$endif ordintern}

    function abs(l : longint) : longint;

      begin
         asm
            move.l 8(a6),d0
            tst.l  d0
            bpl @LMABS1
            neg.l d0
         @LMABS1:
            move.l d0,@RESULT
         end ['d0'];
      end;

    function odd(l : longint) : boolean;

      begin
        if (l and $01) = $01 then
          odd := TRUE
        else
          odd := FALSE;
      end;

    function sqr(l : longint) : longint;

      begin
         sqr := l*l;
      end;

{$ifndef str_intern }
    procedure str(l : longint;var s : string);
{$else str_intern }
    procedure int_str(l : longint;var s : string);
{$endif str_intern }

      var
        value: longint;
        negative: boolean;

      begin
         negative := false;
         s:='';
         { Workaround: }
         if l=$80000000 then
           begin
              s:='-2147483648';
              exit;
           end;
        { handle case where l = 0 }
         if l = 0 then
         begin
           s:='0';
           exit;
         end;
         If l < 0 then
         begin
             negative := true;
             value:=abs(l);
         end
         else
             value:=l;
       { handle non-zero case }
       while value>0 do
         begin
            s:=char((value mod 10)+ord('0'))+s;
            value := value div 10;
         end;
         if negative then
           s := '-' + s;
      end;


    procedure f1;[public,alias: 'FLUSH_STDOUT'];

      begin
         asm
            { Save Registers }
            movem.l d0-a7,-(sp)
         end;
         FileFunc(textrec(output).flushfunc)(textrec(output));
         asm
            { Restore all registers in the correct order }
            movem.l (sp)+,d0-a7
         end;
      end;

Function Sptr : Longint;
begin
  asm
    move.l sp,d0
    add.l  #8,d0
    move.l d0,@RESULT
  end ['d0'];
end;




 Procedure BoundsCheck;assembler;
 { called by code generator with R+ state to    }
 { determine if a range check occured.          }
 { Only in 68000 mode, in 68020 mode this is    }
 { inline.                                      }
 { On Entry:                                    }
 {   A1 = address contaning min and max indexes }
 {   D0 = value of current index to check.      }
 asm
XDEF RE_BOUNDS_CHECK
  cmp.l   (A1),D0        { lower bound ...    }
  bmi     @rebounderr    { is index lower ... }
  add.l   #4,A1
  cmp.l   (A1),D0
  bmi     @reboundend
  beq     @reboundend
@rebounderr:
  move.l  #201,d0
  jsr     HALT_ERROR
@reboundend:
 end;

Function Random(l : Longint) : Longint
{
  the problem Wwth this Function is if l is maxLongint*3/4 then the
  probability to obtain a number in the range maxlongint*1/4 to maxlongint*1/2
  is two times smaller than the probability for other numbers !
}
Begin
  Randseed:=Randseed*134775813+1;
  Random:=abs(Randseed mod l);
End;



{
  $Log$
  Revision 1.3  1998-04-08 07:53:32  michael
  + Changed Random() function. Moved from system to processor dependent files (from Pedro Gimeno)

  Revision 1.2  1998/03/27 23:48:06  carl
    * bugfix of STRCONCAT alignment problem

  Revision 1.18  1998/03/02 04:17:24  carl
    * problem with CHECK_OBJECT fixed, will probably only work with
      GNU tools, as the VMT pointer is an .lcomm and might not be
      zeroed automatically by other loaders.
    * CHECK_OBJECT was not jumping on right condition

  Revision 1.17  1998/02/23 02:26:06  carl
    * bugfix to make it link without problems

  Revision 1.13  1998/02/06 16:35:35  carl
    * oops commited wrong file

  Revision 1.11  1998/01/26 12:01:32  michael
  + Added log at the end


  
  Working file: rtl/m68k/m68k.inc
  description:
  ----------------------------
  revision 1.10
  date: 1998/01/19 10:21:36;  author: michael;  state: Exp;  lines: +1 -12
  * moved Fillchar t(..,char) to system.inc
  ----------------------------
  revision 1.9
  date: 1998/01/13 03:47:39;  author: carl;  state: Exp;  lines: +3 -3
    * bugfix of BoundsCheck invalid opcodes
  ----------------------------
  revision 1.8
  date: 1998/01/13 03:24:58;  author: carl;  state: Exp;  lines: +2 -2
    * moveq.l #201 bugfix (This is of course an impossible opcode)
  ----------------------------
  revision 1.7
  date: 1998/01/12 15:24:47;  author: carl;  state: Exp;  lines: +1 -20
    * bugfix, a function was being duplicated.
  ----------------------------
  revision 1.6
  date: 1998/01/12 03:40:11;  author: carl;  state: Exp;  lines: +2 -2
    * bugfix of RE_OVERFLOW, now gives out a runerror(215)
  ----------------------------
  revision 1.5
  date: 1998/01/05 00:31:43;  author: carl;  state: Exp;  lines: +206 -119
  * Bugfix of syntax errors
  ----------------------------
  revision 1.4
  date: 1998/01/01 16:50:16;  author: michael;  state: Exp;  lines: +1 -21
  - Moved Do_exit to system.inc. Now processor independent.
  ----------------------------
  revision 1.3
  date: 1997/12/10 12:15:05;  author: michael;  state: Exp;  lines: +2 -2
  * changed dateifunc to FileFunc.
  ----------------------------
  revision 1.2
  date: 1997/12/01 12:37:21;  author: michael;  state: Exp;  lines: +14 -0
  + added copyright reference in header.
  ----------------------------
  revision 1.1
  date: 1997/11/27 08:33:48;  author: michael;  state: Exp;
  Initial revision
  ----------------------------
  revision 1.1.1.1
  date: 1997/11/27 08:33:48;  author: michael;  state: Exp;  lines: +0 -0
  FPC RTL CVS start
  =============================================================================
}
