/***********************************************************************/
/*                                                                     */
/*                           Objective Caml                            */
/*                                                                     */
/*            Xavier Leroy, projet Cristal, INRIA Rocquencourt         */
/*                                                                     */
/*  Copyright 1996 Institut National de Recherche en Informatique et   */
/*  Automatique.  Distributed only by permission.                      */
/*                                                                     */
/***********************************************************************/

/* $Id: mips.S,v 1.5 1997/03/17 10:17:31 xleroy Exp $ */

/* Asm part of the runtime system, Mips processor */

        .comm   gc_entry_regs, 4 * 32
        .comm   gc_entry_float_regs, 8 * 16

/* Save all callee-save registers in the stack frame */
#define SAVE_CALLEE_SAVE_REGS \
        sw      $16, 0($sp); \
        sw      $17, 4($sp); \
        sw      $18, 8($sp); \
        sw      $19, 12($sp); \
        sw      $20, 16($sp); \
        sw      $21, 20($sp); \
        sw      $22, 24($sp); \
        sw      $23, 28($sp); \
        sw      $30, 32($sp); \
        s.d     $f20, 40($sp); \
        s.d     $f22, 48($sp); \
        s.d     $f24, 56($sp); \
        s.d     $f26, 64($sp); \
        s.d     $f28, 72($sp); \
        s.d     $f30, 80($sp)

#define RELOAD_CALLEE_SAVE_REGS \
        lw      $16, 0($sp); \
        lw      $17, 4($sp); \
        lw      $18, 8($sp); \
        lw      $19, 12($sp); \
        lw      $20, 16($sp); \
        lw      $21, 20($sp); \
        lw      $22, 24($sp); \
        lw      $23, 28($sp); \
        lw      $30, 32($sp); \
        l.d     $f20, 40($sp); \
        l.d     $f22, 48($sp); \
        l.d     $f24, 56($sp); \
        l.d     $f26, 64($sp); \
        l.d     $f28, 72($sp); \
        l.d     $f30, 80($sp)

/* Save and reload registers from gc_entry_regs and gc_entry_float_regs */

#define SAVE(r) sw $/**/r, gc_entry_regs + r * 4
#define LOAD(r) lw $/**/r, gc_entry_regs + r * 4
#define FSAVE(r) s.d $f/**/r, gc_entry_float_regs + r * 4
#define FLOAD(r) l.d $f/**/r, gc_entry_float_regs + r * 4

/* Allocation */

        .text
        .globl  caml_call_gc
#ifndef _PIC
        .globl  caml_alloc1
        .globl  caml_alloc2
        .globl  caml_alloc3
        .globl  caml_alloc
#endif
        .ent    caml_call_gc

caml_call_gc:
#ifdef _PIC
	.set	noreorder
        .cpload	$25
        .set    reorder
#endif
    /* Record return address and adjust it to point back to
       the beginning of the allocation sequence */
        sw      $31, caml_last_return_address
        subu    $31, $31, 16
    /* Don't request any allocation, will be redone at return */
        li      $24, 0
	b	$110

#ifndef _PIC

/* caml_alloc* : all code generator registers preserved. */

caml_alloc1:
        subu    $22, $22, 8
        bltu    $22, $23, $100
        j	$31
$100:   li      $24, 8
        b       caml_call_gc_internal

caml_alloc2:
        subu    $22, $22, 12
        bltu    $22, $23, $101
        j	$31
$101:   li      $24, 12
        b       caml_call_gc_internal

caml_alloc3:
        subu    $22, $22, 16
        bltu    $22, $23, $102
        j	$31
$102:   li      $24, 16
        b       caml_call_gc_internal

caml_alloc:
        subu    $22, $22, $24
        bltu    $22, $23, caml_call_gc_internal
        j       $31
        
caml_call_gc_internal:
    /* Record return address */
        sw      $31, caml_last_return_address

#endif

$110:
    /* Record lowest stack address */
        sw      $sp, caml_bottom_of_stack
    /* Save actual return address, $gp, requested size.
       Also reserve some stack space for the call. */
        subu    $sp, $sp, 32
        sw      $31, 28($sp)
#ifdef _PIC
        .cprestore 24
