Thursday, 2 February 2017

Tetris in 6502 Assembler - part 14

Hello there and welcome to another post in this epic series.

The end is in sight!  We are now adding the final touches to the game to make it feel better to the player and add some form of customization to the screen colors.  The player interface with the game is very important and can make or break the experience.

So now we introduce some sound fixes, input modifications and graphic options.

As usual, the code can be found on my GitHub page for you to see and play with.

Sound Fixes

The music and sound effects were played at the end of the game play loop, and because of that we were having issues with the timing of the audio. As the time required to run the game loop can change each frame the sound would slow down sometimes. Highly annoying.

So a raster interrupt was introduced to run BEFORE the game loop to avoid any interference. This setup route is called when the game starts:

lda #<irq1
sta $0314
lda #>irq1
sta $0315
lda #$1b
sta $d011
lda #$80
sta $d012
lda #$81
sta $d01a
lda #$7f
sta $dc0d
sta $dd0d

lda $dc0d
lda $dd0d
lda #$ff
sta $d019

lda #$ff
sta $d019
jmp $ea31

I stole this routine from the Kick Assembler example files, and adapted it slightly. Instead of changing the IRQ vector at $FFFE, I use the vector in $0314, as I want kernal IRQs to continue. We are depending on those for keyboard scans, etc.

The above screenshot shows the moment when the various program parts are called. The top white border color change is when the music plays and the bottom color change is when the game code runs. All screen updates are done in the game update code, as you know, while the raster bar is at the bottom of the screen so we will have no flicker in the screen buildup or strange artifacts. This was already the case.

Also, to stop sound effects from overlapping and blocking each other out, a timer was needed to introduce a delay before a new sound can be played. I added a table with the delays for each sound. Playsound can be called when a sound effect or music is required:

// set accumulator before calling this
// it will not play when the sounddelayCounter is not 0
ldx sounddelayCounter
bne !skip+
lda sounddelay,x
sta sounddelayCounter
ldx #0
ldy #0
        jsr music.init

.byte 0

//       0  1  2  3  4  5  6  7  8 9
.byte 10,10,10,35,35,25,25,10,1,1

The sounddelayCounter value is changed in the play loop in play.asm, once per frame. It counts down to zero:

lda sounddelayCounter
beq !skip+
dec sounddelayCounter

Simple as that.

Color Changer

Some people have good taste, and like the color combination I've put in. Some may want to wander from perfection and change the colors. This can now be done by pressing F3 (screen colors) and F5 (character colors)

This is achieved by scanning for these key presses in the main loop.
Changing the screen color is easy:

inc $d020
inc $d021

Changing the character color is a bit more involved: only changing the color RAM is not enough. When you print something to the screen, this is done in the current cursor color and has nothing to do with the screen RAM values. We need to change the cursor color to the correct value and we can to that by printing an ASCII code.

The ASCII table of the C64 is a bit weird, but there is a table in the Programmers' Reference Guide. These ASCII values don't line up AT ALL with the colour values that are in the color RAM, so we need a little table to map it to the color RAM values:

//            0   1 2  3   4   5  6  7   8   9   10  11  12  13  14  15
.byte 144,5,28,159,156,30,31,158,129,149,150,151,152,153,154,155

When the color RAM is changed, the appropriate CHR$ is also printed:

ldx #0
lda charColor
sta $d800,x  // store in color ram
sta $d900,x
sta $da00,x
sta $db00,x
inx  // increment counter
bne !loop-  // continue?
ldx charColor
lda chrColorCodes,x     // get correct chr$ code


Input Fixes

The keyboard input was a bit wonky (as was the joystick) so changes were needed to make it feel as it should. There is nothing worse than a game that does not react in the way you expect.  I moved the keyboard and joystick scan to the main loop and removed all scanning from the game modes. This means that the keyboard and joystick are read ONCE per frame resulting in stable results.

This helps stability of the read input as reading the keyboard buffer can only be done once each frame. Any subsequent query will result in no input detected. Not good.

This is the new keyboard scanning code:

lda keyPressed // get held key code
cmp previousKey // is it a different key than before?
bne !skip+ // yes. dont use key delay

// key is the same. update delay counter
dec keyDelayCounter
beq !skip+
sta inputResult
// restore key delay counter
stx keyDelayCounter
// save key code for next update
sta previousKey

