;***
;cruntime.inc - multi-model assembly macros for interfacing to HLLs
;
;	Copyright (c) 1988, Microsoft Corporation.  All rights reserved.
;
;Purpose:
;	This file defines the current memory model being used.
;
;Revision History:
;	08-04-88  SJM	Initial version to handle all four memory models
;			in 16-bit mode and small model in 32-bit mode
;	08-08-88  JCR	Added CPDIST, ?WIN, PCS, ISHIFT/LSHIFT, OS2,
;			DNPTR/DFPTR, DFLOAT/DDOUBLE/DLDOUBLE
;	08-16-88  PHG	Added FPES, LFPES, CBI, ZXAL, ZXBL, ZXCL, ZXDL
;	08-17-88  JCR	Added CAXDX, modified FPSIZE
;	08-20-88  PHG	Added diagnostic messages, removed 386 16-bit support
;			and 386 large code/data support, added mucho comments,
;			PSS now defined as es: only if SS_NEQ_GROUP defined
;	08-24-88  JCR	Added RBXSAVE and RBXONLY for use in 'proc uses'
;	08-25-88  JCR	Added savereg macro, removed rbxsave/rbxonly...
;	08-26-88  GJF	Added codeseg (text) macro
;	09-15-88  JCR	Corrected savelist/reglist macro to go with new MASM
;	09-21-88  WAJ	Added JS*, static*, global*, and label*, and lab macros
;	09-22-88  JCR	Change 'plm' to 'pascal' in label macro
;	09-26-88  WAJ	Added PUSH16 which will do a 16 bit push in a USE32 seg.
;	09-28-88  WAJ	Added CPWORD and DPWORD
;	09-29-88  WAJ	Added JMPFAR16 macro
;	10-12-88  JCR	Made PCS evaluate to 'cs:' for 16/32 stub testbed
;	04-24-89  JCR	Added 'assume seg:flat' for 386 to avoid masm/link bug
;	05-25-89  GJF	Added APIEXT, a macro that expands to the proper extrn
;			declaration for an API function
;	06-13-89  JCR	Added 'flat:' to DCPTR and DDPTR
;	09-15-89  JCR	Added DCPTR? and DDPTR?, always use "FLAT" not "flat"
;
;*******************************************************************************

