Languages & Coding > Others

Adventures in 6502 Assembler for the Commodore 64

(1/4) > >>

Thought I'd start a seperate thread for talking about this as I've hijacked another thread and gone on a tangent with a couple of posts that had nothing to do with C coding.

I'm a dedicated Mac owner but over the last few years I've been dithering on and off about starting up learning some coding on the old Vic 20 and C64 in assembly language which, while I used to do this back in the day, the lack of tools to do it easy put me off everything but the most basic of routines. The reason why I mention I'm using a Mac is because I've always loved Arthur Jordinsons CBM.prg Studio but it's a VB based development system so there's very little to no chance of it ever being ported to my computer. I've put Parallels on and tried all other sorts of work-arounds, including buying a peice of crap modern laptop that I couldn't stand using as it's so slow, and two second-hand desktop PC's, which took up too much space on my desk and both failed very quickly anyway.

Recently I picked up a pen-stick Win10 machine for around £100 which I've possibly talked about elsewhere, so won't go on about it too much, and found it perfect for what I'm doing here. I just plug it into the HDMI on my second monitor, switch over the mouse/keyboard adapter from the mac into it's USB port and i'm using a Windows machine that can handle Vice, CBM.prg studio and probably a lot more than I actually need. As these things don't work on hard drives - sd card for the win - they're small, take little power and come preinstalled with the OS. You just type in a user name and password on first boot up and off you go.

I could have gone with a raspberry Pi and built another dev system with Vice doing the emulation but I like CBM.prg studio too much so did this so I have no more excuses to not just get stuck in to the actual 6502 assembler coding.

I posted a simple "Hello World" example the other day which is tradition when learning a new language so felt that today it was time to step up a bit and try a bit of simple scrolling text. I kind of diversified and did a bit more than that in the end but what I specifically set out to do was work it out for myself rather than follow a video or example code. This means I'm not going to win coding of the year award for this little effort but it does work, and I did it based on how I used to think it through when trying to code something like this back in  the old days. That's where the "shift key" routine came in because I remember using that a lot as it was quick to add in as a way to exit a machine code program safely.

So, what we have below is code to draw a line of text in the top 40 columns of the screen memory (1024 - 1063) and then cycle through the characters moving them all left and putting what was in the first location (1024) at the end (1063) so it repeats the line over and over. There's nothing complicated like checking for a line over 40 chars (which wouldn't work) and I even had to put a space char at the start of the text because the initial movement of the first char to the last column was bugging out on me.

I had a little background colour cycle routine in there just for error checking to start with as I'm so rusty with coding in assembler I needed some kind of visual input to show where i was in the code and haven't got any kind of debug routines in there, or the monitor/debugger working properly yet. As I say, I added the shift key to exit part as well which gives an idea of how to test for a key combo just because I remembered it. Here we only check if the location 653 is equal to 1 because I know that means one of the shift keys is currently down. From memory, and I could be wrong here, this location would only ever have a maximum value of 7 which indicated the shift, commodore and (maybe) runstop keys were all pressed. That would correspond to the rightmost 3 bits being set in the byte. Or in binary 00000111. There's bound to be much cleaner ways of doing stuff like  that but I prefer to feel my way around as I learn better that way.

Also in there is a very simple delay loop. In basic this scroll routine would scroll as slow as hell and you wouldn't need this but, because it's all compiled machine code, it runs way too fast for the eye to read without getting one hell of a headache. Again, this delay is just feeling my way through code to make it work but does the job and would be a routine I'd keep for an import file to use again in other code. But that's jumping way too far ahead for my current level.

Like last time, I've commented the hell out of this in the hope that it makes it easier to read.

All I would say to someone thinking of picking up and trying out assembler is to take really slow steps at first. That panic moment when you start thinking "What have I let myself in for?" happens a hell of a lot when reading on how to do even the simplest of things but it does stick after a while. Just persevere and you'll find yourself being amazed at even the most simple programs you write because you're doing it the real old-school way. I'm not sure I'll ever get to the level where I could write a full game with it but you never know. I'm already past the level I got to before I gave up on it back in the C64 days and I learned stuff much better back then. If only i had this dev system back in the 80's...

; Scrolling text simple example
; Written by Tony Brice, 26-01-2020

; Create the basic loader at (2048)for SYS (2064)
    BYTE    $0E, $08, $0A, $00, $9E, $20, $28,  $32, $30, $36, $34, $29, $00, $00, $00

; Set program to build from memory location 2064

; Screen memory stat location = 1024
scr$ = $0400

        ; X is the counter and set to 0
start   LDX #$0
        ; Load ACC with text char from the info line X number of chars
cycle   LDA info,X
        ; Compare X to byte 0
        CMP #0
        ; If condition met then jump to scroll label
        BEQ scroll
        ; Otherwise we store the ACC into scr location + X chars
        STA scr$,X
        ; Add one to X
        ; Jump back to cycle label to finish building text line
        JMP cycle