#endif
        sw      $24, 16($sp)
    /* Save current allocation pointer for debugging purposes */
        sw      $22, young_ptr
    /* Save the exception handler (if e.g. a sighandler raises) */
        sw      $30, caml_exception_pointer
    /* Save all regs used by the code generator in the arrays
    /* gc_entry_regs and gc_entry_float_regs. */
        SAVE(2)
        SAVE(3)
        SAVE(4)
        SAVE(5)
        SAVE(6)
        SAVE(7)
        SAVE(8)
        SAVE(9)
        SAVE(10)
        SAVE(11)
        SAVE(12)
        SAVE(13)
        SAVE(14)
        SAVE(15)
        SAVE(16)
        SAVE(17)
        SAVE(18)
        SAVE(19)
        SAVE(20)
        SAVE(21)
        FSAVE(0)
        FSAVE(2)
        FSAVE(4)
        FSAVE(6)
        FSAVE(8)
        FSAVE(12)
        FSAVE(14)
        FSAVE(16)
        FSAVE(18)
        FSAVE(20)
        FSAVE(22)
        FSAVE(24)
        FSAVE(26)
        FSAVE(28)
        FSAVE(30)
    /* Call the garbage collector */
        jal     garbage_collection
    /* Restore all regs used by the code generator */
        LOAD(2)
        LOAD(3)
        LOAD(4)
        LOAD(5)
        LOAD(6)
        LOAD(7)
        LOAD(8)
        LOAD(9)
        LOAD(10)
        LOAD(11)
        LOAD(12)
        LOAD(13)
        LOAD(14)
        LOAD(15)
        LOAD(16)
        LOAD(17)
        LOAD(18)
        LOAD(19)
        LOAD(20)
        LOAD(21)
        FLOAD(0)
        FLOAD(2)
        FLOAD(4)
        FLOAD(6)
        FLOAD(8)
        FLOAD(12)
        FLOAD(14)
        FLOAD(16)
        FLOAD(18)
        FLOAD(20)
        FLOAD(22)
        FLOAD(24)
        FLOAD(26)
        FLOAD(28)
        FLOAD(30)
    /* Reload new allocation pointer and allocation limit */
        lw      $22, young_ptr
        lw      $23, young_limit
    /* Allocate space for the block */
        lw      $24, 16($sp)
        subu    $22, $22, $24
    /* Say that we are back into Caml code */
        sw      $0, caml_last_return_address
    /* Return to caller */
        lw	$31, 28($sp)
        addu    $sp, $sp, 32
        j       $31

        .end    caml_call_gc

/* Call a C function from Caml */

        .globl  caml_c_call
        .ent    caml_c_call

caml_c_call:
    /* Function to call is in $24 */
#ifndef _PIC
    /* Record lowest stack address and return address */
        sw      $31, caml_last_return_address
        sw      $sp, caml_bottom_of_stack
    /* Make the exception handler and alloc ptr available to the C code */
        sw      $22, young_ptr
        sw      $30, caml_exception_pointer
    /* Call the function */
        jal     $24
    /* Reload alloc ptr and alloc limit */
        lw      $22, young_ptr
        lw      $23, young_limit
    /* Reload return address */
        lw      $31, caml_last_return_address
    /* Say that we are back into Caml code */
        sw      $0, caml_last_return_address
    /* Return */
        j       $31
#else
    /* Slightly optimized form of the above when referencing
       global variables is expensive */
        .set    noreorder
        .cpload $25
        .set    reorder
	la	$16, caml_last_return_address
	la	$17, young_ptr
	la	$18, young_limit
        sw      $31, 0($16)	/* caml_last_return_address */
        sw      $sp, caml_bottom_of_stack
        sw      $22, 0($17)	/* young_ptr */
        sw      $30, caml_exception_pointer
        move    $25, $24
        jal     $24
        lw      $31, 0($16)	/* caml_last_return_address */
        lw      $22, 0($17)	/* young_ptr */
        lw      $23, 0($18)	/* young_limit */
        sw      $0, 0($16)	/* caml_last_return_address */
        j       $31
#endif
        .end    caml_c_call

/* Start the Caml program */

        .globl  caml_start_program
        .globl  stray_exn_handler
        .ent    caml_start_program
caml_start_program:
#ifdef _PIC
        .set    noreorder
        .cpload $25
        .set    reorder
#endif
        la      $24, caml_program

    /* Code shared with callback* */
