Scenix Sasmcond.src
;SAsmCond.src by James Newton
;Structured conditionals for SASM (SXKey 2.0 version)
;Copyright 2000,2001,2002 James Newton <james@sxlist.com>
; This program is free software; you can redistribute it and/or modify
; it under the terms of the GNU General Public License version 2 as published
; by the Free Software Foundation. Note that permission is not granted
; to redistribute this program under the terms of any other version of the
; General Public License.
;
; 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.
;
;expects
CPUCarry = 0 ;or 1
; a variable called temp that can be used for some types of comparisons
;MACROS --------------------------------------------------------------------------
; BinJump <reg>, <Address> [, <Address>]
; Call with the first parameter of the register to tbe tested and
; the following parameters a list of addresses to jump to based on
; the value of the register.
; More effecient than a long jump table for 4 or fewer addresses
; GotoW <Address> [, <Address>]
; Implements a jump table using space in the first low half page of memory.
; must be invoked after all <Address>'s are defined.
; Uses BinJump for less than 5 addresses
; Condition enum (IsZero,Eq,Lt,LE,IsNotZero,NE,Gt,GE,EqN,LtN,LEN,NEN,GtN,GEN)
; enum values ending in N indicate that the second operand will be a constant
; Condition := [<reg>, <enum> | <reg>, <enum>, <reg> | <reg>, <enum>, <constant> ]
; Skz <reg>, [IsZero | IsNotZero]
; Generates a skip if the reg is zero or not zero
; Skc <reg1>, [Eq | Lt | LE | NE | Gt | GE], <reg2>
; Generates a skip if reg1 compaires as specified to reg2
; Skc <reg>, [EqN | LtN | LEN | NEN | GtN | GEN], <constant>
; Generates a skip if reg compaires as specified to constant
; StackPUSH, StackPOP, StackTOS and stack1...
; Provide a compile time stack to record and retrieve the addresses of
; locations were jumps need to be compiled once the jump-to address is
; known. Used by the following macros:
; Repeat
; <statements>
; [forever | while <condition> | until <condition>]
;
; compiles Skz or Skc with jumps to implement a structured loop
; DoIf <condition>
; <statements>
; [
; DoElseIf <condition>
; <statements>
; ]...
; [
; DoElse
; <statements>
; ]
; DoEndIf
;
; Compiles Skz or Skc with jumps to implement a structured conditional
; As many DoElseIf statements as desired may be included because each DoElseIf
; links to the next one at run time so that if the first DoElseIf condition
; is true, after its statements a jump will be compiled that will jump to
; the simular jump after the next DoElseIf statements. To avoid this extra
; run time, use DoSelect.
; DoSelect
; [
; DoCase <condition>
; <statements>
; ]...
; [
; DoCaseElse
; <statements>
; ]
; DoEndSelect
;
; Compiles Skz or Skc with jumps to implement a structured conditional
; A limited number of DoCase statments can be compiled because each
; case compiles a jump to the end of the select after the statements
; following the case condition and recording the position were these
; jumps must be org'd takes up space on the "stack" provided by
; StackPUSH, StackPOP and stack1...15
;See lable "Main" for start of examples
ConditionBase equ $0
IsZero equ ConditionBase + %0000
Eq equ ConditionBase + %0001
Lt equ ConditionBase + %0010 ;2
LE equ ConditionBase + %0011 ;3
IsNotZero equ ConditionBase + %0100 ;8
NE equ ConditionBase + %0101 ;9
GE equ ConditionBase + %0110 ;10
Gt equ ConditionBase + %0111 ;11
EqN equ ConditionBase + %1001
LtN equ ConditionBase + %1010 ;2
LEN equ ConditionBase + %1011 ;3
NEN equ ConditionBase + %1101 ;9
GEN equ ConditionBase + %1110 ;10
GtN equ ConditionBase + %1111 ;11
; dabc
SkMskConst equ %1000
;column "d" (mask 8) shows which compare registers with constants and which with registers.
SkMskSwap equ %0100
;column "a" (mask 4) shows which are exact opposites of one another.
; e.g. Eq is the opposite of NE, Lt of GE, LE of Gt
SkMskNeq equ %0010
;column "b" (mask 2) shows which are inequalities and which are equalitites
SkMskC equ %0001
;column "c" (mask 1) differentiates the inequalities
SkMskFlip equ %0101
;Xor with condition to flip the inequality around X op Y becomes Y op X
Skc MACRO 3
local SkcBank,pX,tst,pY
noexpand
;Usage: Skc pX, Condition, pY
pX = \1
tst = \2
pY = \3
SkcBank = 0
IF tst & SkMskConst
IF pX == WReg && ((tst & SkMskNeq) > 1)
expand
mov temp, w ;WARNING! temp modified in macro.
noexpand
pX = temp
ENDIF
IF tst == GtN || tst == LEN
expand
mov w, #(pY + 1)
noexpand
;if tst was GtN its now GE if it was LEN its Lt
tst = (tst ^ SkMskC) & ~SkMskConst
ELSE ; tst == GEN, LtN, NEN, EqN
IF pX == WReg
pX = pY
ELSE
expand
mov w, #pY
noexpand
tst = tst & ~SkMskConst
ENDIF
ENDIF
pY = WReg
ENDIF
IF pX == WReg
IF (tst & SkMskNeq) > 1
;Flip the operation around.
tst = tst ^ SkMskFlip
ENDIF
pX = pY
pY = WReg
ENDIF
;At this point, pX is NOT w
IF pY != WReg
IF pY>$0F ;are we about to access a non-global register?
expand
bank pY ;non-global
noexpand
SkcBank = pY / $10
ENDIF
IF tst == Gt || tst == LE
expand
mov w, ++pY
noexpand
;if tst was Gt its now GE if it was LE its Lt
tst = tst ^ SkMskC
ELSE ; tst = GE, Lt, Eq, NE
expand
mov w, pY
noexpand
ENDIF
pY = WReg
ENDIF
;At this point, pY is in W. pX is a register or a constant
IF pX>$0F && (pX / $10) != SkcBank && tst & SkMskConst == 0
;are we about to access a non-global register in a new bank?
expand
bank pX ;non-global
noexpand
ENDIF
IF tst == Eq || tst == NE || tst == EqN || tst == NEN
IF tst == EqN || tst == NEN
expand
xor w, #pX
noexpand
tst = tst & ~SkMskConst
ELSE
expand
xor w, pX
noexpand
ENDIF
IF tst == Eq
expand
sz
noexpand
ELSE
expand
snz
noexpand
ENDIF
ELSE
IF CpuCarry
IF tst == Gt || tst == LE
expand
clc
noexpand
ELSE
expand
stc
noexpand
ENDIF
ENDIF
expand
mov w, pX - w
noexpand
IF tst == Lt || (tst == LE && CpuCarry)
expand
snc
noexpand
ELSE
expand
sc
noexpand
ENDIF
ENDIF
IF ( ( tst == Gt) || (tst == LE) ) && ! CpuCarry
expand
snz
noexpand
ENDIF
IF tst == Gt && !CpuCarry
expand
skip
noexpand
ENDIF
ENDM
Skz MACRO parm, cond
;Usage: Skz register, [IsZero | IsNotZero]
noexpand
IF parm > $0F
expand
bank parm ;non-global
noexpand
ENDIF
expand
test parm
noexpand
IF cond == IsZero
expand
sz
noexpand
ELSE
IF cond == IsNotZero
expand
snz
noexpand
ELSE
error 'Usage: Skz register, [IsZero | IsNotZero]'
ENDIF
ENDIF
ENDM
StackTOS = -1
stacklet MACRO ptr, val
stack??ptr = val
ENDM
stackget MACRO ptr
stackTOS = stack??ptr
ENDM
stackptr = 0
stackTOS = 0
stackpush MACRO parm
stackptr = stackptr + 1
stacklet ?(stackptr), ?(stackTOS)
stackTOS = parm
ENDM
stackpop MACRO
if stackptr == 0
error 'stack underflow'
endif
stackget ?(stackptr)
stackptr = stackptr - 1
ENDM
StackPUSH 1
StackPUSH 2
StackPUSH 3
StackPUSH 4
StackPUSH 5
StackPUSH 6
StackPUSH 7
StackPUSH 8
StackPUSH 9
StackPUSH 10
StackPUSH 11
StackPUSH 12
StackPUSH 13
StackPUSH 14
StackPUSH 15
StackPOP
StackPOP
StackPOP
StackPOP
StackPOP
StackPOP
StackPOP
StackPOP
StackPOP
StackPOP
StackPOP
StackPOP
StackPOP
StackPOP
StackPOP
;StackPOP
expand
Repeat MACRO
noexpand ;incase expand was already on.
StackPUSH $
ENDM
Until MACRO
noexpand
IF \0 == 2
Skz \1,\2
ELSE
Skc \1,\2,\3
ENDIF
expand
jmp @StackTOS
noexpand
StackPOP
ENDM
While MACRO
noexpand
IF \0 == 2
Skz \1,\2^SkMskSwap
ELSE
Skc \1,\2^SkMskSwap,\3
ENDIF
expand
jmp @StackTOS
noexpand
StackPOP
ENDM
WhileNotZeroDec MACRO 1
noexpand
expand
djnz \1, @StackTOS
noexpand
StackPOP
ENDM
WhileNotZero MACRO 0
noexpand
expand
jnz @StackTOS
noexpand
StackPOP
ENDM
WhileZero MACRO 0
noexpand
expand
jz @StackTOS
noexpand
StackPOP
ENDM
WhileNoCarry MACRO 0
noexpand
expand
jnc @StackTOS
noexpand
StackPOP
ENDM
WhileCarry MACRO 0
noexpand
expand
jc @StackTOS
noexpand
StackPOP
ENDM
Forever MACRO
noexpand ;incase expand was already on.
expand
jmp @StackTOS
noexpand
StackPOP
ENDM
link MACRO 2
local ataddr, toaddr, temp
noexpand
ataddr = \1
toaddr = \2
temp = $
org ataddr ; go back
jmp @toaddr ;<- jmp to here
org temp ; come forward
ENDM
DoIf MACRO
local ComeFrom
noexpand
IF \0 == 2
Skz \1,\2
ELSE
Skc \1,\2,\3
ENDIF
;***Save place to link failure of this test to the Else, ElseIf or EndIf code
StackPUSH $ ;save space here for a jmp
expand
:ComeFrom=$
;mp +FAIL
noexpand
org $+2
ENDM
DoElseIf MACRO
local ComeFrom, ComeFromTo, FAIL, SUCCEED
noexpand
;***If there is a previous succeed place, link it to this one
IF (StackTOS >> 24) > 0
link (StackTOS - (StackTOS >> 24)), $
ENDIF
;***Setup place to Link the prev DoIf or DoElseIf success code out to the DoEndIf
:S = $
expand
:ComeFrom=$
;mp +SUCCEED
noexpand
org $+2
;***Link the last DoIf or DoElseIf fail to the DoElseIf code
ComeFromTo = ( (StackTOS & $FFFFFF) << 16 ) + $
link (StackTOS & $FFFFFF), $
expand
:FAIL =ComeFromTo
noexpand
IF \0 == 2
Skz \1,\2
ELSE
Skc \1,\2,\3
ENDIF
;***Save place to link failure of this test to the Else, ElseIf or EndIf code
StackTOS = ($ - :S)<<24 + $
expand
:ComeFrom=$
;mp +FAIL
noexpand
org $+2
ENDM
DoElse MACRO
local ComeFrom, ComeFromTo, FAIL
noexpand
;***If there is a previous succeed place, link it to this one
IF (StackTOS >> 24) > 0
link (StackTOS - (StackTOS >> 24)), $
ENDIF
;***Setup place to Link the prev DoIf or DoElseIf success code out to the DoEndIf
:S = $
expand
:ComeFrom=$
;mp +SUCCEED
noexpand
org $+2 ; and leave space for it
;***Link the last DoIf or DoElseIf fail to the DoElse code
link StackTOS, $
ComeFromTo = (StackTOS << 16) + $
expand
:FAIL =ComeFromTo
noExpand
StackTOS = :S
ENDM
DoEndIf MACRO
local SUCCEED, FAIL, ComeFromTo
noexpand
;***If there is a previous succeed place, link it to this one
IF (StackTOS >> 24) > 0
link (StackTOS - (StackTOS >> 24)), $
ComeFromTo = ( (StackTOS - (StackTOS >> 24)) << 16 ) + $
expand
:SUCCEED=ComeFromTo
noexpand
ENDIF
link (StackTOS & $FFFFFF), $
ComeFromTo = ( (StackTOS & $FFFFFF) << 16 ) + $
expand
:FAIL =ComeFromTo
noexpand
StackPOP
ENDM
DoSelect:Level = 0
DoCase:Count = 0
DoCase:F = 0
DoSelect MACRO
noexpand
StackPUSH DoCase:Count - 1 ;can't push a zero
DoCase:Count = 0
StackPUSH DoCase:F + 1 ;can't push a zero
DoCase:F = 0
DoSelect:Level = DoSelect:Level + 1
ENDM
DoCase MACRO
local ComeFrom, ComeFromTo, FAIL
noexpand
DoCase:Count = DoCase:Count - 1
IF DoCase:Count < -1
;***Setup place to Link the prev Case success code out to the end
StackPUSH $
expand
ComeFrom = $
;mp +SUCCEED
noexpand
org $+2
;***Link the last fail to this DoCase test code
link DoCase:F, $
ComeFromTo = (DoCase:F << 16 ) + $
expand
:FAIL =ComeFromTo
noexpand
ENDIF
IF \0 == 2
Skz \1,\2
ELSE
Skc \1,\2,\3
ENDIF
;***Save place to link failure of this test to the Else, ElseIf or EndIf code
DoCase:F = $
expand
;mp +FAIL
noexpand
org $+2
ENDM
DoCaseElse MACRO
local ComeFrom, ComeFromTo, FAIL
noexpand
;***Setup place to Link the prev DoCase success code out to the DoCaseEnd
StackPUSH $
expand
ComeFrom = $
;mp +SUCCEED
noexpand
org $+2
;***Link the last fail to the DoCaseElse code
link DoCase:F, $
ComeFromTo = (DoCase:F << 16) + $
DoCase:F = 0
expand
:FAIL =ComeFromTo
noExpand
ENDM
DoCaseEnd MACRO
local ComeFromTo, FAIL, SUCCEED
noexpand
;***If there is a previous succeed place, link it to this one
IF DoCase:Count < 0
REPT 0 - DoCase:Count
link StackTOS, $
ComeFromTo = ( StackTOS << 16 ) + $
expand
:SUCCEED=ComeFromTo
noexpand
StackPOP
ENDR
ENDIF
IF DoCase:F > 0
link DoCase:F, $
ComeFromTo = (DoCase:F << 16) + $
expand
:FAIL =ComeFromTo
noexpand
ENDIF
DoSelect:Level = DoSelect:Level - 1
DoCase:F = StackTOS - 1
StackPOP
DoCase:Count = StackTOS + 1 ;correct for -1 when pushed.
StackPOP
ENDM
doifadr = 0
doendifadr = 0
doelsifadr = 0
doifl = 0
pageaddr = -1
;Can't use binjmp2 with forward references
; because of a bug in SASM,
; where MAIN:FOUR is a forward reference
; 361 m ifdef MAIN:FOUR
; 362 m if (pageaddr == MAIN:FOUR >> 9)
;****** macrotest.SRC(219) Line 362, Error 3, Pass 1:
; Symbol <MAIN:FOUR> is not defined
;if you really need forward references use this instead:
;binjmp2 MACRO index, one, two
; expand
; jnb index.0,@one
; jmp @two
; noexpand
; endm
binjmp2 MACRO index, one, two
;assumes that pageaddr is set to current value of page bits on entry.
noexpand
pageaddr = $>>9
ifdef one
if (pageaddr == one >> 9)
expand
jnb index.0,one
noexpand
else
pageaddr = $ >> 9
expand
jnb index.0,@one
noexpand
endif
else
pageaddr = $ >> 9
expand
jnb index.0,@one
noexpand
endif
ifdef two
if (pageaddr == two >> 9)
expand
jmp two
noexpand
else
pageaddr = $ >> 9
expand
jmp @two
noexpand
endif
else
pageaddr = $ >> 9
expand
jmp @two
noexpand
endif
endm
binjmp4 MACRO index, one, two, three, four
local :1set
;assumes that pageaddr is set to current value of page bits on entry.
if (pageaddr == ($+9)>>9) ;:1set can't be more than 9 words away.
expand
jb index.1, :1set
noexpand
else
expand
sb index.1
page :1set
jmp :1set
noexpand
endif
binjmp2 index, one, two
expand
:1set
noexpand
pageaddr = $ >> 9 ;can't get here except by jump and so must be paged
binjmp2 index, three, four
endm
binjump MACRO
local :2Set, :1Set, parms, index
parms = \0 - 1
index = \1
;Call with the first parameter of the register to tbe tested and
;the following parameters a list of addresses to jump to based on
;the value of the register.
;More effecient than a long jump table for 4 or fewer addresses
expand
page $
noexpand
pageaddr = $ >> 9
if parms > 4
if parms == 5
expand
jb \1.2, @\6 ;=4
noexpand
binjmp4 \1,\2,\3,\4,\5
else ;6 or more
if (pageaddr == ($+19)>>9)
expand
jb \1.2, :2Set ;>4 ;@$+16
noexpand
else
expand
sb \1.2
page :2set
jmp :2set ;>4 ;@$+16
noexpand
endif
binjmp4 \1,\2,\3,\4,\5
expand
:2Set
noexpand
pageaddr = $ >> 9 ;can't get here except by jump and so must be paged
if parms > 6
if parms > 7
binjmp4 \1,\6,\7,\8,\9
else ;7
expand
jb \1.2, @\8 ;=2 or 6
noexpand
binjmp4 \1,\2,\3,\6,\7
endif
else ;6
binjmp2 \1,\6,\7
endif
endif
else ;4 or less
if parms > 2
if parms == 3
expand
jb \1.1, @\4 ;=2 or 6
noexpand
binjmp2 \1,\2,\3
else ;4
binjmp4 \1,\2,\3,\4,\5
endif
else ;2
binjmp2 \1, \2, \3
endif
endif
endm
GotoW MACRO
local _SaveAddr, _GotoWPage, _GotoWTableBegin
noexpand
;must be invoked after all parameters are defined
;i.e. no forward references.
;if you manually expand the macro, forward refs may work?
_SaveAddr = $
_GotoWPage = _SaveAddr >> 9
REPT \0
IF (\% >> 9) != (_SaveAddr >> 9)
_GotoWPage = (\% >> 9) ;
ENDIF
ENDR
;Do we need long jumps?
; we do if any of the jumps are to pages other than where the table is built.
; and we have to build the table in page 0 if we are not in a low half page
IF _GotoWPage != (_SaveAddr >> 9) || ((_SaveAddr // $200) > $FF) ;has to be a long jump table
IF \0 > 127
ERROR 'Long jumps must be used so no more than 127 entries can be supported'
ENDIF
IF \0 == 2
binjump WReg, \1, \2
EXITM
ENDIF
IF \0 == 3
binjump WReg, \1, \2, \3
EXITM
ENDIF
IF \0 == 4
binjump WReg, \1, \2, \3, \4
EXITM
ENDIF
;find a place to build the table
; how about right here? are we in a low half page? Do we have room?
IF (($ & $100) == 0) && ( (\0*2) < ( $100 - ($ & $FF) ) )
expand
clc
noexpand
if myopts & ~OptRTCisW == 0 then
expand
rl WReg ;need long jumps
;WARNING: Insure OPTION:RWT = 0
noexpand
else
expand
mov temp, w
add w, temp
;WARNING: temp modified by Macro
noexpand
endif
ELSE
;can't build it where we are... how about in the space we set aside?
IF LowHalfPage + (\0*2) + 1 > HighHalfPage
ERROR 'Out of LowHalfPage Space'
ENDIF
expand
org LowHalfPage
noexpand
_GotoWPage = -1
ENDIF
ELSE
;wow! All the destinations are in this page!
IF \0 > 255
ERROR 'No more than 255 entries can be supported'
ENDIF
IF \0 == 2
binjump WReg, \1, \2
EXITM
ENDIF
expand
page $+2
noexpand
ENDIF
expand
_GotoWTableBegin = $
add PC,W ;jump to the jump
noexpand
REPT \0
IF _GotoWPage == -1
expand
jmp @\%
noexpand
ELSE
expand
jmp \%
noexpand
ENDIF
ENDR
IF _GotoWPage == -1 ;located in low half page space
LowHalfPage = $
expand
org _SaveAddr
clc
noexpand
if myopts & ~OptRTCisW == 0 then
expand
rl WReg ;need long jumps
;WARNING: Insure OPTION:RWT = 0
noexpand
else
expand
mov temp, w
add w, temp
;WARNING: temp modified by Macro
noexpand
endif
expand
page _GotoWTableBegin
jmp _GotoWTableBegin
noexpand
ENDIF
ENDM
file: /Techref/scenix/sasmcond.src, 17KB, , updated: 2002/12/19 16:56, local time: 2024/11/20 09:23,
|
| ©2024 These pages are served without commercial sponsorship. (No popup ads, etc...).Bandwidth abuse increases hosting cost forcing sponsorship or shutdown. This server aggressively defends against automated copying for any reason including offline viewing, duplication, etc... Please respect this requirement and DO NOT RIP THIS SITE. Questions? <A HREF="http://linistepper.com/Techref/scenix/sasmcond.src"> scenix sasmcond</A> |
Did you find what you needed?
|