Jump to content
IGNORED

Player missile graphics - my missiles aren't showing up


Kenshi

Recommended Posts

I've been trying to figure this out all day and I'm ready to ask for help.

 

I'm learning player missile graphics, among other things, and trying to make a program, perhaps eventually a game, that uses them. I've made a simple tiled background, a space ship at the bottom that moves left and right Galaga-style, and I'm working on getting it to shoot missiles but I haven't seen a hint of a missile. I feel like I'm missing something really simple. Rather than show the whole program, I'm going to mention a few things regarding setting up PMG and just show the two small routines for handling missiles as well as the PMG struct I made. I can provide more information as needed.

 

I know PMG in general is working because I can see my ship. My vblank interrupt routine (from which I call the missile routines) is working because I can move the ship around. The missile routines are called from the vblank routine (right now unconditionally). I set SDMCTL to 46 and GRACTL to 3 which should enable both players and missiles. Missile 0 should be the same color as player 0 which is clearly visible.

 

The missile is supposed to be a 2x2 square. The move_missiles routine should move the missile up two lines every vblank unless it's already at one of the top two lines. The fire_p0missile routine puts it just above the center of the ship (which starts at line 80) if the missile is at the top of the screen. I'll have it only get called if the button is pressed, but for right now, it's called unconditionally so it should fire repeatedly.

 

This is assembled in cc65.

 

.struct _PMG
    .org $180
missiles    .res $80
player0     .res $80
player1     .res $80
player2     .res $80
player3     .res $80
.endstruct


.proc   fire_p0missile

    ldy m0vpos      ; only fire if at top of screen
    cpy #1
    bgt endm0

    lda pmg+_PMG::missiles,y    ; erase missile
    and #%11111100
    sta pmg+_PMG::missiles,y
    lda pmg+_PMG::missiles+1,y
    and #%11111100
    sta pmg+_PMG::missiles+1,y

    lda HPOSP0      ; horizontally align missile with center of ship
    add #3
    sta HPOSM0

    ldy #78         ; two pixels above ship
    sty m0vpos

    lda pmg+_PMG::missiles,y    ; draw missile
    ora #%00000011
    sta pmg+_PMG::missiles,y
    lda pmg+_PMG::missiles+1,y
    ora #%00000011
    sta pmg+_PMG::missiles+1,y

endm0:
    rts

.endproc


.proc   move_missiles

    ldy m0vpos      ; don't move if at top of screen
    cpy #1
    ble endm0

    lda pmg+_PMG::missiles,y    ; erase missile
    and #%11111100              ; turn off left two bits
    sta pmg+_PMG::missiles,y
    lda pmg+_PMG::missiles+1,y
    and #%11111100
    sta pmg+_PMG::missiles+1,y

    dey             ; move up two rows
    dey
    sty m0vpos

    lda pmg+_PMG::missiles,y    ; draw missile
    ora #%00000011
    sta pmg+_PMG::missiles,y
    lda pmg+_PMG::missiles+1,y
    ora #%00000011
    sta pmg+_PMG::missiles+1,y

endm0:
    rts

.endproc

 

Oh I forgot to mention, I can't find anything specifying whether the two bits for missile 0 are the least significant bits or the most significant bits. I've seen diagrams that put it on the left and others that put it on the right, and they never make it clear which side is which. All the examples I've seen show how to draw the player, but they only talk briefly about missiles without putting one in a program. I've tried both ways though and neither show up, so it's not my only problem.

Edited by Kenshi
Last paragraph
Link to comment
Share on other sites

2 hours ago, Kenshi said:

 

    lda HPOSP0      ; horizontally align missile with center of ship
    add #3
    sta HPOSM0

You can't read HPOSP0, it's a write-only register. What you're actually reading here is the read-only register that has the same address $D000, M0PF. The value of that register is always <16 and so your missile is off-screen to the left.

 

In general, there are almost no registers in the Atari that are read/write.

 

2 hours ago, Kenshi said:

Oh I forgot to mention, I can't find anything specifying whether the two bits for missile 0 are the least significant bits or the most significant bits. I've seen diagrams that put it on the left and others that put it on the right, and they never make it clear which side is which. All the examples I've seen show how to draw the player, but they only talk briefly about missiles without putting one in a program. I've tried both ways though and neither show up, so it's not my only problem.

Missile 0 is bits 0/1 (LSB), missile 1 is bits 2/3, missile 2 is 4/5, missile 3 is bits 6/7. The MSB of each two-bit pair is the leftmost pixel on screen displayed starting at the missile position.

  • Like 1
Link to comment
Share on other sites

16 minutes ago, phaeron said:

You can't read HPOSP0, it's a write-only register. What you're actually reading here is the read-only register that has the same address $D000, M0PF. The value of that register is always <16 and so your missile is off-screen to the left.

 

In general, there are almost no registers in the Atari that are read/write.

 