$103:
    /* Save return address */
	subu    $sp, $sp, 96
        sw      $31, 88($sp)
    /* Save all callee-save registers */
	SAVE_CALLEE_SAVE_REGS
    /* Set up a callback link on the stack. */
        subu    $sp, $sp, 8
        lw      $2, caml_bottom_of_stack
        sw      $2, 0($sp)
        lw      $3, caml_last_return_address
        sw      $3, 4($sp)
    /* Set up a trap frame to catch exceptions escaping the Caml code */
        subu    $sp, $sp, 8
        lw      $30, caml_exception_pointer
        sw      $30, 0($sp)
        la      $2, $105
        sw      $2, 4($sp)
        move    $30, $sp
    /* Reload allocation pointers */
	lw	$22, young_ptr
	lw	$23, young_limit
    /* Say that we are back into Caml code */
        sw      $0, caml_last_return_address
    /* Call the Caml code */
#ifdef _PIC
        move    $25, $24
#endif
$104:   jal     $24
#ifdef _PIC
    /* Reload $gp based on return address */
        .set    noreorder
        .cpload $31
        .set    reorder
#endif
    /* Pop the trap frame, restoring caml_exception_pointer */
        lw      $24, 0($sp)
        sw      $24, caml_exception_pointer
        addu    $sp, $sp, 8
    /* Pop the callback link,
       restoring the global variables used by caml_c_call */
        lw      $24, 0($sp)
        sw      $24, caml_bottom_of_stack
        lw      $25, 4($sp)
        sw      $25, caml_last_return_address
        addu    $sp, $sp, 8
    /* Update allocation pointer */
        sw      $22, young_ptr
    /* Reload callee-save registers and return */
        lw      $31, 88($sp)
	RELOAD_CALLEE_SAVE_REGS
        addu    $sp, $sp, 96
        j       $31
        
    /* The trap handler: re-raise the exception through mlraise,
       so that local C roots are cleaned up correctly. */
$105:
#ifdef _PIC
    /* Reload $gp based on trap address (still in $25) */
        .set    noreorder
        .cpload $25
        .set    reorder
#endif
        sw      $22, young_ptr
        sw      $30, caml_exception_pointer
        lw      $24, 0($sp)
        sw      $24, caml_bottom_of_stack
        lw      $25, 4($sp)
        sw      $25, caml_last_return_address
        move    $4, $2          /* bucket as first argument */
        jal     mlraise         /* never returns */

        .end    caml_start_program

/* Raise an exception from C */

        .globl  raise_caml_exception
        .ent    raise_caml_exception
raise_caml_exception:
#ifdef _PIC
        .set    noreorder
        .cpload $25
        .set    reorder
#endif
        move    $2, $4
        lw      $22, young_ptr
        lw      $23, young_limit
        lw      $sp, caml_exception_pointer
        lw      $30, 0($sp)
        lw      $24, 4($sp)
        addu    $sp, $sp, 8
        j       $24

        .end    raise_caml_exception

/* Callback from C to Caml */

        .globl  callback
        .ent    callback
callback:
#ifdef _PIC
        .set    noreorder
        .cpload $25
        .set    reorder
#endif
    /* Initial shuffling of arguments */
        move    $9, $4          /* closure */
        move    $8, $5          /* argument */
        lw      $24, 0($4)      /* code pointer */
        b       $103
        .end    callback

        .globl  callback2
        .ent    callback2
callback2:
#ifdef _PIC
        .set    noreorder
        .cpload $25
        .set    reorder
#endif
    /* Initial shuffling of arguments */
        move    $10, $4                 /* closure */
        move    $8, $5                  /* first argument */
        move    $9, $6                  /* second argument */
        la      $24, caml_apply2        /* code pointer */
        b       $103

        .end    callback2

        .globl  callback3
        .ent    callback3
callback3:
#ifdef _PIC
        .set    noreorder
        .cpload $25
        .set    reorder
#endif
    /* Initial shuffling of arguments */
        move    $11, $4                 /* closure */
        move    $8, $5                  /* first argument */
        move    $9, $6                  /* second argument */
        move    $10, $7                 /* third argument */
        la      $24, caml_apply3        /* code pointer */
        b       $103

        .end    callback3

        .rdata
        .globl  system_frametable
system_frametable:
        .word   1               /* one descriptor */
        .word   $104 + 8        /* return address into callback */
        .half   -1              /* negative frame size => use callback link */
        .half   0               /* no roots here */
