; Common macro

macro assume name*, base*, type
{
  virtual at base
    if ~ type eq
	name type
	name#.Length = $ - name
    else
	name:
    end if
  end virtual
}

macro call proc*, [args]
{
  common if ~ args eq
  reverse push args
  common end if
	call proc
}

; Linux SEH macro

struc function.seh {}
macro begin.seh name {}
macro return.pop name {}
macro return.seh status, value {}
macro except.seh {}

; Win32 SEH macro

struc function.seh
{
  .next		dd ?
  .proc		dd ?
}

macro begin.seh name
{
  if defined name
	push name
	push dword [fs:0]
	mov [fs:0], esp
  end if
}

macro return.pop name
{
  if defined name
    if defined seh.clear
	call seh.clear, name
	mov esp, [fs:0]
	pop dword [esp]
	pop dword [fs:0]
    else
      local loop, done
	push ecx
	xor ecx, ecx
		db 0x64
      loop:
	mov ecx, [ecx]
	jecxz done
	dec ecx
	jecxz done
	inc ecx
	cmp dword [ecx + 4], name
	jnz loop
	mov [fs:0], ecx
      done:
	pop ecx
	mov esp, [fs:0]
	pop dword [fs:0]
	pop dword [esp - 4]
    end if
  end if
}

macro return.seh status, value
{
       if status in <BREAK>
	mov ecx, [.seh.Context]
	mov esp, ebp
    if ~ value eq
	mov eax, value
	mov [.context.eip], .save.break
	mov [.context.eax], eax
    else
	mov [.context.eip], .save.break
    end if
	xor eax, eax
	pop ebp
	retn
  else if status in <CONTINUE>
	mov esp, ebp
	xor eax, eax
	pop ebp
	retn
  else if status in <RETRY>
	mov esp, ebp
	mov eax, 1
	pop ebp
	retn
  end if
}

struc except.arg
{
  .Exception	dd ?
  .Record	dd ?
  .Context	dd ?
  .Dispatcher	dd ?
}

struc except.cpu
{
  .dwFlags	dd ?
  .dr0		dd ?
  .dr1		dd ?
  .dr2		dd ?
  .dr3		dd ?
  .dr6		dd ?
  .dr7		dd ?
  .fcw		dd ?
  .fsw		dd ?
  .ftag		dd ?
  .ferr.Off	dd ?
  .ferr.Sel	dd ?
  .fdata.Off	dd ?
  .fdata.Sel	dd ?
  .st0		dt ?
  .st1		dt ?
  .st2		dt ?
  .st3		dt ?
  .st4		dt ?
  .st5		dt ?
  .st6		dt ?
  .st7		dt ?
  .cr0.npx	dd ?
  .seg.gs	dd ?
  .seg.fs	dd ?
  .seg.es	dd ?
  .seg.ds	dd ?
  .edi		dd ?
  .esi		dd ?
  .ebx		dd ?
  .edx		dd ?
  .ecx		dd ?
  .eax		dd ?
  .ebp		dd ?
  .eip		dd ?
  .seg.cs	dd ?
  .efl		dd ?
  .esp		dd ?
  .seg.ss	dd ?
}

macro except.seh
{
  assume .seh, ebp + 8, except.arg
  assume .context, ecx, except.cpu
	push ebp
	mov ebp, esp
}

; Generic macro