Missile 0 is bits 0/1 (LSB), missile 1 is bits 2/3, missile 2 is 4/5, missile 3 is bits 6/7. The MSB of each two-bit pair is the leftmost pixel on screen displayed starting at the missile position.

I can't believe I did that again. I made a p0hpos variable some days ago when trying to get the ship to move and realizing I couldn't just increment or decrement the HPOSP0 register to do it. I just overlooked it about 30 times today trying to get the missile to work. I just set it to lda p0hpos instead of HPOSP0 and it worked exactly as expected. I'm still getting used to this old Atari way of doing things instead of programming an x86 in protected mode.

 

Thanks for the help. There's no telling how long I would have been stuck on this.

Link to comment
Share on other sites

By the way, this is less critical, but I'm wondering about the end of my main routine where I use the esc key to exit the program. I set CH to $ff and then go into a loop until it equals 28. This seems to work as expected on the actual hardware, dumping me to the memory tester when I hit esc, but this atari800 emulator says "the Atari computer has crashed" once I hit escape. Does this seem like an emulator issue?

 

It's bizarre because even the emulator works fine exiting to Basic if I set it to loop while CH equals $ff, but just changing two lines to make it loop while CH does not equal 28 causes it to crash. I'm thinking it's an emulator issue.

 

lda #$ff
sta CH

keypress:
lda CH
cmp #28
bne keypress

 

Link to comment
Share on other sites

23 minutes ago, Kenshi said:

By the way, this is less critical, but I'm wondering about the end of my main routine where I use the esc key to exit the program. I set CH to $ff and then go into a loop until it equals 28. This seems to work as expected on the actual hardware, dumping me to the memory tester when I hit esc, but this atari800 emulator says "the Atari computer has crashed" once I hit escape. Does this seem like an emulator issue?

This probably has to do with a mix of exactly how your program is exiting and whether your expectations of "exiting" are viable.

 

Exiting a program is only really reasonably defined when the program is loaded from DOS. Otherwise, it's completely up to the loader what happens on program exit, and some may not even support it. This includes a DOS-less loader on real hardware like RespeQt, and emulator direct loading. You also need to exit properly, either by doing an RTS with the same stack level as when your program started or JMP (DOSVEC). Anything else means you're probably running undefined code which can lead to a crash in some scenarios.

 

In particular, if you have forgotten to add an RTS or JMP at end of your program, you may be running off the end of your program into uninitialized memory. This memory will be $00 if it hasn't been touched after boot and this will be interpreted by the 6502 as a BRK instruction. Since the default BRK handler just returns, this will keep advancing the PC +2 at a time. One of the goofier ways to 'exit' to BASIC is to just keep crashing a few bytes at a time all the way to $A000, the entry point for BASIC.

 

The other factor is whether the OS boot flag has been set to indicate whether a disk or cartridge boot has completed. Depending the loader, this may or may not be set, which matters if your program was invoked as part of one of the init stages -- which means that the OS will try to resume the boot process afterward. Again, this is mainly an issue for loaders that don't expect the program to exit.

 

Link to comment
Share on other sites

42 minutes ago, phaeron said:

This probably has to do with a mix of exactly how your program is exiting and whether your expectations of "exiting" are viable.

 

Exiting a program is only really reasonably defined when the program is loaded from DOS. Otherwise, it's completely up to the loader what happens on program exit, and some may not even support it. This includes a DOS-less loader on real hardware like RespeQt, and emulator direct loading. You also need to exit properly, either by doing an RTS with the same stack level as when your program started or JMP (DOSVEC). Anything else means you're probably running undefined code which can lead to a crash in some scenarios.

 

In particular, if you have forgotten to add an RTS or JMP at end of your program, you may be running off the end of your program into uninitialized memory. This memory will be $00 if it hasn't been touched after boot and this will be interpreted by the 6502 as a BRK instruction. Since the default BRK handler just returns, this will keep advancing the PC +2 at a time. One of the goofier ways to 'exit' to BASIC is to just keep crashing a few bytes at a time all the way to $A000, the entry point for BASIC.

 

The other factor is whether the OS boot flag has been set to indicate whether a disk or cartridge boot has completed. Depending the loader, this may or may not be set, which matters if your program was invoked as part of one of the init stages -- which means that the OS will try to resume the boot process afterward. Again, this is mainly an issue for loaders that don't expect the program to exit.

 

My program exits pretty cleanly with an rts, and before that it sets the character register back and disables PMG so the screen isn't garbled. At some point, I'll probably make sure the vblank routine gets disabled, but it's not making a difference right now since I disable PMG. I'm a little surprised the actual machine goes to the memory tester and not some form of DOS as I think I read somewhere that Fujinet will emulate a DOS disk if you tell it to run an executable. However, it's inconsequential at the moment. If I want it to go back to DOS, I'll load it from DOS instead of straight from the file. A reboot isn't a big deal.

 

