C64List 3.00 is here! Some Assembly Required
C64List rings in the new year with a bottle of vintage 3.00, a kazoo, and its own integrated 6510 assembler!
HAPPY NEW YEAR 2012!
Unfortunately, the kazoo and the bottle were lost in the excitement of watching the 2012 ball drop on TV, but the version 3.00 designation and the integrated assembler survived the obligatory hangover on January 1, and C64List is ready for work in the new year with its arsenal of new tools.
[2011] So there you are writing a BASIC program and one particular routine is really, really slow so you want to write in assembly language to speed things up. This means you have to haul out the old assembler, load it up, write your first draft, assemble it, and oh there seems to be a bug, so now you have to reload the assembler and re-assemble. Finally got it working correctly and now you have to write a loader for the machine language .prg file so it will be available to call from your BASIC program...
[2012] Woah! Stop! Slow down Nelly! Now there is a much easier way to seamlessly integrate lighting-fast machine language routines into your BASIC programs. Thanks to C64List 3.00's new integrated assembler, your whole program can fit into a single .prg file: BASIC, machine language and all!
Imagine being able to
LOAD"MY PROGRAM",8,1
RUN
...and not have to have any extra files to load. Well, now you can!
C64LIST COMES TO THE RESCUE!
C64List now allows you to write your BASIC program, and add your assembly code right into the same source file. C64List automatically positions your code in memory immediately after the BASIC code, and allows you to SYS, POKE, PEEK, etc. using symbolic names so you don't have to worry about relocating your machine language code if your BASIC code expands—C64List automatically handles all the details for you.
SHOW ME THE MONEY, ER I MEAN CODE
Someone once said "An example is worth a thousand words", or was that a picture—ok well anyway, how about a quick example to show how easy it is to add assembly code into a BASIC program? If you want to follow along, just cut and paste the following code snippet into a .txt file and run C64List on it. Of course you'll need to download the latest version of C64List from CommodoreServer first!
{renumber}{number:100}{step:10}{alpha:upper}
print "beginning basic code"
print "now switching to assembly"
sys {sym:subroutine}
print "returned to basic"
{asm}
screen00 = $0400
screen24 = $0400+$3c0
checker = $ff
ptr1 = $22
ptr1nonZP = $0022
ptr2 = $24
ptr2nonZP = $0024
storage: byte 0
subroutine:
lda #< Screen00
sta ptr1nonZp ;uses a non zero page store just for the fun of it
lda #> Screen00
sta ptr1 +1 ;uses a zero-page store
lda #< Screen24
sta ptr2
lda #> screen24
sta ptr2nonZP +1;uses a non zp store just for the fun of it
lda #checker
ldy #$27
l0:
sta (ptr1),y
sta (ptr2),y
dey
bpl l0
rts
{endasm}
THE LOW-DOWN
Now that you've had a chance to look at some actual code, let's talk about it. This example shows a number of important elements of the C64List assembler, so we will start from the top. As you can see, we are using C64List's number-line-less format so C64List will fill in line numbers for us, starting at 100, and incrementing by 10.
Hearkening back to my previous Bit64 blog post, the {alpha:upper} allows us to be lazy and type in all lower case. C64List will uppercase all the text within quotes so it will be actual text in the program, instead of random looking graphics characters.
Then we have a couple simple lines of BASIC telling us what is going on, followed by the sys to our assembly language routine. Notice the new directive {sym}. This directive allows BASIC to access the symbols from the assembler's symbol table. The name of the symbol here is "subroutine", which is the entry point for our assembly function. When you run C64List on this program, it will automatically locate the address for "subroutine" and replace the {sym} directive in the code with the actual address we are interested in.
The next interesting part of the code is the {asm} directive. This is how you tell C64List that it should now treat the following lines as assembly language. {endasm} does the opposite. Please note that all BASIC code must come before any assembly code.
Now that we are in assembly land, the code looks much different. I'm not going to give a tutorial on assembly language programming here, since that is a bit out of scope from this blog, but I do want to point out a few features of C64List's assembler.
Assigning a symbol is rather straightforward:
screen00 = $400
does exactly what it looks like it should. Also notice that you can use some simple expressions as the next line illustrates. You may also use decimal values instead of hexadecimal if you wish. Please note that the symbols in the symbol table are completely separate from the BASIC labels.
NOW PAGING PAGE ZERO
Also notice how zero page addresses are specified. Values which are given as two (or one) hex digits are taken as zero page. Values with more than two hex digits are taken as non-zero-page values. Alternately, decimal values of one, two, or three digits that are less than 256 are taken as zero page, while any value greater than 256 or larger than 3 digits are taken as non-zero-page. In the example, the symbol ptr1 is defined as a zero page address, while ptr1nonZP is defined as a non-zero-page symbol, even though it is the exact same value. The difference between the two symbols can be seen when an instruction is assembled using these two symbols.
lda ptr1
will create a two-byte instruction, doing a zero page load accumulator from address $22, while
lda ptr1nonZP
will create a three-byte instruction that does an absolute load accumulator from address $0022 (the same location).
FACIAL EXPRESSIONS
What? Oh, I mean mathematical expressions. Those may include the following operators:
- # immediate (note that this is actually an addressing mode modifier, not an operator)
- < take the low byte of the expression (this can only be the first operator in the expression)
- > take the high byte of the expression (this can only be the first operator in the expression)
- + addition
- - subtraction
- & and operation
- | or operation
BLACK PSEUDO-OPS
The "byte" and "word" pseudo-ops are supported. Note that there is no origin pseudo-op. C64List first tokenizes all the BASIC code, then calculates the end of the BASIC code in memory, and begins assembling immediately after. In this way, as much memory space as possible is preserved. C64List tracks the actual locations and allows BASIC to access them via the symbol table. For example, using the example assembly code, you could POKE {sym:storage},99 to set the byte at that address to 99 from BASIC.
PRICES AND AVAILABILITY ARE ARE SUBJECT
TO THE FOLLOWING LIMITATIONS
Incidentally, I do plan to add an origin pseudo-op, but there are some issues which must be worked out first. The issue becomes obvious if you attempt to assign a symbol using a label. For example, the following would fail.
symbol = subroutine
Fortunately, you shouldn't need to do this most of the time anyway, but this occurs because there is a bit of chicken-and-egg syndrome happening due to the space-saving features of C64List that puts the assembly code immediately after the BASIC code. C64List can't know the address of labels until very late in the assembly process, long after all other symbols have been defined. I plan to work around this eventually, but please be advised that using labels in expressions is currently illegal in all 50 states, Canada, Mexico, and most other locations within the Milky Way Galaxy, and will result in blistering error messages.
SO GET TO IT!
If you are like most people in the world, you've recently been unable to sleep at night, tossing and turning, wondering what exciting new features the next C64List release will bring. Well, now you can now throw off the covers, drop your Ambien in the trash, light a candle, dig out your Commodore 64 and start writing new killer assembly language programs using C64List 3.00!
I hope you like the new assembler and are able to use it to create some new killer apps for the Commodore 64!
Until 3.01 arrives,
--Jeff
Leave a Comment
You must be signed-in to post comments.Responses