macro function name*, [save]
{
  common name:
  irps arg, args base conf eax ebx ecx edx ebp esp esi edi efl \{ .flag.#arg = 0 \}
  .flag.eip = 1
  if ~ save eq
  forward if ( .flag.conf = 0 ) & ( save in <register, fastcall, cdecl, stdcall> )
      .flag.conf = 1
    else
      err
    end if
         if save in <eax, ebx, ecx, edx, ebp, esi, edi, efl>
      .flag.#save = 1	; save register into stack
    else if save in <all>
      irgs arg, eax ebx ecx edx ebp edi esi esp efl \{ .flag.#arg = 1 \}
    else if save in <register>
      .flag.args = 0	; leave args bytes into stack
      .flag.base = 0	; float base for stack frame
    else if save in <fastcall>
      .flag.args = 1	; purge args bytes
      .flag.base = 0	; float base for stack frame
    else if save in <cdecl>
      .flag.args = 0	; leave args bytes into stack
      .flag.base = 1	; fixed base for stack frame
    else if save in <stdcall>
      .flag.args = 1	; purge args bytes
      .flag.base = 1	; fixed base for stack frame
    end if
  common end if; ~ save eq
  if .flag.conf = 0	; if not defined config then stdcall
    .flag.args = 1	; purge args bytes
    .flag.base = 1	; fixed base for stack frame
  end if
  if .flag.base = 0
    virtual at esp
  else
    virtual at ebp
      .save.base:
  end if
  .save.vars	rb .flag.locs
  if defined .save.seHandler
    .save.seh	function.seh
  end if
  irps arg, edi esi ebp esp ebx edx ecx eax efl eip \{ if .flag.#arg = 1
      .save.#arg dd ?
    end if \}
  .save.args:
}

macro var
{
  if ~ definite .save.vars
    err
  end if
  if .flag.args = 1
    .flag.args = ( 3 + $ - .save.args ) and ( -4 )
  end if
  end virtual
  virtual at .save.vars
}

macro begin class
{
  if ~ defined .save.args
    err
  end if
  if $ - .save.args > 0
    @@: var
  end if
  if used .Result
    .Result		dd ?
  end if
  .flag.locs = ( 3 + $ - .save.locs ) and ( -4 )
  end virtual
  assume .this, ecx, class
	begin.seh .save.sehandler
  if defined .save.efl
	pushfd
  end if
  if defined .save.esp
	pushad
  else
    irps arg, eax ecx edx ebx ebp esi edi \{ if defined .save.#arg
	 push arg
      end if \}
  end if
  if deined .save.base
	mov ebp, esp
  end if
  if .flag.locs > 0
	lea esp, [esp - .flag.locs]
  end if
  if defined .Result
	mov [.Result], 0
  end if
}

macro return status*, value
{
  if ~ defined .save.args
    if status > 0
	ret status
    else
	ret
    end if
  else if ~ definite .save.sehandler
    if ~ value eq
	mov eax, value
    else if defined .Result
	mov eax, [.Result]
    end if
    if ( defined [.save.eax] ) & (( ~ value eq ) | ( defined .Result ))
	mov [.save.eax], eax
    end if
    if defined .save.base
	mov esp, ebp
    else if .flag.locs > 0
	lea esp, [esp + .flag.locs]
    end if
    if defined .save.sehandler
      return.pop .save.sehandler
    end if
    if ~ defined .save.esp
      irps arg, edi esi ebp ebx edx ecx eax \{ if defined .save.#arg
	  pop arg
	end if \}
    else
	popad
    end if
    if defined .save.efl
	popfd
    end if
         if status in <SUCC>
	clc
    else if status in <FAIL>
	stc
    else if status in <SWAP>
	cmc
    else if ~ status in <DONE>
      err
    end if
    if .flag.args > 0
	retn .flag.args
    else
	retn
    end if
  else if ( definite .save.sehandler ) & ( status in <BREAK, CONTINUE, RETRY> )
    return.seh status, value
  else
    err
  end if
}

macro except
{
  if used .save.break
    .save.break: 
    if defined .Return
	mov [.Return], eax
    end if
    ret FAIL
  end if
  .save.sehandler: except.seh
}

; Sample (just code section)
;
; struc STARTUPINFO { .cb db 47 dup ? } ; test stack alignment
;
; function main, cdecl
;   .hInstance		dd ?
;   .lpStartup		dd ?
;   .lpArgs		dd ?
;   .cbArgs		dd ?
;   ^-----------------------------------------------------------+
; begin								;
;	call [MessageBoxA], 0, [.lpArgs], .title, 0		;
;	mov [.Result], eax					;
;	return SUCC						;
; except							;
;	return BREAK, 1						;
;								;
; function main.entry, register, all				;
; var								;
;   v- the names of args and vars must begin with the '.' char -+
;   .si			STARTUPINFO
; begin
;	call [SetLastError], 0
;	call [GetStartupInfoA], esp
;	test al, al
;	jz .error
;	call [GetModuleHandleA], 0
;	test eax, eax
;	jz .error
;	mov ebx, eax
;	call [GetCommandLineA]
;	test eax, eax
;	jz .error
;	mov edx, eax
;	or ecx, -1
;	mov edi, eax
;	inc edx
;	xor eax, eax
;	repne scas byte [edi]
;	sub edi, edx
;	lea ecx, [.si]
;	dec edx
;	call main, ebx, ecx, edx, edi
;	jmp .exit
;           ^- . . .and local labels too.
;  .error:
;	call [GetLastError]
;  .exit:
;	call [ExitProcess], eax
; except
;	call [ExitProcess], -1
;
; entry main.entry