scroll  LDY #$01
        ; Load X with value in default screen location 1024
        LDX scr$
        ; Check for Shift key being pressed
        LDA $28D
        CMP #$01
        ; If condition is true then jump to end of program
        BEQ exit
        ; Load ACC with value in screen location + Y
        LDA scr$,Y
        ; Write this char into the location to the left of it.
        STA scr$,Y-1
        ; Increment Y
        ; Compare Y to 40 (last column on line)
        CPY #$28
        ; We are not there yet so go back to loop
        BNE loop
        ; Write the char in X register to the last location on line
        STX scr$+39
        ; Short delay so text is readable while scrolling
        jsr delay
        ; Some colour cycling
        jsr colour
        ; Go back to scroll the line again
        jmp scroll

        ; This is a simple delay loop to slow down program
delay   LDX #$60
        LDY #$00
        BNE counter
        BNE counter

        ; Leave program
exit    RTS

        ; background colour location:53280
        LDX $D020
        ; Add 1 to the number
        ; Write back to the location, changing the colour
        STX $D020
        ; Check if X is max size of 255
        CPX #$FF
        ; If it is then we jump to reset routine to reset to 0
        ; Note: Not sure we even need to actually do this.
        BEQ reset

        ; We reset the background border colour to 0 (black)
        LDX #$00
        STX $D020

        ; This is the scroll text. Currently limited to one line
        ; Change to say anything but first char transfer should be
        ; Space as it is bugged on moving first char at moment.
info    text 'Scrolling text example. Shift to exit '
        byte 0

< Edited to fix the scroll char bug with a space char being needed at the start of the text. Thanks, TomToad >

Just so the assembler menemonics make more sense here's a breakdown of the ones I've used and what they mean:

LDA - Load the accumulator with a value.
LDX - Load the X register with a value.
LDY - Load the Y register with a value.

When using this assembler (and probably most) you prefix with a # to indicate you want to load that number, otherwise it would load whatever number is stored in that location. Additionally you use $ to indicate you're giving it a hexadecimal number. I've used hex throughout just to make it easier to read but there's nothing wrong with just using all decimal values if it makes it easier for you. Then you would just leave out the $ prefix ie: LDA #255 would load the accumulator with the number 255. LDA 255 would load the accumulator with whatever number is currently stored in the memory location 255.

STA - Store the Accumulator with a value.
STX,STY work the same way.

CMP means compare. You need a branching statement as the next line of code otherwise nothing will happen whatever the result of the compare instruction was.

BEQ means Branch If Equal. So, if the previous statment was CMP #$00 which had followed LDX #$1000 then you're basically saying that if X = 0 then .....
BNE means Branch If Not Equal.

INX - Increment the value in X, which just adds 1 but you could use INX #$02 to add Two I believe. I'll have to check that.
INY - Increment the value in Y like INX.

CPY is used to compare the Y register to a number. Offhand I'm not sure if this is different in operation to CMP or perhaps CMP is used just for the accumulator. I typed  the line and it worked earlier so now i've only just considered this :)

CPX works he same way with the X register.

JMP is Jump to a location. The basic equivalent is Goto but with an assembler we can specify a label so we don't have to work out exact memory locations to move to like the old days. This also means code should be relocatable just by specifying a different start address on an assembly code file.
JSR is Jump to a subroutine. The basic equivalent is Gosub. This jumps to a label and executes the code there returning with an RTS statement to the line after the jump. You shouldn't use a jump line to move to a subroutine that uses RTS because, at root level, the RTS command will exit a program. That's why it's also works to end execution of a program when it's not in a JSR call.

You can find a lot more information about all this stuff all over Google with some simple searches so I've tried not to waffle too much.

CBM.PRG Studio -

6502 Instruction reference -

C64 Documentation -

C64 Resources such as char sets -

CodeBase 64 -

RetroGameDev book (A good purchase) -

C64 Pacman series -

OldSkoolCoder (loads of videos on coding for Vic and C64 using CBM.PRG Studio)


--- Quote --- I even had to put a space char at the start of the text because the initial movement of the first char to the last column was bugging out on me.
--- End quote ---
move the lines
        ; Load X with value in default screen location 1024
        LDX scr$
so they are between the
scroll  LDY #$01
like this

--- Code: ---scroll  LDY #$01
        ; Load X with value in default screen location 1024
        LDX scr$
--- End code ---
This will fix the first character problem and you can remove the space.  You are reading the first character into X with every read of Y, immediately overwriting the X with the second character.

What really got my interest is this line
        STA scr$,Y-1

There is no opcode to index by one less than Y without first decreasing Y.  You probably meant  STA scr$-1,Y instead.  Odd thing is, CBM prg Studio assembles both to the same code (99 FF 03 = STA $03FF,Y), so it seems smart enough to use either method.

Kudos, very interesting read and info - keep it up :)


[0] Message Index

[#] Next page

Go to full version