Jump to content
atari2600land

Horizontal movement

Recommended Posts

I can't move the fish sprite left and right. What I want it to do is move in a circle. have it move up and then when it reaches the top, move right. I got it moving up, but I can't get it to move right.

 

flawedcodgod6.asm

Share this post


Link to post
Share on other sites

I cleaned up my code a little. No matter what I do I can not get the fish to move down. WHY? It's especially anger-inducing when the up code is exactly like the down code except the down code doesn't work.

flawedcodgod6a.asm

Share this post


Link to post
Share on other sites

I don't fully follow the code, but I traced through to the first NMI. I see you initialize direction and all that fine, but in mov_cod there's a (small, probably inconsequential) bug where if COD_DIR is not 1-4, you'll start moving up.

 

It looks like in the normal case where it jumps to move_cod_up, move_cod_down, etc, that de is not initialized. You only ld de in the begin_cod_xxx blocks, which are not called during normal movement. It might simply be that up never finishes. Try putting a ld de,COD_DIR after move_cod_up? Otherwise it looks like de is whatever was read from $8002... (which I don't see being initialized...?)

 

 

 

 

 

Share this post


Link to post
Share on other sites

First some suggestions:

 

1. try and split your logic so that you are only writing your sprite table to VRAM during the NM, and any other video writes.

2. Put your logic to update the sprites position in your main code (outside of NMI), use the TIMER routines to control how fast things move

 

Now your actual bug:

You are using a Zero value comparison, and directly calling the next direction setup, with it's own Zero value comparison which ends up already being true i.e. you skip through your different direction set-ups immediately.

 

You should probably split up your logic into two sections:

1. Decide whether it is time to change direction

2. Update the position based on the current direction

 

and each should only be called once per loop.

 

I hope that has not confused you further.  If you want to see some of what I have described above I have made a video series called Lets Make a Retro game on Youtube, that includes sample code for the Coleco.

Share this post


Link to post
Share on other sites

Wow, it's been this long?
Anyway, I have a couple questions again.

#1. Why can't I make the year of the program be 2020? I put in 2020 and the fist doesn't display. If I put in 2019, it does.

#2. I'm trying to make the fish move in a circle. Whenever it gets to the right side of the screen to go down, it moves to the extreme left side of the screen before going down. And when it's time to move left, it goes back to the x position of where it was before it went down. Why does it do that and how do I stop it?

I really want to learn Colecovision programming, but this is so frustrating.

 

flawedcodgod11.asm flawedcodgod11.rom

Share this post


Link to post
Share on other sites
Posted (edited)

I don't know if I can answer all the questions, but I can answer some...

 

Your code asks why you need the "QQQ" at the beginning of your copyright string. This is because that string must start at address $8024 in the ROM. If you count the bytes in your header block, you will see that you only get to $8020, plus the RET makes $8021. So you need three more padding bytes to get to the start address.

 

It looks like you're pretty close! ASM can be confusing, yes, because you the programmer need to keep track of every little detail.

 

I don't fully follow the main loop -- it's not a bad idea in assembly language to comment the intent of /every line/. It helps a lot even when you come back to it in the future. It helps more when other people need to understand it.

 

I accidentally found your 2020 issue while I was trying to debug the main loop. Basically, your NMI vector is at the wrong address too - this must start at $8021, and yours starts much later. As a result, it ends up executing a lot of your header block, and most of that has no side effect. Unfortunately, the "2020" string results in code that does affect something, while " NOT" doesn't. It was just bad luck of the draw there. Generally, we work around that just by putting a JP at the correct place.

 

So the fix for that one is to fix your startup header. Here's an adaptation of mine to fit your program:

 

    org $8000
    
    ; HEADER 
    db 0x55, 0xaa   ; Header: 55AA to skip title page, AA55 to display it 
    dw $7100        ; sprite table pointer for BIOS 
    dw $7000        ; single byte pointer 
    dw 0            ; unknown, unused 
    dw 0            ; unknown, unused 
    dw Start        ; address of start code 

    ; interrupt vectors 
    ei ; RST 0x08 
    reti 
    ei ; RST 0x10 
    reti 
    ei ; RST 0x18 
    reti 
    ei ; RST 0x20 
    reti 
    ei ; RST 0x28 
    reti 
    ei ; RST 0x30 
    reti 
    ei ; RST 0x38 - spinner interrupt 
    reti 
    jp Nmi ; NMI 

    db "I SAWED THE FLAWED COD GOD/CHRIS PRESENTS/ NOT"    

 

That should fix your 2020 issue and the need for the extra 'Q's. Unfortunately you are NOT allowed to move any of those things, they must always sit at those addresses, so the exact size and order of all those header bytes matters.

 

This code appears to do nothing helpful:

 

move_cod:                       ; we are on the NMI
    ld a,COD_DIR                ; COD_DIR equ 1 - immediate load?
    cp 2                        ; as a result, I think this code never matches...
    jr z,move_cod_right_fffff

    cp 3                        ; nor this
    jr z,move_cod_down_fffff    

    cp 4                        ; nor this
    jr z,move_cod_left_fffff    

So the actual movement is the code that happens after that.

 

Most movement functions have a little block like this:

    ld c,COD_DIR    ; COD_DIR equ 1 - load C with '1'
    ld a,4          ; load A with '4'
    ld c,a          ; copy A to C

I think you're trying to record what direction you last moved, but as it stands it doesn't do that, it just loads A and C with a fixed value that doesn't get used. The reason I call these out is that to make your debugging task easier, you might want to just delete these blocks to avoid confusion.

 

I'm not entirely sure I understand the movement code or how it gets to the sprite table. When the fish is moving left/right, you update $7180, and when it's moving up/down you update $7100. I can say that $7100 seems to contain a mirror of your sprite table, and since you aren't changing the X position, when it's being used the X position is locked at $15. (I changed it in the debugger to $80 and proved this). So maybe all you need to do is change the left/right code to change the byte at $7101 instead of at $7180, so that you're always updating the same table.

 

 

 

 

 

 

 

Edited by Tursi
fix formatting - spoiler tag broke the code tag. Twice. I hate web editors.

Share this post


Link to post
Share on other sites

Success!

What I needed to do was change the $7180 to $7101. I'll admit that I have no idea how the Colecovision works, but I do have some experience in coding assembly, the Game Gear uses something similar to this.

As for how I'm moving, what I wanted to do was change the COD_DIR when necessary so that on each loop, it would check to see the value of COD_DIR and then move accordingly.

Eventually I want to add a second sprite, a gray saw (hence the title being "SAWED" and not "SAW".) But first it would be nice to have some sort of idea how to do sound and controller input stuff first. Are there any tutorials for Colecovision programming that stuff?

 

flawedcodgod12.rom flawedcodgod12.asm

Share this post


Link to post
Share on other sites

It has been a while since I got out my Colecovision and played it. It seems as though the emulator I'm using (CoolCV) doesn't add the blue screen with yellow text that asks you to press a number 1-4. So would that show up if I were to try this on a real Colecovision?

Also, how do I do sound? A little fish ditty on the title screen would be cute.

Share this post


Link to post
Share on other sites

That screen needs to be programmed into the software, it's not automatic.

 

Sound can be complicated... I refrained from offering advice on that one because you're using the Coleco BIOS to simplify your program, and I've always gone straight to the hardware. Hopefully someone has some advice on you. ;)

 

