May 27, 2020, 02:25:52 PM

Author Topic: Adventures in 6502 Assembler for the Commodore 64  (Read 541 times)

Offline Xerra

  • Hero Member
  • *****
  • Posts: 919
    • Retro Evolved
Adventures in 6502 Assembler for the Commodore 64
« on: January 26, 2020, 10:30:36 PM »
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)
*=$0801
    BYTE    $0E, $08, $0A, $00, $9E, $20, $28,  $32, $30, $36, $34, $29, $00, $00, $00

; Set program to build from memory location 2064
*=$0810

; 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
        INX
        ; 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$
       
loop
        ; 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
        INY
        ; 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
counter
        DEY
        BNE counter
        DEX
        BNE counter
        RTS

        ; Leave program
exit    RTS

colour
        ; background colour location:53280
        LDX $D020
        ; Add 1 to the number
        INX
        ; 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
        RTS

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

        ; 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 >

Offline Xerra

  • Hero Member
  • *****
  • Posts: 919
    • Retro Evolved
Re: Adventures in 6502 Assembler for the Commodore 64
« Reply #1 on: January 26, 2020, 10:53:35 PM »
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.



Offline Xerra

  • Hero Member
  • *****
  • Posts: 919
    • Retro Evolved
Re: Adventures in 6502 Assembler for the Commodore 64
« Reply #2 on: January 26, 2020, 10:54:01 PM »
CBM.PRG Studio - http://www.ajordison.co.uk/download.html

6502 Instruction reference - http://www.obelisk.me.uk/6502/reference.html

C64 Documentation - http://sta.c64.org/cbmdocs.html

C64 Resources such as char sets - http://kofler.dot.at/c64/

CodeBase 64 - https://codebase64.org/doku.php

RetroGameDev book (A good purchase) - https://www.retrogamedev.com/

C64 Pacman series - https://www.youtube.com/watch?v=AqyxvBKcIRg

OldSkoolCoder (loads of videos on coding for Vic and C64 using CBM.PRG Studio)
https://www.youtube.com/channel/UCtWfJHX6gZSOizZDbwmOrdg/videos

Offline TomToad

  • Sr. Member
  • ****
  • Posts: 482
Re: Adventures in 6502 Assembler for the Commodore 64
« Reply #3 on: January 27, 2020, 03:10:35 AM »
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.
move the lines
        ; Load X with value in default screen location 1024
        LDX scr$
so they are between the
scroll  LDY #$01
loop
like this
Code: [Select]
scroll  LDY #$01
        ; Load X with value in default screen location 1024
        LDX scr$
loop
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.

------------------------------------------------
8 rabbits equals 1 rabbyte.

Offline iWasAdam

  • Hero Member
  • *****
  • Posts: 1713
Re: Adventures in 6502 Assembler for the Commodore 64
« Reply #4 on: January 27, 2020, 07:37:51 AM »
Kudos, very interesting read and info - keep it up :)

Offline Steve Elliott

  • Hero Member
  • *****
  • Posts: 2445
Re: Adventures in 6502 Assembler for the Commodore 64
« Reply #5 on: January 27, 2020, 07:49:32 AM »
Quote
Kudos, very interesting read and info - keep it up :)

Agreed.   :)
Windows 10, 64-bit, 16Gb RAM, CPU Intel i5, 3.2 GHz, Nvidia GeForce GTX 1050 (2Gb).
MacOS Catalina, 64-bit, 8Gb RAM, CPU Intel i5, 2.3 Ghz, Intel Iris Plus Graphics 640 1536 MB.
Linux Mint 19.3, 64-bit, 16Gb RAM, CPU Intel i5, 3.2 GHz, Nvidia GeForce GTX 1050 (2Gb).
Raspberry pi 3 and 4.

Offline Xerra

  • Hero Member
  • *****
  • Posts: 919
    • Retro Evolved
Re: Adventures in 6502 Assembler for the Commodore 64
« Reply #6 on: January 27, 2020, 06:06:30 PM »
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.

Now that is interesting. I just put that line in as I would have done it in Basic and didn't think much more on it as it worked. You are correct though.

I just spotted earlier that the assembler has machine directives and someone posted an example source code that would compile on both a 64 and a Vic 20 which is very interesting. I'm mainly thinking I want to do something a bit more ambitious on the Vic 20, because C64 gets most of the love, and it was my first computer. It's probably very possible to do a game that didn't use sprites for both machines from one source file.

Thanks for finding the bug in my scroller. That was driving me nuts as it took so long to create the program anyway :)

I'll try a full screen scroll routine next, i think. Have cursor key controls so it goes in any direction. That should be a suitable challenge.

Offline Xerra

  • Hero Member
  • *****
  • Posts: 919
    • Retro Evolved
Re: Adventures in 6502 Assembler for the Commodore 64
« Reply #7 on: January 27, 2020, 06:15:53 PM »
I've edited the third post to add some useful links in the 3rd post for you guys that jump straight to new messages only.

Offline Pfaber11

  • Hero Member
  • *****
  • Posts: 545
  • Bonjour
    • FABERSGAMES
Re: Adventures in 6502 Assembler for the Commodore 64
« Reply #8 on: February 05, 2020, 12:37:38 PM »
I had a bash at learning Z80 Assembler back in the days of the zx spectrum . Didn't understand what the guy who wrote the book was talking about . carry the flag and load the accumulator etc . And the stack. went right over my head . Kudos to anybody who can understand assembler . The book I had was master z80 machine language I believe that's what it was called anyway. Basic on the spectrum was so slow and pretty much useless . I guess in the last 20 years right up until now basic has grown up. Who would of thought it could be used for professional programming 30 years later.
  I believe my BBC micro was a 6502 processor too. Had a disassembler for it too but not much use to me as I didn't understand what it all meant . Had my zxspectrum in 1983 and moved on to my BBC model B in 1984 , bought it second hand for 250 quid .  Added an OPUS DDOS and a 5 1/4 floppy drive . Loved that machine . After that jump forward a few years and it was the Atari STe followed by an A600 Amiga then the PC as we know it.
Celeron N3060 1.6 ghz duel core  4 Gb ram 32 gig storage  HD400 Graphics 160 gig external hard drive....

 

SimplePortal 2.3.6 © 2008-2014, SimplePortal