*** THIS FILE IS ALSO NOW IN THE DEMO SECTION TO ENABLE THE "TRY IT NOW" FEATURE ***
This may be of interest to absolute 6502 assembly beginners like me, although advanced 6502 programmers may cringe at the way I've done things here! This program does very little, but it is a repository of useful assembly routines for things like printing different bytes of memory (useful for debugging) as well as some basic math operations. I will keep adding to this as I progress through my assembly journey (I'm aiming to write my fractal BASIC programs in assembly).
Thanks to the following YouTubers for their excellent tutorials on all things 6502:
Ben Eater - YouTube
Matt Heffernan - YouTube
ChibiAkumas - YouTube (and also his excellent website: Assembly Tutorials: Learn 6502 Assembly Programming... With ChibiAkumas!)
Function usage: (notation for cc65 assembler)
.byte (list of PETSCII character codes to print, ending in a $0 byte)
.byte (list of PETSCII character codes to print, ending in a $0 byte)
.word (start address of memory dump)
Set MEMDUMPLEN to the number of addresses you wish print_mem to display.
Here is a little demo of how to dynamically load files in RAM bank in assembly.
It's very simple to do but I think that it can be helpful for someone who don't know how to do this right away. In fact I personally would love to see more of this kind of programs in the download section 🙂
How this loader works ?
First thing to do is to tell the Kernal that we want to use a logical file. For this we need to use the SETLFS subroutine.
From the "C64 Programmer Reference Guide" :
Since we want to load from the disk (or filesystem) we'll need to use the device number 8. The logical file is not important so we use the file 0.
Also, as we want to relocate the program into a bank at $A000, we'll set the Y register to #0 to activate secondary address mode.
Next step is telling the Kernal which file we want to load. For this we'll use the SETNAME subroutine.
From the "C64 Programmer Reference Guide" :
For this we'll need to store our file names somewhere in our program so we write at the bottom of our file the names as a string of petscii characters. We then prepare our registers with the size of the filename to load, and then the address.
Our final step to load a file is obviously the LOAD subroutine.
From the "C64 Programmer Reference Guide" :
As the Reference guide said, we want to load a file so we set our A register to 0 and since we want to load to banked RAM ($A000) we enter the address in our X and Y registers.
One last thing that we need to do just after each of our LOAD calls, is to verify that our file has been successfully loaded. For this, we'll need to use the READST subroutine.
From the "C64 Programmer Reference Guide" :
As usual, following the Reference guide, all we need to do is call this subroutine just after our LOAD call, and check the content of the Accumulator. If zero, the load was successful.
And that's all ! You can chain file loading as much as you need, and even you just need to call SETLFS once at the start of the chain.
Note that you'll need to switch the bank between file loads to prevent overwriting previously added program. And since Bank 0 is also reserved you'll need to first switch to bank 1 and start from here.
At the end you can also load a file in place of the loader, just avoid overwriting memory where the code is currently being executed. You can for example leave this kind of code in your first bank and at last run it to load a program from $0800 to $9EFF.
Kernal Subroutines full documentation : https://www.pagetable.com/c64ref/kernal/
If you have any suggestions for the code or even want to change things in this description, don't hesitate to tell me !
By TomXP411This reads the disk directory in BASIC. This is a super simple example and doesn't actually parse the files... it's just smart enough to read "$" and print it to the screen.
By CursorKeysA simple game fully in Basic.
NOTE: If you just want to run the latest version of this game on the Web based emulator do like this.
1. Copy the code from: https://raw.githubusercontent.com/JoystickAndCursorKeys/x16basic/main/fallingsnake.v4.bas.txt (CTRL-C)
2. Go here: https://www.commanderx16.com/emulator/x16emu.html and paste the code. (CTRL-V)
3. Press "Run"
This (version 0 of the game) is a very little game I wrote on the commodore 64 ages ago, I changed it a little to work with vpeek, vpoke, the new color command, and the new screen resolution.
To me starting with a simple game in basic is how I learned about the c64, and now the X16, before jumping in machine language.
In the code for this game you can see practical examples, on how to set characters on the screen, read characters from the screen, change colors for characters, listen to the keyboard, set a screen mode.
What surprised me was that I needed a delay in the code in order to have it not run too fast. This was not the case on the c64.
I really like the way you can use the emulator in the browser, as it allows you to type in basic inside the browser, using "regular key mappings", and the run it.
When I wanted to save it, I copy-pasted it into the installed emulator (hats off for who added he paste function there), and types the save command.
This was done, because in the browser you cannot save it, and see it on disk (as far as I know)
I added many REMs in the code, in order so it can be understood, and it is more of a tutorial, then a "real" game.
Below I will go through the code.
From line 5, the program initializes From line 9, the title screen is drawn From line 49, the game is initialized From line 199, the game loop starts From line 300, the game over screen is drawn Some miscelanious notes:
The SCREEN command is used to set the screen in text mode (Petscii) 40x30 The color command is used to set character fore and background colors. Unlike the c64 each char has its own background color. There is no "global" background color CHR$(113) is used to draw the Petscii circle character, used on the title screen. Adding the actual character into the code listing, makes it hard to edit outside the emulator. CHR$(119) is another Petscii character VPOKE 0,0,x, pokes a character on the top level corner of the screen. So the text screen address = 0 (not like on the C64) On line 52 the bottom of the screen offset is calulated. NOTE: Even though the screen is 40 chars long, to get to VPOKE address of the next line, you need to add 256 bytes. SI and PI (StoneImage and PlayerImage) are Petsci chars, that are used in the game. NOTE: Petscii chars have different values in the PRINT or the VPOKE commands. Reminder: In C64/Microsoft basic a variable name is only two characters long max. Reminder: Lines in Basic should not be longer then 80 chars. When you make them longer, the emulator ignores them. A good place to see all PETSCII character codes is here: https://www.commanderx16.com/forum/index.php?/files/file/23-vera-chars/ Have fun!
And the code itself:
(it is easier to copy paste it in the browsers emulator and be able to modify it more easy here: https://www.commanderx16.com/emulator/x16emu.html
----- code below for copy & pasting, also you can get it by downloading FALLSNAKE.PRG or checked out at: https://github.com/JoystickAndCursorKeys/x16basic/blob/main/fallingsnake.v0.bas.txt-----
5 REM SET TO 40X30 CHARS SCREEN
6 SCREEN 0
9 REM TITLE SCREEN -----------------
10 REM SET COLORS TO BLACK AND WHITE, CLEAR SCREEN
11 COLOR 1,0: CLS
12 PRINT:PRINT:PRINT:PRINT : REM PRINT TITLE
13 PRINT " FALLING SNAKE": PRINT: COLOR 15
14 PRINT " YOU ARE FALLING DOWN A HOLE"
15 PRINT " AVOID ALL OBSTACLES"
16 COLOR 7: PRINT " PRESS SPACE TO START"
17 BA$=CHR$(113):BB$=CHR$(119) : REM BALL SYMBOLS
18 COLOR 2,0 : PRINT " "+BA$
19 PRINT " "+BA$:PRINT " "+BA$
20 PRINT " "+BA$+BA$+BA$+BA$+BA$+BA$+BA$+BA$+BA$;:COLOR 8:PRINT BB$
25 GET A$: IF A$="" GOTO 25 : REM WAIT FOR KEY
35 TS=0 : REM SET TOP SCORE
49 REM START GAME -----------------
50 COLOR 1,0: CLS
51 FOR T=1TO29:PRINT:NEXT
52 BO=29*256:SI=42: : REM BOTTOMSCREENOFFSET, STONEIMAGE
53 PO=15*256:PI=81:PC=2:PX=20 : REM PLAYEROFFSET, PLAYERIMAGE, PLAYERCOLOR
54 DE=1000 : REM DELAY VALUE
55 S=0 : REM SET SCORE
199 REM GAME LOOP -----------------
200 GET A$
201 IF A$=CHR$(29) AND PX<40 THEN PX=PX+1 : REM GO RIGHT
202 IF A$=CHR$(157) AND PX>0 THEN PX=PX-1: REM GO LEFT
210 X=INT(RND(1)*40)*2: C=INT(RND(1)*15)+1 : REM GET RANDOM STONE POSITION
211 PE=VPEEK( 0, PO+(PX*2))
212 IF PE=SI THEN GOTO 400
220 VPOKE 0, BO+X, SI: VPOKE 0, BO+X+1, C
221 VPOKE 0, PO+(PX*2), PI: VPOKE 0, PO+(PX*2)+1, PC
296 FOR W=1 TO DE: NEXT : REM DELAY, SLOW DOWN CODE
297 PRINT : S=S+1 : DE=DE-1 : IF DE<0 THEN DE=0
298 GOTO 200
399 REM GAME OVER -----------------
400 IF S>TS THEN TS=S
401 PRINT HM$:PRINT:PRINT:PRINT " GAME OVER": PRINT
402 PRINT " SCORE: "+STR$(S);
405 PRINT " TOP SCORE="+STR$(TS)
410 COLOR 7: PRINT: PRINT " PRESS SPACE TRY AGAIN" : PRINT:PRINT
420 GET A$
421 IF A$ = "" OR A$=CHR$(29) OR A$=CHR$(157) THEN GOTO 420
422 GOTO 50
---------------------------------------------------------- End of Code First Version ----------------------------------------------------------
The way I normally work when creating a game, I create first the skeleton, and then add more and more niceties, like effects, and game play elements.
Above, we have the skeleton, we have a minimal game. We have a title screen, we have a playable section, we have a score, a game over section, and a high score.
Below I will show the improvements I made so far, and are saved in FALLSNAKE2.
In the init section of the program, I rearranged the line numbers, so I would not overlap with the rest of the program, when adding two more lines. The added lines are in Bold/Underline. Line 3, I allocate an array called EX to store the "petscii picture" of an "explosion". It is 9 chars. The "picture" is 3x3 chars. No colors. 3x3=9, which is how much we need to allocate with the DIM command.
Line 4, reads data from the DATA statements at the end of the program into the array called "EX".
0 REM SET TO 40X30 CHARS SCREEN
1 SCREEN 0
2 HM$=CHR$(19) : REM HOME CHARACTER
3 DIM EX(9)
4 FOR T=0TO8: READ C : EX(T)=C: NEXT : REM READ PETSCII EXPLOSION
In the "Game over" section of the game, I added the drawing of the explosion picture, and a flashing color effect to go with it. Again, I renumbered the code around it a bit. We iterate through the characters in the EX array, by looping x and y from 0 to 2, and calculating the offset to EX (called DO, data offset). We use VPOKE to put the bytes on the screen memory, at address SO (screen offset). SO is calculated by using the xx + the player y coordinate, minus 1 (this will be XS). Similar will be done for y, but to calculate SO, we add XS*2 (each char takes two bytes) + YS*256 (from one row to the next the distance offset is 256 chars)
401 REM DRAW EXPLOSION
402 FOR XX=0TO2:FOR YY=0TO2
403 XS=XX+PX-1: YS=YY+15-1 : REM SCREEN X,Y
404 IF PX<1 THEN XS=XX+1 : REM CHECK PLAYER X TO BE ON SCREEN
405 IF PX>38 THEN XS=XX+38
406 DO=XX+(YY*3):SO=(XS*2)+(YS*256) : REM CALC DATA OFFSET, SCREEN OFFSET
407 VPOKE 0,SO,EX(DO) : REM WRITE TO SCREEN
Below there is a simple color cycling effect for the background, this is done by adding random values into palette register for color 0. The register is stored in two addresses, $FA00 and $FA01. Actually some bytes are not used, but for a random effect we ignore that. If we want to have for example a red explosion flicker, we need to be more careful on the values we put into the register.
410 REM EXPLOSION COLOR EFFECT
411 FOR T=1 TO 50
414 VPOKE 1,$FA00,C0: VPOKE 1,$FA01,C1
415 NEXT: VPOKE 1,$FA00,0: VPOKE 1,$FA01,0
and the last part (after renumbering even more lines of code), we added the data for the explosion petscii "picture".
2000 REM PETSCI EXPLOSION
2001 DATA $4D, $20 , $4E, $20, $D6, $20, $4E, $20, $4D
The complete code can be found in FALLSNAKE1.PRG, or checked out at: https://github.com/JoystickAndCursorKeys/x16basic/blob/main/fallingsnake.v1.bas.txt
One thing that keeps me going when creating a game, is but adding sessions where I make the game look nicer, without adding functionality.
The changes below (1.0.2 version, FALLSNAKE2.PRG), is all about this.
Just Renumber it
To start off, we have been renumbering again the lines of the source code.
For those that are not used to using old school type basic, it is very easy to add extra code and run out of numbers. And it is important to "reserve" numbers. So for example if you make a program like this.
1 PRINT "HELLO WORLD"
2 GOTO 1
And you want to add something between line 1 and 2, you cannot do it, unless you renumber the existing lines. For that reason it is a better idea to start coding like this:
10 PRINT "HELLO WORLD"
20 GOTO 10
This way you have some space in between, and you could easily add a line 15 to print something else, if you want to.
Nevertheless it can still happen even when you have reserve ranges, and they get to be "full". This is especially the case when you add new sections of verbose code between other code. And this is exactly why I am once more renumbering.
-- -- --
To add sprites, you need to have the images stored some where. The old school way of doing it, is to add "DATA" commands to the code, like below. I will not write it completely here, since it becomes very verbose, but it looks something like this:
11000 DATA $00, $00, $22, $22, $22, $22, $00, $00
11001 DATA $00, $22, $82, $82, $82, $82, $22, $00
11002 DATA $02, $88, $88, $28, $28, $28, $28, $20
11003 DATA $28, $08, $08, $22, $22, $22, $82, $8b
11004 DATA $28, $88, $88, $20, $00, $28, $28, $b0
11005 DATA $02, $82, $82, $00, $02, $82, $8b, $00
11006 DATA $00, $22, $20, $00, $28, $22, $b0, $00
..... Many more lines of this
NOTE: Only in the 80s would you type in lines and lines of DATA like this. Now there are easier ways, for example a sprite editor with an export function. There are many options, below is one of them that I am coding on myself.
The important thing is to export to a sprite data that the X16 understands. In this case 16x16 dimensions, 16 colors, so 4bpp (4 bits per pixel for color info), and basic support HEX numbers.
And to use them, you write something like this.
1000 REM READ SPRITE DATA
1001 FOR I=O TO 255
1011 READ PX : REM READ ONE NUMBER FROM THE DATA.
1012 VPOKE $0,$4000+I,PX: REM USE VPOKE TO MOVE THE DATA IN VIDEO MEMORY
1013 NEXT I
At the end of these lines of code, you see the command RETURN. What is this about?
Well, with basic we can simulate calling a function/subrouting, with GOSUB. RETURN returns back to where it was called.
The code is being called in the beginning, something like this:
10 GOSUB 1000 : REM READ SPRITE DATA
After the call to the subroutine at line 1000, it continues to the next line. This is one way to organize the code inside Commodore/Micro$oft basic.
I will not mention all the details of setting up the sprite here, you can check the code yourself, if you want to. Later I will go deep into this topic, but not here.
However to get the sprite in the right position we have another subroutine.
1070 REM SET SPRITE 0,X0,Y0
1171 VPOKE $1,$FC12,X0 AND $FF
1172 VPOKE $1,$FC13,(X0 AND $0300)/$100 : REM MAGIC TO GET HI BYTE OF THE X COORDINATE
1173 VPOKE $1,$FC14,Y0 : REM YPOS 128
1174 VPOKE $1,$FC15,$00 : REM WE DON'T CALCULATE THE HI BYTE OF THE Y COORDINATE
Which I call in the title screen build up like this.
75 X0=150: Y0=68: GOSUB 1070 : REM SET SPRITE X,Y
Note that for calling the subroutine with parameters, we simply assign global variables. To keep from clashing with other global variables, use a naming convention.
Here I used 0 (zero) as the second 'letter' in the name as the naming convention.
In the game, the position changes all the time, so instead we do like this:
295 X0=PX*8: Y0=122: GOSUB 1070 : REM SET SPRITE X,Y
X0 is calculated. Y0 is static, but X0 is PlayerX (PX) times 8 (the width of a character on screen in bits)
Now for the colour effect, we chose one color to be the one that has the effect, and we draw our blinking characters with this color. Like below.
60 PRINT "FALLING SNAKE";: COLOR 14: PRINT "**": PRINT: COLOR 15
And we cycle / blink, by doing something like this.
80 C0=C0+1: IF C0>2 THEN C0=0: C1=C1+1: IF C1>255 THEN C1=0:
82 VPOKE 1,$FA1C,C1: VPOKE 1,$FA1D,C1
Offset $FA1C and $FA1D in the VERA video ram, control the palette registers for color 14. Each color has two bytes, so if you want to find the color register for color 0, you just substract 28 decimal (2*14) from hex $FA1C.
(Convert 28 to hex first)
Feel free to check out the code or the PRG file, at this stage.
PRG file: FALLSNAKE2.PRG
In the next session we will concentrate a bit more on gameplay. It is no use to pimp up the graphics, if the gameplay is boring.
Since this is really a simple game, we easily can do a few things to spice it up.
So now it is time to look a little at the game play.
So far the gameplay has been very simplistic. Avoid obstacles, and do it as long as you can. And the obstacles are the same.
And the speed is the same always.
...... Hold on, not so fast.
In the previous sections I managed to sneak in two lines of code that change the play speed over time. The longer you play the quicker the game becomes.
This was done as such.
54 DE=1000 : REM DELAY VALUE
Here the delay between each "cycle" in the game is defined as 1000, before the game starts.
And then to use the delay, we have:
296 FOR W=1 TO DE: NEXT : REM DELAY, SLOW DOWN CODE
And to decrease we have:
297 ......................... DE=DE-1 : IF DE<0 THEN DE=0
This already gives a little variation in the game. Let's add even more.
(ps, you can see all the latest changes here on github
Slow and Extra Points
The two gameplay elements I intend to add are "Slow down" and "Extra (Bonus) Points"'
Since the game goes faster and faster it it reasonable tro asume that the player wants to slow down from time to time. With this "feature", this will be possible.
How will it work? Some of the objects that need to be avoided will look different. (Arrow up sign) And to make them recognizable they also have a blue blinking color.
When you catch one, your speed (DE) will be increased.
Extra points is implemented in a similar way. There is another symbol (a green clover, to symbolize snake food (yes Simon is a vegetarian snake :) ). When you run into this symbol, your score will be increased by a fixed amount.
NOTE: In order to do all this, we needed to renumber a few things again. You see that happening alot in old school basics, at least when I am programing there 🙂
To do all this, first we defined all the colors characters for the different types of obstacles.
6 DIM CO(6): CO(0)=9: CO(1)=11: CO(2)=12: CO(3)=15: CO(4)=5: CO(5)=6
#CHARACTERS. see any Petscii table like on page https://www.linusakesson.net/art/three-petscii-pieces/index.php
7 DIM IO(6): IO(0)=$66: IO(1)=$6F: IO(2)=$54: IO(3)=$75: IO(4)=$58: IO(5)=$1E
Now we have 6 characters defined, we can select a random one by just doing a INT(RND(1)*6)
But actually I do a little differently, to make the changes to select a special character lower, my code looks like this.
225 R=( INT(RND(1)*5) 😞 IF RND(1) > .95 THEN R=5
226 C=CO(R): I=IO(R)
C = the value for the color, I is the value for the character (I=image)
And we poke them on the screen like so:
231 VPOKE 0, BO+X, I: VPOKE 0, BO+X+1, C
But now we cannot just jump to "explosion", when we hit something, we we also adjust our code here:
IF PE<>32 THEN O0=O: C0=PE: D0=1: GOSUB 500 : IF D0=1 THEN GOTO 400
O0,C0 are the input parameters for the gosub, and D0 is the output.
O0, is the object offset on the screen of the object we collide with. C0, is the character code of this object. D0, is returned. It is 1 if we died, zero otherwise. If D0 is returned 1, we jump to 400, where we go into the "die/explosion" state.
The handling of the different collisions with "slow" and "extra points" is done at line 500.
500 REM CHECK COLISSION
501 D0 = 1
502 IF C0 = $58 THEN S=S+50 : D0=0: GOSUB600 : REM GOT FOOD = EXTRA POINTS
503 IF C0 = $1E THEN DE=DE+100 : D0=0: GOSUB630 : REM GOT "SLOW DOWN" SYMBOL
Write "50" on the screen.
600 VPOKE 0,O0-256,$35 : VPOKE 0,2+O0-256,$30
601 VPOKE 0,1+O0-256,1 : VPOKE 0,3+O0-256,1
Write Slow on the screen
630 VPOKE 0,O0-4-256,$13 : VPOKE 0,2+O0-4-256,$0C:
631 VPOKE 0,4+O0-4-256,$0F :VPOKE 0,6+O0-4-256,$17
632 VPOKE 0,1+O0-4-256,1 : VPOKE 0,3+O0-4-256,1
633 VPOKE 0,5+O0-4-256,1 : VPOKE 0,7+O0-4-256,1
And this i basically our changes. Feel free to copy the code from:
To test it in the emulator.
As I mentioned before, in order to keep the interest for such a game, I usually sneak in some visual improvements, so that also happened during my focus on game play.
The "slow" character has been made to flash blue/cyan.
This is done like this.
1. The character is always drawn with color 6
2. The pallette color for color 6 flips between 2 presets.
This is done with these 3 lines of code.
Increase a counter. This counter has only one purpose, to control the speed of the blinking.
281 IF CL>2 THEN CL=0: CC=1-CC : LO=255:HI=8: IF CC=1 THEN LO=15:HI=1
If the counter is larger then 2, then reset it, and flip a flag called CC, by inverting it's value with CC=1-CC
Set the low and high value of the color index. If the flag is set, overwrite the color low and high value with different values.
284 VPOKE 1,$FA0C,LO: VPOKE 1,$FA0D,HI
And this last line, writes it into the palette register, so it becomes visible. Remember to only now use color 6 for things that should blink.
(And the same goes for color 15, which is blinking during the title screen)
To make it a little more looking like a professional game, we add a score display on the screen. While we are add it, we also add the speed.
Making it finally look like this:
Let's have a look on how we added this.
Writing the information to the top of the screen buffer would not work very well, since the whole screen buffer is scrolling upwards, and so it would keep dissapearing, resulting in a flicker.
Also, since this is Basic, updating 80 bytes for each cycle, would create alot of delays.
The X16 has hardware support for two layers of text. And the top layer can have transparency, so you can still see the bottom layer. The transparency is enabled by using color 0, which is black.
The score is "printed" in the frontmost layer.
We can add this extra layer by direcly poking in Vera's registers.
There are some things to keep in mind:
The extra text layer is not accessible through "PRINT", so it needs to be changed by POKING directly in the buffer. Per default BASIC uses layer 1, which is the front layer. We want BASIC to use layer 0, which is the background, since we're printing everything on the background with basic commands. Writing a number to the screen, is time consuming, and slows down the game. So for now, we only update the score-bar, each ten cycles, to save on speed. We start out by adding a new routing, to setup the layered text based screens.
1030 REM SETUP 2 LAYERS SCREEN
1031 POKE $9F29,($20 OR $10 OR PEEK($9F29))
Enable layer 0 and 1.
1032 POKE $9F2F, PEEK( $9F36)
1033 POKE $9F2D, PEEK( $9F34)
Swap screens. Move layer 0 pointers to layer 1 pointers.
These registers point to the tilebase and the layer 0 config. So layer 0 looks now like layer 1. Same font, same screen dimensions.
1034 POKE $9F2E, $00: POKE $9F35, $0F
Now setting layer 0 to point to screen memory offset 0, and layer 1 to screen offset "$0F" (note this is only the "high byte" of the offset)
And last, we clear the screen with the right colors. (this affects now layer 0, after our changes)
1035 COLOR 1,0: CLS
More problems to solve:
Since one of our layer is now in a different memory space then basic looks at, we need to manually update this layer with the poke command.
By running a simple for loop, we can clear the while screen, while poking a value into the screen buffer.
The code turned out so, that it was very slow. So I created a "fill text line" routine, which is somewhat quicker. When I want to clear the screen, I call it with the right parameters (offset, draw character, draw color)
The code below, as you see is repeating alot of pokes, instead of doing a for loop with one poke in it. This not-so-nice trick, gets us just a tiny bit of speed improvement. Since clearing the screen takes long, any improvement in speed is worth is.
2499 REM CLEAR LINE FAST(O0,P0,C0)
2500 FOR X=0TO39 STEP 10: O=(X*2)+O0
Many pokes below. Instead of just one poke. To get this tiny speed improvement.
2501 VPOKE 0,O,P0: VPOKE 0,O+1,C0:VPOKE 0,O+2,P0: VPOKE 0,O+3,C0
2503 VPOKE 0,O+4,P0: VPOKE 0,O+5,C0:VPOKE 0,O+6,P0: VPOKE 0,O+7,C0
2505 VPOKE 0,O+8,P0: VPOKE 0,O+9,C0:VPOKE 0,O+10,P0: VPOKE 0,O+11,C0
2507 VPOKE 0,O+12,P0: VPOKE 0,O+13,C0:VPOKE 0,O+14,P0: VPOKE 0,O+15,C0
2509 VPOKE 0,O+16,P0: VPOKE 0,O+17,C0:VPOKE 0,O+18,P0: VPOKE 0,O+19,C0
Note that O0, P0, and C0 are the "input parameters" to the routine. (offset, draw character, draw color)
The next part is the routine to draw a number on the screen, for the score. We have the similar routine for the speed, but the code is basically a copy-paste.
The trick is to get the score, which is a number, into a sequence of printable (pokeable) characters.
We want the score to also always have the same "size", which is six digits. There are not commands in Basic to format the string nicely for us, so we use tricks.
2999 REM UPDATE SCORE
3000 S$=STR$(N0 + 1000000)
We convert N0 (the input of the routing, which is the Score in our case), to a string with STR$. But before that we add 1000000 to it so the output is always score + 1000000, which is 7 digits always.
(unless you get more the 1 millions points, then the code stops working fine)
And the trick is to use the last 6 characters from the string only. Example: Score = 555 -> 10000000 + Score = 1000555 -> last 6 digits is -> 000555
To get the last 6 digits, we start our loop below at position 2. Then we use MID$ to get one character from the string, and ASC to convert that character to a pokeable number.
3001 FOR T=2TOLEN(S$)-1
3004 VPOKE 0,7676+O0+(T*2),C
And here we poke it to the video memory. (O0 is the offset)
And we're done.
So far this tutorial.
The code can be found at:
FALLSNAKE4.PRG is the final program.
Recreating this old program on the X16 was fun, and quite informative on how the VERA video chip worked. There is many more things you can do to improve the game. One thing we found is that the game currently is "almost" a little on the slow side.
So adding more features, would need rewrites of certain sections, or moving things into assembly language.
Since this is a "simple game basic manual", I will not do this here, but the game may evolve in that way in the future.
Things you can easily add to the program, without compromising speed, is "cutscenes", dedicated character graphics, and perhaps some simple sound effects.
On the code itself.
For the snake and snake head, I could have used character graphics instead of sprites. In fact there is no real need to use sprites here, but then again, in the end it made the snake head less flickery, which is a nice bonus. Using text mode was a design decision, since Basic has the build in scroll-screen feature, if you print a newline on the bottom line. This is what this game utilizes to it's fullest. Why does the snake fall? In fact it has nothing to do with the story, it is more that it is the simplest way of having a scrolling game. It started out as a car racing game, but the care left trails. So calling the trails for snake was easiest. And since it went down, falling seemed appropriate. If you want a more interesting background story, I can also provide. Simon is domesticated snake, and he goes out one day, and falls down a hole. He needs to avoid obstacles like branches and so on to survive, so he can splash down in the water at the bottom safely, before climbing up again. 🙂 Feel free to clone the code, and make a more convincing background story 😉 if you wish. Color palette changes. Changing the palette is a really cool way to change many things on the screen, with only one command. Which is excellent for BASIC since it saves in CPU usage, and you do not loose to much speed. Even though running fine in the beginning, almost too fast. When you add features to the game main loop, it is very noticeable how the game slows down, and the delay constant DE needs to be set to lower, to run at the same speed. At the end I was running into a limit, since if the delay is too small, you cannot decrease and decrease the delay during the game, since there would be not much to decrease. Going to Assembly, would be an obvious next move. The ability to very simply set the extra layer is a very nice feature. The layer is hardware, so it does not slow the game down at all, and you can overlay semi static information like the score. Sprites: There are so many sprites on the X16, this game is not utilizing them at all, so there is alot of room for improvement for the visuals when it comes to that. Most design goals were also marked by the fact that the reason why the game is simple, is that it uses PETSCII, and PRINT. I wanted to keep that look and feel, so only minimal use of graphics, to spice it up only a little.
This game will probably be possihed a while more, since it is fun to do, but that will not be part of this tutorial.
However feel free to watch the download section / games, to see the next version being worked on.
Check out as well the youtube video below, where I do a (quick-ish) "coding basic on x16" + game + code walk-through:
Have fun, and thanks for reading!
Especially thanks to all who helped me with my questions, when creating this short (medium sized ;)) tutorial.