Kenshi Posted October 15, 2022 Share Posted October 15, 2022 (edited) 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 October 15, 2022 by Kenshi Last paragraph Quote Link to comment Share on other sites More sharing options...
phaeron Posted October 15, 2022 Share Posted October 15, 2022 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. 1 Quote Link to comment Share on other sites More sharing options...
Kenshi Posted October 15, 2022 Author Share Posted October 15, 2022 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. Quote Link to comment Share on other sites More sharing options...
Kenshi Posted October 15, 2022 Author Share Posted October 15, 2022 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 Quote Link to comment Share on other sites More sharing options...
phaeron Posted October 15, 2022 Share Posted October 15, 2022 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. Quote Link to comment Share on other sites More sharing options...
Kenshi Posted October 15, 2022 Author Share Posted October 15, 2022 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. Quote Link to comment Share on other sites More sharing options...
TGB1718 Posted October 15, 2022 Share Posted October 15, 2022 6 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 From De Re Atari - M0 = LSB 1 Quote Link to comment Share on other sites More sharing options...
Kenshi Posted October 15, 2022 Author Share Posted October 15, 2022 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. 1 Quote Link to comment Share on other sites More sharing options...
+MrFish Posted October 15, 2022 Share Posted October 15, 2022 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. 1 Quote Link to comment Share on other sites More sharing options...
ivop Posted October 15, 2022 Share Posted October 15, 2022 (edited) 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 October 15, 2022 by ivop Quote Link to comment Share on other sites More sharing options...
TGB1718 Posted October 16, 2022 Share Posted October 16, 2022 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 3 Quote Link to comment Share on other sites More sharing options...
Kenshi Posted October 17, 2022 Author Share Posted October 17, 2022 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. 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.