6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Thu May 09, 2024 11:20 am

All times are UTC




Post new topic Reply to topic  [ 7 posts ] 
Author Message
 Post subject: My own little assembler
PostPosted: Mon Jun 08, 2015 6:23 am 
Offline
User avatar

Joined: Tue Feb 10, 2015 5:27 pm
Posts: 80
Location: Germany
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:
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:
+ - * / ;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:
;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:
.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:
.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:
.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:
.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:
<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:
.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:
.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


Top
 Profile  
Reply with quote  
PostPosted: Mon Jun 08, 2015 10:47 am 
Offline

Joined: Mon Jan 07, 2013 2:42 pm
Posts: 576
Location: Just outside Berlin, Germany
Nice. Does it include the 65c02 instructions?


Top
 Profile  
Reply with quote  
PostPosted: Mon Jun 08, 2015 11:03 am 
Offline
User avatar

Joined: Tue Feb 10, 2015 5:27 pm
Posts: 80
Location: Germany
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.


Top
 Profile  
Reply with quote  
PostPosted: Wed Jun 10, 2015 2:43 pm 
Offline
User avatar

Joined: Tue Feb 10, 2015 5:27 pm
Posts: 80
Location: Germany
scotws wrote:
Nice. Does it include the 65c02 instructions?


Now it does. 65c02, R65C02, WDC65C02, CSG65CE02.

For later added following base page option:
Code:
.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:
.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?)


Top
 Profile  
Reply with quote  
PostPosted: Tue Jul 28, 2015 12:49 pm 
Offline

Joined: Sat Dec 08, 2012 8:29 pm
Posts: 62
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 ?


Top
 Profile  
Reply with quote  
PostPosted: Tue Jul 28, 2015 4:45 pm 
Offline
User avatar

Joined: Tue Feb 10, 2015 5:27 pm
Posts: 80
Location: Germany
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.


Top
 Profile  
Reply with quote  
PostPosted: Tue Jul 28, 2015 4:47 pm 
Offline
User avatar

Joined: Tue Mar 02, 2004 8:55 am
Posts: 996
Location: Berkshire, UK
You can browse the source for mine here:

https://sourceforge.net/p/dev65/code/HEAD/tree/trunk/src/uk/co/demon/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.

_________________
Andrew Jacobs
6502 & PIC Stuff - http://www.obelisk.me.uk/
Cross-Platform 6502/65C02/65816 Macro Assembler - http://www.obelisk.me.uk/dev65/
Open Source Projects - https://github.com/andrew-jacobs


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 7 posts ] 

All times are UTC


Who is online

Users browsing this forum: Google [Bot] and 8 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to: