A$$emble IT!
A C64 Coding Tutorial - Now featuring example screen shots
Introduction / What is An Assembler? / ASM Commands / Writing Your First Program / Using X&Y Loops / Displaying Text/Characters & Colours on Screen / Introduction to $D012 / IRQ Raster Interrupts /  Introduction to Sprites using M/L
 Colour-Washing Text / Making Your First Game
 
Chapter 1: INTRODUCTION

The main purpose for this web site is to produce legal future reference for those people who would like to be involved with assembly programming side of the C64 scene. This documentation differs through various assembly coding techniques, commands, etc. By reading through this documentation, you should soon end up creating cool, exciting and possibly crazy programs in no time. Later on in this documentation, you will learn how to create your own games, that involve moving sprites, animating background objects, playing SID music, adding scores, etc. Hopefully you should be able to understand assembly programming in approximately one or two months. Oh and you'll be having a go at making a C64 game in no time.


Chapter 2: WHAT IS AN ASSEMBLER?

WHAT IS AN ASSEMBLER?
An assembler is a programming language that allows you to program in a different way, compared to the BASIC programming language. BASIC is a very easy programming language, but assembly language is more professional and it can do a lot more enhanced techniques. Many demo programmers, such as Civitas (The demo group which I'm in) use an assembler, which creates cool demos, effects etc.

WHAT TYPE OF ASSEMBLERS ARE THERE?
Assemblers may differ, as different commands are used, but they all build the same routines. You write a program listing in an assembler, and then you need to assemble it before you create a runnable file and test your file. There are different types of assemblers, which have their own features. The assemblers are as follows:

Chapter 3: ASM Commands
When you are writing your own programs, it would usually be the best thing to take a look at various resources, so that you can understand all these following: commands. I can explain a few of these, but not all of them.

LDX #$00 (LDX #0), LDY #$00 (LDY #0)
This command sets the starting loop, at a value of 0. This command is very useful for writing control loops in your own programs. You can change various commands for your program, by indicating LDX #nn, where nn is the number you want to start.

When you are using LDX, or LDY loops, you can start from 0 to 256, or ($00 - $FF). Easily done

LDA #$00 (LDA #0)
This command allows you to set a value. This value could be used for either raster splits, charsets, colours, RAM, etc.

STA $xxxx (STA xxxxx)
When you set values, using LDA you can build the type of RAM which are using, when entering STA. For example if you want to build a black screen, then you enter:

LDA #$00
STA $D020
STA $D021

Quite simple eh? I'm sure it is. Ah, but you can't just LDA and STA things, you can also use LDX and LDY and instead of use STA, you can use STX or STY, for example:

LDX #$00
STX $D020
STX $D021

LDY #$00
STY $D020
STY $D021
 
Let us say for example you wanted to draw a black screen, but you want a red frame. You could use LDA and STA like as the example below:

LDA #$02
STA $D020
LDA #$00
STA $D021

Or then again, you would prefer to tidy your code, by using the X,Y method, for example:

LDX #$02
LDY #$00
STX $D020
STY $D021

There, that is MORE tidy!

INX , INY
These are two command that are use to increment a value which is inside a timed loop. These can also be used for continuous moving of objects, flashing, rasters, etc.

CPX #$00, CPY #$00
These are comparing loop values of a loop, which is the value of X or Y. Basically if you use 0 as start loop and want the C64 to count the loop 255 times, then we CPX or CPY the limit. I'll show you how to use these later on :o)

BNE $xxxx
This command is used in a loop. For example, compare a value and if it does not match, go to the looping command. For example:

      LDX #$00                                LDY #$00
LOOP   INC VALUE                             LOOP INC VALUE
            INX                    OR                INY
            CPX #$FF                               CPY #$FF
            BNE LOOP                             BNE LOOP

Easy!

INC $xxxx or INC VALUE
The INC command is used to increment a value. For example, this command can be used for creating your own sprite movements, timers, etc.

DEC $xxxx or DEC VALUE
DEC is opposite to INC. Instead of incrementing a value, this command decrements a value. Yet again it can be used for practically the same as with INC, but goes the opposite way :o)

BCC & BCS
BCC & BCS are similar to the BNE command, but these are more special. Many C64 programs use BCC and BCS for their programs, I.E. some nice little game I made in the past, that use collision detection with an enemy and player, I used the following command:

                LDX #$00
LOOP      LDA ENEMYPOS+$00,x
                CMP COLLISION
                BCC NO COLLISION
                CMP COLLISION+$02
                BCS NO COLLISION
                LDA ENEMYPOS+$01,x
                CMP COLLISION+$03
                BCC NO COLLISION
                CMP COLLISION+$04
                BCS NO COLLISION
                INC $D020
                RTS

NO COLLISION       INX
                              INX
                              CPX #$10
                              BNE LOOP

Pretty simple, but we will take a look at this when we start a simple little two player game project.

CMP #$00 (or CMP #0)
The command CMP #$nn compares a value that IS/NOT in a loop. For example you can compare values for positions of sprite movements and set the stopping values rather than just have them continuously looping :o)

JSR $xxxx, JSR ROUTINE
The JSR routine is always used inside or outside an Interrupt ReQuest (IRQ) flag, which jumps to a routine, but allows you to continue your programming. For example:

        LDX #$00
        JSR $1048

        JSR COLOURWASH
        JSR ANIMATE
        JSR $1021 ;Play music

JMP $xxxx, JMP ROUTINE
JMP is similar to JSR, but this time it jumps to a routine that you want. For example, freezing your own program

HOLD JMP HOLD

There are also a couple of special JMP routines, which are JMP $EA81 and JMP $EA31, these are of course used in IRQ routines

CLC
ADC #$00 or (ADC #0)
Calculate and add a value

SEC
SBC #$00 or (SBC #0)
Reverse calculate and subtract a value

BEQ STATEMENT
Similar BNE, BCC and BCS, but a bit different. These are mainly used in loops, to defualt values of pointers.

SEI
SEI turns off an IRQ interrupt flag, when a loop is inside the program

CLI
Clears registers and allows the programmer to insert additional commands, which they cannot use inside an IRQ loop!

PLA
PHA
RTI
These are special commands.

TAX or TAY
Mainly used to control scrolling types, initialise everything, for example, music!

LDA #$00
TAX
TAY
JSR $1000 ;Initialise DMC 4 tune

.BYTE (or .BYT)
Data tables for animation, colours, etc

.TEXT (or .TXT)
Data tables for creating your own text

There are many other commands, but the commands (PLA, PHA, RTI, SED, CLD) that I don't use, and they're for more advanced programmers.
 

Chapter 4: WRITING YOUR FIRST PROGRAM
We are going to write our first program, which is just something small and simple. This program shows you how to create your own flashing border, while asking for the user to press the space bar, so that then the program exits. So load up your assembler and type in the following listing. There are two different methods for this phase ;o)

METHOD 1

;===================
;RAINBOW COLOURS
;===================

               * = $0900 ;Jump start for code
               SEI                ;CLEAR IRQ
LOOP    INC $D020   ; INCREMENT BORDER
               LDA $DC01 ; CHECK BUFFER
               CMP #$EF   ; IS SPACE PRESSED?
               BNE LOOP ; IF NOT THEN JUMP TO LOOP
               RTS             ; IF SPACE IS PRESSED THEN END

Can you see how easy it is to create your own short program? Now all you need to do is assemble the program and run it. Guess what? You'll see a flashing border. The border will continue to flash until you press space. Then the program will finish. Well why does the program continue flashing, before SPACEBAR is pressed? Well, that's easy, because the program is inside a control loop, which continues to loop until the value #$EF is found, and then the C64 stops running the program, as the program tells it to do so. Now why not try deleting LDA $DC01, CMP #$EF and BNE LOOP into:

METHOD 2:

;===================
;RAINBOW COLOURS
;===================

               * = $0900 ;Jump start for code
               SEI                ;CLEAR IRQ
LOOP    INC $D020   ; INCREMENT BORDER
               LDA $DC01 ; CHECK SPACEBAR
                LSR              ; If SPACEBAR IS NOT
                LSR              ; PRESSED THEN JUMP
                LSR              ; TO THE LOOP!
                LSR
                LSR
                BCS LOOP
                RTS

Not much of a difference there, but this is also checking for a button on joystick port 1. Yes, that's right FIREBUTTON. The spacebar will still work.

I would prefer to use method 1, in order of processing the code. This is because it is shorter compared to method 2. Shorter routines can compress the program, which you write. It also saves the amount of memory that has been used in an assembler.


Chapter 5: USING X & Y LOOPS
In Chapter 4, I gave you an example program, which allowed you to flash the colour borders, but now, I'm going to show you how you can EXPAND colour sizes. Here is what we'll do. We shall use an LDX prompt and also an LDY prompt (for X and Y loops). We shall still use INC $D020 and INC $D021 and the pressing spacebar method, but this is just a little taster of what we have in store for this section :o)

;==================
;FLASHING BORDERS
;USING X & Y LOOPS
;==================
                        *=$0900                     <- you should know what this routine does
                        SEI                             <- and this one
MAINLOOP  LDX #$00              ;Set 'X' as 0
LOOPX           LDY #$00              ;Set 'Y' as 0
LOOPY           INC $D020            ;Flashy border
                        INY                         ;Increment Y
                        CPY #$77               ; Is 'Y' equal to #$77
                        BNE LOOPY         ; If not then Y=Y+1, goto LOOPY
                        INX                        ; Increment X
                       CPX #$77               ; Is 'X' equal to #$77
                       BNE LOOPX         ;If not then X=X+1, goto LOOPX
                       LDA $DC01           ;Load Spacebar command
                       CMP #$EF             ;Is spacebar pressed
                       BNE MAINLOOP ; If not then jump to the MAINLOOP prompt
                       RTS                         ; else END program operation

You will notice that there wont be much of a difference in the flashing borders. However, we could try something different. Here is what to do.

Underneath RTS, build your own colour table. Include the label COLOURS. You could try the following:

COLOURS
                        .BYTE $00,$06,$0E,$03
                        .BYTE $03,$0E,$06,$00

Now let's try a little experiment shall we?

Where we have INC $D020, replace this with LDA COLOURS,X and then add STA $D020. We're not finished there yet. In order to read the amount of colours that are in the colour table, the next thing for you to do is change CPX #$77 into CPX #$08. Now let's test it ;o)

Different.

Now, why not try and change CPY #$08 and see what happens next ;o) Notice any differences? The border thins down and it scrolls something like those colour bars, which have been used on SEUCK games apart from SEUCK bars go upwards instead of downwards.

Thinning colour bars, etc is plain easy to handle, but when $D012 is involved, things change.

The borders look a mess, as they are flickering so what should be done now is some minor adjustment to the flickers. All we do is add NOP, a few times, until the borders are nice and straight. Let's try it.Erm, no, as not much effort would be put into it. Maybe using $D012 would help. We'll look at this later on
 

Chapter 6: Displaying text/characters & their colours on screens
How can we display text or characters on the screen? Easy, we use loops, but we need to use screen RAM, which is located in different memory banks. We shall stick to the normal C64 display bank ($0400 - $07E7), so that then we can display some text.

For a start off, let us try and something, which is located at $4000 and then pastes it through the whole of the screen area. Remember, this is only an experiment. There are different methods, but we'll use a simple method on displaying a screen, that is located at $4000.

;================================
;DISPLAYING TEXT AT BANK #$03
;================================

;SET UP PERAMETERS

SCREENLOC1 = $4000
SCREENLOC2 = $4100
SCREENLOC3 = $4200
SCREENLOC4 = $42E8

SCREENPOS1 = $0400
SCREENPOS2 = $0500
SCREENPOS3 = $0600
SCREENPOS4 = $06E8

                    * = $0900
                    SEI
                    LDX #$00                                ;X=0
DISPLAY   LDA SCREENLOC1,X          ;READ FROM SCREENLOC1 'X' TIMES
                    STA SCREENPOS1,X           ;PASTE TO SCREENPOS1 'X' TIMES
                    LDA SCREENLOC2,X         ;READ FROM SCREENLOC2 'X' TIMES
                    STA SCREENPOS2,X           ;PASTE TO SCREENPOS2 'X' TIMES
                    LDA SCREENLOC3,X         ;READ FROM SCREENLOC3 'X' TIMES
                    STA SCREENPOS3,X           ;PASTE TO SCREENPOS3 'X' TIMES
                    LDA SCREENLOC4,X         ;READ FROM SCREENLOC4 'X' TIMES
                    STA SCREENPOS4,X           ;PASTE TO SCREENPOS4 'X' TIMES
                    INX                                         ; X=X+1 UNTIL X = $FF (256)
                    BNE DISPLAY                      ;IF X <> $FF THEN GOTO DISPLAY
                    RTS                                        ;END PROGRAM

Compared to using BASIC, this routine is smaller and more compact to display text.

Now that you know how to display the screens, we want to display colours for the text. And here is how we can do this:

The colour RAM is always between $D800 and $DBE7. Therefore, variables can be created for the colours. So create some new variables, which are as follows:

COLOURPOS1 = $D800
COLOURPOS2 = $D900
COLOURPOS3 = $DA00
COLOURPOS4 = $DAE8

Then inside your loop (underneath STA SCREENPOS4,X) enter the following:
                    LDA #$0A
                    STA COLOURPOS1,X
                    STA COLOURPOS2,X
                    STA COLOURPOS3,X
                    STA COLOURPOS4,X

You don't just get the screen displaying only text, but you get the text to display painted in pink ;o)

Why not play around with the colours. Here is a small table on which colour does what job :o)

 
 
Hexadecimal ($)
Colour
$00
Black
$01
White
$02
Red
$03
Cyan
$04
Purple
$05
Green
$06
Blue
$08
Orange
$09
Brown
$0A
Pink
$0B
Dark Grey
$0C
Medium Grey
$0D
Light Green
$0E
Light Blue
$0F
Light Grey
Chapter 7: USING $D012
This is the fun bit. $D012 is used to create some raster splits. For example, if you wanted to make your own title screen for a C64 game, you would need to code in the raster splits. Let us say for example, you paint a logo using drawing programs, save them as a VIDCOM format and you want to display the normal font you created which is located at $2000. A raster split would need to be used, therefore you would need to split everything up. For example:
If you look at the thin line, you can see that it is the raster bar, splitting an image. A prime example for something like this is a title logo, which was done for the game Balloonacy. Look below and you'll see what I mean by a raster split :o)
 
Now we are going to try something similar, but more simpler. We are going to program our own raster splits, but we are going to do this the more easier way. That's right, we are going to split the screen into two different colours. Now how do you do this? Easy, type in the listing below and you should begin to understand how the code will work :o)

;===========================
;COLOUR SPLITTING USING $D012
;===========================

                *=$0810
                SEI
MAIN        LDA #$30
                LDX #$00 ;BLACK
RASTER1 CMP $D012
                BNE RASTER1
                STX $D020 ;ASSIGN BLACK TO
                STX $D021 ;BORDER AND FRAME
                LDA #$A8
                LDX #$01 ;WHITE
RASTER2 CMP $D012
                BNE RASTER2
                STX $D020
                STX $D021
                JMP MAIN

Now that we have typed in this listing if you run it, just assemble it and run the little program. You will see that the top border and frame is black, and that the bottom border and frame is white. You will also notice that using this routine, there are a few screen flickers. This is because we are not using an IRQ loop, and we're also not using TIMING either :o)

How do we add timing to rasters? There are different ways, but one of the easy ways is by adding the 'NOP' command a few times, before we actually start a process. For example

RASTER1 CMP $D012
                BNE RASTER1
                NOP
                ----- Rest of program

Adding NOPs will move the little flickers further across the screen. Therefore you will need to add a few of these until the split is STRAIGHT and not FLICKERING :o)

There are other methods of timing, which you can use, which can be easier. You could use timing inside an x or y loop. For example

RASTER1 CMP $D012
                BNE RASTER1
                LDX #$XX  ;Timing Value
TIMER1    DEX
                BNE TIMER1
                ------ Rest of program

You could reverse the timing loop, but if you do, you would need to change the values into the following.

                LDX #$00
TIMER1     INX
                CPX #$XX ;Timing Value
                BNE TIMER1

There is also another method in timing, which is the hardest of them all. Imagine you are doing a raster bar, which uses loads of colours, like some demo groups use. Well a timing table would need to be produced. To find out more about timing, read the disk mag 'Coders World' issue 2.

Chapter 8 - IRQ Raster Interrupts

What is an IRQ Raster Interrupt?

An IRQ Raster Interrupt is a routine, known as the Interrupt Request flag. This is a continuous loop, which can hold more than one control loop. Therefore uses more complicated techniques. Most people use IRQ interrupts to do various effects in their programs. I mainly use IRQ interrupts for my C64 games. A little explanation will be added in a short while. But first copy this routine into your Turbo Assembler before you actually do anything. Below is a listing that shows a nicr and small IRQ raster interrupt routine.

                SEI
                LDA #<INIT
                LDX #>INIT
                STA $0314
                STX $0315
                LDA #$00
                STA $D012
                LDA #$7F
                STA $DC0D
                LDA #$01
                STA $D01A
                CLI
HOLD       JMP HOLD
INIT          LDA #$01
                STA $D019
                INC $0400
                JMP $EA31
 

Right, looking from the top of the IRQ flag, the values $0314 and $0315 are very special, these call INIT to produce the main IRQ interrupt flag. However you MUST use (as highlighted) the prompts else your IRQ wont work properly. This is because they are vital to use in your program.

What can IRQ be used for? Let us say for example you are writing a demo, game or utility, you might need to use IRQ raster interrupt request flags to link various sources, such as the following:
 

However, INC $0400 does not mean much, apart from you'll see a character changing on the top left corner of the screen.

Okay, press RUN/STOP and restore, load a Demo Tune or a tune of your own into $1000, (JCH, DMC or equivalent), enter sys 36864 and do as follows:

Underneath STA $D01A enter:

LDA #$00
TAX
TAY
JSR $1000

This is to initialise your music

Now, delete INC $0400 and change to JSR $1003. This is to play your music. Now, assemble and run.

You'll be able to hear some music playing in the background. Later on, we'll be taking a look at preparing a demo.

If you link other routines, be sure to JSR (routine) before JMP $EA31 Let's try something simple, such as flashing the screen. Before JSR $1003, put JSR FLASH and then underneath JMP $EA31, enter the following code:

FLASH INC $D020
           DEC $D021
           RTS

Why do we add an RTS command at the end of this prompt? This is because if we don't use RTS, the program will not respond properly, in fact it will virtually crash and you may lose everything.

Now, let's add another command to the IRQ. After JSR flash, try JSR CHAR, Underneath RTS inset the label CHAR and enter INC $D018 and add another RTS, assemble and run. You'll get some funny effects happening. This is because the C64 is playing with your charset memory.

Basically that is all for this chapter. We'll be taking a look at a few other chapters regarding assembly code :)



Chapter 8 - Introducing Sprites

What is a Sprite?

A sprite is a small object that can be moved around the C64 screen. A sprite is usually used in games and demos. Sprites can be repositioned, moved and also use hi-res or multicolour. Later on we are going to try and make a nice little 2 player game that involves sprites. But that will be in a later chapter. What we are going to do is play around with sprites. Here is what to do.

1. Load a sprite editor from a PD utilities disk (Or download one).
2. Draw a sprite (can be anything)
3. Save to disk
4. Use an action replay cartridge and load in your own sprite at $2000, using the M/C prompt: L "(filename),8,2000
5. Load up and enter turbo assembler
6. Read on.

Right, the first thing, which we are going to do is learn how to turn the sprites on and off. If you remember right, $D020 was for border colour, but what's that to do with sprites? Ehm, nothing.

Turning On/Off Sprites

We use $D015 to turn on sprites. To turn a sprite on we can use LDA #$01, STA $D015, or if you wanted all 8 sprites turned on, we use LDA #$FF, STA $D015. This will be easy to remember.

Setting Sprite Properties

Also, setting up your sprite correctly would be quite tricky. Anyway, because we are using bank $03 on the C64, we will use $07F8 - $07FF for our sprites. However, because our sprite data is loaded at $2000, we will need to use LDA #$80, STA $07F8 (for sprite 1), STA $07F9 (for sprite 2),etc. LDA #$80 reads from the first few lines at $2000 and then pastes it into $07F8, etc to perform a perfect display for your sprites.

Sprite Positioning

To position sprites, we use $D000 - $D00F. Why are there 16 instead of 8 values? Well, the reason for this is because $D000, $D002, $D004, $D006, $D008, $D00A, $D00C, $D00E use the sprite's x-axis, while $D001, $D003, $D005, $D007, $D009, $D00B, $D00D, $D00F all use sprite's y-axis, both of these are according to the sprite number. Here is a simple routine to get you started on how to position, display and turn on sprites. Call out a start (*=xxxx) and SEI then enter the following.

A Practical Example with Sprites

LDA #$01   ;1 SPRITE
STA $D015 ;TURN ALL SPRITES ON
LDA #$80
STA $D018
LDA #$70
STA $D000
LDA #$89
STA $D001
RTS

Assemble and run. You will see your sprite sitting on the screen at x = #$70 pixels, and y = #$89 pixels. However, 1 sprite is not enough, so let us make another 7, turn all sprites on and then position them into different places. Refer to the example at the top, then you'll know what you're doing :o)

Sprite Colours

This is something which we have not looked at in this feature. Sprite's colours are simple. We have a hi-res sprite which needs a touch of colour, so here's how it is done. The colour refers from $D027 (sprite 1) to $D02E (sprite 8)

LDA #$colour
STA $D027

You can even toggle multicolour sprites, using LDA #$FF, STA $D01C (Multi colour)

and to change the 2 multi-colours, we use STA $D025 and STA $D026. $D025 uses multicolour 1, and $D026 uses multicolour 2. To get these to work, we need to set LDA #$colour before STA. The 'colour' has to be between #$00 - #$0F, as these are the main 16 colours. (Please refer to your C64 user guide).

Priorities

Sprites have their own priorities. You can put certain sprites in front or behind the characters on screen. This can be toggled by using $D01B. For example LDA #$00, STA $D01B puts all sprites over the text, and LDA #$FF, STA $D01B puts all sprites under the text. The thing is that you can actually toggle the sprites moving over and under the screen (like in a classic intro screen, which involves a green sprite bar going under and over the logo), however, advanced techniques would be required. We'll be taking a look at this later on in the feature.

Expanding Sprites

Another priority, which sprites have is expanding in two different ways x, and y axis. This transformation can sometimes be a good laugh to try (like I did in a BASIC demo called Biblet Land in 1996), but how do we expand our sprites? We use LDA #$FF (for all sprites), STA $D017, and STA $D01D. $D017 expands 'x' and $D01D expands 'y' for the sprites. You can turn one expansion off and another on.

What to do Next

Simple. Load up the IRQ player, which you've made. Before the IRQ routine, call up your sprites, and before JMP $EA31, enter INC $D000 and $DEC $D001. You'll see that your sprite will move diagonally. Try experimenting with these fun routines. I am pretty sure you would have fun with this.



Chapter 9 - Colour-Washing Charsets

This is one of the most simplest tasks. In some demos and other programs on the Commodore 64, you see colours moving from the left hand side - to the right and also vice-versa. How do these simple routines work? Well, this is the easy part - in fact it is the easiest method of them all. Copy this listing below, and all will be revealed straight after the listing.

;Simple colour washing routine, inside
;an IRQ Raster Interrupt Player

                         * = $1000

                         LDX #$00
SHOWMS          LDA MESSAGE,X
                         STA $0400,x
                         INX
                         CPX #$28
                         BNE SHOWMS

Now create your own IRQ raster interrupt, make the screen black and set the C64 charset mode to $16 (LDA #$16 STA $D018)

Inside your IRQ raster interrupt routine, call the colour washing routine.

IRQ     JSR COLWASH
           JMP $EA31

;======================
;COLOUR WASHING ROUTINE
;======================
COLWASH     LDA COLOUR+$00
                     STA COLOUR+$28
                     LDX #$00
CYCLE          LDA COLOUR+$01,X
                     STA COLOUR+$00,X
                     LDA COLOUR,X
                     STA $D800,X
                     INX
                     CPX #$28
                     BNE CYCLE
                     RTS

;DATA TABLES FOR COLOURS

COLOUR .BYTE $09,$09,$02,$02,$08
              .BYTE $08,$0A,$0A,$0F,$0F
             .BYTE $07,$07,$01,$01,$01
             .BYTE $01,$01,$01,$01,$01
             .BYTE $01,$01,$01,$01,$01
             .BYTE $01,$01,$01,$07,$07
             .BYTE $0F,$0F,$0A,$0A,$08
             .BYTE $08,$02,$02,$09,$09
             .BYTE $00,$00,$00,$00,$00

;DATA FOR TEXT MESSAGE

MESSAGE .TEXT "RICHARD BAYLISS'"
 .TEXT " COLOUR SCROLLER"
 .TEXT " ACTIVE......"
 .TEXT "                "

If you take a look at this routine, during the control of the colour washing routine, we take the byte, which is at COLOUR+00 and then we place it at COLOUR+$28, then we call a continuous loop which makes the data table cycle, by subtracting each piece of data in the data table by one, and read the calculations 40 times ($28 times), therefore the data table pulls each byte of data. Also inside our loop, the colours are read from the data table and the positioned on to the C64's colour screen RAM, which is from $D800 until $D828. As for reading the message, you'll see for yourself how that works. It is easy to remember ;o)


 

There is also a simple way to -reverse- the direction of your colour-washing routine. The listing below shows how you can do this :)

;======================
;COLOUR WASHING ROUTINE
;======================
COLWASH     LDA COLOUR+$28
                     STA  COLOUR+$00
                     LDX #$28
CYCLE          LDA COLOUR-$01,X
                     STA COLOUR+$00,X
                     LDA COLOUR,X
                     STA $D7FF,X
                     DEX
                     BNE CYCLE
                     RTS

We reverse the process. You'll also notice that $D800,x has been changed into $D7FF,x. Why is this? Well, simply because if you used $D800 instead of $D7FF you would find that the C64 will miss the first character, therefore you will need to subtract 1 from the screen ram location. Pretty easy to understand huh?

Something to try:

Now that you have learned how to do simple colour cycling, try and put BOTH routines on to the screen. Create your own message at the top of the screen, and bottom of the screen and use one cycling routine at the top and a reverse routine at the bottom.



Chapter 10 - Building Our Very First Game

Don't get too excited because it will not be that easy to code your own C64 game :o) There are loads of new routines which you need to learn. To make things a lot easier, I have added a little explanation to each part of the program. You can also download everything in zipped .D64 format so you can read the code off-line and try to understand it a little more.