cmp #NOKEY
bne !skip+
lda #NOINPUT // yes
sta inputResult
cmp #DOWN
bne !skip+

// if we press down, the delay is shorter
ldx #4
stx keyDelayCounter
sta inputResult // store input result

This also solves an issue when pressing DOWN. The times between the down movement were changing all the time, now it is stable. Adding a little section to have a shorter delay when pressing DOWN makes a manual drop behave like it should.

To make the time delays between inputs more stable, I also added the same structure and a separate delay counter for the joystick so there is no interference.



The hardest part of game development is finishing the project and while this series has been a long time in the making, I was determined to finish it and THEN move on to other challenges.

For me, this has been a great exploration of the Commodore 64. I hope you enjoyed it as well, and that you now have a better understanding of game development on the C64, with assembly. A lot of things in the C64 we did not even talk about, like sprites, multi-colour, raster interrupts, scrolling, border manipulation etc.  but these are all things that were not necessary for this game.  It would be great to play with that in a future project though! :)

Thanks for reading and showing an interest in this great little machine that gave (and keeps giving) so much pleasure the world over.

Happy coding and see you next time!

Saturday, 28 January 2017

Tetris in 6502 Assembler - part 13

Hi there and welcome to a new part of this series. We are going to look at hiscores!

As usual, the code can be found on github.  Warning! The code can be a bit unstable at the moment because the final bits are tweaked to get rid of the minor annoyances that still remain, and I am tweaking all kinds of stuff at this moment.

Here is a screenshot of the old Tetris code, as seen in VisAss in VICE, and the code being ported to kick assembler.

Lots of code follows! :)  I decided to not add new all code to this post as it is already a very long one. The complete files can be found on GitHub as I said. I have posted the most interesting bits here and it already is quite a lot.

Hiscore Table

As Tetris is a score attack game it is essential that progress is saved. A hiscore table is mandatory for this type of game. It's even better if it is saved and re-loaded as well. So let's get to it.

The table itself is very simple. It is a data area, and each entry has a score length of 3 bytes (as we use decimal mode for scoring, we can have a score up to 999999) and then we have a name length of 7. Why? Seven bytes fit in the window that holds the score display. Here is the table definition:

.const TABLE_ENTRIES = 3


We add an extra entry at the back of the table to ensure we have a buffer when moving data. This might not be required, but at least it guarantees we do not overwrite any code or data we put behind it.  As you might have guessed, this moving entries code might require a rewrite to ensure only valid data is moved. We'll get to that later on, I don't like loose ends. Loose ends in assembly code tend to have consequences!! :)

Initializing the table goes as follows: We go through the data area, and we add a default entry for as often as defined by TABLE_ENTRIES:

sta current_entry
ldx #0
ldy #0
lda default_table_entry,y
sta hiscore_table_start,x
bne !loop-
dec current_entry
bne start_entry_reset

.byte $00,$12,$06
.text "WDWBEST"

.byte 0

Printing the table requires quite a few steps. We are re-using the level select screen when printing the hiscore table, so when that is done, we add the table information over the data in that screen.  We only need to print the scores and the names, as the level select screen already has an empty table in it with an index.

We set the x and y registers to the correct screen location and call this routine:

// save coordinates
stx hiscore_table_position
sty hiscore_table_position+1
jsr PLOT

lda #1
sta current_entry

// reset table data offset
ldy #0

ldx #TABLE_SCORELENGTH // this amount of bytes in score
lda hiscore_table_start, y
pha // store value
lsr // shift right 4 times
adc #$30 // add $30 to get a screen code
jsr PRINT // print it
pla // retrieve original value
and #001111  // get rid of leftmost bits
adc #$30
dex // dec number counter
bne !loop-

lda #$20

// print the name

lda hiscore_table_start,y
bne !loop-
lda current_entry
beq !exit+

inc current_entry

// save memory pointer offset

// go one line down
inc hiscore_table_position

// position cursor
ldx hiscore_table_position
ldy hiscore_table_position+1
jsr PLOT

  // restore memory pointer offset
jmp print_table_entry

// x and y positions for PRINT_HISCORE_TABLE
.byte 0,0

Game Over And New Hiscore?

Looking back at the code from 23 years ago, I quite liked the approach. Instead of using sorting methods, like bubble sort, an entry comparison is done on all the bytes in each table entry.

