# 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 memory ranges 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 # Memory ranges have two advantages. The more obvious advantage is that if I # write code than will not fit in my memory spaces, I'll see an error message. # The less obvious advantage is that I can randomly switch between these # sections in my source code, placing memory variables near the code which # uses them, which drastically improves readability. # Anyway, to begin writing code, choose the memory range you want that code to # exist in, switch to that memory range with the 'section' directive, and then # write your code: 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. # Now we will place some data in the 'data' section. section data # The 'bytes' and 'words' statements accept bytes and words respectively. bytes $0 $01 $002 $0003 4 5 06 0007 words $100 $00302 1284 1798 # Both decimal and hexidecimal numbers are accepted, however, numbers with # leading zeros are not octal as in other programming languages. I've never # in my life felt the need to type an octal number, as the only thing the # number base has going for it is that it uses fewer than ten digits, and # I'm just not that obsessed with the number ten, so I use hexidecimal. # On the other hand, I use leading zeros all the time, as they are useful # for formatting, and so leading zeros mean nothing in Sarcasm. # The 'data' statement accepts four types of data, which can be intermixed # The most common is text strings: text_strings data "This is a text string." data 'This is also a string.' data 'It isn't a problem if single quoted strings contain apostrophes.' data ""This double-quoted string contains a double-quoted string."" # The trick with the last two examples is that a quotation mark isn't accepted # as the end of the string unless it is followed by a comma, space, semicolon, # hash symbol, or the end of the line. In other words, if the only other way # Sarcasm could look at it would be to view it as malformed data, then it makes # a little more sense to just include it in the string and not bitch about it. # data 'isn't # You see, no one ever types data like that. From the moment you see that 't' # after the single quote, the only logical conclusion is that the string # contiues and the real closing quotation mark comes later, and so that's how # Sarcasm looks at it. If there is another single quote later on, everything # works out just fine, and if there isn't, then you see an error message about # an unterminated string. # The second data type Sarcasm accepts is the byte string. data !DEAD !BEEF !DECADE !12345678 # That's basically shorthand for this: bytes $DE $AD $BE $EF $DE $CA $DE $12 $34 $56 $78 # Byte strings can contain any number of bytes, but the number of hexadecimal # digits must be a multiple of two, two digits for each byte. The bytes are # placed into memory in the order they are typed, which is more "big-endian" # as opposed to the "little-endian" ordering you would get if you tried to # save time by typing words instead of bytes. # The remaining two data types the 'data' statement accepts are bytes and # words. Each must contain either two or four hexadecimal digits, so that # Sarcasm knows if they are a byte or a word. One and three digits aren't # allowed, in a sort of a "don't you know this isn't the 'bytes' or 'words' # statement" sort of way. What can I say? The 'data' statement is my # invention and this is how I want it to work. data $01 $0203 "Four" $0506 !070809 # That results in the same data as this: bytes $01 $03 $02 $46 $6F $75 $66 $06 $05 $07 $08 $09 # ...or what you might type in old-school assemblers... # db $01 # dw $0203 # db "Four" # dw $0506 # db $05, $06, $07 # In other words, the 'data' statement has a lot of rules, but it's damn nice # in that it allows you to type your data more compactly. # Basic addition and subtraction is possible: words 1 + 2, 3 + 4 - 5, 6 + -1 - -7, text_strings + $10, .code. - 2 # Note that Sarcasm's 'commas optional' feature applies here as well. words 1 + 2 3 + 4 - 5 6 + -1 - -7 text_strings + $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, 4 -5 # ...Sarcasm will see this... bytes 1 + 2 - 3 4 -5 # ...which is effectively this... bytes 1 + 2 - 3, 4 - 5 # ...which isn't the same thing at all, the problem being the spacing around # the numbers and symbols: bytes 3 - 2 # This is obviously subtraction, one byte with value 1. bytes 3- 2 # This is subtraction only because nothing else makes sense. bytes 3 -2 # This is two independant bytes with values 3 and -2. bytes 3-2 # With no spaces on either side, once again it must be subtraction. bytes -1 # This is obviously a single byte, one byte with value -1. bytes - 1 # This is nothing minus one. It's subtraction, but it works out. # Basically, just don't type things in silly ways and you'll be fine. # About the only way you can really go wrong is to put spaces after hyphens # which you intend to negate numbers, which I've never seen anyone do. # The nice thing about memory ranges 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 arrange your # source code how you like it, without regard to how it has to appear in Z80 # memroy, thereby making it easier to write and maintain since related items # can be placed near eachother. 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 'yyy' in the 'xxx' namespace, or # is it 'xxx.yyy' in the 'whatever' namespace? # 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.