What confused me was the atari800 emulator. Before setting the screen back and doing an rts, I have this:

 

keypress:
    lda CH
    cmp #28     ; esc key
    bne keypress

 

If I hit esc, the emulator tells me the Atari has crashed. If I simply change the last two lines to cmp #$ff and beq instead, it exits cleanly to Basic when I press a key. Why would it matter if I'm checking for any key versus checking for just the esc key? In neither case does it do what the hardware does even though I give it the -xl option (I have an 800XL) and the -nobasic option. I'm still learning certain aspects of the computer as I haven't even had it for a month, but this emulator behavior is especially strange to me.

Link to comment
Share on other sites

9 hours ago, TGB1718 said:

From De Re Atari - M0 = LSB

That's one of the ones I was looking at. It puts M0 on the right which usually means LSB. This one is similar but puts it on the left.

 

https://www.atariarchives.org/mapping/appendix7.php

 

Without either explicitly saying which side is MSB/LSB, it was hard to tell. I know now though.

  • Like 1
Link to comment
Share on other sites

The same book (Mapping the Atari) does specify which bits correspond to which missiles, in the info for the following register.

Although, the info here might not be noticed so readily by someone attempting to use P/M's with DMA.

 

53265          D011          GRAFM

 

     (W) Graphics for all missiles, not used with DMA. GRAFM works
     the same as GRAFP0 above. Each pair of bits represents one
     missile, with the same allocation as in 53260 ($D00C) above.

 

         Bit  7 6  5 4  3 2  1 0
     Missile  -3-  -2-  -1-  -0-

 

     Each bit set will create a vertical line running the entire height of
     the TV screen. Missile graphics shapes may be set separately
     from each other by using the appropriate bit pairs. To mask out
     unwanted players, write zeros to the bits as above.

 

                             TRIG1

 

     (R) Joystick trigger 1 (645). Controller jack two, pin six.

 

  • Like 1
Link to comment
Share on other sites

This order is not very obvious BTW, because the HPOS registers for missiles are inverted in relation to the players. For a 40 pixel wide single color "sprite" you need this:

 

    lda xpos_pmg
    sta $d007           ; HPOSM3
    adc #2
    sta $d006           ; HPOSM2
    adc #2
    sta $d005           ; HPOSM1
    adc #2
    sta $d004           ; HPOSM0
    adc #2
    sta $d000           ; HPOSP0
    adc #8
    sta $d001           ; HPOSP1
    adc #8
    sta $d002           ; HPOSP2
    adc #8
    sta $d003           ; HPOSP3

 

Note how the addresses go down for missiles, and up for players. This is while using the missiles as player minus one (-1, instead of 5), which it basically is in memory.

Edited by ivop
Link to comment
Share on other sites

On 10/15/2022 at 7:25 AM, Kenshi said:

It's bizarre because even the emulator works fine exiting to Basic if I set it to loop while CH equals $ff, but just changing two lines to make it loop while CH does not equal 28 causes it to crash. I'm thinking it's an emulator issue.

I have a small program I used to "test" Missiles, this is what I do when exiting back to DOS, I've used this in Altirra

and it has never crashed.

 

I added the keyboard test you used, and it still works fine.

First I save the Immediate VBLANK vector

0100     *=  $3000
0110     .INCLUDE #D:HEADER.M65
0120 PBASE = $4000
0130     LDA VVBLKI
0140     STA VBSAVE
0150     LDA VVBLKI+1
0160     STA VBSAVE+1

 

and when exiting I do the following.

 

0570     STA 764
0580 LOOP LDA 764 ; TEST for 'ESC'
0590     CMP #28
0600     BNE LOOP
0610     LDA #0
0620     STA GRACTL ; TURN OFF PM 
0630     LDA #34
0640     STA SDMCTL ; TURN OFF PM DMA, STANDARD PLAYFIELD, DMA ENABLED
0650     SEI ; DISABLE INTERUPTS, JUST IN CASE
0660     LDA VBSAVE
0670     STA VVBLKI
0680     LDA VBSAVE+1
0690     STA VVBLKI+1
0700     CLI ; RE-ENABLE INT'S
0710     RTS

 

  • Like 3
Link to comment
Share on other sites

20 hours ago, TGB1718 said:

I have a small program I used to "test" Missiles, this is what I do when exiting back to DOS, I've used this in Altirra

and it has never crashed.

 

I added the keyboard test you used, and it still works fine.

Yeah, there must be something weird going on with Atari800. I was worried there was something critical I didn't understand that it would cause it to be fine when checking for any key but crash when checking for a particular key. No matter; I use the emulator to do a quick check of my code but now that I have a TNFS service running on this machine, it doesn't take much longer to run into the next room, fire up the Atari, and download and test the program on the actual hardware.

 

Everything is working as expected after correcting a few mistakes I'd made. Maybe this will eventually evolve into an actual game. Thanks to everyone for the help.

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