We are going to create a small 2 player game, where you have two ships. We'll be using three sprites for this tutorial, but in chapter 11, we will enhance the game more. Here is what to do. Using the sprite editor, draw 2 triangles (not in multi-colour) One pointing up, and the other pointing down, then draw 1 small circle. The two triangles will be the two players and the circle will be the player's bullet. Save your sprite data, and rip or compose your own demo tune using any music composer which initialise your tune at $1000 and play at $1003. Save your music to disk. However, If this is too much hassle to get you started then I have attached a .D64 image with the data and code. You'll just need Turbo Assembler. Please read the 2 note files supplied with the code and data. There is also a runnable file for you so you can see the sort of game, we're teaching you to create :). The runnable file was crunched using Time Cruncher V5.0, as I wanted to make quick file compression. While you're coding the game, this is how it should look.

Download code and data here

...and now the code:
 
 
;A$$EMBLE IT! - CODING TUTORIAL 

;Creating your first game 

;This is an example tutorial for you to 
;learn how to create your own 2 player 
;Shot 'em Up, which includes joystick 
;control, a score system, etc. 

;Let's setup the perameters for our 
;game. These are the player positions. 

plr1Äx   = $0340 ;Player 1 x-position 
plr1Äy   = $0341 ;Player 1 y-position 
plr2Äx   = $0342 ;Player 2 x-position 
plr2Äy   = $0343 ;Player 2 y-position 

;Our parameters for the player bullets 

plr1bÄx  = $0344 ;Player 1 bullet-xpos 
plr1bÄy  = $0345 ;Player 1 bullet-ypos 
plr2bÄx  = $0346 ;Player 2 bullet-xpos 
plr2bÄy  = $0347 ;Player 2 bullet-ypos 

;Collisions parameters 

plr1col  = $0350 ;Value storage for p1 
plr2col  = $0354 ;Value storage for p2 

;bullet lockup routines 

plr1lockup = $0360 ;Lockup for players 
plr2lockup = $0361 ;shooting 
  

;We need to create a jump start for this 
;example, so we will create our own jump 
;address where it does not overlap data 
;which had been loaded. 

         *= $2400 

start    sei ;Set irq flag 

;Clear the screen without JSR $E544 

         ldx #$00    ;Calls a routine 
clear    lda #$20    ;to fill the whole 
         sta $0400,x ;screen with #$20, 
         sta $0500,x ;which is the 
         sta $0600,x ;blank space 
         sta $06e8,x ;routine. 
         inx         ; 
         bne clear   ; 

;You should be familiar with the next 
;example code. If not then look at the 
;earlier chapters of A$$EMBLE IT! 

         lda #$00 
         sta $d020 
         sta $d021 

         lda #$16 
         sta $d018 

         lda #$1b 
         sta $d011 

         lda #$ff 
         sta $d015 

;Now here we create the sprite objects 

         lda #$81 
         sta $07f8 ;Player 1 ship 
         lda #$82 
         sta $07f9 ;Player 1 bullet 
         lda #$80 
         sta $07fa ;Player 2 ship 
         lda #$82 
         sta $07fb ;Player 2 bullet 

;This is where we setup the colours of 
;the two players and bullets. 

         lda #$02  ;Colour red 
         sta $d027 ;Player 1 
         sta $d028 ;Player 1 bullet 

         lda #$07  ;Colour yellow 
         sta $d029 ;Player 2 
         sta $d02a ;Player 2 bullet 

;This is a different routine, as now the 
;default sprite positions are copied to 
;the declared parameters. 

         lda $d000   ;This is a method 
         sta plr1Äx  ;of copying and 
         lda $d001   ;pasting the sprite 
         sta plr1Äy  ;positions so that 
         lda $d002   ;you can use this 
         sta plr1bÄx ;to program the 
         lda $d003   ;sprites positions 
         sta plr1bÄy ;later on in the 
         lda $d004   ;routines, for a 
         sta plr2Äx  ;much faster 
         lda $d005   ;and decent 
         sta plr2Äy  ;movement for all 
         lda $d006   ;the sprites in 
         sta plr2bÄx ;this game. 
         lda $d007 
         sta plr2bÄy 

;Now we reposition the two players and 
;put the bullets into zero 

         lda #$42    ;All this is the 
         sta plr1Äy  ;repositioning the 
         lda #$18    ;two player ships, 
         sta plr1Äx  ;by using the 'x' 
         lda #$e0    ;positions and the 
         sta plr2Äy  ;'y' positions, 
         lda #$98    ;as simple as that 
         sta plr2Äx  ;:) 

         lda #$00    ;All bullets are 
         sta plr1bÄx ;repositioned to 
         sta plr1bÄy ;the zero value 
         sta plr2bÄx ;yet again 'x' and 
         sta plr2bÄy ;'y' positions 

;Setup the scoreboard 

         lda #$30  ;We put zero on: 
         sta $0400 ;first line 
         lda #$02  ;paint first line 
         sta $d800 ;red 
         lda #$30  ;zero put on 
         sta $0427 ;first line as last 
         lda #$07  ;paint character 
         sta $d827 ;yellow 