;==============================================================================
;
;Use the following defines to control processor/segment model
;
;   -DI86		8086/8088 processor
;   -DI286		80286 processor
;   -DI386		80386 processor with 32-bit code/data segment
;
;   -Dmem_S		Small memory model   (near code, near data)
;   -Dmem_M		Medium memory model  (far code, near data)
;   -Dmem_C		Compact memory model (near code, fat data)
;   -Dmem_L		Large memory model   (far code, far data)
;
;   -DSS_NEQ_DGROUP	SS and DS point to different segments
;
;   default is -DI86 -Dmem_S
;
;==============================================================================
;
;The following variables are defined by this file:
;   cpu 			86, 286, or 386
;   sizeC			code distance; 1 = far code, 0 = near code
;   sizeD			data distance; 1 = far data, 0 = near data
;   mmodel			english name of the memory model, i.e. "Medium"
;   ISIZE, LSIZE, NSIZE 	size of ints, longs, shorts
;   FLTSIZE, DBLSIZE, LDBLSIZE	size of float, double, long double
;   NPSIZE, FPSIZE		size of near/far pointers
;   CPSIZE, DPSIZE		size of code/data pointers
;   ISHIFT, LSHIFT		bits to shift to convert byte to int/long
;
;The following macros allow easy writing of combined 16/32 bit code:
;
; 16/32 bit registers:
;   rax, rbx, rcx, rdx, 	expand to native registers (rax = eax or ax)
;   rsi, rdi, rsp, rbp
; 16/32 bit register instructions:
;   JRCXZ			jump when rcx is zero
;   CBI 			convert byte to int (al to rax)
;   CAXDX			convert rax to rax:rdx
;   ZXAL, ZXBL, ZXCL, ZXDL	zero extend al,bl,cl,dl to rax,rbx,rcx,rdx
; Pointer instructions:
;   LPDS, LPES			load data pointer with ES or DS
;   PDS, PES			segment overrides when pointer loaded as above
;   PCS, PSS			segment override to get at code/stack segment
;   LFPDS, LFPES		load far pointer with ES or DS
;   FPDS, FPES			segment overrides when pointer loaded as above
;   CPTR			data type of code pointer
;   CPDIST			distance of code (near/far)
;   DNPTR, DFPTR		define near/far pointer
;   DCPTR, DDPTR		define code/data pointer
;   DCPTR?, DDPTR?		define uninitialized code/data pointer
;   CPWORD, DPWORD		data type of code or data pointer
; Numeric type instructions:
;   IWORD, LWORD, SWORD 	data type of int, long, short
;   DINT, DLONG, DSHORT 	define int, long, short
;   DFLOAT, DDOUBLE, DLDOUBLE	define float, double, long double
; Offsets:
;   codeoffset, dataoffset	offsets from code and data segments
; API calls:
;   APIDIST			distance of API calls (near/far)
;   APIEXT ApiName		extrn declaration for an API function
;
;The following utility macros are provided:
;   codeseg			define/declare code segment
;   error <msg> 		stop assembly with message
;   display <msg>		display a message, unless QUIET defined
;   savelist [<reg> ...]	init list of regs to be save by 'proc uses'
;   _if cond <instruction>	assemble instruction only if cond is TRUE
;   _ife cond <instruction>	assemble instruction only if cond is FALSE
;   _ifd symbol <instruction>	assemble instruction only if symbol defined
;   _ifnd symbol <instruction>	assemble instruction only if symbol not defined
;
;   lab  LabelName		assembles to "LabelName:" If DEBUG is defined
;				LabelName is made public
;
;   JS* (ex. JSE,JSZ,JSB ...)	assemble to "je short","jz short","jb short"
;
;   Cmacro look alikes
;   static* Name, InitialValue, Repeat	 defines a static variable of type *
;   global* Name, InitialValue, Repeat	 defines a global variable of type *
;   label*  Name, {PUBLIC,PASCAL,C}	 defines a label of type *
;
;   PUSH16  SegmentReg		pushes 16 bits in a use32 segment
;   JMPFAR16  label		will do a far 16:16 jmp from a use32 segment
;
;==============================================================================

; error <msg>	-    Output message and generate error

error	MACRO	msg
if2			;; only on pass 2 can we generate errors
	%out	**********************************************************
	%out	*** E r r o r  --  msg
	%out	**********************************************************
	.err
endif
        ENDM

; display msg	-    Output message unless QUIET defined

display MACRO	msg
ifndef QUIET		;; only when quiet flag not set
if1			;; and on pass 1, then display message
	%out msg
endif
endif
	ENDM

; One line conditionals:
;   here we create the capability of writing code lines like
;
; _if sizeD   <push ds>   as opposed to    if sizeD
;					       push  ds
;					   endif

_if	MACRO	cond,text
    if	cond
	text
    endif
	ENDM

_ife	MACRO	cond,text
    ife cond
	text
    endif
	ENDM

_ifd	MACRO	cond,text
    ifdef   cond
	text
    endif
	ENDM

_ifnd	MACRO	cond,text
    ifndef  cond
	text
    endif
	ENDM

; set windows flag to 0

	?WIN	equ	0	; disable windows-specific code

; check for MTHREAD, requires 286 or greater processor

ifdef MTHREAD
ifndef I386
ifndef I286
; MTHREAD implies 286 processor
display <Multi-thread specified - assuming 80286 processor>
I286 equ <>
endif
endif
endif

; Process memory-model arguments

ifdef  mem_M
	; Medium model
	sizeC	equ	1
	sizeD	equ	0
	mmodel	equ	<Medium>
elseifdef  mem_C
	; Compact model
	sizeC	equ	0
	sizeD	equ	1
	mmodel	equ	<Compact>
elseifdef  mem_L
	; Large model
	sizeC	equ	1
	sizeD	equ	1
	mmodel	equ	<Large>
else
	; Small model - default
	sizeC	equ	0
	sizeD	equ	0
	mmodel	equ	<Small>