That said, if they don't, here's a very very basic low-level introduction on how to make a tone. This is NOT using the BIOS, this is straight to the hardware.

 

So basic information on the sound chip: the ColecoVision sound chip is an SN76489, and you can find the official datasheet here: http://map.grauw.nl/resources/sound/texas_instruments_sn76489an.pdf

 

It has a single interface point on the ColecoVision, you write data to it by using an OUT instruction, and it is accessed at port 0xFF. So all commands are written in assembly like OUT (0xff),value -- I assume that you understand the OUT instruction better than I do. ;)

 

The sound chip has three tone channels and one noise channel. Each tone channel has a "shift rate" which defines the frequency, it has 6 bits of resolution (or a range from 0x000 to 0x3ff), and four bits of volume control (from 0x00 to 0x0F). The noise channel defines two types of noise (white or periodic), and has the same 4-bit volume. I'll break these down below.

 

Tone channels:

 

The way the sound chip generates sound is very primitive. Basically, it has an internal register that counts down from the value you program at a fixed rate based on the hardware clock. When it reaches zero, the output switches. When it switches twice, that's one cycle, and the number of cycles you get per second is the frequency in hertz.

 

But nobody thinks that way. Usually, you know the frequency you want - probably as a musical note. You can find lookup tables online to convert a musical note to hertz, and to convert hertz to the shift counter there is a fixed formula: 111860.8/hertz = code. The code is what you write to the sound chip.

 

