# This is a simple example of Sarcasm's syntax. # # First, Sarcasm needs to know the layout of your Z80 system's memory. # # I use a system with 32K of EEPROM and 32K of SRAM. This configuration # can be specified as simply as this: range rom $0000 $7FFF range ram $8000 $FFFF # However, you can specify as many sections as you like, and so I usually # use these lines instead: range start $0000 $0037 range int $0038 $0065 range nmi $0066 $007F range code $0080 $3FFF range data $4000 $7FFF range mem $8000 $EFFF range stack $F000 $FFFF # This has the advantage that Sarcasm will tell me if my code or data is too # large to fit in the memory space I wish it to fit in. section start ld sp $0000 jp .code. # The above tells sarcasm to start assembling in the 'start' range. # It then initalizes the stack pointer, then jumps to the beginning of # the 'code' section. section data # Now we will place some data in the 'data' section. # The 'data' statement accepts four types of data: text strings, byte strings, # bytes, and words. Here is an example of each: text_string; data "This is a text string." byte_string; data !000102030405060708090A0B0C0D0E0F some_bytes; data $00 $01 $02 $03 $04 $05 $06 $07 $08 $09 $0A $0B $0C $0D $0E $0F some_words; data $0100 $0302 $0504 $0706 $0908 $0B0A $0D0C $0F0E # The last three statements all create the same data. I invented the byte # string data type because it is much easier to type than the other two. # Bytes and words are differentiated by the number of hexadecimal digits. # The 'bytes' and 'words' statements accept bytes and words respectively, # and as such do not require a specific number of hexadecimal characters. more_bytes; bytes $0 $01 $002 $0003 4 05 006 0007 more_words; words $100 $00302 1284 01798 # Sarcasm doesn't do octal numbers. Leading zeros are just zeros. # Basic addition and subtraction is possible: some_math; words 1 + 2, 3 + 4 - 5, 6 + -1 - -7, text_string + $10, .code. - 2 # Note that Sarcasm's 'commas optional' feature applies here as well. more_math; words 1 + 2 3 + 4 - 5 6 + -1 - -7 text_string + $10 .code. - 2 # Obviously you might want to include commas for readibility, but be warned # that Sarcasm treats them as spaces, so if you type this... bytes 1 + 2, - 3, 1 + 2,-3 # ...Sarcasm will see this... bytes 1 + 2 - 3 1 + 2 -3 # ...and you'll end up with three bytes of data: 0, 3, -3 # So in other words: No spaces between hyphens and the numbers they negate. # The nice thing about these sections is that you can switch between them # as much as you like: section code ld hl [message] call print_text section data message; data "Wow, this is sweet!" !00 section code ld hl [yes] call print_text section data yes; data "Yes, it is!" !00 # The result is that the two text strings end up one right after the other # in the 'data' section, and the two pieces of code end up one right after # the other in the 'code' section. This makes it possible to keep data # near the code that uses it, thus making it easier to write and maintain. section code label; jp label # Note that Sarcasm requires semicolons between labels and code that appears # after them. Most other assemblers do not. In fact, most assemblers use # semicolons to mark comments, whereas Sarcasm uses semicolons to seperate # multiple instructions on a line: section code print_text ld a [hl]; out $80 cp $00; ret z jr print_text # Assembly code is a lot easier to read when a small function doesn't span # several pages, and so semicolons can be used to group instructions which # work towards the same goal into a single line to improve readability. # When you're all done, you need to tell Sarcasm what memory you want to # write into which file, like this: output "simple.rom" $0000 $7FFF # This writes the first 32K into a file, which can then be burnt to a ROM chip. # Note that the data written is what Sarcasm has assembled thus far, and so # you will usually want this to be at the end of the file, unless you are # assembling code for a system with multiple paged ROM chips and thus you # need multiple different ROM files for the same range of memory. # Now for the topic of namespaces and sub-labels: namespace one apple; data "apple in one" peach; data "peach in one" namespace two apple; data "apple in two" peach; data "peach in two" # The identical lables are allowed because each is in a seperate namespace. # When you use a label, the current namespace is used if one is not specified: namespace one ld hl apple # loads address of "apple in one" ld hl one.apple # loads address of "apple in one" ld hl two.apple # loads address of "apple in two" # However, to complicate things further, there are also sub-labels: namespace three apple .skin; data "apple.skin in three" .core; data "apple.core in three" pear .skin; data "pear.skin in three" .core; data "pear.core in three" # Sub labels can only be accessed in short form until a new label is declared. ld hl .skin # loads address of "pear.skin in three" ld hl .core # loads address of "pear.core in three" orange .skin; data "orange.skin in three" # Once you declare a new label, you need to specify sub-labels as such: ld hl apple.skin # loads address of "apple.skin in three" # ...or if you are in a different namespace... namespace four ld hl three.apple.skin # loads address of "apple.skin in three" # So you might ask, what if you have this: namespace xxx yyy; data "xxx.yyy" .zzz; data "xxx.yyy.zzz" namespace whatever xxx; data "whatever.xxx" .yyy; data "whatever.xxx.yyy" # ...and then you do something like this... ld hl xxx.yyy # Is that "xxx.yyy.zzz" or "whatever.xxx.yyy" # Well, the answer is that it is "whatever.xxx.yyy" so long as you are in the # 'whatever' namespace, but if you are elsewhere, then it is "xxx.yyy" # I think that covers everything, except for the "goto" statement: goto $4000 # It just changes Sarcasm's "instruction pointer" or whatever you want to call # it for the currently active section, in case you should want to overwrite # code or to place certain code at a certain address.