### -*-Midas-*-
###
### $Id: mc68k.m4,v 1.27 2000/12/05 21:23:50 cph Exp $
###
### Copyright (c) 1989-2000 Massachusetts Institute of Technology
###
### This program 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 2 of the
### License, or (at your option) any later version.
###
### 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.  See the GNU
### General Public License for more details.
###
### You should have received a copy of the GNU General Public License
### along with this program; if not, write to the Free Software
### Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
###

#### 68K assembly language (HP/Motorola Syntax) part of the compiled
#### code interface.  See cmpint.txt, cmpint.c, cmpint-mc68k.h, and
#### cmpgc.h for more documentation.
####
#### NOTE:
####	Assumptions:
####
####	1) All registers (except double floating point registers) and
####	stack locations hold a C long object.
####
####	2) The C compiler divides registers into three groups:
####	- Linkage registers, used for procedure calls and global
####	references.  On MC68K: a6, sp.
####	- super temporaries, not preserved accross procedure calls and
####	always usable. On MC68K: a0, a1, d0, d1
####	- preserved registers saved by the callee if they are written.
####	On MC68K: all others.
####
####	3) Arguments, if passed on a stack, are popped by the caller
####	or by the procedure return instruction (as on the VAX).  Thus
####	most "leaf" procedures need not worry about them.
####
####	4) There is a hardware or software maintained stack for
####	control.  The procedure calling sequence may leave return
####	addresses in registers, but they must be saved somewhere for
####	nested calls and recursive procedures.  On MC68K: saved on
####    the stack.
####
####	5) C procedures return long values in a super temporary
####    register.  Two word structures are returned in super temporary
####    registers as well.  On MC68K: d0 is used for long returns.
####    Since there are two methods for returning structures on MC68K,
####    there is a flag to choose a mechanism:
####    o  GCC returns two word structures in d0/d1 (set flag GCC in
####       M4_MACHINE_SWITCHES in m.h)
####    o  Other compilers return the address of the structure in d0
####	o  The HP compiler requires that the address of this structure
####	   be in a1 before the procedure is called (set flag HP in
####	   M4_MACHINE_SWITCHES in m.h)
####
####	6) Floating point registers are not preserved by this
####	interface.  The interface is only called from the Scheme
####	interpreter, which does not use floating point data.  Thus
####	although the calling convention would require us to preserve
####	them, they contain garbage.
####
#### Compiled Scheme code uses the following register convention:
####	- a7 (sp) contains the Scheme stack pointer, not the C stack
####	pointer.
####	- a6 (fp) contains a pointer to the Scheme interpreter's
####	"register" block.  This block contains the compiler's copy of
####	MemTop, the interpreter's registers (val, env, exp, etc),
####	temporary locations for compiled code, and the mechanism used
####	to invoke the hooks in this file.
####	- a5 contains the Scheme free pointer.
####	- a4 contains the dynamic link when needed.
####	- d7 contains the Scheme datum mask.
####	- d6 is where Scheme compiled code returns values.
####
####	All other registers are available to the compiler.  A
####	caller-saves convention is used, so the registers need not be
####	preserved by subprocedures.

####	Utility macros and definitions

define(KEEP_HISTORY,0)			# Debugging switch