Each byte is checked against the new score and the difference is logged in a corresponding flag. This flag is set to $ff if the new score was lower, $00 if it was the same and $01 if it was higher. Depending on the byte checked a new hiscore is detected or rejected. If a new score is detected a new entry is inserted at the current location, else the next entry is tested:

// start with no hiscore
lda #0
sta new_hiscore
// reset entries counter
lda #1
sta current_entry
  // reset table data offset
ldy #0

// reset byte compare flags for this entry
lda #0
sta compare_flags
sta compare_flags+1
sta compare_flags+2
//reset the compare flag counter
ldx #0

// check each byte in the new_score with current entry
// set compare flag accordingly
lda new_score,x
cmp hiscore_table_start,y
beq byte_compared // score is same. skip
bpl byte_is_higher // score is higher
dec compare_flags,x // score is lower
jmp byte_compared
inc compare_flags,x
iny // inc data counter
inx // inc byte counter
bne byte_compare_loop

// lets see if new_score was higher than the table entry
// this is fixed to a score length of 3 bytes !!
lda compare_flags
beq !skip+ // this byte was the same, check next
bpl found_hiscore // higher! :)
jmp no_hiscore // lower :(
lda compare_flags+1
beq !skip+ // same, check 3rd byte
bpl found_hiscore   // :)
jmp no_hiscore      // :(
lda compare_flags+2
bmi no_hiscore // last byte is lower. so no new hi
jmp found_hiscore // all 3 digits the same or last higher. new hiscore!
// new_score was lower than this entry.
// check the rest of the entries if not yet all done.
inc current_entry
ldx current_entry
beq all_entries_compared // all entries compared
// so no hiscore at all! exit

// goto start of next table entry
jsr GET_ENTRY_OFFSET    // this uses X register.
ldy entry_offset        // get offset to beginnig of next entry
jmp entry_compare // do the next entry


// hiscore found and its position is in current_entry
ldx current_entry

// add the score to the entry
ldy entry_offset
lda new_score
sta hiscore_table_start,y
lda new_score+1
sta hiscore_table_start+1,y
lda new_score+2
sta hiscore_table_start+2,y

// clear the name. add dots
lda #$2e
sta hiscore_table_start+3,y
bne !loop-

// mark that a new hiscore has been detected at this entry.
lda current_entry
sta new_hiscore

Some data manipulation is required to insert a new entry in the list:

// first we need to move the data down.
// point memory offset to end of table.
ldy #hiscore_table_end - hiscore_table_start
// move data until we're on the wanted offset.
lda hiscore_table_start - ENTRY_LENGTH,y
sta hiscore_table_start,y
cpy entry_offset
bpl !loop-   // keep going

ldy entry_offset
lda #$01    // insert some values
ldx #0
sta hiscore_table_start,y
bne !loop-

When this is all done we print the level select screen and we overwrite the level select text with a happy or sad message to indicate a new hiscore or a loser attempt:

ldy #0
ldx #17
lda hiscoremessage1,y
sta $0400+52,y
lda hiscoremessage2,y
sta $0400+52+40,y
bne !loop-

ldy #0
ldx #17
lda noHiscoremessage1,y
sta $0400+52,y
lda noHiscoremessage2,y
sta $0400+52+40,y
bne !loop-

.text " a new hiscore!! "
.text " enter your name "
.text "   too bad  :(   "
.text "    game over    "

Code to enter the name is also added, in the file controlled_input.asm. It uses the kernal routine at $FFE4 (GETIN) to scan for keyboard entry and only the accepted characters are added to the name buffer:

ldy input_len
beq !exit+ //input
sta input_buffer,y
inc input_len

lda #0

lda #1

The label input_done is jumped to when the user presses the return key. A flag is set so the main program can exit this game mode, or keep waiting for characters. The complete code for the input routes can be found in controlled_input.asm.


Load and Save

Saving and loading data on the C64 is relatively easy.  It is memory based, so you need to know which memory parts need to be written to the open device. The open device is #8 (the disk drive)

First, we need to open the device and set the file name to load:

.label SETLFS = $ffba
.label SETNAM = $ffbd
.label LOAD = $ffd5

// set logical device
lda #0
ldx #8
ldy #0

// get length of file name
lda #filename_end - str_filename
ldx #<str_filename
ldy #>str_filename

.text "filename here"
.byte 0


Then we point to the memory address we want to load the data before calling the load kernal routine:

// set memory destination and load
lda #0
ldx load_destination
ldy load_destination+1
jsr LOAD

.byte 0,0

load_destination must point to hiscore_table_start.

Don't forget to close the file afterwards. We need to do this as we might save several times during one play session.

Saving is done as loading, except we also have to set the end memory address. This is easy, as we labeled it in the source: data_start should point to hiscore_table_start and data_end should point to hiscore_table_end.

// set pointers to the memory block to save
lda #<data_start
sta startsave
lda #>data_start
sta startsave+1

// save up until to the end address
lda #<startsave
ldx #<data_end
ldy #>data_end
jsr SAVE


Adding a hiscore to a game is not trivial. It requires quite a bit of code, certainly more than I expected when I started this addition. But it gives the game an important feature and it is appreciated by everyone who plays the game, we can be sure of that.

Next time we will start adding the final touches to the game. We will be adding more robust sound features, some colour options and tweaks to try and finalize the game.

Remember: to view the full code --this post contains only excerpts-- visit my github page linked at the top of this post.

Happy coding and see you later!!

Wednesday, 28 December 2016

C64 development setup on Linux Mint in 8 steps

Here's another one of those "Let's write this down for future reference" posts. After re-installing Mint 18.1 there was need of setting up some essential development tools.

Here is how to set up an Commodore 64 assembly development on a fresh Mint install in eight easy steps:

1 - Download VICE from the software manager. The executables (X64, X128, etc) will be added to /usr/bin. As this path is already part of your PATH variable you do not need to do anything extra for the executables.

2 - Download the Windows version of VICE, unpack and copy the C64, C128 and DRIVES folders in /home/<username>/.vice/

3 - Download Kick Assembler and unpack it into /home/<username>/Development/tools/KickAssembler.

4 - Add the variable CLASSPATH to /etc/environment, and point it to the FULL path of the kickassembler jar file. Use sudo to edit this file (only root can edit this file) and add


to this file.

5 - Log off and back on.

6 - Install Sublime Text from the software manager, and then package control.

7 - Install the KickAssembler package for Sublime Text.

8 - Load one of the KickAssembler demo files, compile and do a test run.

And there you have it, a fresh development setup. It can be extended with a Git client of course.

One of the problems with working on Linux is that most of the content creation tools are Windows only. Next up is an attempt to go and try to see these can work in Wine. If this turns out not to be the case then maybe it's time to develop some native Linux character, screen and sprite editors!

Thanks for now, have fun coding!

Wednesday, 13 July 2016

Monkey2 development setup

Today's post will be a bit different. It will not be about retro computing per se, but about my development setup and the tools  I use to create retro styled games. You know, the ones you can download from the My Games page.

This post is by request, but also for myself to remind me later on how I did things. :)

I am using Linux to develop but the idea is the same on all major platforms (Linux, Windows, MacOS)

Development Setup

Monkey2 is the new language by Amiga veteran Mark Sibly. It evolved from the Blitz Basic language and from the multi-platform Monkey language.  Monkey2 code is translated to C++ code and then compiled, and it uses SDL2 for graphics, input, audio, etc so it's perfect to create games!!

Monkey2 is in development, but the 1.0 version is very capable and a lot of fun to use!

The things I make with Monkey2 are located on my Github page, in a new area called mutated monkey. Here you can find my code examples, game framework, sublime text configurations, code snippets and more...

Here's an overview of my development system setup:

  • Operating System: Linux Mint 17.3
  • IDE/Editor: Sublime Text 3
  • Builder: Gradle
  • SmartGit: source repository client
  • Monkey2: language translator
  • Java: to run SmartGit and Gradle


I downloaded the archive from this page and unpacked it in my home folder. I am not going to explain how to set this up, but after starting SmartGit you can clone repositories from Github easily.

Sublime Text 3

ST3 can be downloaded from its website, or you can use the software manager. The website has the newer version, almost all of the time. Install it using the downloaded .deb package. 

Monkey2 Language Definition

I've made a Monkey2 language definition file and handy snippets. This file needs to go into your ST3 Packages/User/ folder.  For Linux, this is under ~/.config/sublime-text3/. This path will differ on other OS types.

Simplest thing to do is to make a monkey2 sub-folder in the User folder and put the contents of the repository in there. Now, when you restart ST3, you can select Monkey2 under View/Syntax/


Monkey2 is free to use and you can download it from the Monkey2 web site. A smarter thing to do is to use SmartGit to clone the github repository to your local drive. I cloned the repository to my home directory, and then ran monkey2/scripts/ to build the modules, editor (TED2) and docs. You may have to set the execution bit on the scripts to make them executable.


The build system used is Gradle ( Gradle is free and requires Java. Install Java and Gradle according to the installation guide, and make sure you can start Gradle from the command line, from anywhere on the system (meaning: add the Gradle path to the system path variable)


Now we have all the tools in place, lets get to work. Here's now to setup a ST3 project which will use Gradle to build your application or module.

To achieve this, we need to create 3 files in a project folder:
  • <projectname>.sublime-project
  • build.gradle
  • main source file

sublime-project file

I always work with projects, it makes life so much easier inside ST3. ST3 makes it simple to move between projects and each project can have its own build systems.

To use Gradle for building we use a build system from the Sublime Text Tools menu We create these by adding a build system (or more than one) to a Sublime Text project file.

You create a new project by selecting Add folder to Project from the Project pull down menu. So first create a folder, then use this menu item. Save the project by using Project/Save Project As. I advise you to put the sublime-project file in the project folder.

Now, choose Project/Edit Project and paste the following template in it:

"folders": [
"path": ".",
"file_exclude_patterns": [ ".git*", "*.i", "*.sublime-workspace"],
"folder_exclude_patterns": [".gradle"]
    "build_systems": [
            "name": "Gradle: Build and run TEST debug",
            "cmd": "gradle --daemon build_project_debug",
            "working_dir": "${project_path}",
            "shell": "true"
            "name": "Gradle: Build and run TEST debug CLEAN",
            "cmd": "gradle --daemon build_project_debug_clean",
            "working_dir": "${project_path}",
            "shell": "true"
            "name": "Gradle: Build and run TEST release",
            "cmd": "gradle --daemon build_project_release",
            "working_dir": "${project_path}",
            "shell": "true"



This project template has three build systems. The first will create a debug build. The second will also create a debug build, but it will clean the build folder beforehand. The third build system will create a release build.

As you can see, the command to run for each build system is a shell command; each will start Gradle with the required parameters. First --deamon, which will keep Gradle running in the background for faster builds, and then the task to execute, which is defined in the build.gradle file in the same folder. These build systems do NOT call mx2cc! We will let Gradle do that.

Save the project and the build systems will appear in Sublime Text's Tools/Build System menu. You need to select one and then you can use CTRL-B to build.

build.gradle file

Now we need to create the build.gradle file. This file will hold the definitions of the build tasks we want to execute, mainly the entry source file and the command line to build. Create a new file in the project folder and edit it by pasting this text into it:

def PROJECT_NAME = "asteroids.monkey2"

task build_project_debug << {
        println "Building project DEBUG...\n"

        def command = "/home/wiebo/Monkey2/bin/mx2cc_linux makeapp -run -target=desktop -config=debug $PROJECT_NAME"
        print "Running command: '$command'\n"

        def proc = command.execute()  {line -> println line}
        proc.err.eachLine {line -> println 'ERROR: ' + line}

        def exitValue = proc.exitValue()
        if (exitValue != 0) {
                throw new GradleException ("Build project failed with exit code: $exitValue")

This is an example build.gradle file, showing only one task, but you'll get the idea, you can add more tasks. Make sure you can call them from the build systems definitions in the ST3 project file.

The PROJECT_NAME variable is set to the main file to build, in this case it is asteroids.monkey2. This file needs to be in the same location as the .gradle file, so make sure to put it there.

A task is then defined called build_project_debug. You saw this definition in the ST3 project file.

I used a hard coded path to my monkey folder inside my Linux home directory to call mx2cc_linux. If you're on windows, you need to change this to your own monkey2 bin folder and use mx2cc_windows.exe. For OSX this is mx2cc_macos. The -config=debug option is used to create a debug build.


That's it. Once you create the main monkey2 file in the project folder, and add some code to it you should be able to build. Don't forget the select the correct build system!

Happy coding!