;Now for the main body of this program 
;the Interrupt flag, but we wont use 
;JMP $EA81 or JMP $EA31, as no keyboard 
;control will be required 

         lda #<int ; Call INT values 
         ldx #>int ; into an IRQ raster 
         ldy #$00  ; interrupt value and 
         sta $0314 ; zero the rasterline 
         stx $0315 ; 
         sty $d012 
         lda #$7f  ; Keep the screen on 
         ldx #$1b  ; and continue the 
         sta $dc0d ; main interrupt 
         stx $d011 ; read 

         lda #$00  ;Initialise music 
         tax       ;according to tune 
         tay       ;number 
         jsr $1000 ; 

         lda #$01 
         sta $d019 ;IRQ is turned on 
         sta $d01a ;---------------- 

         lda $dc0d ;Copy $DC0D to $DD0D 
         sta $dd0d ;to have the IRQ 
                   ;working properly. 

         cli       ;Clear IRQ flag 
loop     jmp loop  ;Jump to the loop 
  

;Our main interrupt 

int      asl $d019 ;Keep $D019 running 
  

;Call routine to expand and reconvert 
;the sprite positions 

         jsr expand 

;Call routine to read joystick port 2 
;for player 1 and player 2 

         jsr read1up 
         jsr read2up 

;Call routine for bullet movements 

         jsr bullmove 

;Call routine for collision detection 
;and player 1 and player 2 bullet to 
;player collision. 

         jsr detect 
         jsr p1col 
         jsr p2col 

;And finally play the music 

         jsr $1003 ;Play music 

         pla ; An IRQ loop routine. 
         tay ; 
         pla ; This will keep all the 
         tax ; jsr routines playing 
         pla ; without using JMP $EA81 
         rti ; or JMP $EA31 

;Expand and reconver the sprite position 

expand   lda plr1Äy   ;Copy player y 
         sta $d001    ;to exact position 
         lda plr1bÄy  ;Copy bullet 1 y 
         sta $d003    ;to exact position 
         lda plr2Äy   ;The same goes 
         sta $d005    ;with this routine 
         lda plr2bÄy  ;but instead it 
         sta $d007    ;works with p2. 

         lda plr1Äx   ;Copy player x 
         asl a        ;calculate 64 
         ror $d010    ;Expand x pos. 
         sta $d000    ;put at exact xpos 
         lda plr1bÄx  ;Copy bullet 
         asl a        ;and do the same 
         ror $d010    ;as with the 
         sta $d002    ;player. 
         lda plr2Äx   ; 
         asl a        ;All this is the 
         ror $d010    ;same except that 
         sta $d004    ;it will work with 
         lda plr2bÄx  ;Player 2 and the 
         asl a        ;Player 2 bullet 
         ror $d010    ;instead. 
         sta $d006    ; 
         rts          ; 

;Read joystick control for player1 

read1up  lda $dc00 ;Read port 2 
         lsr a ;Joystick up 
         lsr a ;Joystick down 
left1    lsr a ;Joystick left 
         bcs right1 
         ldx plr1Äx ;Move player 1 
         dex        ;left across screen 
         dex        ;using 2 for speed 
         cpx #$0e   ;does the player 
         bcs set1   ;move further than 
         ldx #$0e   ;$0e, if so then 
set1     stx plr1Äx ;stop. Else continue 
right1   lsr a      ;Joystick right 
         bcs fire1 
         ldx plr1Äx ;Move player 1 
         inx        ;right across screen 
         inx        ;using the same 
         cpx #$9a   ;speed. If player 
         bcc set2   ;exceeds $9a, then 
         ldx #$9a   ;make the player 
set2     stx plr1Äx ;stop. 

fire1    lsr a ;Firebutton 
         bcs nojoy1 
         lda plr1lockup ;Is player fire 
         cmp #$00       ;control 
         bne nojoy1     ;unlocked. If so 
         lda #$01       ;then lock fire 
         sta plr1lockup ;and position 
         ldx plr1Äx     ;the bullet x 
         stx plr1bÄx    ;and y positions 
         ldx plr1Äy     ;exactly at the 
         stx plr1bÄy    ;same areas as 
                        ;the player. 
nojoy1   rts 

;Read joystick control for Player 2 
;(joystick port 1) 

read2up  lda $dc01    ;Read JOY Port 1 
         lsr a ;up 
         lsr a ;down  ;Please read the 
left2    lsr a ;left  ;player 1 
         bcs right2   ;joystick control 
         ldx plr2Äx   ;as this routine 
         dex          ;is exactly the 
         dex          ;same as the 
         cpx #$0e     ;player 1 controls 
         bcs set1Ä1   ;but works for 
         ldx #$0e     ;player 2. 
set1Ä1   stx plr2Äx 
right2   lsr a 
         bcs fire2 
         ldx plr2Äx 
         inx 
         inx 
         cpx #$9a 
         bcc set1Ä2 
         ldx #$9a 
set1Ä2   stx plr2Äx 
fire2    lsr a 
         bcs nojoy2 
         lda plr2lockup 
         cmp #$00 
         bne nojoy2 
         lda #$01 
         sta plr2lockup 
         ldx plr2Äx 
         stx plr2bÄx 
         ldx plr2Äy 
         stx plr2bÄy 
nojoy2   rts 

;call bullet routines 

bullmove ldx plr1bÄy  ;The bullet moves 
         inx          ;but if it hits 
         inx          ;$F6+ it will stop 
         inx          ;off screen else 
         inx          ;if below that 
         inx          ;limit, the bullet 
         inx          ;/ moves on, else 
         cpx #$f6     ;bullet off screen 
         bcc repsbul1 
         lda #$00       ;Turn off the 
         sta plr1lockup ;fire lockup 
         ldx #$f6 