define(reference_external,`')		# Declare desire to use an external
define(extern_c_label,`_$1')		# The actual reference

define(define_c_label,
`	global	extern_c_label($1)
extern_c_label($1):')

define(define_debugging_label,
`	global	$1
$1:')

# Call a SCHEME_UTILITY (see cmpint.c) and then dispatch to the
# interface procedure requested with the data to be passed to the
# procedure in d1.
#
# NOTE: Read introductory note about GCC and HP switches

define(allocate_utility_result,
	`ifdef(`HP',
	      `subq.l	&8,%sp
	       mov.l	%sp,%a1',
	      `')')


define(utility_call,
       `jsr	(%a0)		# call C procedure
	ifdef(`HP',
	      `lea	eval(($1+2)*4)(%sp),%sp',
              `lea	eval($1*4)(%sp),%sp')
	mov.l	%d0,%a0
	ifdef(`GCC',
              `',
	      `mov.l	4(%a0),%d1
	       mov.l	0(%a0),%a0')
	jmp	(%a0)')

# Scheme object representation.  Must match object.h

define(HEX, `0x$1')
define(TC_LENGTH, ifdef(`TYPE_CODE_LENGTH', TYPE_CODE_LENGTH, 6))
define(ADDRESS_MASK, eval(((2 ** (32 - TC_LENGTH)) - 1), 16))
define(TYPE_CODE_FACTOR, eval(2 ** (8 - TC_LENGTH)))
define(TYPE_CODE_MASK, eval((256 - TYPE_CODE_FACTOR), 16))
define(CLEAR_TYPE_MASK, eval((TYPE_CODE_FACTOR - 1), 16))

define(TYPE_CODE_TO_BYTE, `$1*TYPE_CODE_FACTOR')
define(TYPE_CODE_TO_OBJECT, `TYPE_CODE_TO_BYTE($1)*0x1000000')

define(EXTRACT_TYPE_CODE,
	`ifelse(TC_LENGTH, 8,
	`mov.b	$1,$2',
	`mov.b	$1,$2
	and.b	&HEX(TYPE_CODE_MASK), $2')')

define(COMPARE_TYPE_CODE,
	`cmp.b	$1,&TYPE_CODE_TO_BYTE($2)')

### External conventions

	set	regblock_memtop,0	# from const.h (* 4)
	set	regblock_int_mask,4
	set	regblock_val,8
	set	regblock_stack_guard,44
	set	regblock_int_code,48
	set	address_mask,HEX(ADDRESS_MASK)

# This must match the compiler (machin.scm)

define(dlink, %a4)			# Dynamic link register (contains a
					# pointer to a return address) 
define(rfree, %a5)			# Free pointer
define(regs, %a6)			# Pointer to Registers[0]
define(rmask, %d7)			# Mask to clear type code
define(rval,%d6)			# Procedure value

reference_external(Ext_Stack_Pointer)
reference_external(Free)
reference_external(Registers)

# These must match the C compiler

define(switch_to_scheme_registers,
	`mov.l	%a6,(%sp)
	mov.l	%sp,c_save_stack
	mov.l	extern_c_label(Ext_Stack_Pointer),%sp
	mov.l	extern_c_label(Free),rfree
	lea	extern_c_label(Registers),regs
	mov.l	&address_mask,rmask')

define(switch_to_C_registers,
	`mov.l	rfree,extern_c_label(Free)
	mov.l	%sp,extern_c_label(Ext_Stack_Pointer)
	mov.l	c_save_stack,%sp
	mov.l	(%sp),%a6')

###
### Global data
###

	data

define_debugging_label(c_save_stack)
	space	4
ifelse(KEEP_HISTORY, 1,
`define_debugging_label(ring_pointer)
	long	ring_block_1
define_debugging_label(ring_block_1)
	long	ring_block_2
	space	28
define_debugging_label(ring_block_2)
	long	ring_block_3
	space	28
define_debugging_label(ring_block_3)
	long	ring_block_4
	space	28
define_debugging_label(ring_block_4)
	long	ring_block_5
	space	28
define_debugging_label(ring_block_5)
	long	ring_block_1
	space	28')
	text

### Initialize the 68881 if present.

define_c_label(interface_initialize)
	link	%a6,&0
	ifdef(`MC68881', `fmov.l	&0x3480,%fpcr')
	unlk	%a6
	rts

### Callable by C conventions.  Swaps to Scheme register set and jumps
### to the entry point specified by its only argument.

define_c_label(C_to_interface)
	link	%a6,&-44
	movm.l	%d2-%d7/%a2-%a5,4(%sp)
	mov.l	8(%a6),%a0		# Argument: entry point
	bra.b	interface_to_scheme_internal

### Called by Scheme through a jump instruction in the register block.
### It expects an index in %d0, and 4 longword arguments in %d1-%d4

reference_external(utility_table)

define_c_label(asm_scheme_to_interface)
define_debugging_label(scheme_to_interface)
	ifelse(KEEP_HISTORY, 1,
	`lea	ring_pointer,%a1
	mov.l	(%a1),%a0
	mov.l	(%a0),(%a1)
	mov.l	%sp,4(%a0)
	mov.l	%a5,8(%a0)
	mov.l	%d0,12(%a0)
	mov.l	%d1,16(%a0)
	mov.l	%d2,20(%a0)
	mov.l	%d3,24(%a0)
	mov.l	%d4,28(%a0)
	cmp.l	%sp,%a5
	bgt.b	scheme_to_interface_proceed
	nop
define_debugging_label(scheme_to_interface_proceed)')
	mov.l	rval,regblock_val(regs)
	switch_to_C_registers()
	allocate_utility_result()
	mov.l	%d4,-(%sp)		# Push arguments to scheme utility
	mov.l	%d3,-(%sp)
	mov.l	%d2,-(%sp)
	mov.l	%d1,-(%sp)
	lea	extern_c_label(utility_table),%a0
	mov.l	(0,%a0,%d0.w*4),%a0	# C-written Scheme utility
	utility_call(4)                 # 4 arguments

### The data in %d1 is the address of an entry point to invoke.

define_c_label(interface_to_scheme)
	mov.l	%d1,%a0
###
###	Enter the scheme compiled world.
###	The value register is copied to %d0 because some utilities are
###	expected to return their value there (this should be fixed),
###	and it is stripped and placed in the dlink register since
###	we may be returning after interrupting a procedure which
###	needs this register.  This should also be separated or handled
###	inline.
###
define_debugging_label(interface_to_scheme_internal)
	switch_to_scheme_registers()
	mov.l	regblock_val(regs),rval
	mov.l	rval,%d0
	mov.l	%d0,%d1
	and.l	rmask,%d1
	mov.l	%d1,dlink	
	jmp	(%a0)

### The data in %d1 is a return code (integer) to the interpreter.

define_c_label(interface_to_C)
	mov.l	%d1,%d0			# C return value location
	movm.l	4(%sp),%d2-%d7/%a2-%a5
	unlk	%a6
	rts	

#### Optimized entry points

### Additional entry points that take care of common cases and are used to
### shorten code sequences.
### These are not strictly necessary, since the code sequences emitted by
### the compiler could use scheme_to_interface instead, but a few instructions
### are saved this way.

### Called by linker-generated trampolines to invoke the appropriate
### C-written handler.  The return address on the stack is the address
### of the trampoline storage area, passed to the C handler as the
### first argument.

### IMPORTANT:
### All the asm_* routines are declared in cmpint-mc68k.h.
### New ones need to be declared there as well!

define_c_label(asm_trampoline_to_interface)
define_debugging_label(trampoline_to_interface)
	mov.l	(%sp)+,%d1
	bra	scheme_to_interface

### Called by Scheme through a jump instruction in the register block.
### It is a special version of scheme_to_interface below, used when
### a return address is stored in the Scheme stack.

define_c_label(asm_scheme_to_interface_jsr)
define_debugging_label(scheme_to_interface_jsr)
	mov.l	(%sp)+,%d1              # Return addr -> d1
	addq.l	&4,%d1			# Skip format info.
	bra	scheme_to_interface

define(define_interface_indirection,
`define_c_label(asm_$1)
	movq	&HEX($2),%d0
	bra	scheme_to_interface')

define(define_interface_jsr_indirection,
`define_c_label(asm_$1)
	movq	&HEX($2),%d0
	bra	scheme_to_interface_jsr')

define_interface_indirection(primitive_lexpr_apply,13)
define_interface_indirection(error,15)
define_interface_jsr_indirection(link,17)
define_interface_indirection(interrupt_closure,18)
define_interface_jsr_indirection(interrupt_procedure,1a)
define_interface_jsr_indirection(interrupt_continuation,1b)
define_interface_jsr_indirection(assignment_trap,1d)
define_interface_jsr_indirection(reference_trap,1f)
define_interface_jsr_indirection(safe_reference_trap,20)
###
### These are handled directly below.
###
### define_interface_indirection(generic_decrement,22)
### define_interface_indirection(generic_divide,23)
### define_interface_indirection(generic_equal,24)
### define_interface_indirection(generic_greater,25)
### define_interface_indirection(generic_increment,26)
### define_interface_indirection(generic_less,27)
### define_interface_indirection(generic_subtract,28)
### define_interface_indirection(generic_multiply,29)
### define_interface_indirection(generic_negative,2a)
### define_interface_indirection(generic_add,2b)
### define_interface_indirection(generic_positive,2c)
### define_interface_indirection(generic_zero,2d)
###
define_interface_jsr_indirection(primitive_error,36)
define_interface_indirection(generic_quotient,37)
define_interface_indirection(generic_remainder,38)
define_interface_indirection(generic_modulo,39)

# Save an additional instruction here to load the dynamic link.
define_c_label(asm_interrupt_dlink)
	mov.l	dlink,%d2		# Dynamic link -> d2
	movq	&HEX(19),%d0
	bra	scheme_to_interface_jsr

# Bum this one for speed.
define_c_label(asm_primitive_apply)
	switch_to_C_registers()
	allocate_utility_result()
	mov.l	%d1,-(%sp)		# only one argument
	ifdef(`SUNASM',
	`lea	extern_c_label(utility_table),%a0
	mov.l	HEX(12)*4(%a0),%a0',
	`mov.l	extern_c_label(utility_table)+HEX(12)*4,%a0')
	utility_call(1)                 # one argument

	set	tc_compiled_entry,HEX(28)
	set	tc_flonum,HEX(06)
	set	tc_fixnum,HEX(1A)
	set	tc_manifest_nmv,HEX(27)
	set	tc_false,HEX(0)
	set	tc_true,HEX(8)
	set	offset_apply,HEX(14)

define(call_utility,
	`movq	&offset_$1,%d0
	bra	scheme_to_interface')

### Called by Scheme when invoking an unknown procedure.
### Having this short sequence in assembly language avoids the C call
### in the common case where the procedure is compiled and the number
### of arguments is correct.
### The number of actual arguments is in d2, the procedure on top
### of the stack.

define_c_label(asm_shortcircuit_apply)
define_debugging_label(shortcircuit_apply)
	EXTRACT_TYPE_CODE((%sp),%d0)	# Get procedure type
	mov.l	(%sp)+,%d1		# Get procedure
	COMPARE_TYPE_CODE(%d0,tc_compiled_entry)
	bne.b	shortcircuit_apply_1
	mov.l	%d1,%d3			# Extract entry point
	and.l	rmask,%d3
	mov.l	%d3,%a0
	mov.b	-3(%a0),%d3		# Extract the frame size
	ext.w	%d3
	cmp.w	%d2,%d3			# Is the frame size right?
	bne.b	shortcircuit_apply_1
	jmp	(%a0)			# Invoke

define_debugging_label(shortcircuit_apply_1)
	call_utility(apply)

### Optimized versions of shortcircuit_apply for 0-7 arguments.

define(define_apply_size_n,
`define_c_label(asm_shortcircuit_apply_size_$1)
define_debugging_label(shortcircuit_apply_size_$1)
	EXTRACT_TYPE_CODE((%sp),%d0)	# Get procedure type
	mov.l	(%sp)+,%d1		# Get procedure
	COMPARE_TYPE_CODE(%d0,tc_compiled_entry)
	bne.b	shortcircuit_apply_size_$1_1
	mov.l	%d1,%d3			# Extract entry point
	and.l	rmask,%d3
	mov.l	%d3,%a0
	cmp.b	-3(%a0),&$1		# Is the frame size right?
	bne.b	shortcircuit_apply_size_$1_1
	jmp	(%a0)			# Invoke

define_debugging_label(shortcircuit_apply_size_$1_1)
	movq	&$1,%d2			# initialize frame size
	call_utility(apply)')

define_apply_size_n(1)
define_apply_size_n(2)
define_apply_size_n(3)
define_apply_size_n(4)
define_apply_size_n(5)
define_apply_size_n(6)
define_apply_size_n(7)
define_apply_size_n(8)

###	This utility depends on the C compiler preserving d2-d7 and a2-a7.
###	It takes its parameters in d0 and d1, and returns its value in a0.

define_c_label(asm_allocate_closure)
	switch_to_C_registers()
	mov.l	%a1,-(%sp)		# Preserve reg.
	mov.l	%d1,-(%sp)		# Preserve reg.
	mov.l	%d0,-(%sp)		# Push arg.
	jsr	extern_c_label(allocate_closure)
	addq.l	&4,%sp			# Pop arg.
	mov.l	%d0,%a0			# Return value
	mov.l	(%sp)+,%d1		# Restore reg.
	mov.l	(%sp)+,%a1		# Restore reg.
	switch_to_scheme_registers()
	rts

###	These utilities improve the performance of floating point code
###	significantly.
###	Arguments on top of the stack followed by the return address.

define_debugging_label(asm_generic_flonum_result)
	mov.l	rfree,rval
	mov.l	&TYPE_CODE_TO_OBJECT(tc_manifest_nmv)+2,(rfree)+
	fmove.d	%fp0,(rfree)+
	or.l	&TYPE_CODE_TO_OBJECT(tc_flonum),rval
	and.b	&TYPE_CODE_TO_BYTE(1)-1,(%sp)
	rts

define_debugging_label(asm_true_result)
	mov.l	&TYPE_CODE_TO_OBJECT(tc_true),rval
	and.b	&TYPE_CODE_TO_BYTE(1)-1,(%sp)
	rts

define_debugging_label(asm_false_result)
	mov.l	&TYPE_CODE_TO_OBJECT(tc_false),rval
	and.b	&TYPE_CODE_TO_BYTE(1)-1,(%sp)
	rts

define(define_generic_unary,
`define_c_label(asm_generic_$1)
	EXTRACT_TYPE_CODE((%sp),%d0)	# Get arg1s type
 	COMPARE_TYPE_CODE(%d0,tc_flonum)
	bne.b	asm_generic_$1_hook
	mov.l	(%sp)+,%d0		# arg1
	and.l	rmask,%d0
	mov.l	%d0,%a0
	fmove.d	4(%a0),%fp0
	$3.b	&1,%fp0
	bra	asm_generic_flonum_result

asm_generic_$1_hook:
	movq	&HEX($2),%d0
	bra	scheme_to_interface')

define(define_generic_unary_predicate,
`define_c_label(asm_generic_$1)
	EXTRACT_TYPE_CODE((%sp),%d0)	# Get arg1s type
 	COMPARE_TYPE_CODE(%d0,tc_flonum)
	bne.b	asm_generic_$1_hook
	mov.l	(%sp)+,%d0		# arg1
	and.l	rmask,%d0
	mov.l	%d0,%a0
	fmove.d	4(%a0),%fp0
	fb$3	asm_true_result
	bra	asm_false_result

asm_generic_$1_hook:
	movq	&HEX($2),%d0
	bra	scheme_to_interface')

define(define_generic_binary,
`define_c_label(asm_generic_$1)
	EXTRACT_TYPE_CODE((%sp),%d0)	# Get arg1s type
	EXTRACT_TYPE_CODE(4(%sp),%d1)	# Get arg2s type
	mov.l	(%sp),%d2		# arg1
	mov.l	4(%sp),%d3		# arg2
	and.l	rmask,%d2
	and.l	rmask,%d3
	mov.l	%d2,%a0
	mov.l	%d3,%a1
 	COMPARE_TYPE_CODE(%d0,tc_flonum)
	bne.b	asm_generic_$1_fix_flo
	COMPARE_TYPE_CODE(%d1,tc_flonum)
	bne.b	asm_generic_$1_flo_fix
	fmove.d	4(%a0),%fp0
	$3.d	4(%a1),%fp0
	addq.l	&8,%sp
	bra	asm_generic_flonum_result

asm_generic_$1_fix_flo:
	COMPARE_TYPE_CODE(%d0,tc_fixnum)
	bne.b	asm_generic_$1_hook
	COMPARE_TYPE_CODE(%d1,tc_flonum)
	bne.b	asm_generic_$1_hook
	lsl.l	&TC_LENGTH,%d2
	asr.l	&TC_LENGTH,%d2
	fmove.l	%d2,%fp0
	$3.d	4(%a1),%fp0
	addq.l	&8,%sp
	bra	asm_generic_flonum_result

asm_generic_$1_flo_fix:
	COMPARE_TYPE_CODE(%d1,tc_fixnum)
	bne.b	asm_generic_$1_hook
	lsl.l	&TC_LENGTH,%d3
	asr.l	&TC_LENGTH,%d3
	fmove.d	4(%a0),%fp0
	$3.l	%d3,%fp0
	addq.l	&8,%sp
	bra	asm_generic_flonum_result

asm_generic_$1_hook:
	movq	&HEX($2),%d0
	bra	scheme_to_interface')

define(define_generic_binary_predicate,
`define_c_label(asm_generic_$1)
	EXTRACT_TYPE_CODE((%sp),%d0)	# Get arg1s type
	EXTRACT_TYPE_CODE(4(%sp),%d1)	# Get arg2s type
	mov.l	(%sp),%d2		# arg1
	mov.l	4(%sp),%d3		# arg2
	and.l	rmask,%d2
	and.l	rmask,%d3
	mov.l	%d2,%a0
	mov.l	%d3,%a1
 	COMPARE_TYPE_CODE(%d0,tc_flonum)
	bne.b	asm_generic_$1_fix_flo
	COMPARE_TYPE_CODE(%d1,tc_flonum)
	bne.b	asm_generic_$1_flo_fix
	addq.l	&8,%sp
	fmove.d	4(%a0),%fp0
	fcmp.d	%fp0,4(%a1)
	fb$3	asm_true_result
	bra	asm_false_result

asm_generic_$1_fix_flo:
	COMPARE_TYPE_CODE(%d0,tc_fixnum)
	bne.b	asm_generic_$1_hook
	COMPARE_TYPE_CODE(%d1,tc_flonum)
	bne.b	asm_generic_$1_hook
	addq.l	&8,%sp
	lsl.l	&TC_LENGTH,%d2
	asr.l	&TC_LENGTH,%d2
	fmove.l	%d2,%fp0
	fcmp.d	%fp0,4(%a1)
	fb$3	asm_true_result
	bra	asm_false_result

asm_generic_$1_flo_fix:
	COMPARE_TYPE_CODE(%d1,tc_fixnum)
	bne.b	asm_generic_$1_hook
	addq.l	&8,%sp
	lsl.l	&TC_LENGTH,%d3
	asr.l	&TC_LENGTH,%d3
	fmove.d	4(%a0),%fp0
	fcmp.l	%fp0,%d3
	fb$3	asm_true_result
	bra	asm_false_result

asm_generic_$1_hook:
	movq	&HEX($2),%d0
	bra	scheme_to_interface')

define_generic_unary(decrement,22,fsub)
define_generic_binary(divide,23,fdiv)
define_generic_binary_predicate(equal,24,eq)
define_generic_binary_predicate(greater,25,gt)
define_generic_unary(increment,26,fadd)
define_generic_binary_predicate(less,27,lt)
define_generic_binary(subtract,28,fsub)
define_generic_binary(multiply,29,fmul)
define_generic_unary_predicate(negative,2a,lt)
define_generic_binary(add,2b,fadd)
define_generic_unary_predicate(positive,2c,gt)
define_generic_unary_predicate(zero,2d,eq)

### Close-coded stack and interrupt check for use when stack checking
### is enabled.

define_c_label(asm_stack_and_interrupt_check_12)
	mov.l	&-12,-(%sp)
	bra.b	stack_and_interrupt_check

define_c_label(asm_stack_and_interrupt_check_14)
	mov.l	&-14,-(%sp)
	bra.b	stack_and_interrupt_check

define_c_label(asm_stack_and_interrupt_check_18)
	mov.l	&-18,-(%sp)
	bra.b	stack_and_interrupt_check

define_c_label(asm_stack_and_interrupt_check_22)
	mov.l	&-22,-(%sp)
	bra.b	stack_and_interrupt_check

define_c_label(asm_stack_and_interrupt_check_24)
	mov.l	&-24,-(%sp)
#	bra.b	stack_and_interrupt_check

### On entry, 4(%sp) contains the resumption address, and 0(%sp) is
### the offset between the resumption address and the GC label
### address.
define_debugging_label(stack_and_interrupt_check)

### If the Scheme stack pointer is <= Stack_Guard, then the stack has
### overflowed -- in which case we must signal a stack-overflow interrupt.
	cmp.l	%sp,regblock_stack_guard(regs)
	bgt.b	stack_and_interrupt_check_1

### Set the stack-overflow interrupt bit. If the stack-overflow
### interrupt is disabled, skip forward to gc test.  Otherwise, set
### MemTop to -1 and signal the interrupt.
	bset	&0,regblock_int_code+3(regs)
	btst	&0,regblock_int_mask+3(regs)
	beq.b	stack_and_interrupt_check_1
	mov.l	&-1,regblock_memtop(regs)
	bra.b	stack_and_interrupt_check_2

### If (Free >= MemTop), signal an interrupt.
stack_and_interrupt_check_1:
	cmp.l	rfree,regblock_memtop(regs)
	bge.b	stack_and_interrupt_check_2

### No action necessary -- return to resumption address.
	addq.l	&4,%sp
	rts

### Must signal the interrupt -- return to GC label instead.
stack_and_interrupt_check_2:
	mov.l	%d0,-(%sp)
	mov.l	4(%sp),%d0
	add.l	%d0,8(%sp)
	mov.l	(%sp),%d0
	addq.l	&8,%sp
	rts

### Assembly-language implementation of SET-INTERRUPT-ENABLES!
### primitive.  Argument appears at top of stack, return address below
### that.

define_c_label(asm_set_interrupt_enables)
define_debugging_label(set_interrupt_enables)
	# Return value is previous contents of mask register.
	mov.l	regblock_int_mask(regs),rval
	or.l	&TYPE_CODE_TO_OBJECT(tc_fixnum),rval
	mov.l	(%sp)+,%d0		# get new interrupt mask
	and.l	rmask,%d0		# strip fixnum type
	mov.l	%d0,regblock_int_mask(regs) # store it in mask register
	# Setup compiled memtop register: -1 if pending interrupt,
	# Memtop if GC enabled, else Heap_Top.
	movq	&-1,%d1
	mov.l	regblock_int_code(regs),%d2
	and.l	%d0,%d2
	bne.b	set_interrupt_enables_1
	mov.l	extern_c_label(MemTop),%d1
	btst	&2,%d0
	bne.b	set_interrupt_enables_1
	mov.l	extern_c_label(Heap_Top),%d1
set_interrupt_enables_1:
	mov.l	%d1,regblock_memtop(regs)
	# Setup compiled stack_guard register: Stack_Guard if
	# stack-overflow enabled, else Stack_Bottom
	mov.l	extern_c_label(Stack_Guard),%d1
	btst	&0,%d0
	bne.b	set_interrupt_enables_2
	mov.l	extern_c_label(Stack_Bottom),%d1
set_interrupt_enables_2:
	mov.l	%d1,regblock_stack_guard(regs)
	mov.l	(%sp)+,%d0
	and.l	rmask,%d0
	mov.l	%d0,%a0
	jmp	(%a0)


syntax highlighted by Code2HTML, v. 0.9.1