Once you have a code, like 0x105, you need to get it to the sound chip. There are two issues to overcome:

- you have to write more than 8 bits, but OUT only does 8 bits at a time

- you have to tell the chip WHICH channel, but you only have one access port

 

The chip looks at the top four bits of the byte that you write to decide where you meant for the data to go. For tones, those command bits are 0x80 for channel 1, 0xA0 for channel 2, and 0xC0 for channel 3. These bits are decided by Texas Instruments and can't be changed.
 

The need to write a total of 10 bits complicates the tone channel a little bit - if you simply set the bits the way it immediately looks obvious it would require extra hardware to understand first or second byte, and they chose not to do that. Instead, they "swizzle" the bits a bit to write in a different order.

 

First write: 0xYZ - Y is the command bits, Z is the /least significant/ 4 bits.

Second write: 0xAB - A is the most significant 2 bits, and B is the middle 4 bits.

 

So, to write that value 0x105 to channel 1, you would do OUT (0xFF),0x85, then OUT (0xFF),0x10 -- the order matters. While there are advanced tricks you can do, just assume for now you always need both writes for a tone.

 

Volume:

 

Tone channels are the only ones that require more than 1 write to update, making volume and noise much simpler. Volume is treated as "attenuation", which means that the output is maximum volume and you specify how much quieter to make it. So 0x0 is maximum volume, and 0xF is silent. The ramp is not linear, but for basic use you don't need to worry about that.

 

The command bits for volume are 0x90 for channel 1, 0xB0 for channel 2, 0xD0 for channel 3, and 0xF0 for noise volume. Just a single OUT is all it takes. So for instance, to set the volume on channel 1 to 3 lower than maximum, OUT (0xFF),0x93

 

Noise:

 

Noise has two modes, periodic and white noise. Periodic is just like it sounds - it's a periodic tick. The tick is generally very rapid, and so the net effect is a buzzing sound. Most of the time you would use white noise, which is the hiss that you might associate with an untuned radio, for instance. This would be used for shooting or explosion sounds, usually.

 

Each noise has three fixed frequencies, and one user-defined one. I am going to recommend not worrying about the user-defined one for now, but basically, it makes the noise take the frequency from tone channel 3. This is often used for special effects like pitch shifted explosions, or buzzy bass lines in music.

 

The noise type is written with the command bits 0xE0, and there are 8 possible types:

- 0x00 - periodic noise high

- 0x01 - periodic noise medium

- 0x02 - periodic noise low

- 0x03 - periodic noise custom

- 0x04 - white noise high

- 0x05 - white noise medium

- 0x06 - white noise low

- 0x07 - white noise custom

 

So, to play white noise medium, you would use OUT (0xFF),0xE6

 

 Putting it together

 

To hear a sound, you need to set the frequency AND the volume, and to stop hearing it, you can just turn off the volume. The sound chip will remember the last settings, so you never need to repeatedly set values you've already set.

 

So let's play 523Hz (which should be a 'C' note). 111860.8/523 = 213.88. Round that up to 214, then convert to hexadecimal: 0x0D6. (To make the "swizzle" obvious, I always write all three nibbles). And we'll play at maximum volume, so that's 0. Our command codes are 0x80 for tone, and 0x90 for volume. So these three OUT statements will turn the sound on:

 

OUT (0xFF),0x86   ; first command for tone, least significant nibble

OUT (0xFF),0x0D   ; second write for tone, the other 6 bits

OUT (0xFF),0x90   ; channel 1 volume to maximum

 

And to mute it, just mute the volume:

 

OUT (0xFF),0x9F   ; channel 1 volume to mute

 

