NASM - The Netwide Assembler
NASM Forum => Programming with NASM => Topic started by: ben321 on March 27, 2015, 04:23:47 AM
-
Is there an x86 mnemonic for Select Case, where you have multiple possible values that AX can be (maybe 10 different valid values) and each one should jump to a different piece of code?
-
Sure. When the question is: "can I do this in assembly?", the answer is always "yes". Do you imagine that HLLs have access to some instructions that the guys who wrote Nasm didn't know about?
There's no special mnemonic, though. You just use the regular ones.
Either:
cmp ax, 1
je loadpic1
cmp ax, 2
je loadpic2
...
...
cmp ax, 10
je loadpic10
; and you probably want a "default"
jmp ask_again
Or:
; 1 to 10 in ax
; we want index to start with 0
dec ax
mov bx, ax ; pesky 16-bit addressing modes
add bx, bx ; addresses are two bytes each
jmp [table + bx]
...
; possibly in a ".data" section
; outside your execution path, anyway!
table dw loadpic1, loadpic2, loadpic3, ... loadpic10
Easier in 32-bit code, with the more flexible addressing modes: jmp [mytable + eax * 4] (with the table being "dd" of course).
(I know this as "switch case", but I'm pretty sure we're talking about the same thing.)
Best,
Frank
-
What is the easiest way to do "jump if the value is between this high number, and that low number, inclusive on both high and low"? Is there an easy way to do this?
-
cmp al, low
jb not_this_one
cmp al, high
ja not_this_one
this_one:
ASSuming unsigned numbers...
Best,
Frank
-
This may be a little belated, but I notice folks still preview this topic.
I put together some macros that simulate SWITCH-CASE-ENDCASE capability for a book I am writing about Inline Assembly Code using NASM.
REM -----------------------------------------------------------------------
REM Section 9.10.2 Demonstrate SWITCH-CASE Selection Statements
REM PACKAGE: CSTRUCT.inc
REM -----------------------------------------------------------------------
_asm
extern _printf
;----------------------------------------------------------------------
; SWITCH-CASE directives provide multiple-selection capability
; General form:
; SWITCH expression
; CASE argument1
; statements
; BREAK
; CASE argumentN
; statements
; BREAK
; DEFAULT
; statements
; ENDSWITCH
;----------------------------------------------------------------------
;----------------------------------------------------------------------
; SWITCH expression
; expression : DWORD integer control value or TYPE range with IS
; PURPOSE: Macro provides multiple-selection capability
; USAGE: SWITCH directive initializes the control value or detects that
; a TYPE range is being used as signified by the IS keyword. IS
; tells SWITCH to make sure literals are of the same TYPE.
;----------------------------------------------------------------------
%imacro SWITCH 1-2
%push SWITCH ; Push context
%assign %$case 1 ; Assign 1 to the nextcase index
%if %0 = 1 ; Check if only one argument passed
mov eax, dword %1 ; One argument = non-enumerated type
%else ; Two arguments = enumerated type
%ifidni %2, IS ; Check for correct keyword
%define __TYPE %1.type ; Set __TYPE flag with argument TYPE
%define __ARG %1 ; Preserve argument
mov eax, dword %1 ; Copy argument value into EAX
%else
%error SWITCH %1, %2 uses invalid keyword %2 instead of IS.
%endif
%endif
%endmacro
;----------------------------------------------------------------------
; CASE argument
; argument : comparison value, which must evaluate to an integer
; PURPOSE: Macro provides action statements if CASE statement is true
; USAGE: Compares CASE argument with SWITCH expression or TYPE range
;----------------------------------------------------------------------
%imacro CASE 1
%ifctx SWITCH ; Verify context
%$next%$case:
%assign %$case %$case+1 ; Increment nextcase index value
%ifdef __TYPE ; Check if TYPE flag is set with a type
%assign _j __TYPE ; Copy TYPE ID value to _j
%ifn %1.type = _j ; Verify literals are of the same TYPE
%error __ARG and %1 literals are different TYPES.
%endif
%endif
cmp eax, %1 ; Compare SWITCH value with CASE value
jne %$next%$case ; Jump to next case if not equal
%else
%error "CASE is not allowed outside a SWITCH block."
%endif
%endmacro
;----------------------------------------------------------------------
; DEFAULT (no parameters) (optional)
; PURPOSE: Macro provides default action statements when all CASE
; statements fail. Use of DEFAULT case is optional.
; USAGE: Must be last selection statement before ENDSWITCH if used.
; WARNING: If the DEFAULT case is not used and none of the CASE
; statements evaluate to true, then program flow passes to the
; instruction following the ENDSWITCH directive.
;----------------------------------------------------------------------
%imacro DEFAULT 0
%ifctx SWITCH ; Verify context
%define __DEFAULT ; Show DEFAULT case is present
%$next%$case:
%else
%error "DEFAULT is not allowed outside a SWITCH block."
%endif
%endmacro
;----------------------------------------------------------------------
; BREAK (no parameters)
; PURPOSE: Performs immediate exit from CASE statements
; USAGE: Used at the end of a CASE block when an early exit is desired.
; There are no checks for missing BREAK directives.
;----------------------------------------------------------------------
%imacro BREAK 0
%ifctx SWITCH ; Verify context
jmp %$break ; Immediate exit from context block
%elifctx IF
jmp %$break
%else
%error "BREAK is not allowed outside a CASE block."
%endif
%endmacro
;----------------------------------------------------------------------
; ENDSWITCH (no parameters)
; PURPOSE: Closes out SWITCH-CASE statement blocks and removes context
; USAGE: Must be used at the end of every SWITCH block. A missing
; ENDSWITCH directive is indicated by a
; '..@XX._endswitch undefined' error message
;----------------------------------------------------------------------
%imacro ENDSWITCH 0
%ifctx SWITCH ; Verify context
%$break: ; Jump here from a BREAK directive
%ifndef __DEFAULT ; Check if DEFAULT
%$next%$case: ; If not, make ENDSWITCH the nextcase
%endif
%undef __DEFAULT ; Undefine __DEFAULT if defined
%undef __TYPE ; Undefine __TYPE if defined
%undef __ARG
%pop SWITCH ; Remove context from context stack
%else
%error "ENDSWITCH is not allowed outside a SWITCH block."
%endif
%endmacro
;----------------------------------------------------------------------
; Supporting Macros
;----------------------------------------------------------------------
%imacro output.int 1
push dword %1
push fmtsint
call _printf
add esp, 8
%endmacro
;----------------------------------------------------------------------
section .data
value dd 500
orange dd 500
fmtsint db "%lu", 10, 0
red equ 100
white equ 200
blue equ 300
true equ 1
false equ 0
Monday equ 1
Tuesday equ 2
Wednesday equ 3
Thursday equ 4
Friday equ 5
Today equ 3
Today.type equ 100
Monday.type equ 100
Wednesday.type equ 100
section .text
SWITCH 200 ; Control value = 200
CASE red ; Evaluates to false
output.int red
BREAK
CASE white ; Evaluates to true because white = 200
output.int white ; Print 200
BREAK
CASE blue
output.int blue
BREAK
ENDSWITCH
SWITCH [value] ; Control value = 500
CASE red ; These 3 CASE statements evaluate to false
CASE white
CASE blue
output.int false
BREAK
CASE [orange] ; Evaluates to true because orange = 500
output.int true ; Print 1 (true)
BREAK
DEFAULT
output.int false
ENDSWITCH
SWITCH Today, IS ; Today = 3
CASE Monday
output.int Monday
BREAK
CASE Wednesday
output.int Wednesday ; Print 3
BREAK
DEFAULT
output.int 0
ENDSWITCH
_endasm
Don't worry about the "IS" keyword. I included this as part of the code to create and operate on enumeration types such as found in C or Ada. You can remove code dealing with this topic. Enumeration types and type checking are implemented using another set of macros. Note that I did not replicate a CONTINUE macro as I have really found no use for it in any of my assembly code that utilizes the SWITCH-CASE construct.
Logman