endif

; Process processor arguments

ifdef	    I286
	display <Processor:		80286>
	cpu equ 286
	.286
elseifdef   I386
	display <Processor:		80386>
	cpu equ 386
	OS2   equ <>	; Define "OS2" since 386 can only run on that OS
	.386
else
	display <Processor:		8086/8088>
	cpu equ 86
	.8086
endif

;  386 32-bit checking.  Currently we are only expecting small model
;  32 bit segments, so we make a few checks to be sure nothing is
;  incorrectly being defined.

ifdef I386
    if sizeC or sizeD
	error <Must use Small memory model for 386 version.>
    endif

    ifdef _LOAD_DGROUP
	error <No loading DGROUP in 386 version.>
    endif

    ifdef SS_NEQ_DGROUP
	error <SS always equals DGROUP in 386 version.>
    endif
endif

;  Set memory model

%	display <Memory model:		mmodel>
%	.model	mmodel, C

;
; *** Temporary Workaround ***
; Currently, MASM will not recognize the 'FLAT' keyword unless it previously
; appears in an 'assume' statement.  Presumably, when the '.model FLAT' feature
; is implemented, this will go away.  [Use 'gs:' since we never use that
; segment register.
;

ifdef	I386
	; ensure that MASM recognizes 'FLAT'
	assume	gs:FLAT
endif


; Define registers:
; Instead of using the "word" registers directly, we will use a set of
; text equates.  This will allow you to use the native word size instead of
; hard coded to 16 bit words.  We also have some instruction equates for
; instruction with the register type hard coded in.

ifdef I386

    rax equ <eax>
    rbx equ <ebx>
    rcx equ <ecx>
    rdx equ <edx>
    rdi equ <edi>
    rsi equ <esi>
    rbp equ <ebp>
    rsp equ <esp>

    JRCXZ equ <jecxz>
    CBI   equ <movsx eax, al>	 ; convert byte to int (al to rax)
    CAXDX equ <cdq>		 ; convert rax to rdx:rax
    ZXAL  equ <movzx eax, al>	 ; zero extend al
    ZXBL  equ <movzx ebx, bl>	 ; zero extend bl
    ZXCL  equ <movzx ecx, cl>	 ; zero extend cl
    ZXDL  equ <movzx edx, dl>	 ; zero extend dl

else

    rax equ <ax>
    rbx equ <bx>
    rcx equ <cx>
    rdx equ <dx>
    rdi equ <di>
    rsi equ <si>
    rbp equ <bp>
    rsp equ <sp>

    JRCXZ equ <jcxz>
    CBI   equ <cbw>		 ; convert byte to int (al to rax)
    CAXDX equ <cwd>		 ; convert rax to rdx:rax
    ZXAL  equ <xor ah, ah>	 ; zero extend al
    ZXBL  equ <xor bh, bh>	 ; zero extend bl
    ZXCL  equ <xor ch, ch>	 ; zero extend cl
    ZXDL  equ <xor dh, dh>	 ; zero extend dl

endif

; The following equates deal with the differences in near versus
; far data pointers, and segment overrides.
;
; Use LPES and PES when loading a default size pointer -- it loads
; a 16-bit pointer register in 286 Small/Medium model,
; a 16-bit pointer register and 16-bit segment register in 8086/286
; Compact/Large model, and a 32-bit pointer register in 386 mode.
;
; Use LFPES and FPES when loading an always far pointer -- it loads a
; 16-bit pointer register and 16-bit segment register in 8086/286,
; all models; a 32-bit pointer register in 386 mode.

if sizeD
    LPES equ <les>
    LPDS equ <lds>
    PDS  equ <ds:>
    PES  equ <es:>
else
    LPES equ <mov>
    LPDS equ <mov>
    PDS  equ <>
    PES  equ <>
endif

ifdef I386
    LFPES equ <mov>
    LFPDS equ <mov>
    FPES equ <>
    FPDS equ <>
else
    LFPES equ <les>
    LFPDS equ <lds>
    FPES equ <es:>
    FPDS equ <ds:>
endif

if sizeC or @WordSize eq 2
    PCS  equ <cs:>		; large code model or non-386
else
 IF 1	;*** TEMP 16/32 TESTBED ***
    PCS  equ <cs:>
 ELSE
    PCS  equ <> 		; 386 small code model
 ENDIF	;*** END TEMP CODE
endif

ifdef SS_NEQ_DGROUP
    PSS   equ <ss:>		; SS != DS
else
    PSS   equ <>		; SS == DS
endif

; Define offset macros:
;   The 32-bit segments will not have 'groups'

ifdef I386
    codeoffset	equ <offset FLAT:>
    dataoffset	equ <offset FLAT:>
else
    codeoffset	equ <offset @code:>
    dataoffset	equ <offset DGROUP:>
endif

; The next set of equates deals with the size of SHORTS, INTS, LONGS, and
; pointers in the 16 and 32 bit versions.

ifdef I386	 ;--- 32 bit segment ---

    ; parameters and locals
    IWORD   equ <dword>
    LWORD   equ <dword>
if @Version LT 600
    SWORD   equ <word>
endif

    ; static storage
    DINT    equ <dd>
    DLONG   equ <dd>
    DSHORT  equ <dw>

    ; sizes for fixing SP, stepping through tables, etc.
    ISIZE   equ 4
    LSIZE   equ 4
    SSIZE   equ 2
    NPSIZE  equ 4
    FPSIZE  equ 4

    ; bit shift count to convert byte cnt/ptr to int/long cnt/ptr
    ISHIFT  equ 2		; byte-to-int shift count
    LSHIFT  equ 2		; byte-to-long shift count

    ; sizes dependent upon memory model.  dq -vs- df is not yet clear
    DNPTR equ <dd>		; near pointer
    DFPTR equ <dd>		; far pointer

    DCPTR   equ <dd offset FLAT:>; 32 bit offset only
    DCPTR?  equ <dd>		; No seg override for uninitialized values
    CPSIZE  equ 4
    CPDIST  equ <near>		; code pointers are near
    CPTR    equ <near ptr>

    DDPTR   equ <dd offset FLAT:>
    DDPTR?  equ <dd>
    DPSIZE  equ 4

    CPWORD  equ <dword> 	; code pointers are dwords
    DPWORD  equ <dword> 	; data pointers are dwords

    APIDIST equ <near>		; all API calls are NEAR in the 32 bit model

; macro to declare API functions
EXTAPI	macro	apiname
	extrn pascal apiname:near
endm

else	;--- 16-bit segment ---

    ; parameters and locals
    IWORD   equ <word>
    LWORD   equ <dword>
if @Version LT 600
    SWORD   equ <word>
endif

    ; static storage
    DINT    equ <dw>
    DLONG   equ <dd>
    DSHORT  equ <dw>

    ; sizes for fixing SP, stepping through tables, etc
    ISIZE   equ 2
    LSIZE   equ 4
    SSIZE   equ 2
    NPSIZE  equ 2
    FPSIZE  equ 4

    ; bit shift count to convert byte cnt/ptr to int/long cnt/ptr
    ISHIFT  equ 1		; byte-to-int shift count
    LSHIFT  equ 2		; byte-to-long shift count

    ; sizes dependent upon memory model
    DNPTR equ <dw>		; near pointer
    DFPTR equ <dd>		; far pointer

    if sizeC
	DCPTR	equ <dd>       ; 16 bit segment and 16 bit offset
	DCPTR?	equ <dd>
	CPSIZE	equ 4
	CPDIST	equ <far>      ; code pointers are far
	CPTR	equ <far ptr>
	CPWORD	equ <dword>    ; code pointers are dwords
    else
	DCPTR	equ <dw>       ; 16 bit offset only
	DCPTR?	equ <dw>
	CPSIZE	equ 2
	CPDIST	equ <near>     ; code pointers are near
	CPTR	equ <near ptr>
	CPWORD	equ <word>     ; code pointers are words
    endif

    if sizeD
	DDPTR	equ <dd>
	DDPTR?	equ <dd>
	DPSIZE	equ 4
	DPWORD	equ <dword>    ; data pointers are dwords
    else
	DDPTR	equ <dw>
	DDPTR?	equ <dw>
	DPSIZE	equ 2
	DPWORD	equ <word>     ; data pointers are words
    endif

    APIDIST equ <far>		; API calls are FAR in 16 bit model

; macro to declare API functions
EXTAPI	macro	apiname
	extrn pascal apiname:far
endm

endif	; --- 16/32 segment ---

; Float/double definitions
; (currently the same for 16- and 32-bit segments)

FLTSIZE  equ	4	; float
DBLSIZE  equ	8	; double
LDBLSIZE equ	10	; long double

DFLOAT	 equ	<dd>
DDOUBLE  equ	<dq>
DLDOUBLE equ	<dt>

;
; savelist - Generate a list of regs to be saved by the proc 'uses' option.
;
; Input:
;	reg1, reg2, reg3, reg4 = registers to be saved across function
; Output:
;	reglist = text string of registers that can be passed to the 'uses'
;	option on the 'proc' command.
;

savelist  MACRO   reg1, reg2, reg3, reg4
	local	ws, listsize
	ws	catstr	< >		; whitespace char

	IFNDEF	I386
	 rbx equ <>			; 8086/286 don't save rbx
	ENDIF

	IFNB	    <reg4>
	 reglist     catstr  reg1, ws, reg2, ws, reg3, ws, reg4
	ELSEIFNB    <reg3>
	 reglist     catstr  reg1, ws, reg2, ws, reg3, ws
	ELSEIFNB    <reg2>
	 reglist     catstr  reg1, ws, reg2, ws,       ws
	ELSEIFNB    <reg1>
	 reglist     catstr  reg1, ws,	     ws,       ws
	ELSE
	 reglist     catstr  <>
	ENDIF

	listsize sizestr reglist	; size of register list

	IF	listsize LE 3		; if list is only the 3 ws chars...
	 reglist catstr  <>
	ENDIF

	IFNDEF	I386
	 rbx equ <bx>			; restore rbx
	ENDIF

	ENDM	; savelist

;
; codeseg - Define/declare the standard code segment. Maps to the proper
; form of the .code directive.
;
; Input:
;
; Output:
;	.code _TEXT	; for large code models
;	.code		; for small code models
;	assume	cs:FLAT ; for 386
;	assume	ds:FLAT ; for 386
;	assume	es:FLAT ; for 386
;	assume	ss:FLAT ; for 386
;

codeseg MACRO

if	sizeC
	.code _TEXT
else
	.code
endif

ifdef	I386
if	@Version LT 600
	assume	cs:FLAT
endif	;@Version LT 600
	assume	ds:FLAT
	assume	es:FLAT
	assume	ss:FLAT
endif

	ENDM

;***************************************************************
;*
;*  Debug lab macro
;*
;***************************************************************

lab	macro name
ifdef	DEBUG
    public  pascal name	    ;; define label public for Symdeb
endif
name:
	endm


;***************************************************************
;*
;*  Conditional jump short macros
;*
;***************************************************************


	irp	x,<Z,NZ,E,NE,S,NS,C,NC,P,NP,PE,PO,A,AE,B,BE,NB,G,GE,L,LE>
JS&x	equ   <j&x short>
	endm


;***************************************************************
;*
;*  Global data definition macros
;*
;*  Usage:
;*	globalI   Name, InitialValue, Repeat
;*
;***************************************************************


MakeGlobal  macro   suffix, DataType	    ;; makes all of the global* macros

global&suffix  macro   name, data, rep
public	name
ifb	<rep>
    _repeat = 1
else
    _repeat = (rep)
endif

name	&DataType  _repeat dup( data )
	endm

	endm


    MakeGlobal	T, dt			; globalT
    MakeGlobal	Q, dq			; globalQ
    MakeGlobal	D, dd			; globalD
    MakeGlobal	W, dw			; globalW
    MakeGlobal	B, db			; globalB

%   MakeGlobal	I, <DINT>		; globalI

%   MakeGlobal	DP, <DDPTR>		; globalDP
%   MakeGlobal	CP, <DCPTR>		; globalCP
%   MakeGlobal	FP, <DFPTR>		; globalFP
%   MakeGlobal	NP, <DNPTR>		; globalNP



;***************************************************************
;*
;*  Static data definition macros
;*
;*  Usage:
;*	staticI   Name, InitialValue, Repeat
;*
;***************************************************************


MakeStatic  macro   suffix, DataType	    ;; makes all of the static* macros

static&suffix  macro   name, data, rep

ifdef  DEBUG
    public  pascal name			    ;; make statics public if DEBUG
endif

ifb	<rep>
    _repeat = 1
else
    _repeat = (rep)
endif

name	&DataType  _repeat dup( data )
	endm

	endm


    MakeStatic	T, dt			; staticT
    MakeStatic	Q, dq			; staticQ
    MakeStatic	D, dd			; staticD
    MakeStatic	W, dw			; staticW
    MakeStatic	B, db			; staticB

%   MakeStatic	I, <DINT>		; staticI

%   MakeStatic	DP, <DDPTR>		; staticDP
%   MakeStatic	CP, <DCPTR>		; staticCP
%   MakeStatic	FP, <DFPTR>		; staticFP
%   MakeStatic	NP, <DNPTR>		; staticNP

;***************************************************************
;*
;*  Label definition macros
;*
;***************************************************************
;*
;*  Label definition macros
;*
;*  Usage:
;*	labelI	 Name, {PUBLIC, PASCAL, C}
;*
;***************************************************************

__MakePublic	macro	name, option	;; decides if a label should be
ifidni	<option>, <PUBLIC>		;; made public
    public  name
elseifidni  <option>, <PASCAL>
    public  pascal name
elseifidni  <option>, <C>
    public  C name
elseifb  <option>
    ifdef  DEBUG
	public	pascal name	;; make public if DEBUG
    endif
endif
		endm


if	@Version GE 600

MakeLabel   macro suffix, LabelType	;; makes all of the label* macros

%@CatStr(<label>,<suffix>)	macro	name, option
	__MakePublic	<name>,<option>
name	label	&LabelType
	endm

	endm

else	;!(@Version GE 600)

MakeLabel   macro suffix, LabelType	;; makes all of the label* macros

label&suffix	macro	name, option
	__MakePublic	<name>,<option>
name	label	&LabelType
	endm

	endm

endif	;!(@Version GE 600)


	MakeLabel   T, tbyte	; make labelT
	MakeLabel   Q, qword	; make labelQ
	MakeLabel   D, dword	; make labelD
	MakeLabel   W, word	; make labelW
	MakeLabel   B, byte	; make labelB

	MakeLabel   P, proc	; make labelP
	MakeLabel   FP, far	; make labelFP
	MakeLabel   NP, near	; make labelNP

%	MakeLabel   I, IWORD	; make labelI


labelDP macro	name, option		    ;; labelDP
	__MakePublic	<name>,<option>
ifdef  I386
    if sizeD
	name	label	fword
    else
	name	label	dword
    endif
else	;not I386
    if sizeD
	name	label	dword
    else
	name	label	word
    endif
endif	;not I386
	endm

labelCP macro	name, option		    ;; labelCP
	__MakePublic	<name>,<option>
ifdef  I386
    if sizeC
	name	label	fword
    else
	name	label	dword
    endif
else	;not I386
    if sizeC
	name	label	dword
    else
	name	label	word
    endif
endif	;not I386
	endm


;*
;*  PUSH16 SegReg   - pushes 16 bits in a use32 segment
;*

PUSH16	macro	SegReg

ifdef I386
	nop
	db	66h	    ; operand size over-ride
endif	; I386

	push	SegReg
	endm


;*
;*  JMPFAR16  label - jmps far from a use32 to a use16 segment
;*

JMPFAR16 macro	label

ifndef I386
	error  <JMPFAR16 can only be used in a use32 code segment>
endif	;I386

	nop
	db	66h	    ;; operand size over-ride
	db	0eah	    ;; jmp far immediate op code
	dw	offset label
	dw	seg label
	endm