Jump to content
IGNORED

C64 assembly - simple loop


Yoruk

Recommended Posts

Hello there,

 

I'm trying to learn the 6502 assembly language by writing a very simple shoot game for the C64. Nothing complicated : a couple of moving sprites, a player, a weapon.😄

 

For now, I'm looking for a way to move a sprite back and forth horizontally on the screen.

 

$d000 is the X coordinate of my sprite. So I want this register to move from 0 to $FF, then back from $FF to 0. But not in a simple loop, so I tried something like this inside my main game loop :


 

"ennemyState" is a register. set it to 0


Main game loop:


check if "ennemyState" is > 1 or not

if > 1, increment $d000
if < 1, decrement $d000


if $d000 ==0 : 
      check ennemyState ==0, set it to 2. Else, set it to 0 instead. (to swap the increment)


... do other things


jump to main game loop.

 

And, in assembly (I removed some additional code for clarity) :

 

incrementEnnemi = $0003   ; dummy memory address 


    jsr $e544  ; clear screen routine 

    lda #$03  ;  set color
    
    sta $d020 ; border color
    lda #$00  ;  black color 
    sta $d021 ; main central screen
    
    lda #$80   
    sta $07f8 ; adress pointer sprite 1

   lda #$03
   sta $d027 ; color sprite 1
   
    lda #$80    ; 128 to start
    sta $d000   ; x position  sprite 1
     
    lda #$80
    sta $d001   ; y position  sprite 1
    
    lda #%0000001   ; show sprite 1
    sta $d015

            
    

      
    lda #$010
    sta incrementEnnemi
        
       
        
mainLoop:    ; main loop
       
    jsr loopNop ;wait a bit
              
    lda #$02    ; check if ennemy must go left to right or right to left
    cmp incrementEnnemi       ; http://6502.org/tutorials/compare_instructions.html
    bcc move_ennemy_right  ;comparison 
    
move_ennemy_left:
    ldx $d000  ;move sprite
    dex
    stx $d000      
    jmp checkEnnemyPosition
 
move_ennemy_right:    
    ldx $d000  ;move sprite
    inx
    stx $d000  
    jmp checkEnnemyPosition
    
checkEnnemyPosition:  
    
   ;check if position ==0
   lda $d000  ; if zero set flag
  
   bne ennemyDone ; sortie car pas a zero        Branch on EQual (zero Z flag set)    ; Branch on Not Equal (zero Z flag clear)
            
    ;we are =0, so we must change the register value
    
   lda #$02
   cmp incrementEnnemi
   bcc ennemy_switchNegative
    
ennemy_switchPositive:       
    lda #$010
    sta incrementEnnemi           
    jmp ennemyDone
    
ennemy_switchNegative:  
   lda #$01
   sta incrementEnnemi
   jmp ennemyDone
 
ennemyDone:  

 jmp mainLoop        

 

And it works, but as a beginner I wanted to know if there could be a better way to achieve the same result ?

 

I'm using a full byte to check is I must increment or decrement the sprite position, I'm not sure to know how to do the same thing with a single bit. (maybe an AND ?). Or if there is a very easiest way to do this ?

 

And other difficulty, the sprite position can be not only from 0 to 255, but from 0 to 320. To put the sprite coordinate from 255 to 320, I need to set a bit from $d010 to add 256 to my sprite coordinate. Is there a simple way to achieve this, and not using tons of jump and compare operations ? Maybe I'm not familiar yet with the good instructions...

Thanks in advance !

 

Edited by Yoruk
typo
Link to comment
Share on other sites

I would use the values $FF, $00 and $01 for incrementEnnemi. In that way, you can use the instructions BMI for values > $7F (treated as negative) or BNE for non-zero (in your case $01). It would save you LDA $02, though it is good to know about BCC/BCS as well. Also note that DEC and INC work directly towards memory so you don't need LDX INX STX in your movement routines, only if you happen to store the exact X coordinate in another variable and want to alter it instead of the register directly.

 