repsbul1 stx plr1bÄy 

         ldx plr2bÄy  ;The bullet moves 
         dex          ;up but if it hits 
         dex          ;$0C- it will stop 
         dex          ;off screen, else 
         dex          ;if below that 
         dex          ;limit, the bullet 
         dex          ;/ moves on. else 
         cpx #$06     ;bullet off screen 
         bcs repsbul2 
         lda #$00       ;Turn off the 
         sta plr2lockup ;fire lockup 
         ldx #$06 
repsbul2 stx plr2bÄy 
         rts 

;Setup collision detection for player 1 
;and player 2 

detect   lda plr1Äx       ;Calculate 
         sec              ;the storage 
         sbc #$06         ;values for 
         sta plr1col+$00  ;the exact 
         clc              ;positions of 
         adc #$0c         ;player 1, so 
         sta plr1col+$01  ;that when the 
         lda plr1Äy       ;opponents' 
         sec              ;bullet hits 
         sbc #$0c         ;the player, 
         sta plr1col+$02  ;it has to be 
         clc              ;in the exact 
         adc #$18         ;position of 
         sta plr1col+$03  ;player 1. 

         lda plr2Äx       ;The same 
         sec              ;calculations 
         sbc #$06         ;for player 2 
         sta plr2col+$00  ;bullet to 
         clc              ;player 
         adc #$0c         ;collision. 
         sta plr2col+$01 
         lda plr2Äy 
         sec 
         sbc #$0c 
         sta plr2col+$02 
         clc 
         adc #$18 
         sta plr2col+$03 
         rts 

;Check player 1 bullet collision on 
;player 2 ship 

p2col    lda plr1bÄx     ;Is the bullet 
         cmp plr2col+$00 ;at the correct 
         bcc missp2      ;position where 
         cmp plr2col+$01 ;the player is? 
         bcs missp2      ; 
         lda plr1bÄy     ;If not then 
         cmp plr2col+$02 ;the bullet 
         bcc missp2      ;misses the 
         cmp plr2col+$03 ;player. 
         bcs missp2      ; 
         lda #$f6        ;Else move 
         sta plr1bÄy     ;bullet off 
         lda #$00        ;screen, turn 
         sta plr1lockup  ;off fire lock 
         inc $0400       ;add 1 point 
         lda $0400       ;check score 
         cmp #$3a        ;is it over 9? 
         bne missp2      ;if not then 
                         ;miss, else 
         jmp victory1    ;jump to win 
missp2   rts 

;Check player2 bullet on player 1 ship 

p1col    lda plr2bÄx     ;Does the 
         cmp plr1col+$00 ;bullet hit 
         bcc missp1      ;the player in 
         cmp plr1col+$01 ;exact position 
         bcs missp1      ;of the player 
         lda plr2bÄy     ;ship? If so 
         cmp plr1col+$02 ;then continue 
         bcc missp1      ;else jump to 
         cmp plr1col+$03 ;missp1 
         bcs missp1 
         lda #$06 
         sta plr2bÄy 
         lda #$00 
         sta plr2lockup 
         inc $0427       ;Add 1 point 
         lda $0427       ;check score 
         cmp #$3a        ;is it over '9' 
         bne missp1      ;if so then 
         jmp victory2    ;player2 wins 
missp1   rts 

victory1 sei             ;Stop all IRQs 
         lda #$00        ;Turn off all 
         sta $d015       ;the sprites 

         ldx #$00        ;Call a routine 
win1     lda vic1,x      ;to display 
         sta $0400,x     ;player1 wins 
         lda #$01        ;message and 
         sta $d800,x     ;paint the 
         inx             ;message white 
         cpx #$28        ;and display 
         bne win1        ;as 40 chars 

         jmp space       ;Jump to space 
                         ;prompt 

victory2 sei             ;   Look at 
         lda #$00        ;  'victory1' 
         sta $d015       ;same function 

         ldx #$00        ;This routine 
win2     lda vic2,x      ;does exactly 
         sta $0400,x     ;the same, but 
         lda #$01        ;it reads the 
         sta $d800,x     ;text from 
         inx             ;vic2 so that 
         cpx #$28        ;player 2 wins 
         bne win2        ;the game 

         jmp space       ;You know this 
                         ;if not then 
                         ;look at the 
                         ;previous 
                         ;'jmp space' 
                         ;prompt 

space    ldx #$00        ;Another text 
setspc   lda spc,x       ;display 
         sta $07c0,x     ;routine. This 
         lda #$03        ;time position 
         sta $dbc0,x     ;the text at 
         inx             ;the bottom of 
         cpx #$28        ;the screen as 
         bne setspc      ;40 chars 

hitspace lda #$80        ;Create a 
raster   cmp $d012       ;raster control 
         bne raster      ;to continue 
         jsr $1003       ;playing music 

         lda $dc01       ;Read SPACEBAR 
         cmp #$ef        ;if not pressed 
         bne raster      ;then jump to 
                         ;the raster to 
                         ;continue 
                         ;the SPACE read 
                         ;routine and 
                         ;play music 

         jmp start       ;Restart game 
  

;Our text display data tables. 

         ;Player 1 victory 

vic1     .text "player 1 proves that" 
         .text " player 2 is a loser" 

         ;Player 2 victory 

vic2     .text "player 2 proves that" 
         .text " player 1 is a loser" 

         ;Spacebar prompt message 

spc      .text "  press the spacebar" 
         .text " for another game!  " 
 
 

 

Next Month: Enhancing your game a little more