Hope that helps!

 

 

 

 

Share this post


Link to post
Share on other sites

Tried putting the three lines in the game. I got an error message that reads "No valid register."

EDIT: This works:

    ld a,$86
    out ($ff),a
    ld a,$0d
    out ($ff),a
    ld a,$90
    out ($ff),a

  • Like 1

Share this post


Link to post
Share on other sites

I tried this for three hours and I can't make it work. What I want to do is make the MUSIC_TIMER variable increase by 1 each loop and then have it play the corresponding code. But it won't. What am I doing wrong? Why won't it add? I've tried various crap for three hours and none of what I tried works.

little_tune:
    ld (hl),MUSIC_TIMER
    inc hl
    ld a,MUSIC_TIMER
    
    cp 1
    jr z,note2

 

    cp 2
    jr z,note4    
        
    cp 3
    jr z,note2
    
    cp 4
    jr z,note5    
    
    cp 5
    jr z,note3    

    cp 6
    jr z,note2
    
    cp 7
    jr z,silence   
    
    ret

Share this post


Link to post
Share on other sites
17 hours ago, atari2600land said:

I tried this for three hours and I can't make it work. What I want to do is make the MUSIC_TIMER variable increase by 1 each loop and then have it play the corresponding code. But it won't. What am I doing wrong? Why won't it add? I've tried various crap for three hours and none of what I tried works.

 

little_tune:
    ld (hl),MUSIC_TIMER
    inc hl
    ld a,MUSIC_TIMER
    
    cp 1
    jr z,note2

 

    cp 2
    jr z,note4    
        
    cp 3
    jr z,note2
    
    cp 4
    jr z,note5    
    
    cp 5
    jr z,note3    

    cp 6
    jr z,note2
    
    cp 7
    jr z,silence   
    
    ret

 

Try with LD A,(MUSIC_TIMER)

note the brackets

Share this post


Link to post
Share on other sites

I think you are trying to do this:

 

ld    hl,MUSIC_TIMER    ;load hl with the address of the value for MUSIC_TIMER
inc    (hl)        ;increment the MUSIC_TIMER value at location hl
ld    a,(hl)        ;load A with the incremented value at location hl

Share this post


Link to post
Share on other sites
Posted (edited)

Your problem starts here:

 

MUSIC_TIMER: equ 1

 

"equ" means "EQUATE", and literally means "where you see this string, replace it with this number".

 

So later in the code when you do "ld de,MUSIC_TIMER", you are actually writing "ld de,1"

 

You need a memory location to store your data at, currently your code kind of hand-waves the RAM data, using a fixed address in the header (the $7100). This is the same reason that the direction tracking code I called out above didn't work.

 

Memory allocation in assembly is entirely on your shoulders. But the simplest way is to ORG and define it inline.

 

You have three basic equates at the top of your file:

 

NBR_SPRITES: equ 1
COD_DIR: equ 1
MUSIC_TIMER: equ 1

 

NBR_SPRITES is, I assume "number of sprites", and so setting it to 1 makes sense. But the other two are data that you want your program to change. They are variables, and so they need to be located in RAM. Remove COD_DIR and MUSIC_TIMER from here and add this after your "cpu z80" but before the "org $8000":

 

   org $7200
COD_DIR: ds 1
MUSIC_TIMER: ds 1

 

RAM on the ColecoVision runs from $7000 to $73FF - 1KB. You've got a sprite table at $7100, and I don't know if the BIOS uses anything lower. I don't think it does - but I leave that as an exercise for you. The sprite table technically only takes 128 bytes, but I jumped all the way to $7200 anyway. ;)

 

"ds" means "define space" and allocates the number of bytes you ask for. You can't pre-initialize it here because it's not part of your program, it's just a hint for the assembler. I've allocated 1 byte for each and though it's not important to remember, COD_DIR ends up at $7200 and MUSIC_TIMER ends up at $7201

 

Now when you want to access them, you need to use the indirect syntax described to you above by Artrag and Pearsoe, because you're accessing a memory location, similar to how you're using hl and de in the movement code.

 

 

Edited by Tursi
equ means equ, duh....
  • Like 1

Share this post


Link to post
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.

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