When it comes to the rightmost position, I'm afraid that you'll have to fiddle with the bits in $D010 and subtract from $D000/02/04. Many games solved this by putting the score display to the right instead of the top or bottom, and in that way limited sprite positions to 0-255. You get a smaller horizontal playfield, but slightly more vertical plus less work. Check the C64 games library, in particular games from the first 5 years or so to see how common this was.

  • Like 1
Link to comment
Share on other sites

That is exactly what INC and DEC do, since there is no way to increase or decrease the accumulator, only the two index registers. If you have a value in the accumulator, you often need to CLC; ADC #$01 which is somewhat expensive. I can't recall if even the undocumented "illegal" op-codes have one that will alter the accumulator only.

Link to comment
Share on other sites

33 minutes ago, carlsson said:

That is exactly what INC and DEC do, since there is no way to increase or decrease the accumulator, only the two index registers.

Yup.  You can use the zero-page modes of INC and DEC to bum one byte and one cycle per instruction, too.

 

If you want to bum as much as possible, you can just use the X or Y index register as a counter and use BNE, BEQ, CPX, CPY, &c.  A lot of possibilities.

Link to comment
Share on other sites

Yeah. Some say the 6502 only has two index registers. Others say it has 256, as they count all memory locations in zero-page as if those were registers. That is not entirely true, but for counters and temporary storage, it often is useful. Note though that the Kernel is using most of those locations so tread lightly. I understand that Yoruk arrives from the Z80 6809 world which is similar but different.

Edited by carlsson
Link to comment
Share on other sites

2 hours ago, carlsson said:

Note though that the Kernel is using most of those locations so tread lightly.

The "Commodore 64 Programmer's Reference Guide" was my best friend for years for this very reason.  It has a very comprehensive memory map.  I whole-heartedly recommend it.  (There might be better references available on the Internet today, who knows.)

Link to comment
Share on other sites

Thanks guys for your feedback. I'm really learning this from scratch, and every source of information is useful.

 

I'm stuck with something weird. Didn't know if the problem is "solvable" or if I have to change the routine...

 

I'm using this to display characters on the screen :

 

 ldx #$00    
loopText:
     lda msgTitle,x
     ;sec
     ;sbc #$40
     sta $0410,x ; starting position
     inx
     cpx #$06 ; number of chars  
     bne loopText


(...)

msgTitle:
   .byte $01,$0C,$05,$12,$14,$05    ; "ALERTE"

 

It works fine, I just have to deal with the code numbers to convert a string code to the bytes table. I just have to subtract #64 from the key table code, that's ok. (The syntax ".text" is not recognized by DASM but that's ok)

 

But my question is : how to display a character that is BEFORE the "A" in the keyboard table ? For example, I want to show a "!" (code $21) or a space ?. I cannot subtract 64 here...

Any ideas ? Thanks !

Edited by Yoruk
typo
Link to comment
Share on other sites

Essentially, the screen codes follow ASCII/PETSCII with the exception that @ = 0, A = 1, B = 2 and so on until <- = 31. Then follows space at 32, ! = 33 etc up till ? = 63. Next follows Shift-@ = 64, Shift-A = 65 etc, where you normally would expect to find the regular ABC.

 

Instead of SBC #$40, I would use AND #$BF, which means bitwise you clear the 6th bit.

 

For that matter, you can use the syntax .byte "MYTEXT" to put strings into DASM. Those will be stored as PETSCII so if you want to plot them character by character, you need to AND #$BF to get screen codes.

 

Another way to print texts is to use a combination of KERNAL and BASIC routines:
 

ldx text ; row
ldy text+1 ; column
clc
jsr $fff0 ; move cursor to desired position
lda #<(text+2)
ldy #>(text+2)
jsr $ab1e ; print string pointed to at A/Y until zero found

