What is asmc if I may ask?
Asmc is basically a modified version of JWasm made in 2011. It was used as a TASM clone to maintain some DOS tools at the time.
https://github.com/nidud/asmc/blob/master/source/dos/tasm/tasm.asmI later added some enhancement to the HLL section and a few other things, so I have been playing with it for some time. However the features added so far seems stable so in that sense the playtime has come to a conclusion in a somewhat usable product.
Some more info from the help file:
Asmc Macro Assembler ReferenceThis lists some of the differences between Asmc, JWasm, and Masm.
Asmc ExtensionsThe main goal with Asmc is an attempt to provide more readability to the assembly language based on Masm syntax but at the same time keep compatibility with existing source code. In order to achieve this some of the main core of the assembler has to be rewritten and the HLL section enhanced as discussed below.
Parsing of labelsAll expansions are pre-processed by the assembler and this may expand macros and other directives before labels. If a macro is added at the same line as a label this may fail.
Example:
foo macro reg
bswap reg
exitm <reg>
endm
do: mov eax,foo( eax )
...
mov ecx,"3210"
jmp do
As a result the code produced by the macro will be expanded above the label and thus the jump will fail.
bswap ecx
do: mov eax,ecx
...
Asmc will expand the line left to right in this case.
Expansion of macrosThe label issue becomes a problem in the HLL section where labels are created later:
.WHILE macro(...)Asmc will for this reason delay expansion of macros in some of the HLL directives until labels are created. This include .WHILE, .ELSEIF, and .CASE.
The invoke directiveIn Asmc a macro is handled at the same level as a procedure. The header file may then control the expansion:
ifdef __INLINE__
strlen macro string
...
endm
else
strlen proto :dword
endif
This is achieved by simply excluding invoke as appose to allow invocations of macros.
strlen( esi )Asmc sees the combination of a procedure followed by an open bracket as invoke. Empty brackets will be given special handling if the token in front is not a macro.
plabel proto
extern elabel:dword
clabel:
call eax
call plabel
call elabel
call clabel
call xlabel
eax()
plabel()
elabel()
clabel()
xlabel()
xlabel:
This simple solution avoids breaking any existing code with a few exceptions:
Masm allows brackets to access memory.
.if edx < foo( 1 )
; MASM: cmp edx,foo+1
; ASMC: invoke foo, 1 : cmp edx,eax
So
square brackets should be used for accessing memory and round brackets to execute. However, an error must then be issued if Asmc extensions are turned off and labels are accessed using round brackets to ensure compatibility.
The inside of brackets may be recursive used at any length including C-strings. However, the return code for a procedure is [R|E]AX so there is a limit with regards to OR/AND testing of nested functions.
.if foo( bar( 1 ), 2 ) == TRUEHandling of stringsGiven "quoted strings" may be used as arguments, or in general as a const value, C-strings are limited to be used inside brackets of a procedure.
.if fopen( "readme.txt", "rt" )
@CStr(
string )
Macro function that creates a string in the .DATA segment. The macro accepts C-escape characters in the string. Strings are added to a stack and reused if duplicated strings are found. The macro returns offset string.
Example:
mov eax,@CStr( "\tCreate a \"C\" string: %s%d\n" )
mov ebx,@CStr( "string: %s%d\n" )
mov ecx,@CStr( "%s%d\n" )
mov edx,@CStr( "%d\n" )
mov edi,@CStr( "\n" )
Generated code:
.data
DS0000 db 9,"Create a ",'"',"C",'"'," string: %s%d",10,0
.code
mov eax,offset DS0000
mov ebx,offset DS0000[14]
mov ecx,offset DS0000[22]
mov edx,offset DS0000[24]
mov edi,offset DS0000[26]
@DateThe system date in the format
yyyy-mm-dd (text macro).
.SWITCHThe switch comes in three main variants: a structured switch, as in Pascal, which takes exactly one branch, an unstructured switch, as in C, which functions as a type of goto, and a control table switch with the added possibility of testing for combinations of input values, using boolean style AND/OR conditions, and potentially calling subroutines instead of just a single set of values.
The control table switch is declared with no arguments and each .CASE directive does all the testing.
.switch
.case strchr( esi, '<' )
.case strchr( esi, '>' )
jmp around
...
.endsw
The unstructured switch works as a regular C switch where each .CASE directive is just a label.
.switch eax
.case 0: .repeat : movsb
.case 7: movsb
.case 6: movsb
.case 5: movsb
.case 4: movsb
.case 3: movsb
.case 2: movsb
.case 1: movsb : .untilcxz
.endsw
The structured switch works as a regular Pascal switch where each .CASE directive is a closed branch.
.switch eax
.case 1: printf("Gold medal")
.case 2: printf("Silver medal")
.case 3: printf("Bronze medal")
.default
printf("Better luck next time")
.endsw
.CASECase opens a case statement. The case statement compares the value of an ordinal expression to each selector, which can be a constant, a subrange, or a list of them separated by commas.
The selector field is separated from action field by Colon or a new line.
.CASE 1: mov ax,2 : .ENDC
.CASE 2
mov ax,3
.ENDC
.CASE al
.CASE 0,1,4,7
.CASE 0..9
In the control table switch .CASE is equal to .IF:
.CASE al
.CASE ax <= 2 && !bx
.ENDC.ENDC closes a .CASE statement.
The name was separated from BREAK to have more flexibility with regards to control flow of loops. However, ENDC have the same qualities as BREAK and thus can be used in combination with .IF:
.ENDC .IF al == 2.DEFAULT.DEFAULT executes when none of the other cases match the control expression.
.ENDSW.ENDSW closes a .SWITCH statement.