Page 1 of 1

My own little assembler

Posted: Mon Jun 08, 2015 6:23 am
by Hobbit1972
I'm not sure if it is of interest, but here what my assembler looks like:

[I follow the usual style: [...] are optionals etc.]

For now I only have implemented the original NMOS 6502 and 8085 as I was not working with other CPU types, but extension to CMOS or other architectures should be rather easy, if I ever need it.


Invocation:

Code: Select all

rasm infile[.end][;/:] [output[.end]] [listfile]
With ';' the endings of the filenames are added if not given by the user, ':' the same but skips the listfile.
If the output has '.hex' the asm will produce no binary but an ASCII hex-file (with checksum, intended for serial transmission).

Operators:

Code: Select all

+ - * / ;basic arithmetic
> ;Hi-Byte of value
< ;Lo-Byte of value
~ ;NOT
! ;OR
& ;AND
^;XOR
>> ;SHR
<< ;SHL
(..) ;parentheses for more complicated math - they can't be first place in a statement LDA (xx),y => you have to write LDA +(1+3)*5,y to be not confused w/ addressing mode
?[i]<label>[/i] ;returns 1 if the label is undefined or if a library label is unused
! ;returns 0 if the label is undefned or a library is unused
++ --			;post- or pre-inc-/decrement, like in c
* ;also is the program counter
=,<>,>=,<= ;comparison, returns 1 or 0 if true or false

Those are my pseudops:

Code: Select all

;this is a comment
/// and this
is a block of
/// coment -- for commenting out sections of code in a fast way

.CODE +|-	;turns code generation on or off, used for generating virutal tables:
*=0 ;zp space
.cod -
zpptr1 .wor 0
zpptr2 .wor 0
tmpbyte .byt0
.cod +
*=$c000 ;start of actual program

.ILL +|- ;allows or forbids NMOS undocumented opcodes
.CPU cpu ;defines target CPU: 6502,65N02,65N02_ILL,65C02,Rockwell65C02,WDC65C02,65C02S,WDC65C816 - but olny NMOS is fully implemented yet
The asm uses an enviroment variable, if none given it looks for .INC/.BIN/... in the directory 'include' inside program file's directory, recursing through all subdirectories:

Code: Select all

.INC name		;includes the file "name" and processes it as asm-code
.BIN name|"name" [,+<skip>][,<len>] ;file is incorporated as binary, if +skip is given, <skip> bytes are skipped, if <len> is given this is the number of bytes to be incorporated.
.PATH "list" ;adds directories where to look for .inc - just like DOS PATH
.EXPORT "filename" ;defines the name of a file where to export label values - for generating ROM-maps
.EXPORT <libraryname>|<makname> ;macros or libraries are exported
The basic pseudops for data:

Code: Select all

.BYTE <val>[,val...]	;list of byte values
.WORD <val>[,val...]	;liste of word values
.FILL <address>[,byteval=$FF] ;fill from * to address-1 with byte-val ($ff is default) - .e.g. for filling space till Vectors: .fill $fffa,$ea
.FLOAT <float>[,float] ;produces floating point data in 5--byte-CBM format
.GRAPH ;producing data for character maps or C64-Sprites
		.GRAPH 10100011		;single-color
		.GRAPH ..--||##		;multi-color (..=00,--=01,||=10,##=11)
