Yoruk Posted November 4, 2023 Share Posted November 4, 2023 (edited) 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 November 4, 2023 by Yoruk typo Quote Link to comment Share on other sites More sharing options...
carlsson Posted November 4, 2023 Share Posted November 4, 2023 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. 1 Quote Link to comment Share on other sites More sharing options...
Yoruk Posted November 5, 2023 Author Share Posted November 5, 2023 Thanks for your feedback. I'll try to dig around this, and I'll let you know ! Ok didn't know that it was possible to INC and DEC directly on a given address. I'll save some cycles ^^ Quote Link to comment Share on other sites More sharing options...
carlsson Posted November 5, 2023 Share Posted November 5, 2023 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. Quote Link to comment Share on other sites More sharing options...
+OLD CS1 Posted November 5, 2023 Share Posted November 5, 2023 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. Quote Link to comment Share on other sites More sharing options...
carlsson Posted November 5, 2023 Share Posted November 5, 2023 (edited) 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 November 5, 2023 by carlsson Quote Link to comment Share on other sites More sharing options...
+OLD CS1 Posted November 5, 2023 Share Posted November 5, 2023 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.) Quote Link to comment Share on other sites More sharing options...
carlsson Posted November 5, 2023 Share Posted November 5, 2023 Here is a very comprehensive cross-reference of multiple sources. You can switch between memory map or ROM disassembly at the top. https://www.pagetable.com/c64ref/c64mem/ 1 Quote Link to comment Share on other sites More sharing options...
Yoruk Posted November 6, 2023 Author Share Posted November 6, 2023 (edited) 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 November 6, 2023 by Yoruk typo Quote Link to comment Share on other sites More sharing options...
carlsson Posted November 6, 2023 Share Posted November 6, 2023 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. Quote Link to comment Share on other sites More sharing options...
Yoruk Posted November 7, 2023 Author Share Posted November 7, 2023 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 : 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 !!  4 Quote Link to comment Share on other sites More sharing options...
+SpiceWare Posted November 7, 2023 Share Posted November 7, 2023 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. Â Â I did a search on PETSCII in case there was a directive to change that, but nothing was found. Â Quote Link to comment Share on other sites More sharing options...
carlsson Posted November 7, 2023 Share Posted November 7, 2023 Ok, I'm unsure what the difference is. Graphics symbols definitely, perhaps that lower case comes before upper case? Quote Link to comment Share on other sites More sharing options...
carlsson Posted November 7, 2023 Share Posted November 7, 2023 Btw, collision detection tends to be a headache on almost every system, the C64 included, so steel yourself for that. Quote Link to comment Share on other sites More sharing options...
+SpiceWare Posted November 7, 2023 Share Posted November 7, 2023 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: Â Â they then used the undefined $60-$7B range for semigraphic characters: Â Â Â 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. Quote Link to comment Share on other sites More sharing options...
+OLD CS1 Posted November 7, 2023 Share Posted November 7, 2023 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. Quote Link to comment Share on other sites More sharing options...
+SpiceWare Posted November 7, 2023 Share Posted November 7, 2023 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:   1 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
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.