text:
.byte 5,10,"HELLO FRIEND",0

 

Of course if you have very large programs where you shift out the BASIC ROM (done by fiddling with address $0001), it wouldn't work but for most smaller programs you usually have access to ROM routines as you can find in the link above.

Link to comment
Share on other sites

Works ! Many thanks ! I got it.

 

By reading the listing output, my string "ALERTE !" is converted into $41 $4c $45 $52 $54 $45 $20 $21

 

And so I understand why clearing the 6th bit from each byte is giving me back the good character code. (the Hex features of the windows calculator are very helpful 😄)

 

So far so good, here is what my game looks like now :

image.thumb.png.97adfebbff5c0d44fec7ea35c8b030a3.png

Two moving sprites on the top, one moving player at the bottom (driven by the keyboard) and the player can shoot a weapon (no collision detection for now). I used two tables to display the background (one for char codes, one for the color codes). Maybe a bit heavy but it works.

Of course it will not be THE game of the year, but it's a way for me to learn assembly language !!

 

  • Like 4
Link to comment
Share on other sites

18 hours ago, carlsson said:

For that matter, you can use the syntax .byte "MYTEXT" to put strings into DASM. Those will be stored as PETSCII so if you want to plot them character by character, you need to AND #$BF to get screen codes.

 

PETSCII?  DASM's documentation only mentions ASCII when dealing with strings.

 

image.thumb.png.3748dff5d859714deabdf2260633292f.png

 

I did a search on PETSCII in case there was a directive to change that, but nothing was found.

 

Screenshot2023-11-07at9_48_20AM.thumb.png.e34f46448843041e1e72d2b76fb70b40.png

Link to comment
Share on other sites

3 hours ago, carlsson said:

Ok, I'm unsure what the difference is. Graphics symbols definitely, perhaps that lower case comes before upper case?

 

Yep, case is swapped. Apparently ASCII-1963, which did not include lowercase, was Commodore's starting point for PETSCII:

 

image.thumb.png.867b30a073d5fe79bbe6d2f86f4470a5.png

 

they then used the undefined $60-$7B range for semigraphic characters:

 

image.png.5b4ee3a9a90b5525bd3ff6d0dbc40683.png

 

 

There were other differences too, such as which character was delete (backspace). After answering a call my Commodore BBS software would prompt a new caller to hit backspace to determine if the PETSCII <--> ASCII translation tables needed to be used.

Link to comment
Share on other sites

42 minutes ago, SpiceWare said:

After answering a call my Commodore BBS software would prompt a new caller to hit backspace to determine if the PETSCII <--> ASCII translation tables needed to be used.

Color 64 or C-Net?  It think the latter as, IIRC, Color 64 printed a red box and asked if you saw it, Y/N.  Good old days of BBSing.

Link to comment
Share on other sites

1 hour ago, OLD CS1 said:

Color 64 or C-Net?  It think the latter as, IIRC, Color 64 printed a red box and asked if you saw it, Y/N.  Good old days of BBSing.

 

Neither, I wrote my own - VIC-Net.

 

My VIC-20 had a 3K RAM, 24K RAM, and HesWare cartridge port expander (photos of the later 2 in this reply from April 24, 2012) so both RAM cartridges were plugged in at the same time.

 

Due to how the VIC-20 works the 3K is not accessible to BASIC when 8K+ is also plugged in.  I used it to hold machine language routines that would clone screen output to the modem (converting to ASCII if needed) and likewise convert anything received from the modem to keyboard input.  The BBS was written in BASIC.

 

When I started writing it I only had a dataset, so it didn't go online until a few months later,  after I received a 1541 for Xmas.

 

I eventually ported it to my 64, then 128.  These 2 worked hand-in-hand with my custom terminal program to provide real-time music, character redefinition & animation, sprites w/animation and joystick control - all at 300 baud. You can read more about it, and see a video of it in action, in this blog entry:

 

 

  • Like 1
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...