The pseudops for strings are more complicated. Strings can be translated (the CBM PETSCII is a little different from ASCII, so versatile translation is built in:

Code: Select all

.ASC "string"|byteval[,...]	;liste of strings and/or byte values
.ASC "Hallo\$12\x34\255 ;= .asc "Hallo",$12,$34,$ff
.ASC00 ;like .asc but adds a 0 at the end of string
.ASC80 ;like .asc but the last character gets OR$80 (=for printstr routines or like the CBM BASIC token tables are done)
.ASCLEN ;like asc, but the length of the string is added [i]in front of[/i] the string, e.g. .asclen "Hallo" = .asc 5,"Hallo" - for fast pascal-style strings
.ASCREV ;like .asclen but the	string is in reversed order .ascrev "Hallo" = .asc 5,"ollaH"

.TRANS byte[-byte]|"cc"=byte[-byte]|"cc"[,...] ;controls string translation:
.trans 1=2 ;in .asc-statemtents values of 1 would be replaced with 2
.trans "A-Z"="a-z" ;all capital letters would be replaced with their small counterpart
.trans "a"="^a"  ;"a" would be replaced with control-a =$01, i.e. value and $3f
;| is like ^but value or $80; ~ is value and $3f or $80
.trans \<text>=<byte> ; defines an escape-sequence:
.trans \white=5 ;in .asc you could write "Hal\whitelo" => .asc "Hal",5,"lo"
.trans without parameters  clears the translation table, i.e. every character is put to object file as is

.TRANS_PET graph|text ;.trans with values and escape-sequences for CBM PETSCII
.TRANS_POKE graph|text ;.trans with values fo CBM screen codes
Some pseudos for creating error messages or on-screen output:

Code: Select all

.ERR message ;produce error message, e.g. a library needs some function defined which is forgotten the main code

.WRITE "xx"|[f]ausdruck[,...]	;prints either string "xx" or the result of the expression - without CR/LF at the end
;result can be formatted with preceding....
.write $val1,%val2,!val3 ;values are printed as hexadecimal, binary or simple decimal numbers
.write wval, bval ;hexadecimal output formatted as word or byte (=4/2 digits)
.write 3#val ;value shall be printed with 3 digits
.write -5#val ;5 digits but right-aligned
					
.WRITELN   ;like .write but adding CR/LF
Macros and libraries:

Code: Select all

<name> .BLK ;starts a block with local labels
.end <name> ;name must be identical to above - helps readability with longer blocks

or

.BLK <name>
.end <name>

In a block you can do
test .blk
#label1 ... ;this label will be visible in global scope
<label2 ... ;this label is visible in the surrounding block
.end test

.MAK [!]name [param,..]		;macro definition, is invoced with name or name paramlist or name (poaramlist)
...code ;in the code parameters are substituted
.END

.mak plot xval,yval
ldx #xval
ldy #yval
jsr plotfunc
.end

plot 123,myvalue  ;this will result in:
.blk
ldx #123
ldy #myvalue
jsr plotfunc
.end

plot 123,!myvalue ;here myvalue will be evaluated first - e.g. for counters that change their value constantly

.mak !hallo ... ;the macro generates no local labels, i.e. labels are visible in the next higher block


.LIB <name> ;a library function which is only incorporated if used by main code, usage is evaluated at the end of pass 1, code is placed there if used.
.end <name>

.lib testlib
...
#entry ... ;this defines an additional entry point, so you can access the libfn not only at the beginning
...
.end testlib 

.USES lib-fn ;markls a library function as used
Conditional code:

Code: Select all

.IF expression			;if expression<>0 then assemble next block
		...code...
	.ELSE				;if expression=0 assemble this block
		...code...
.ENDIF

or alternatively as single-line .if:
.IF expression code			;no else statement and cannot be mixed with .if-style above

.FOR [name=]startval,endval[,step=1] ;	nomen est omen
	...code...			;in the code block, name will be replaced by the for's value
.END

.for @i=1,3
.byte @i
.end
=>
.byte 1
.byte 2
.byte 3

.for @i=1,3,2 ; you can add a step value
.byte @i
.end
=>
.byte 1
.byte 3

.foreach name1,[name2...] ;the line of code at the end will be repeated with every element of the list
elem1
elem2
...
.end .byte name1[,name2]

.foreach vector
reset
$ffd2
fancy+expression*here
.end .word vector

=>
.word reset
.word $ffd2
.word <whatever result this expression has>
And finally segments:

Code: Select all

.SEGMENT <seg>		;different segments of code like code, data, variables,...
.end <seg>

.segment init ;(or exit)
...
.end init  ;=> segment for init (or clear-up) code, allows libraries to incorporate stuff for fire-up and shut-down. The segment is placed after the library code.

.segment data ;data with fixed value, will be incorporated in the file after lib/init/exit right before end of object file
.segment var ;data without fixed value, includes .cod - = generates no code just address space after the end of object file (if no *= is given)

Projects for the future:
  • Temporary label assignment - so routines can request temp space without me keeping track of what space is used/free
    more CPU types - but as I have no need at the moment, this might take
    built-in "optimization": assembler generates BNE xxx or BEQ yy JMP xx if relative jump is possible or not

Re: My own little assembler

Posted: Mon Jun 08, 2015 10:47 am
by scotws
Nice. Does it include the 65c02 instructions?

Re: My own little assembler

Posted: Mon Jun 08, 2015 11:03 am
by Hobbit1972
scotws wrote:
Nice. Does it include the 65c02 instructions?
Not yet - but shouldn't be too much of a problem. Only 65816 would require some rearrangement.
I haven't implementet it yet for not having had need for it.

Re: My own little assembler

Posted: Wed Jun 10, 2015 2:43 pm
by Hobbit1972
scotws wrote:
Nice. Does it include the 65c02 instructions?
Now it does. 65c02, R65C02, WDC65C02, CSG65CE02.

For later added following base page option:

Code: Select all

.cpu base=<adr> ;Assembler assumes bp is containing hi-byte of adr

.cpu base=check ;Assembler checks if address given is within basepage and issues error if not
.cpu base=ignore ;Assembler doesn't check if the address given is within basepage
.cpu base=deny ;Assembler forces absolute addressing (and rejects indirect basepage modes)
check is the normal mode, ignore might be useful if moving basepage around a lot. If deny is good for anything I don't know. An example:

Code: Select all

.cpu base=$0400
.cpu base=ignore
sta $0412 ;=>85 12
sta $0034 ;=>85 34
sta ($0412),y ;=>91 12
sta ($0034),y ;=>91 12

.cpu base=deny
sta $0412 ;=> 8d 12 04
sta $0034 ;=> 8d 34 00
sta ($0412),y ;=>error
sta ($0034),y ;=>error

.cpu base=check
sta $0412 ;=>85 12
sta $0034 ;=>8d 34 00
sta ($0412),y ;=>91 12
sta ($0034),y ;=>error
WDC65C816 will be implemented next. (Is CSG65C816 the same?)

Re: My own little assembler

Posted: Tue Jul 28, 2015 12:49 pm
by legacy
I have been coding for weeks a little lexer, now it's quite working and I have also implemented an RPN parser
it looks this way (I have posted a few lines)

is there any source of your project ?

Re: My own little assembler

Posted: Tue Jul 28, 2015 4:45 pm
by Hobbit1972
legacy wrote:
is there any source of your project ?
There surely is - the time of hard-wiring programs has passed a long time ;)
But I didn't put it online - didn't think someone would be interested.

Re: My own little assembler

Posted: Tue Jul 28, 2015 4:47 pm
by BitWise
You can browse the source for mine here:

https://sourceforge.net/p/dev65/code/HE ... n/obelisk/

The xasm folder holds the code for the generic part of the assembler (e.g. expressions, macro handling, etc.) while xobj is the 'in memory' object code representation. As my assembler is relocatable the object code can contain expressions evaluated by the linker.

The w6500, m6800, cdp1802, etc. folders provide the customisation for each family of devices.